summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AconfigFlags.bp2
-rw-r--r--api/api.go5
-rw-r--r--core/java/android/os/TEST_MAPPING26
-rw-r--r--data/etc/preinstalled-packages-platform.xml2
-rw-r--r--libs/WindowManager/Shell/Android.bp1
-rw-r--r--libs/WindowManager/Shell/AndroidManifest.xml2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt49
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt44
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt25
-rw-r--r--native/android/TEST_MAPPING16
-rw-r--r--packages/SettingsLib/aconfig/settingslib.aconfig10
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java16
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt3
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java2
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml5
-rw-r--r--packages/SystemUI/aconfig/Android.bp1
-rw-r--r--packages/SystemUI/aconfig/accessibility.aconfig10
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt2
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt228
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt70
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeExpandedStateInteractorTest.kt6
-rw-r--r--packages/SystemUI/res/layout/qs_footer_impl.xml5
-rw-r--r--packages/SystemUI/res/layout/volume_dialog_slider.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/BuildTextView.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeExpandedStateInteractor.kt68
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/shared/VolumeDialogLogger.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java72
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt19
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt14
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt17
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModelKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModelKosmos.kt2
-rw-r--r--ravenwood/texts/ravenwood-framework-policies.txt2
-rwxr-xr-xravenwood/tools/hoststubgen/scripts/dump-jar29
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt28
-rwxr-xr-xravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh9
-rwxr-xr-xravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py20
-rw-r--r--services/core/java/com/android/server/power/hint/TEST_MAPPING17
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java20
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java5
-rw-r--r--services/core/java/com/android/server/wm/SnapshotPersistQueue.java7
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java83
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java54
-rw-r--r--services/core/java/com/android/server/wm/WindowSurfacePlacer.java1
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java24
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java36
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java3
71 files changed, 1000 insertions, 515 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index a60ced5835ea..1a0afadd5fe2 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -467,7 +467,7 @@ java_aconfig_library {
"//apex_available:platform",
"com.android.art",
"com.android.art.debug",
- "com.android.btservices",
+ "com.android.bt",
"com.android.mediaprovider",
"com.android.permission",
],
diff --git a/api/api.go b/api/api.go
index cbdb7e81ab86..640773be0f9b 100644
--- a/api/api.go
+++ b/api/api.go
@@ -104,8 +104,9 @@ func (a *CombinedApis) DepsMutator(ctx android.BottomUpMutatorContext) {
}
func (a *CombinedApis) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- ctx.WalkDeps(func(child, parent android.Module) bool {
- if _, ok := android.OtherModuleProvider(ctx, child, java.AndroidLibraryInfoProvider); ok && child.Name() != "framework-res" {
+ ctx.WalkDepsProxy(func(child, parent android.ModuleProxy) bool {
+ javaInfo, ok := android.OtherModuleProvider(ctx, child, java.JavaInfoProvider)
+ if ok && javaInfo.AndroidLibraryDependencyInfo != nil && child.Name() != "framework-res" {
// Stubs of BCP and SSCP libraries should not have any dependencies on apps
// This check ensures that we do not run into circular dependencies when UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT=true
ctx.ModuleErrorf(
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index effe5554aff4..48fa0c8277df 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -112,6 +112,32 @@
{
"file_patterns": ["Bugreport[^/]*\\.java"],
"name": "ShellTests"
+ },
+ {
+ "file_patterns": [
+ "CpuHeadroom[^/]*",
+ "GpuHeadroom[^/]*",
+ "health/SystemHealthManager\\.java"
+ ],
+ "name": "CtsOsTestCases",
+ "options": [
+ {"include-filter": "android.os.health.cts.HeadroomTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
+ },
+ {
+ "file_patterns": [
+ "CpuHeadroom[^/]*",
+ "GpuHeadroom[^/]*",
+ "health/SystemHealthManager\\.java"
+ ],
+ "name": "FrameworksCoreTests",
+ "options": [
+ {"include-filter": "android.os.SystemHealthManagerUnitTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
}
],
"ravenwood-presubmit": [
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
index 3403bbfa2384..3b4014867ef7 100644
--- a/data/etc/preinstalled-packages-platform.xml
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -102,7 +102,7 @@ Changes to the whitelist during system updates can result in installing addition
to pre-existing users, but cannot uninstall pre-existing system packages from pre-existing users.
-->
<config>
- <!-- Bluetooth (com.android.btservices apex) - visible on the sharesheet -->
+ <!-- Bluetooth (com.android.bt apex) - visible on the sharesheet -->
<install-in-user-type package="com.android.bluetooth">
<install-in user-type="SYSTEM" />
<install-in user-type="FULL" />
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 957d1b835ec2..c6e7f854eef6 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -170,6 +170,7 @@ android_library {
"res",
],
static_libs: [
+ "//frameworks/base/packages/SystemUI/aconfig:com_android_systemui_flags_lib",
"//frameworks/libs/systemui:com_android_systemui_shared_flags_lib",
"//frameworks/libs/systemui:iconloader_base",
"//packages/apps/Car/SystemUI/aconfig:com_android_systemui_car_flags_lib",
diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml
index 4f1cd9780f8b..9c15319585b7 100644
--- a/libs/WindowManager/Shell/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/AndroidManifest.xml
@@ -32,7 +32,7 @@
<activity
android:name=".desktopmode.DesktopWallpaperActivity"
android:excludeFromRecents="true"
- android:launchMode="singleInstance"
+ android:launchMode="singleInstancePerTask"
android:showForAllUsers="true"
android:theme="@style/DesktopWallpaperTheme" />
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java
index 82ef00e46e8c..10023c9dba40 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/appzoomout/AppZoomOutController.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.appzoomout;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.systemui.Flags.spatialModelAppPushback;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
@@ -93,7 +94,9 @@ public class AppZoomOutController implements RemoteCallable<AppZoomOutController
mDisplayAreaOrganizer = displayAreaOrganizer;
mMainExecutor = mainExecutor;
- shellInit.addInitCallback(this::onInit, this);
+ if (spatialModelAppPushback()) {
+ shellInit.addInitCallback(this::onInit, this);
+ }
}
private void onInit() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 73d15270c811..f38957e48dbf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -552,7 +552,11 @@ class DesktopTasksController(
)
val wct = WindowContainerTransaction()
exitSplitIfApplicable(wct, taskInfo)
- moveHomeTask(wct, toTop = true)
+ if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
+ moveHomeTask(wct, toTop = true, taskInfo.displayId)
+ } else {
+ moveHomeTask(wct, toTop = true)
+ }
val taskIdToMinimize =
bringDesktopAppsToFrontBeforeShowingNewTask(taskInfo.displayId, wct, taskInfo.taskId)
addMoveToDesktopChanges(wct, taskInfo)
@@ -1309,11 +1313,15 @@ class DesktopTasksController(
): Int? {
logV("bringDesktopAppsToFront, newTaskId=%d", newTaskIdInFront)
// Move home to front, ensures that we go back home when all desktop windows are closed
- moveHomeTask(wct, toTop = true)
+ if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
+ moveHomeTask(wct, toTop = true, displayId)
+ } else {
+ moveHomeTask(wct, toTop = true)
+ }
// Currently, we only handle the desktop on the default display really.
if (
- (displayId == DEFAULT_DISPLAY || Flags.enableBugFixesForSecondaryDisplay()) &&
+ (displayId == DEFAULT_DISPLAY || Flags.enablePerDisplayDesktopWallpaperActivity()) &&
ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY.isTrue()
) {
// Add translucent wallpaper activity to show the wallpaper underneath
@@ -1359,9 +1367,13 @@ class DesktopTasksController(
return taskIdToMinimize
}
- private fun moveHomeTask(wct: WindowContainerTransaction, toTop: Boolean) {
+ private fun moveHomeTask(
+ wct: WindowContainerTransaction,
+ toTop: Boolean,
+ displayId: Int = DEFAULT_DISPLAY,
+ ) {
shellTaskOrganizer
- .getRunningTasks(context.displayId)
+ .getRunningTasks(displayId)
.firstOrNull { task -> task.activityType == ACTIVITY_TYPE_HOME }
?.let { homeTask -> wct.reorder(homeTask.getToken(), /* onTop= */ toTop) }
}
@@ -1370,12 +1382,19 @@ class DesktopTasksController(
logV("addWallpaperActivity")
if (Flags.enableDesktopWallpaperActivityForSystemUser()) {
val intent = Intent(context, DesktopWallpaperActivity::class.java)
+ if (
+ desktopWallpaperActivityTokenProvider.getToken(displayId) == null &&
+ Flags.enablePerDisplayDesktopWallpaperActivity()
+ ) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
+ }
val options =
ActivityOptions.makeBasic().apply {
launchWindowingMode = WINDOWING_MODE_FULLSCREEN
pendingIntentBackgroundActivityStartMode =
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
- if (Flags.enableBugFixesForSecondaryDisplay()) {
+ if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
launchDisplayId = displayId
}
}
@@ -1391,13 +1410,20 @@ class DesktopTasksController(
val userHandle = UserHandle.of(userId)
val userContext = context.createContextAsUser(userHandle, /* flags= */ 0)
val intent = Intent(userContext, DesktopWallpaperActivity::class.java)
+ if (
+ desktopWallpaperActivityTokenProvider.getToken(displayId) == null &&
+ Flags.enablePerDisplayDesktopWallpaperActivity()
+ ) {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
+ }
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId)
val options =
ActivityOptions.makeBasic().apply {
launchWindowingMode = WINDOWING_MODE_FULLSCREEN
pendingIntentBackgroundActivityStartMode =
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
- if (Flags.enableBugFixesForSecondaryDisplay()) {
+ if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
launchDisplayId = displayId
}
}
@@ -1414,8 +1440,8 @@ class DesktopTasksController(
}
}
- private fun removeWallpaperActivity(wct: WindowContainerTransaction) {
- desktopWallpaperActivityTokenProvider.getToken()?.let { token ->
+ private fun removeWallpaperActivity(wct: WindowContainerTransaction, displayId: Int) {
+ desktopWallpaperActivityTokenProvider.getToken(displayId)?.let { token ->
logV("removeWallpaperActivity")
if (Flags.enableDesktopWallpaperActivityForSystemUser()) {
wct.reorder(token, /* onTop= */ false)
@@ -1440,9 +1466,6 @@ class DesktopTasksController(
if (!taskRepository.isOnlyVisibleNonClosingTask(taskId, displayId)) {
return
}
- if (displayId != DEFAULT_DISPLAY) {
- return
- }
} else if (
Flags.enableDesktopWindowingPip() &&
taskRepository.isMinimizedPipPresentInDisplay(displayId) &&
@@ -1457,7 +1480,7 @@ class DesktopTasksController(
desktopModeEnterExitTransitionListener?.onExitDesktopModeTransitionStarted(
FULLSCREEN_ANIMATION_DURATION
)
- removeWallpaperActivity(wct)
+ removeWallpaperActivity(wct, displayId)
}
fun releaseVisualIndicator() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index b9a65fee7d4d..14c8429766cc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -60,7 +60,9 @@ class DesktopTasksTransitionObserver(
shellInit: ShellInit,
) : Transitions.TransitionObserver {
- private var transitionToCloseWallpaper: IBinder? = null
+ data class CloseWallpaperTransition(val transition: IBinder, val displayId: Int)
+
+ private var transitionToCloseWallpaper: CloseWallpaperTransition? = null
/* Pending PiP transition and its associated display id and task id. */
private var pendingPipTransitionAndPipTask: Triple<IBinder, Int, Int>? = null
private var currentProfileId: Int
@@ -248,9 +250,10 @@ class DesktopTasksTransitionObserver(
desktopRepository.getVisibleTaskCount(taskInfo.displayId) == 0 &&
change.mode == TRANSIT_CLOSE &&
taskInfo.windowingMode == WINDOWING_MODE_FREEFORM &&
- desktopWallpaperActivityTokenProvider.getToken() != null
+ desktopWallpaperActivityTokenProvider.getToken(taskInfo.displayId) != null
) {
- transitionToCloseWallpaper = transition
+ transitionToCloseWallpaper =
+ CloseWallpaperTransition(transition, taskInfo.displayId)
currentProfileId = taskInfo.userId
}
}
@@ -265,25 +268,28 @@ class DesktopTasksTransitionObserver(
}
override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {
+ val lastSeenTransitionToCloseWallpaper = transitionToCloseWallpaper
// TODO: b/332682201 Update repository state
- if (transitionToCloseWallpaper == transition) {
+ if (lastSeenTransitionToCloseWallpaper?.transition == transition) {
// TODO: b/362469671 - Handle merging the animation when desktop is also closing.
- desktopWallpaperActivityTokenProvider.getToken()?.let { wallpaperActivityToken ->
- if (Flags.enableDesktopWallpaperActivityForSystemUser()) {
- transitions.startTransition(
- TRANSIT_TO_BACK,
- WindowContainerTransaction()
- .reorder(wallpaperActivityToken, /* onTop= */ false),
- null,
- )
- } else {
- transitions.startTransition(
- TRANSIT_CLOSE,
- WindowContainerTransaction().removeTask(wallpaperActivityToken),
- null,
- )
+ desktopWallpaperActivityTokenProvider
+ .getToken(lastSeenTransitionToCloseWallpaper.displayId)
+ ?.let { wallpaperActivityToken ->
+ if (Flags.enableDesktopWallpaperActivityForSystemUser()) {
+ transitions.startTransition(
+ TRANSIT_TO_BACK,
+ WindowContainerTransaction()
+ .reorder(wallpaperActivityToken, /* onTop= */ false),
+ null,
+ )
+ } else {
+ transitions.startTransition(
+ TRANSIT_CLOSE,
+ WindowContainerTransaction().removeTask(wallpaperActivityToken),
+ null,
+ )
+ }
}
- }
transitionToCloseWallpaper = null
} else if (pendingPipTransitionAndPipTask?.first == transition) {
val desktopRepository = desktopUserRepositories.getProfile(currentProfileId)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 95ed8b4d4511..f549b0563827 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -601,7 +601,32 @@ class DesktopTasksControllerTest : ShellTestCase() {
}
@Test
+ @EnableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
+ Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY,
+ )
+ fun showDesktopApps_onSecondaryDisplay_desktopWallpaperEnabled_perDisplayWallpaperEnabled_shouldShowWallpaper() {
+ val homeTask = setUpHomeTask(SECOND_DISPLAY)
+ val task1 = setUpFreeformTask(SECOND_DISPLAY)
+ val task2 = setUpFreeformTask(SECOND_DISPLAY)
+ markTaskHidden(task1)
+ markTaskHidden(task2)
+
+ controller.showDesktopApps(SECOND_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(4)
+ // Expect order to be from bottom: home, wallpaperIntent, task1, task2
+ wct.assertReorderAt(index = 0, homeTask)
+ wct.assertPendingIntentAt(index = 1, desktopWallpaperIntent)
+ wct.assertReorderAt(index = 2, task1)
+ wct.assertReorderAt(index = 3, task2)
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ @DisableFlags(Flags.FLAG_ENABLE_PER_DISPLAY_DESKTOP_WALLPAPER_ACTIVITY)
fun showDesktopApps_onSecondaryDisplay_desktopWallpaperEnabled_shouldNotShowWallpaper() {
val homeTask = setUpHomeTask(SECOND_DISPLAY)
val task1 = setUpFreeformTask(SECOND_DISPLAY)
diff --git a/native/android/TEST_MAPPING b/native/android/TEST_MAPPING
index 70560a84b88e..e40af595f248 100644
--- a/native/android/TEST_MAPPING
+++ b/native/android/TEST_MAPPING
@@ -24,6 +24,22 @@
{
"name": "NativeThermalUnitTestCases",
"file_patterns": ["thermal.cpp"]
+ },
+ {
+ "file_patterns": ["system_health.cpp"],
+ "name": "NativeSystemHealthUnitTestCases",
+ "options": [
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
+ },
+ {
+ "file_patterns": ["system_health.cpp"],
+ "name": "CtsSystemHealthTestCases",
+ "options": [
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
}
]
}
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index bbe08f254283..d94450b1cabd 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -219,3 +219,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "adopt_primary_group_management_api"
+ namespace: "cross_device_experiences"
+ description: "Adopt Bluetooth LE broadcast primary group management APIs"
+ bug: "381946931"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index 4b7cb36f2753..bf86911ee683 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -134,6 +134,8 @@ public class CsipDeviceManager {
// Do nothing if GroupId has been assigned
if (!isValidGroupId(cachedDevice.getGroupId())) {
final int newGroupId = getBaseGroupId(cachedDevice.getDevice());
+ log("updateCsipDevices: propose new group id " + newGroupId + " for device "
+ + cachedDevice.getDevice());
// Do nothing if there is no GroupId on Bluetooth device
if (isValidGroupId(newGroupId)) {
cachedDevice.setGroupId(newGroupId);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 7c24df9e9019..ff5e9e657213 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -358,6 +358,9 @@ public class LocalBluetoothProfileManager {
&& mProfile instanceof CsipSetCoordinatorProfile;
if (isAshaProfile && (newState == BluetoothProfile.STATE_CONNECTED)) {
+ if (DEBUG) {
+ Log.d(TAG, "onReceive, hearing aid profile connected, check hisyncid");
+ }
// Check if the HiSyncID has being initialized
if (cachedDevice.getHiSyncId() == BluetoothHearingAid.HI_SYNC_ID_INVALID) {
long newHiSyncId = getHearingAidProfile().getHiSyncId(cachedDevice.getDevice());
@@ -375,7 +378,9 @@ public class LocalBluetoothProfileManager {
}
if (isHapClientOrLeAudioProfile && newState == BluetoothProfile.STATE_CONNECTED) {
-
+ if (DEBUG) {
+ Log.d(TAG, "onReceive, hap/lea profile connected, check hearing aid info");
+ }
// Checks if both profiles are connected to the device. Hearing aid info need
// to be retrieved from these profiles separately.
if (cachedDevice.isConnectedLeAudioHearingAidDevice()) {
@@ -389,10 +394,16 @@ public class LocalBluetoothProfileManager {
}
if (isCsipProfile && (newState == BluetoothProfile.STATE_CONNECTED)) {
+ if (DEBUG) {
+ Log.d(TAG, "onReceive, csip profile connected, check group id");
+ }
// Check if the GroupID has being initialized
if (cachedDevice.getGroupId() == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
final Map<Integer, ParcelUuid> groupIdMap = getCsipSetCoordinatorProfile()
.getGroupUuidMapByDevice(cachedDevice.getDevice());
+ if (DEBUG) {
+ Log.d(TAG, "csip group uuid map = " + groupIdMap);
+ }
if (groupIdMap != null) {
for (Map.Entry<Integer, ParcelUuid> entry: groupIdMap.entrySet()) {
if (entry.getValue().equals(BluetoothUuid.CAP)) {
@@ -431,6 +442,9 @@ public class LocalBluetoothProfileManager {
mProfile.getProfileId());
}
if (needDispatchProfileConnectionState) {
+ if (DEBUG) {
+ Log.d(TAG, "needDispatchProfileConnectionState");
+ }
cachedDevice.refresh();
mEventManager.dispatchProfileConnectionStateChanged(cachedDevice, newState,
mProfile.getProfileId());
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
index 9aaefe47fda2..58c7907f77de 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
@@ -37,8 +37,9 @@ class FakeZenModeRepository : ZenModeRepository {
override val globalZenMode: StateFlow<Int>
get() = mutableZenMode.asStateFlow()
- private val mutableModesFlow: MutableStateFlow<List<ZenMode>> =
+ private val mutableModesFlow: MutableStateFlow<List<ZenMode>> by lazy {
MutableStateFlow(listOf(TestModeBuilder.MANUAL_DND))
+ }
override val modes: Flow<List<ZenMode>>
get() = mutableModesFlow.asStateFlow()
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index cb656bdd5d54..9b75a47d92ac 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1869,7 +1869,7 @@ public class SettingsProvider extends ContentProvider {
}
case MUTATION_OPERATION_RESET -> {
return mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_SECURE,
- UserHandle.USER_SYSTEM, callingPackage, mode, tag);
+ owningUserId, callingPackage, mode, tag);
}
}
}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml b/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml
index 0f210e7e5e7b..b40a11469172 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/AndroidManifest.xml
@@ -20,7 +20,10 @@
<uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS"/>
<uses-permission android:name="android.permission.MANAGE_USERS"/>
- <application android:supportsRtl="true">
+ <application
+ android:supportsRtl="true"
+ android:allowBackup="true"
+ android:restoreAnyVersion="true">
<service
android:name="com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService"
android:exported="false"
diff --git a/packages/SystemUI/aconfig/Android.bp b/packages/SystemUI/aconfig/Android.bp
index 1858c80ca901..088ec136f24e 100644
--- a/packages/SystemUI/aconfig/Android.bp
+++ b/packages/SystemUI/aconfig/Android.bp
@@ -23,6 +23,7 @@ package {
default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
default_visibility: [
"//visibility:override",
+ "//frameworks/base/libs/WindowManager/Shell:__subpackages__",
"//frameworks/base/packages/SystemUI:__subpackages__",
"//frameworks/libs/systemui/tracinglib:__subpackages__",
"//frameworks/base/services/accessibility:__subpackages__",
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 72b3d08396c6..fb21be4c3bd1 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -38,6 +38,16 @@ flag {
}
flag {
+ name: "floating_menu_display_cutout_support"
+ namespace: "accessibility"
+ description: "Makes FAB properly react to and avoid DisplayCutouts."
+ bug: "384399408"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "floating_menu_drag_to_hide"
namespace: "accessibility"
description: "Allows users to hide the FAB then use notification to dismiss or bring it back."
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
index 0054a4c899ec..439968590dad 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -168,7 +168,7 @@ private fun StandardLayout(viewModel: BouncerSceneContentViewModel, modifier: Mo
LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Expanded
FoldAware(
- modifier = modifier.padding(start = 32.dp, top = 92.dp, end = 32.dp, bottom = 48.dp),
+ modifier = modifier.padding(top = 92.dp, bottom = 48.dp),
viewModel = viewModel,
aboveFold = {
Column(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
index 8321238b28b1..3d0354a578f7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
@@ -24,6 +24,8 @@ import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.awaitEachGesture
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.detectDragGestures
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.material3.MaterialTheme
@@ -35,6 +37,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.geometry.Offset
@@ -45,6 +48,7 @@ import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.integerResource
+import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.Easings
@@ -212,23 +216,27 @@ fun PatternBouncer(
var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
var offset: Offset by remember { mutableStateOf(Offset.Zero) }
var scale: Float by remember { mutableStateOf(1f) }
+ // This is the size of the drawing area, in dips.
+ val dotDrawingArea =
+ remember(colCount, rowCount) {
+ DpSize(
+ // Because the width also includes spacing to the left and right of the leftmost and
+ // rightmost dots in the grid and because UX mocks specify the width without that
+ // spacing, the actual width needs to be defined slightly bigger than the UX mock
+ // width.
+ width = (262 * colCount / 2).dp,
+ // Because the height also includes spacing above and below the topmost and
+ // bottommost
+ // dots in the grid and because UX mocks specify the height without that spacing,
+ // the
+ // actual height needs to be defined slightly bigger than the UX mock height.
+ height = (262 * rowCount / 2).dp,
+ )
+ }
- Canvas(
- modifier
- .sysuiResTag("bouncer_pattern_root")
- // Because the width also includes spacing to the left and right of the leftmost and
- // rightmost dots in the grid and because UX mocks specify the width without that
- // spacing, the actual width needs to be defined slightly bigger than the UX mock width.
- .width((262 * colCount / 2).dp)
- // Because the height also includes spacing above and below the topmost and bottommost
- // dots in the grid and because UX mocks specify the height without that spacing, the
- // actual height needs to be defined slightly bigger than the UX mock height.
- .height((262 * rowCount / 2).dp)
- // Need to clip to bounds to make sure that the lines don't follow the input pointer
- // when it leaves the bounds of the dot grid.
- .clipToBounds()
- .onGloballyPositioned { coordinates -> gridCoordinates = coordinates }
- .thenIf(isInputEnabled) {
+ Box(
+ modifier =
+ modifier.fillMaxWidth().thenIf(isInputEnabled) {
Modifier.pointerInput(Unit) {
awaitEachGesture {
awaitFirstDown()
@@ -257,105 +265,125 @@ fun PatternBouncer(
inputPosition = change.position
change.position.minus(offset).div(scale).let {
viewModel.onDrag(
- xPx = it.x,
+ xPx =
+ it.x -
+ ((size.width - dotDrawingArea.width.roundToPx()) / 2),
yPx = it.y,
- containerSizePx = size.width,
+ containerSizePx = dotDrawingArea.width.roundToPx(),
)
}
}
}
}
- .motionTestValues {
- entryAnimationCompleted exportAs entryCompleted
- dotAppearFadeInAnimatables.map { it.value.value } exportAs dotAppearFadeIn
- dotAppearMoveUpAnimatables.map { it.value.value } exportAs dotAppearMoveUp
- dotScalingAnimatables.map { it.value.value } exportAs dotScaling
- }
) {
- gridCoordinates?.let { nonNullCoordinates ->
- val containerSize = nonNullCoordinates.size
- if (containerSize.width <= 0 || containerSize.height <= 0) {
- return@let
- }
+ Canvas(
+ Modifier.sysuiResTag("bouncer_pattern_root")
+ .width(dotDrawingArea.width)
+ .height(dotDrawingArea.height)
+ // Need to clip to bounds to make sure that the lines don't follow the input pointer
+ // when it leaves the bounds of the dot grid.
+ .clipToBounds()
+ .align(Alignment.Center)
+ .onGloballyPositioned { coordinates -> gridCoordinates = coordinates }
+ .motionTestValues {
+ entryAnimationCompleted exportAs entryCompleted
+ dotAppearFadeInAnimatables.map { it.value.value } exportAs dotAppearFadeIn
+ dotAppearMoveUpAnimatables.map { it.value.value } exportAs dotAppearMoveUp
+ dotScalingAnimatables.map { it.value.value } exportAs dotScaling
+ }
+ ) {
+ gridCoordinates?.let { nonNullCoordinates ->
+ val containerSize = nonNullCoordinates.size
+ if (containerSize.width <= 0 || containerSize.height <= 0) {
+ return@let
+ }
- val horizontalSpacing = containerSize.width.toFloat() / colCount
- val verticalSpacing = containerSize.height.toFloat() / rowCount
- val spacing = min(horizontalSpacing, verticalSpacing)
- val horizontalOffset =
- offset(
- availableSize = containerSize.width,
- spacingPerDot = spacing,
- dotCount = colCount,
- isCentered = true,
- )
- val verticalOffset =
- offset(
- availableSize = containerSize.height,
- spacingPerDot = spacing,
- dotCount = rowCount,
- isCentered = centerDotsVertically,
- )
- offset = Offset(horizontalOffset, verticalOffset)
- scale = (colCount * spacing) / containerSize.width
+ val horizontalSpacing = containerSize.width.toFloat() / colCount
+ val verticalSpacing = containerSize.height.toFloat() / rowCount
+ val spacing = min(horizontalSpacing, verticalSpacing)
+ val horizontalOffset =
+ offset(
+ availableSize = containerSize.width,
+ spacingPerDot = spacing,
+ dotCount = colCount,
+ isCentered = true,
+ )
+ val verticalOffset =
+ offset(
+ availableSize = containerSize.height,
+ spacingPerDot = spacing,
+ dotCount = rowCount,
+ isCentered = centerDotsVertically,
+ )
+ offset = Offset(horizontalOffset, verticalOffset)
+ scale = (colCount * spacing) / containerSize.width
- if (isAnimationEnabled) {
- // Draw lines between dots.
- selectedDots.forEachIndexed { index, dot ->
- if (index > 0) {
- val previousDot = selectedDots[index - 1]
- val lineFadeOutAnimationProgress =
- lineFadeOutAnimatables[previousDot]!!.value
- val startLerp = 1 - lineFadeOutAnimationProgress
- val from =
- pixelOffset(previousDot, spacing, horizontalOffset, verticalOffset)
- val to = pixelOffset(dot, spacing, horizontalOffset, verticalOffset)
- val lerpedFrom =
- Offset(
- x = from.x + (to.x - from.x) * startLerp,
- y = from.y + (to.y - from.y) * startLerp,
+ if (isAnimationEnabled) {
+ // Draw lines between dots.
+ selectedDots.forEachIndexed { index, dot ->
+ if (index > 0) {
+ val previousDot = selectedDots[index - 1]
+ val lineFadeOutAnimationProgress =
+ lineFadeOutAnimatables[previousDot]!!.value
+ val startLerp = 1 - lineFadeOutAnimationProgress
+ val from =
+ pixelOffset(previousDot, spacing, horizontalOffset, verticalOffset)
+ val to = pixelOffset(dot, spacing, horizontalOffset, verticalOffset)
+ val lerpedFrom =
+ Offset(
+ x = from.x + (to.x - from.x) * startLerp,
+ y = from.y + (to.y - from.y) * startLerp,
+ )
+ drawLine(
+ start = lerpedFrom,
+ end = to,
+ cap = StrokeCap.Round,
+ alpha = lineFadeOutAnimationProgress * lineAlpha(spacing),
+ color = lineColor,
+ strokeWidth = lineStrokeWidth,
)
- drawLine(
- start = lerpedFrom,
- end = to,
- cap = StrokeCap.Round,
- alpha = lineFadeOutAnimationProgress * lineAlpha(spacing),
- color = lineColor,
- strokeWidth = lineStrokeWidth,
- )
+ }
}
- }
- // Draw the line between the most recently-selected dot and the input pointer
- // position.
- inputPosition?.let { lineEnd ->
- currentDot?.let { dot ->
- val from = pixelOffset(dot, spacing, horizontalOffset, verticalOffset)
- val lineLength =
- sqrt((from.y - lineEnd.y).pow(2) + (from.x - lineEnd.x).pow(2))
- drawLine(
- start = from,
- end = lineEnd,
- cap = StrokeCap.Round,
- alpha = lineAlpha(spacing, lineLength),
- color = lineColor,
- strokeWidth = lineStrokeWidth,
- )
+ // Draw the line between the most recently-selected dot and the input pointer
+ // position.
+ inputPosition?.let { lineEnd ->
+ currentDot?.let { dot ->
+ val from = pixelOffset(dot, spacing, horizontalOffset, verticalOffset)
+ val lineLength =
+ sqrt((from.y - lineEnd.y).pow(2) + (from.x - lineEnd.x).pow(2))
+ drawLine(
+ start = from,
+ end = lineEnd,
+ cap = StrokeCap.Round,
+ alpha = lineAlpha(spacing, lineLength),
+ color = lineColor,
+ strokeWidth = lineStrokeWidth,
+ )
+ }
}
}
- }
- // Draw each dot on the grid.
- dots.forEach { dot ->
- val initialOffset = checkNotNull(dotAppearMaxOffsetPixels[dot])
- val appearOffset =
- (1 - checkNotNull(dotAppearMoveUpAnimatables[dot]).value) * initialOffset
- drawCircle(
- center =
- pixelOffset(dot, spacing, horizontalOffset, verticalOffset + appearOffset),
- color =
- dotColor.copy(alpha = checkNotNull(dotAppearFadeInAnimatables[dot]).value),
- radius = dotRadius * checkNotNull(dotScalingAnimatables[dot]).value,
- )
+ // Draw each dot on the grid.
+ dots.forEach { dot ->
+ val initialOffset = checkNotNull(dotAppearMaxOffsetPixels[dot])
+ val appearOffset =
+ (1 - checkNotNull(dotAppearMoveUpAnimatables[dot]).value) * initialOffset
+ drawCircle(
+ center =
+ pixelOffset(
+ dot,
+ spacing,
+ horizontalOffset,
+ verticalOffset + appearOffset,
+ ),
+ color =
+ dotColor.copy(
+ alpha = checkNotNull(dotAppearFadeInAnimatables[dot]).value
+ ),
+ radius = dotRadius * checkNotNull(dotScalingAnimatables[dot]).value,
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index 56a97bb34172..fff6def52803 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import android.graphics.PointF;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
@@ -210,7 +211,7 @@ public class MenuListViewTouchHandlerTest extends SysuiTestCase {
mTouchHandler.onInterceptTouchEvent(mStubListView, stubMoveEvent);
mTouchHandler.onInterceptTouchEvent(mStubListView, stubUpEvent);
- verify(mMenuAnimationController).flingMenuThenSpringToEdge(anyFloat(), anyFloat(),
+ verify(mMenuAnimationController).flingMenuThenSpringToEdge(any(PointF.class), anyFloat(),
anyFloat());
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt
index fd9f5f02ee62..20dfd3e11947 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicyTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.shade.display
import android.view.Display
import android.view.Display.TYPE_EXTERNAL
+import android.view.MotionEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -28,11 +29,16 @@ import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.shade.domain.interactor.notificationElement
+import com.android.systemui.shade.domain.interactor.qsElement
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runTest
import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -50,9 +56,19 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
keyguardRepository,
testScope.backgroundScope,
shadeOnDefaultDisplayWhenLocked = shadeOnDefaultDisplayWhenLocked,
+ shadeInteractor = { kosmos.shadeInteractor },
+ { kosmos.qsElement },
+ { kosmos.notificationElement },
)
}
+ private fun createMotionEventForDisplay(displayId: Int, xCoordinate: Float = 0f): MotionEvent {
+ return mock<MotionEvent> {
+ on { getX() } doReturn xCoordinate
+ on { getDisplayId() } doReturn displayId
+ }
+ }
+
@Test
fun displayId_defaultToDefaultDisplay() {
val underTest = createUnderTest()
@@ -67,7 +83,7 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
val displayId by collectLastValue(underTest.displayId)
displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
- underTest.onStatusBarTouched(2)
+ underTest.onStatusBarTouched(createMotionEventForDisplay(2), STATUS_BAR_WIDTH)
assertThat(displayId).isEqualTo(2)
}
@@ -79,7 +95,7 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
val displayIds by collectValues(underTest.displayId)
assertThat(displayIds).isEqualTo(listOf(Display.DEFAULT_DISPLAY))
- underTest.onStatusBarTouched(2)
+ underTest.onStatusBarTouched(createMotionEventForDisplay(2), STATUS_BAR_WIDTH)
// Never set, as 2 was not a display according to the repository.
assertThat(displayIds).isEqualTo(listOf(Display.DEFAULT_DISPLAY))
@@ -92,7 +108,7 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
val displayId by collectLastValue(underTest.displayId)
displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
- underTest.onStatusBarTouched(2)
+ underTest.onStatusBarTouched(createMotionEventForDisplay(2), STATUS_BAR_WIDTH)
assertThat(displayId).isEqualTo(2)
@@ -108,7 +124,7 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
val displayId by collectLastValue(underTest.displayId)
displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
- underTest.onStatusBarTouched(2)
+ underTest.onStatusBarTouched(createMotionEventForDisplay(2), STATUS_BAR_WIDTH)
assertThat(displayId).isEqualTo(2)
@@ -124,7 +140,7 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
val displayId by collectLastValue(underTest.displayId)
displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
- underTest.onStatusBarTouched(2)
+ underTest.onStatusBarTouched(createMotionEventForDisplay(2), STATUS_BAR_WIDTH)
assertThat(displayId).isEqualTo(2)
@@ -136,4 +152,48 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
assertThat(displayId).isEqualTo(2)
}
+
+ @Test
+ fun onStatusBarTouched_leftSide_intentSetToNotifications() =
+ testScope.runTest {
+ val underTest = createUnderTest(shadeOnDefaultDisplayWhenLocked = true)
+
+ underTest.onStatusBarTouched(
+ createMotionEventForDisplay(2, STATUS_BAR_WIDTH * 0.1f),
+ STATUS_BAR_WIDTH,
+ )
+
+ assertThat(underTest.consumeExpansionIntent()).isEqualTo(kosmos.notificationElement)
+ }
+
+ @Test
+ fun onStatusBarTouched_rightSide_intentSetToQs() =
+ testScope.runTest {
+ val underTest = createUnderTest(shadeOnDefaultDisplayWhenLocked = true)
+
+ underTest.onStatusBarTouched(
+ createMotionEventForDisplay(2, STATUS_BAR_WIDTH * 0.95f),
+ STATUS_BAR_WIDTH,
+ )
+
+ assertThat(underTest.consumeExpansionIntent()).isEqualTo(kosmos.qsElement)
+ }
+
+ @Test
+ fun onStatusBarTouched_nullAfterConsumed() =
+ testScope.runTest {
+ val underTest = createUnderTest(shadeOnDefaultDisplayWhenLocked = true)
+
+ underTest.onStatusBarTouched(
+ createMotionEventForDisplay(2, STATUS_BAR_WIDTH * 0.1f),
+ STATUS_BAR_WIDTH,
+ )
+ assertThat(underTest.consumeExpansionIntent()).isEqualTo(kosmos.notificationElement)
+
+ assertThat(underTest.consumeExpansionIntent()).isNull()
+ }
+
+ companion object {
+ private const val STATUS_BAR_WIDTH = 100
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeExpandedStateInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeExpandedStateInteractorTest.kt
index 58396e7cef82..8aa8a50afcd4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeExpandedStateInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeExpandedStateInteractorTest.kt
@@ -22,8 +22,6 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
-import com.android.systemui.shade.domain.interactor.ShadeExpandedStateInteractorImpl.NotificationElement
-import com.android.systemui.shade.domain.interactor.ShadeExpandedStateInteractorImpl.QSElement
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -52,7 +50,7 @@ class ShadeExpandedStateInteractorTest : SysuiTestCase() {
val element = currentlyExpandedElement.value
- assertThat(element).isInstanceOf(QSElement::class.java)
+ assertThat(element).isInstanceOf(QSShadeElement::class.java)
}
@Test
@@ -62,7 +60,7 @@ class ShadeExpandedStateInteractorTest : SysuiTestCase() {
val element = underTest.currentlyExpandedElement.value
- assertThat(element).isInstanceOf(NotificationElement::class.java)
+ assertThat(element).isInstanceOf(NotificationShadeElement::class.java)
}
@Test
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index b3f32a2863a8..5e8a8a570466 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -34,13 +34,14 @@
android:layout_height="match_parent"
android:layout_gravity="center_vertical">
- <TextView
+ <com.android.systemui.qs.BuildTextView
android:id="@+id/build"
android:layout_width="0dp"
android:layout_height="match_parent"
android:paddingEnd="4dp"
android:layout_weight="1"
- android:clickable="true"
+ android:marqueeRepeatLimit="1"
+ android:clickable="false"
android:ellipsize="marquee"
android:focusable="true"
android:gravity="center_vertical"
diff --git a/packages/SystemUI/res/layout/volume_dialog_slider.xml b/packages/SystemUI/res/layout/volume_dialog_slider.xml
index 6eb7b730e105..c5f468e731f5 100644
--- a/packages/SystemUI/res/layout/volume_dialog_slider.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_slider.xml
@@ -14,8 +14,8 @@
limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="0dp"
- android:layout_height="0dp"
+ android:layout_width="@dimen/volume_dialog_slider_width"
+ android:layout_height="match_parent"
android:maxHeight="@dimen/volume_dialog_slider_height">
<com.google.android.material.slider.Slider
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
index 5b433464c1c6..5cba464fc24c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
@@ -609,15 +609,12 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
final WindowMagnificationController windowMagnificationController =
mWindowMagnificationControllerSupplier.get(displayId);
if (windowMagnificationController != null) {
- boolean isWindowMagnifierActivated = windowMagnificationController.isActivated();
- if (isWindowMagnifierActivated) {
- windowMagnificationController.updateDragHandleResourcesIfNeeded(shown);
- }
+ windowMagnificationController.updateDragHandleResourcesIfNeeded(shown);
if (shown) {
mA11yLogger.logWithPosition(
MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_PANEL_OPENED,
- isWindowMagnifierActivated
+ windowMagnificationController.isActivated()
? ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
: ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 08d3e17c03d7..1587ab16fc38 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -1519,12 +1519,12 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
}
void updateDragHandleResourcesIfNeeded(boolean settingsPanelIsShown) {
+ mSettingsPanelVisibility = settingsPanelIsShown;
+
if (!isActivated()) {
return;
}
- mSettingsPanelVisibility = settingsPanelIsShown;
-
mDragView.setBackground(mContext.getResources().getDrawable(settingsPanelIsShown
? R.drawable.accessibility_window_magnification_drag_handle_background_change_inset
: R.drawable.accessibility_window_magnification_drag_handle_background_inset));
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
index 030d147e21c3..edbede8fa865 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java
@@ -24,6 +24,7 @@ import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
+import android.view.DisplayCutout;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.OvershootInterpolator;
@@ -197,7 +198,7 @@ class MenuAnimationController {
constrainPositionAndUpdate(position, /* writeToPosition = */ true);
}
- void flingMenuThenSpringToEdge(float x, float velocityX, float velocityY) {
+ void flingMenuThenSpringToEdge(PointF position, float velocityX, float velocityY) {
final boolean shouldMenuFlingLeft = isOnLeftSide()
? velocityX < ESCAPE_VELOCITY
: velocityX < -ESCAPE_VELOCITY;
@@ -205,9 +206,17 @@ class MenuAnimationController {
final Rect draggableBounds = mMenuView.getMenuDraggableBounds();
final float finalPositionX = shouldMenuFlingLeft
? draggableBounds.left : draggableBounds.right;
-
+ final DisplayCutout displayCutout = mMenuViewAppearance.getDisplayCutout();
+ final float finalPositionY =
+ (displayCutout == null) ? position.y
+ : mMenuViewAppearance.avoidVerticalDisplayCutout(
+ position.y, draggableBounds,
+ shouldMenuFlingLeft
+ ? displayCutout.getBoundingRectLeft()
+ : displayCutout.getBoundingRectRight()
+ );
final float minimumVelocityToReachEdge =
- (finalPositionX - x) * (FLING_FRICTION_SCALAR * DEFAULT_FRICTION);
+ (finalPositionX - position.x) * (FLING_FRICTION_SCALAR * DEFAULT_FRICTION);
final float startXVelocity = shouldMenuFlingLeft
? Math.min(minimumVelocityToReachEdge, velocityX)
@@ -219,11 +228,19 @@ class MenuAnimationController {
createSpringForce(),
finalPositionX);
- flingThenSpringMenuWith(DynamicAnimation.TRANSLATION_Y,
- velocityY,
- FLING_FRICTION_SCALAR,
- createSpringForce(),
- /* finalPosition= */ null);
+ if (com.android.systemui.Flags.floatingMenuDisplayCutoutSupport()) {
+ flingThenSpringMenuWith(DynamicAnimation.TRANSLATION_Y,
+ velocityY,
+ FLING_FRICTION_SCALAR,
+ createSpringForce(),
+ (finalPositionY != position.y) ? finalPositionY : null);
+ } else {
+ flingThenSpringMenuWith(DynamicAnimation.TRANSLATION_Y,
+ velocityY,
+ FLING_FRICTION_SCALAR,
+ createSpringForce(),
+ /* finalPosition= */ null);
+ }
}
private void flingThenSpringMenuWith(DynamicAnimation.ViewProperty property, float velocity,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java
index 9511e3769a8d..aca020d235be 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandler.java
@@ -105,7 +105,8 @@ class MenuListViewTouchHandler implements RecyclerView.OnItemTouchListener {
if (mDragToInteractAnimationController.maybeConsumeUpMotionEvent(motionEvent)
== empty) {
mVelocityTracker.computeCurrentVelocity(VELOCITY_UNIT_SECONDS);
- mMenuAnimationController.flingMenuThenSpringToEdge(endX,
+ mMenuAnimationController.flingMenuThenSpringToEdge(
+ new PointF(endX, mMenuTranslationDown.y + dy),
mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
mMenuAnimationController.fadeOutIfEnabled();
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
index a700cbef2e16..bd3dfe049587 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
@@ -28,12 +28,14 @@ import android.graphics.Insets;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.view.DisplayCutout;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
import androidx.annotation.DimenRes;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.res.R;
import java.lang.annotation.Retention;
@@ -291,7 +293,7 @@ class MenuViewAppearance {
final WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
final WindowInsets windowInsets = windowMetrics.getWindowInsets();
final Insets insets = windowInsets.getInsetsIgnoringVisibility(
- WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
+ WindowInsets.Type.systemBars());
final Rect bounds = new Rect(windowMetrics.getBounds());
bounds.left += insets.left;
@@ -302,6 +304,37 @@ class MenuViewAppearance {
return bounds;
}
+ DisplayCutout getDisplayCutout() {
+ return mWindowManager.getCurrentWindowMetrics().getWindowInsets().getDisplayCutout();
+ }
+
+ float avoidVerticalDisplayCutout(float y, Rect bounds, Rect cutout) {
+ int menuHeight = calculateActualMenuHeight();
+ return avoidVerticalDisplayCutout(y, menuHeight, bounds, cutout);
+ }
+
+ @VisibleForTesting
+ public static float avoidVerticalDisplayCutout(
+ float y, float menuHeight, Rect bounds, Rect cutout) {
+ if (cutout.top > y + menuHeight || cutout.bottom < y) {
+ return y;
+ }
+
+ boolean topAvailable = cutout.top - bounds.top >= menuHeight;
+ boolean bottomAvailable = bounds.bottom - cutout.bottom >= menuHeight;
+ boolean topOrBottom;
+ if (!topAvailable && !bottomAvailable) {
+ return y;
+ } else if (topAvailable && !bottomAvailable) {
+ topOrBottom = true;
+ } else if (!topAvailable && bottomAvailable) {
+ topOrBottom = false;
+ } else {
+ topOrBottom = y + menuHeight * 0.5f < cutout.centerY();
+ }
+ return (topOrBottom) ? cutout.top - menuHeight : cutout.bottom;
+ }
+
boolean isMenuOnLeftSide() {
return mPercentagePosition.getPercentageX() < 0.5f;
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
index 95b9b17b3fd1..81095220b4a6 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -477,7 +477,7 @@ class MenuViewLayer extends FrameLayout implements
return;
}
mMenuAnimationController.flingMenuThenSpringToEdge(
- mMenuView.getMenuPosition().x, 100f, 0f);
+ mMenuView.getMenuPosition(), 100f, 0f);
Intent intent = getIntentForEditScreen();
PackageManager packageManager = getContext().getPackageManager();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/BuildTextView.kt b/packages/SystemUI/src/com/android/systemui/qs/BuildTextView.kt
new file mode 100644
index 000000000000..1f864e9d7b8b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/BuildTextView.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.accessibility.AccessibilityNodeInfo
+import com.android.systemui.res.R
+import com.android.systemui.util.DelayableMarqueeTextView
+
+class BuildTextView
+@JvmOverloads
+constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0,
+) : DelayableMarqueeTextView(context, attrs, defStyleAttr, defStyleRes) {
+
+ override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) {
+ super.onInitializeAccessibilityNodeInfo(info)
+ // Clear selected state so it's not announced by accessibility, but we can still marquee.
+ info?.isSelected = false
+ info?.addAction(
+ AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.id,
+ resources.getString(R.string.copy_to_clipboard_a11y_action),
+ )
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
index 17b5e5b584b4..d53f9f7ec595 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/ShadeDisplayPolicy.kt
@@ -16,6 +16,7 @@
package com.android.systemui.shade.display
+import com.android.systemui.shade.domain.interactor.ShadeExpandedStateInteractor.ShadeElement
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoSet
@@ -33,11 +34,33 @@ interface ShadeDisplayPolicy {
val displayId: StateFlow<Int>
}
+/** Return the latest element the user intended to expand in the shade (notifications or QS). */
+interface ShadeExpansionIntent {
+ /**
+ * Returns the latest element the user intended to expand in the shade (notifications or QS).
+ *
+ * When the shade moves to a different display (e.g., due to a touch on the status bar of an
+ * external display), it's first collapsed and then re-expanded on the target display.
+ *
+ * If the user was trying to open a specific element (QS or notifications) when the shade was on
+ * the original display, that intention might be lost during the collapse/re-expand transition.
+ * This is used to preserve the user's intention, ensuring the correct element is expanded on
+ * the target display.
+ *
+ * Note that the expansion intent is kept for a very short amount of time (ideally, just a bit
+ * above the time it takes for the shade to collapse)
+ */
+ fun consumeExpansionIntent(): ShadeElement?
+}
+
@Module
interface ShadeDisplayPolicyModule {
@Binds fun provideDefaultPolicy(impl: StatusBarTouchShadeDisplayPolicy): ShadeDisplayPolicy
+ @Binds
+ fun provideShadeExpansionIntent(impl: StatusBarTouchShadeDisplayPolicy): ShadeExpansionIntent
+
@IntoSet
@Binds
fun provideDefaultDisplayPolicyToSet(impl: DefaultDisplayShadePolicy): ShadeDisplayPolicy
diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt
index 30b086f03d66..91020aa7bdb0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt
@@ -18,16 +18,25 @@ package com.android.systemui.shade.display
import android.util.Log
import android.view.Display
+import android.view.MotionEvent
import com.android.app.tracing.coroutines.launchTraced
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DisplayRepository
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.shade.ShadeOnDefaultDisplayWhenLocked
+import com.android.systemui.shade.domain.interactor.NotificationShadeElement
+import com.android.systemui.shade.domain.interactor.QSShadeElement
+import com.android.systemui.shade.domain.interactor.ShadeExpandedStateInteractor.ShadeElement
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
+import dagger.Lazy
+import java.util.concurrent.atomic.AtomicReference
import javax.inject.Inject
+import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -49,14 +58,20 @@ class StatusBarTouchShadeDisplayPolicy
constructor(
displayRepository: DisplayRepository,
keyguardRepository: KeyguardRepository,
- @Background val backgroundScope: CoroutineScope,
- @ShadeOnDefaultDisplayWhenLocked val shadeOnDefaultDisplayWhenLocked: Boolean,
-) : ShadeDisplayPolicy {
+ @Background private val backgroundScope: CoroutineScope,
+ @ShadeOnDefaultDisplayWhenLocked private val shadeOnDefaultDisplayWhenLocked: Boolean,
+ private val shadeInteractor: Lazy<ShadeInteractor>,
+ private val qsShadeElement: Lazy<QSShadeElement>,
+ private val notificationElement: Lazy<NotificationShadeElement>,
+) : ShadeDisplayPolicy, ShadeExpansionIntent {
override val name: String = "status_bar_latest_touch"
private val currentDisplayId = MutableStateFlow(Display.DEFAULT_DISPLAY)
private val availableDisplayIds: StateFlow<Set<Int>> = displayRepository.displayIds
+ private var latestIntent = AtomicReference<ShadeElement?>()
+ private var timeoutJob: Job? = null
+
override val displayId: StateFlow<Int> =
if (shadeOnDefaultDisplayWhenLocked) {
keyguardRepository.isKeyguardShowing
@@ -75,8 +90,29 @@ constructor(
private var removalListener: Job? = null
/** Called when the status bar on the given display is touched. */
- fun onStatusBarTouched(statusBarDisplayId: Int) {
+ fun onStatusBarTouched(event: MotionEvent, statusBarWidth: Int) {
ShadeWindowGoesAround.isUnexpectedlyInLegacyMode()
+ updateShadeDisplayIfNeeded(event)
+ updateExpansionIntent(event, statusBarWidth)
+ }
+
+ override fun consumeExpansionIntent(): ShadeElement? {
+ return latestIntent.getAndSet(null)
+ }
+
+ private fun updateExpansionIntent(event: MotionEvent, statusBarWidth: Int) {
+ val element = classifyStatusBarEvent(event, statusBarWidth)
+ latestIntent.set(element)
+ timeoutJob?.cancel()
+ timeoutJob =
+ backgroundScope.launchTraced("StatusBarTouchDisplayPolicy#intentTimeout") {
+ delay(EXPANSION_INTENT_EXPIRY)
+ latestIntent.set(null)
+ }
+ }
+
+ private fun updateShadeDisplayIfNeeded(event: MotionEvent) {
+ val statusBarDisplayId = event.displayId
if (statusBarDisplayId !in availableDisplayIds.value) {
Log.e(TAG, "Got touch on unknown display $statusBarDisplayId")
return
@@ -90,6 +126,17 @@ constructor(
}
}
+ private fun classifyStatusBarEvent(
+ motionEvent: MotionEvent,
+ statusbarWidth: Int,
+ ): ShadeElement {
+ val xPercentage = motionEvent.x / statusbarWidth
+ val threshold = shadeInteractor.get().getTopEdgeSplitFraction()
+ return if (xPercentage < threshold) {
+ notificationElement.get()
+ } else qsShadeElement.get()
+ }
+
private fun monitorDisplayRemovals(): Job {
return backgroundScope.launchTraced("StatusBarTouchDisplayPolicy#monitorDisplayRemovals") {
currentDisplayId.subscriptionCount
@@ -112,5 +159,6 @@ constructor(
private companion object {
const val TAG = "StatusBarTouchDisplayPolicy"
+ val EXPANSION_INTENT_EXPIRY = 2.seconds
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt
index 691a383cb338..f67d33122063 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractor.kt
@@ -30,6 +30,7 @@ import com.android.systemui.shade.ShadeDisplayChangeLatencyTracker
import com.android.systemui.shade.ShadeTraceLogger.logMoveShadeWindowTo
import com.android.systemui.shade.ShadeTraceLogger.traceReparenting
import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
+import com.android.systemui.shade.display.ShadeExpansionIntent
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import com.android.window.flags.Flags
import java.util.Optional
@@ -49,6 +50,7 @@ constructor(
@Main private val mainThreadContext: CoroutineContext,
private val shadeDisplayChangeLatencyTracker: ShadeDisplayChangeLatencyTracker,
shadeExpandedInteractor: Optional<ShadeExpandedStateInteractor>,
+ private val shadeExpansionIntent: ShadeExpansionIntent,
) : CoreStartable {
private val shadeExpandedInteractor =
@@ -90,10 +92,7 @@ constructor(
withContext(mainThreadContext) {
traceReparenting {
shadeDisplayChangeLatencyTracker.onShadeDisplayChanging(destinationId)
- val expandedElement = shadeExpandedInteractor.currentlyExpandedElement.value
- expandedElement?.collapse(reason = "Shade window move")
- reparentToDisplayId(id = destinationId)
- expandedElement?.expand(reason = "Shade window move")
+ collapseAndExpandShadeIfNeeded { reparentToDisplayId(id = destinationId) }
checkContextDisplayMatchesExpected(destinationId)
}
}
@@ -106,6 +105,18 @@ constructor(
}
}
+ private suspend fun collapseAndExpandShadeIfNeeded(wrapped: () -> Unit) {
+ val previouslyExpandedElement = shadeExpandedInteractor.currentlyExpandedElement.value
+ previouslyExpandedElement?.collapse(reason = COLLAPSE_EXPAND_REASON)
+
+ wrapped()
+
+ // If the user was trying to expand a specific shade element, let's make sure to expand
+ // that one. Otherwise, we can just re-expand the previous expanded element.
+ shadeExpansionIntent.consumeExpansionIntent()?.expand(COLLAPSE_EXPAND_REASON)
+ ?: previouslyExpandedElement?.expand(reason = COLLAPSE_EXPAND_REASON)
+ }
+
private fun checkContextDisplayMatchesExpected(destinationId: Int) {
if (shadeContext.displayId != destinationId) {
Log.wtf(
@@ -125,5 +136,6 @@ constructor(
private companion object {
const val TAG = "ShadeDisplaysInteractor"
+ const val COLLAPSE_EXPAND_REASON = "Shade window move"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeExpandedStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeExpandedStateInteractor.kt
index dd3abeec5a72..c1ea71e8e757 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeExpandedStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeExpandedStateInteractor.kt
@@ -24,6 +24,7 @@ import com.android.systemui.shade.domain.interactor.ShadeExpandedStateInteractor
import com.android.systemui.shade.shared.flag.DualShade
import com.android.systemui.util.kotlin.Utils.Companion.combineState
import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
@@ -47,7 +48,7 @@ interface ShadeExpandedStateInteractor {
val currentlyExpandedElement: StateFlow<ShadeElement?>
/** An element from the shade window that can be expanded or collapsed. */
- abstract class ShadeElement {
+ sealed class ShadeElement {
/** Expands the shade element, returning when the expansion is done */
abstract suspend fun expand(reason: String)
@@ -60,13 +61,12 @@ interface ShadeExpandedStateInteractor {
class ShadeExpandedStateInteractorImpl
@Inject
constructor(
- private val shadeInteractor: ShadeInteractor,
+ shadeInteractor: ShadeInteractor,
@Background private val bgScope: CoroutineScope,
+ private val notificationElement: NotificationShadeElement,
+ private val qsElement: QSShadeElement,
) : ShadeExpandedStateInteractor {
- private val notificationElement = NotificationElement()
- private val qsElement = QSElement()
-
override val currentlyExpandedElement: StateFlow<ShadeElement?> =
if (SceneContainerFlag.isEnabled) {
combineState(
@@ -84,35 +84,47 @@ constructor(
} else {
MutableStateFlow(null)
}
+}
- inner class NotificationElement : ShadeElement() {
- override suspend fun expand(reason: String) {
- shadeInteractor.expandNotificationsShade(reason)
- shadeInteractor.shadeExpansion.waitUntil(1f)
- }
+private suspend fun StateFlow<Float>.waitUntil(f: Float, coroutineContext: CoroutineContext) {
+ // it's important to not do this in the main thread otherwise it will block any rendering.
+ withContext(coroutineContext) {
+ withTimeout(1.seconds) { traceWaitForExpansion(expansion = f) { first { it == f } } }
+ }
+}
- override suspend fun collapse(reason: String) {
- shadeInteractor.collapseNotificationsShade(reason)
- shadeInteractor.shadeExpansion.waitUntil(0f)
- }
+@SysUISingleton
+class NotificationShadeElement
+@Inject
+constructor(
+ private val shadeInteractor: ShadeInteractor,
+ @Background private val bgContext: CoroutineContext,
+) : ShadeElement() {
+ override suspend fun expand(reason: String) {
+ shadeInteractor.expandNotificationsShade(reason)
+ shadeInteractor.shadeExpansion.waitUntil(1f, bgContext)
}
- inner class QSElement : ShadeElement() {
- override suspend fun expand(reason: String) {
- shadeInteractor.expandQuickSettingsShade(reason)
- shadeInteractor.qsExpansion.waitUntil(1f)
- }
+ override suspend fun collapse(reason: String) {
+ shadeInteractor.collapseNotificationsShade(reason)
+ shadeInteractor.shadeExpansion.waitUntil(0f, bgContext)
+ }
+}
- override suspend fun collapse(reason: String) {
- shadeInteractor.collapseQuickSettingsShade(reason)
- shadeInteractor.qsExpansion.waitUntil(0f)
- }
+@SysUISingleton
+class QSShadeElement
+@Inject
+constructor(
+ private val shadeInteractor: ShadeInteractor,
+ @Background private val bgContext: CoroutineContext,
+) : ShadeElement() {
+ override suspend fun expand(reason: String) {
+ shadeInteractor.expandQuickSettingsShade(reason)
+ shadeInteractor.qsExpansion.waitUntil(1f, bgContext)
}
- private suspend fun StateFlow<Float>.waitUntil(f: Float) {
- // it's important to not do this in the main thread otherwise it will block any rendering.
- withContext(bgScope.coroutineContext) {
- withTimeout(1.seconds) { traceWaitForExpansion(expansion = f) { first { it == f } } }
- }
+ override suspend fun collapse(reason: String) {
+ shadeInteractor.collapseQuickSettingsShade(reason)
+ shadeInteractor.qsExpansion.waitUntil(0f, bgContext)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index aa1308931f99..3f44f7bdef90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -234,7 +234,7 @@ private constructor(
)
}
if (ShadeWindowGoesAround.isEnabled && event.action == MotionEvent.ACTION_DOWN) {
- lazyStatusBarShadeDisplayPolicy.get().onStatusBarTouched(context.displayId)
+ lazyStatusBarShadeDisplayPolicy.get().onStatusBarTouched(event, mView.width)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt b/packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt
index ef9340a332dc..ffaddfbb9b15 100644
--- a/packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt
@@ -20,11 +20,13 @@ import android.content.Context
import android.util.AttributeSet
import com.android.systemui.res.R
-class DelayableMarqueeTextView @JvmOverloads constructor(
+open class DelayableMarqueeTextView
+@JvmOverloads
+constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
- defStyleRes: Int = 0
+ defStyleRes: Int = 0,
) : SafeMarqueeTextView(context, attrs, defStyleAttr, defStyleRes) {
var marqueeDelay: Long = DEFAULT_MARQUEE_DELAY
@@ -39,16 +41,20 @@ class DelayableMarqueeTextView @JvmOverloads constructor(
}
init {
- val typedArray = context.theme.obtainStyledAttributes(
+ val typedArray =
+ context.theme.obtainStyledAttributes(
attrs,
R.styleable.DelayableMarqueeTextView,
defStyleAttr,
- defStyleRes
- )
- marqueeDelay = typedArray.getInteger(
- R.styleable.DelayableMarqueeTextView_marqueeDelay,
- DEFAULT_MARQUEE_DELAY.toInt()
- ).toLong()
+ defStyleRes,
+ )
+ marqueeDelay =
+ typedArray
+ .getInteger(
+ R.styleable.DelayableMarqueeTextView_marqueeDelay,
+ DEFAULT_MARQUEE_DELAY.toInt(),
+ )
+ .toLong()
typedArray.recycle()
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
index 96630ca36b97..908249dbca08 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
@@ -144,7 +144,6 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
ringerState.orientation,
)
}
-
is RingerDrawerState.Closed -> {
if (
uiModel.selectedButton.ringerMode ==
@@ -189,7 +188,6 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
}
}
}
-
is RingerDrawerState.Open -> {
drawerContainer.animateAndBindDrawerButtons(
viewModel,
@@ -220,7 +218,6 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
}
}
}
-
is RingerViewModelState.Unavailable -> {
drawerContainer.visibility = View.GONE
volumeDialogBackgroundView.setBackgroundResource(
@@ -251,7 +248,7 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
.requireViewById<ImageButton>(R.id.volume_drawer_button)
val previousIndex =
uiModel.availableButtons.indexOfFirst {
- it?.ringerMode == uiModel.drawerState.previousMode
+ it.ringerMode == uiModel.drawerState.previousMode
}
val unselectedButton =
getChildAt(count - previousIndex)
@@ -306,20 +303,18 @@ constructor(private val viewModel: VolumeDialogRingerDrawerViewModel) {
) {
val count = uiModel.availableButtons.size
uiModel.availableButtons.fastForEachIndexed { index, ringerButton ->
- ringerButton?.let {
- val view = getChildAt(count - index)
- val isOpen = uiModel.drawerState is RingerDrawerState.Open
- if (index == uiModel.currentButtonIndex) {
- view.bindDrawerButton(
- if (isOpen) it else uiModel.selectedButton,
- viewModel,
- isOpen,
- isSelected = true,
- isAnimated = isAnimated,
- )
- } else {
- view.bindDrawerButton(it, viewModel, isOpen, isAnimated = isAnimated)
- }
+ val view = getChildAt(count - index)
+ val isOpen = uiModel.drawerState is RingerDrawerState.Open
+ if (index == uiModel.currentButtonIndex) {
+ view.bindDrawerButton(
+ if (isOpen) ringerButton else uiModel.selectedButton,
+ viewModel,
+ isOpen,
+ isSelected = true,
+ isAnimated = isAnimated,
+ )
+ } else {
+ view.bindDrawerButton(ringerButton, viewModel, isOpen, isAnimated = isAnimated)
}
}
onAnimationEnd?.run()
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModel.kt
index 96d4f62416bf..8613610ad1bf 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/RingerViewModel.kt
@@ -19,7 +19,7 @@ package com.android.systemui.volume.dialog.ringer.ui.viewmodel
/** Models volume dialog ringer */
data class RingerViewModel(
/** List of the available buttons according to the available modes */
- val availableButtons: List<RingerButtonViewModel?>,
+ val availableButtons: List<RingerButtonViewModel>,
/** The index of the currently selected button */
val currentButtonIndex: Int,
/** Currently selected button. */
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
index eec64d9a2f86..07c4de0ac0c4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/viewmodel/VolumeDialogRingerDrawerViewModel.kt
@@ -50,7 +50,9 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -106,9 +108,25 @@ constructor(
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
.build()
+ init {
+ ringerViewModel
+ .onEach { viewModelState ->
+ when (viewModelState) {
+ is RingerViewModelState.Available ->
+ volumeDialogLogger.onRingerDrawerAvailable(
+ viewModelState.uiModel.availableButtons.map { it.ringerMode }
+ )
+ is RingerViewModelState.Unavailable ->
+ volumeDialogLogger.onRingerDrawerUnavailable()
+ }
+ }
+ .launchIn(coroutineScope)
+ }
+
fun onRingerButtonClicked(ringerMode: RingerMode, isSelectedButton: Boolean = false) {
if (drawerState.value is RingerDrawerState.Open && !isSelectedButton) {
Events.writeEvent(Events.EVENT_RINGER_TOGGLE, ringerMode.value)
+ volumeDialogLogger.onRingerModeChanged(ringerMode)
provideTouchFeedback(ringerMode)
maybeShowToast(ringerMode)
ringerInteractor.setRingerMode(ringerMode)
@@ -159,7 +177,9 @@ constructor(
RingerViewModelState.Available(
RingerViewModel(
availableButtons =
- availableModes.map { mode -> toButtonViewModel(mode, isZenMuted) },
+ availableModes.mapNotNull { mode ->
+ toButtonViewModel(mode, isZenMuted)
+ },
currentButtonIndex = currentIndex,
selectedButton = it,
drawerState = drawerState,
@@ -219,7 +239,6 @@ constructor(
hintLabelResId = R.string.volume_ringer_hint_unmute,
ringerMode = ringerMode,
)
-
availableModes.contains(RingerMode(RINGER_MODE_VIBRATE)) ->
RingerButtonViewModel(
imageResId = R.drawable.ic_speaker_on,
@@ -232,7 +251,6 @@ constructor(
hintLabelResId = R.string.volume_ringer_hint_vibrate,
ringerMode = ringerMode,
)
-
else ->
RingerButtonViewModel(
imageResId = R.drawable.ic_speaker_on,
@@ -269,17 +287,14 @@ constructor(
null
}
}
-
RINGER_MODE_SILENT ->
applicationContext.getString(
internalR.string.volume_dialog_ringer_guidance_silent
)
-
RINGER_MODE_VIBRATE ->
applicationContext.getString(
internalR.string.volume_dialog_ringer_guidance_vibrate
)
-
else ->
applicationContext.getString(
internalR.string.volume_dialog_ringer_guidance_vibrate
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/VolumeDialogLogger.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/VolumeDialogLogger.kt
index 9a3aa7e3d79f..cccf090fae85 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/VolumeDialogLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/shared/VolumeDialogLogger.kt
@@ -45,6 +45,52 @@ class VolumeDialogLogger @Inject constructor(@VolumeLog private val logBuffer: L
)
}
+ fun onVolumeSliderAdjustmentFinished(volume: Int, stream: Int) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ int1 = volume
+ int2 = stream
+ },
+ { "Volume adjusted: volume=$int1 stream=$int2" },
+ )
+ }
+
+ fun onVolumeSlidersUpdated(primaryStream: Int, floating: Collection<Int>) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ int1 = primaryStream
+ str1 = floating.joinToString(",") { it.toString() }
+ },
+ { "Showing streams: primary=$int1 floating=$str1" },
+ )
+ }
+
+ fun onRingerModeChanged(ringerMode: RingerMode) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { int1 = ringerMode.value },
+ { "Ringer mode changed to: $int1" },
+ )
+ }
+
+ fun onRingerDrawerAvailable(modes: List<RingerMode>) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { str1 = modes.joinToString(",") { it.value.toString() } },
+ { "Ringer drawer available with modes: $str1" },
+ )
+ }
+
+ fun onRingerDrawerUnavailable() {
+ logBuffer.log(TAG, LogLevel.DEBUG, {}, { "Ringer drawer unavailable" })
+ }
+
fun onCurrentRingerModeIsUnsupported(ringerMode: RingerMode) {
logBuffer.log(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index 3b964fdec1b8..d40302408dd6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -27,6 +27,7 @@ import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSlide
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderStateModel
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
import com.google.android.material.slider.Slider
+import com.google.android.material.slider.Slider.OnSliderTouchListener
import javax.inject.Inject
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
@@ -68,6 +69,15 @@ constructor(
sliderView.addOnChangeListener { _, value, fromUser ->
viewModel.setStreamVolume(value.roundToInt(), fromUser)
}
+ sliderView.addOnSliderTouchListener(
+ object : OnSliderTouchListener {
+ override fun onStartTrackingTouch(slider: Slider) {}
+
+ override fun onStopTrackingTouch(slider: Slider) {
+ viewModel.onStreamChangeFinished(slider.value.roundToInt())
+ }
+ }
+ )
viewModel.isDisabledByZenMode.onEach { sliderView.isEnabled = !it }.launchIn(this)
viewModel.state
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
index 71fe22ba4b01..9cf02f26c9f7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
@@ -82,10 +82,6 @@ constructor(
ringerMode: RingerMode?,
): Int {
val isStreamOffline = level == 0 || isMuted
- when (ringerMode?.value) {
- AudioManager.RINGER_MODE_VIBRATE -> return R.drawable.ic_volume_ringer_vibrate
- AudioManager.RINGER_MODE_SILENT -> return R.drawable.ic_ring_volume_off
- }
if (isRoutedToBluetooth) {
return if (stream == AudioManager.STREAM_VOICE_CALL) {
R.drawable.ic_volume_bt_sco
@@ -98,29 +94,39 @@ constructor(
}
}
+ val isLevelLow = level < (levelMax + levelMin) / 2
return if (isStreamOffline) {
+ val ringerOfflineIcon =
+ when (ringerMode?.value) {
+ AudioManager.RINGER_MODE_VIBRATE -> return R.drawable.ic_volume_ringer_vibrate
+ AudioManager.RINGER_MODE_SILENT -> return R.drawable.ic_ring_volume_off
+ else -> null
+ }
when (stream) {
AudioManager.STREAM_MUSIC -> R.drawable.ic_volume_media_mute
- AudioManager.STREAM_NOTIFICATION -> R.drawable.ic_volume_ringer_mute
+ AudioManager.STREAM_NOTIFICATION ->
+ ringerOfflineIcon ?: R.drawable.ic_volume_ringer_mute
+ AudioManager.STREAM_RING -> ringerOfflineIcon ?: R.drawable.ic_volume_ringer_vibrate
AudioManager.STREAM_ALARM -> R.drawable.ic_volume_alarm_mute
AudioManager.STREAM_SYSTEM -> R.drawable.ic_volume_system_mute
else -> null
- } ?: getIconForStream(stream)
- } else {
- if (level < (levelMax + levelMin) / 2) {
- // This icon is different on TV
- R.drawable.ic_volume_media_low
- } else {
- getIconForStream(stream)
}
- }
+ } else {
+ null
+ } ?: getIconForStream(stream = stream, isLevelLow = isLevelLow)
}
@DrawableRes
- private fun getIconForStream(stream: Int): Int {
+ private fun getIconForStream(stream: Int, isLevelLow: Boolean): Int {
return when (stream) {
AudioManager.STREAM_ACCESSIBILITY -> R.drawable.ic_volume_accessibility
- AudioManager.STREAM_MUSIC -> R.drawable.ic_volume_media
+ AudioManager.STREAM_MUSIC ->
+ if (isLevelLow) {
+ // This icon is different on TV
+ R.drawable.ic_volume_media_low
+ } else {
+ R.drawable.ic_volume_media
+ }
AudioManager.STREAM_RING -> R.drawable.ic_ring_volume
AudioManager.STREAM_NOTIFICATION -> R.drawable.ic_volume_ringer
AudioManager.STREAM_ALARM -> R.drawable.ic_alarm
@@ -135,7 +141,9 @@ constructor(
* affect the [stream]
*/
private fun ringerModeForStream(stream: Int): Flow<RingerMode?> {
- return if (stream == AudioManager.STREAM_RING) {
+ return if (
+ stream == AudioManager.STREAM_RING || stream == AudioManager.STREAM_NOTIFICATION
+ ) {
audioVolumeInteractor.ringerMode
} else {
flowOf(null)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
index d999910675b0..89dd0352afa7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
@@ -20,9 +20,11 @@ import com.android.systemui.util.time.SystemClock
import com.android.systemui.volume.Events
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogVisibilityInteractor
+import com.android.systemui.volume.dialog.shared.VolumeDialogLogger
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSliderInteractor
+import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -60,6 +62,8 @@ constructor(
@VolumeDialog private val coroutineScope: CoroutineScope,
private val volumeDialogSliderIconProvider: VolumeDialogSliderIconProvider,
private val systemClock: SystemClock,
+ private val sliderType: VolumeDialogSliderType,
+ private val logger: VolumeDialogLogger,
) {
private val userVolumeUpdates = MutableStateFlow<VolumeUpdate?>(null)
@@ -110,6 +114,10 @@ constructor(
}
}
+ fun onStreamChangeFinished(volume: Int) {
+ logger.onVolumeSliderAdjustmentFinished(volume = volume, stream = sliderType.audioStream)
+ }
+
private fun getTimestampMillis(): Long = systemClock.uptimeMillis()
private data class VolumeUpdate(val newVolumeLevel: Int, val timestampMillis: Long)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
index d8e6aec026c6..344dadcce229 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModel.kt
@@ -18,6 +18,7 @@ package com.android.systemui.volume.dialog.sliders.ui.viewmodel
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
+import com.android.systemui.volume.dialog.shared.VolumeDialogLogger
import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderComponent
import com.android.systemui.volume.dialog.sliders.domain.interactor.VolumeDialogSlidersInteractor
import javax.inject.Inject
@@ -33,13 +34,18 @@ class VolumeDialogSlidersViewModel
@Inject
constructor(
@VolumeDialog coroutineScope: CoroutineScope,
- private val slidersInteractor: VolumeDialogSlidersInteractor,
+ slidersInteractor: VolumeDialogSlidersInteractor,
private val sliderComponentFactory: VolumeDialogSliderComponent.Factory,
+ private val volumeDialogLogger: VolumeDialogLogger,
) {
val sliders: Flow<VolumeDialogSliderUiModel> =
slidersInteractor.sliders
.map { slidersModel ->
+ volumeDialogLogger.onVolumeSlidersUpdated(
+ slidersModel.slider.audioStream,
+ slidersModel.floatingSliders.map { it.audioStream },
+ )
VolumeDialogSliderUiModel(
sliderComponent = sliderComponentFactory.create(slidersModel.slider),
floatingSliderComponent =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
index 9f6ad56335d7..c14cb87064ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
@@ -194,7 +194,7 @@ public class MenuAnimationControllerTest extends SysuiTestCase {
final Runnable onSpringAnimationsEndCallback = mock(Runnable.class);
mMenuAnimationController.setSpringAnimationsEndAction(onSpringAnimationsEndCallback);
- mMenuAnimationController.flingMenuThenSpringToEdge(/* x= */ 0, /* velocityX= */
+ mMenuAnimationController.flingMenuThenSpringToEdge(new PointF(), /* velocityX= */
100, /* velocityY= */ 100);
mMenuAnimationController.mPositionAnimations.values()
.forEach(animation -> verify((FlingAnimation) animation).addEndListener(
@@ -212,7 +212,7 @@ public class MenuAnimationControllerTest extends SysuiTestCase {
final Runnable onSpringAnimationsEndCallback = mock(Runnable.class);
mMenuAnimationController.setSpringAnimationsEndAction(onSpringAnimationsEndCallback);
- mMenuAnimationController.flingMenuThenSpringToEdge(/* x= */ 0, /* velocityX= */
+ mMenuAnimationController.flingMenuThenSpringToEdge(new PointF(), /* velocityX= */
200, /* velocityY= */ 200);
mMenuAnimationController.mPositionAnimations.values()
.forEach(animation -> verify((FlingAnimation) animation).addEndListener(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java
new file mode 100644
index 000000000000..146488b523ad
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility.floatingmenu;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.graphics.Rect;
+import android.testing.TestableLooper;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for {@link MenuViewAppearanceTest}. */
+@RunWith(AndroidJUnit4.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class MenuViewAppearanceTest extends SysuiTestCase {
+ static final Rect DRAGGABLE_BOUNDS = new Rect(0, 0, 10, 10);
+ static final int MENU_HEIGHT = 1;
+
+ @Test
+ public void avoidVerticalDisplayCutout_roomAbove_placesAbove() {
+ final int y = 2;
+ final Rect cutout = new Rect(0, 3, 0, 10);
+
+ final float end_y = MenuViewAppearance.avoidVerticalDisplayCutout(
+ y, MENU_HEIGHT, DRAGGABLE_BOUNDS, cutout);
+
+ assertThat(end_y + MENU_HEIGHT).isAtMost(cutout.top);
+ }
+
+ @Test
+ public void avoidVerticalDisplayCutout_roomBelow_placesBelow() {
+ final int y = 2;
+ final Rect cutout = new Rect(0, 0, 0, 5);
+
+ final float end_y = MenuViewAppearance.avoidVerticalDisplayCutout(
+ y, MENU_HEIGHT, DRAGGABLE_BOUNDS, cutout);
+
+ assertThat(end_y).isAtLeast(cutout.bottom);
+ }
+
+ @Test
+ public void avoidVerticalDisplayCutout_noRoom_noChange() {
+ final int y = 2;
+ final Rect cutout = new Rect(0, 0, 0, 10);
+
+ final float end_y = MenuViewAppearance.avoidVerticalDisplayCutout(
+ y, MENU_HEIGHT, DRAGGABLE_BOUNDS, cutout);
+
+ assertThat(end_y).isEqualTo(end_y);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index d3135026ce06..437ccb6a9821 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -63,7 +63,6 @@ import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.user.ui.viewmodel.StatusBarUserChipViewModel
-import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.view.ViewUtil
@@ -75,7 +74,6 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
@@ -83,6 +81,8 @@ import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -438,25 +438,28 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
@Test
@EnableFlags(ShadeWindowGoesAround.FLAG_NAME)
fun onTouch_actionDown_propagatesToDisplayPolicy() {
- controller.onTouch(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0))
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+ controller.onTouch(event)
- verify(statusBarTouchShadeDisplayPolicy).onStatusBarTouched(eq(mContext.displayId))
+ verify(statusBarTouchShadeDisplayPolicy).onStatusBarTouched(eq(event), any())
}
@Test
@EnableFlags(ShadeWindowGoesAround.FLAG_NAME)
fun onTouch_actionUp_notPropagatesToDisplayPolicy() {
- controller.onTouch(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0))
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
+ controller.onTouch(event)
- verify(statusBarTouchShadeDisplayPolicy, never()).onStatusBarTouched(any())
+ verify(statusBarTouchShadeDisplayPolicy, never()).onStatusBarTouched(any(), any())
}
@Test
@DisableFlags(ShadeWindowGoesAround.FLAG_NAME)
fun onTouch_shadeWindowGoesAroundDisabled_notPropagatesToDisplayPolicy() {
- controller.onTouch(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0))
+ val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+ controller.onTouch(event)
- verify(statusBarTouchShadeDisplayPolicy, never()).onStatusBarTouched(any())
+ verify(statusBarTouchShadeDisplayPolicy, never()).onStatusBarTouched(eq(event), any())
}
@Test
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
index 636cb37adf03..aaef27d257c5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/data/repository/ShadeDisplaysRepositoryKosmos.kt
@@ -23,7 +23,11 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.shade.display.AnyExternalShadeDisplayPolicy
import com.android.systemui.shade.display.DefaultDisplayShadePolicy
import com.android.systemui.shade.display.ShadeDisplayPolicy
+import com.android.systemui.shade.display.ShadeExpansionIntent
import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
+import com.android.systemui.shade.domain.interactor.notificationElement
+import com.android.systemui.shade.domain.interactor.qsElement
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.util.settings.fakeGlobalSettings
val Kosmos.defaultShadeDisplayPolicy: DefaultDisplayShadePolicy by
@@ -37,16 +41,20 @@ val Kosmos.anyExternalShadeDisplayPolicy: AnyExternalShadeDisplayPolicy by
)
}
-val Kosmos.focusBasedShadeDisplayPolicy: StatusBarTouchShadeDisplayPolicy by
+val Kosmos.statusBarTouchShadeDisplayPolicy: StatusBarTouchShadeDisplayPolicy by
Kosmos.Fixture {
StatusBarTouchShadeDisplayPolicy(
displayRepository = displayRepository,
backgroundScope = testScope.backgroundScope,
keyguardRepository = keyguardRepository,
shadeOnDefaultDisplayWhenLocked = false,
+ shadeInteractor = { shadeInteractor },
+ notificationElement = { notificationElement },
+ qsShadeElement = { qsElement },
)
}
-
+val Kosmos.shadeExpansionIntent: ShadeExpansionIntent by
+ Kosmos.Fixture { statusBarTouchShadeDisplayPolicy }
val Kosmos.shadeDisplaysRepository: MutableShadeDisplaysRepository by
Kosmos.Fixture {
ShadeDisplaysRepositoryImpl(
@@ -62,7 +70,7 @@ val Kosmos.shadeDisplayPolicies: Set<ShadeDisplayPolicy> by
setOf(
defaultShadeDisplayPolicy,
anyExternalShadeDisplayPolicy,
- focusBasedShadeDisplayPolicy,
+ statusBarTouchShadeDisplayPolicy,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt
index 6e44df833582..923de2dcbf68 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeDisplaysInteractorKosmos.kt
@@ -23,6 +23,7 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.shade.ShadeDisplayChangeLatencyTracker
import com.android.systemui.shade.ShadeWindowLayoutParams
import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository
+import com.android.systemui.shade.data.repository.shadeExpansionIntent
import java.util.Optional
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
@@ -49,5 +50,6 @@ val Kosmos.shadeDisplaysInteractor by
testScope.backgroundScope.coroutineContext,
mockedShadeDisplayChangeLatencyTracker,
Optional.of(shadeExpandedStateInteractor),
+ shadeExpansionIntent,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
index 1dc7229a6506..32a30502a370 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeInteractorKosmos.kt
@@ -31,7 +31,6 @@ import com.android.systemui.statusbar.phone.dozeParameters
import com.android.systemui.statusbar.policy.data.repository.userSetupRepository
import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
import com.android.systemui.user.domain.interactor.userSwitcherInteractor
-import org.mockito.kotlin.mock
var Kosmos.baseShadeInteractor: BaseShadeInteractor by
Kosmos.Fixture {
@@ -73,7 +72,19 @@ val Kosmos.shadeInteractorImpl by
shadeModeInteractor = shadeModeInteractor,
)
}
-var Kosmos.mockShadeInteractor: ShadeInteractor by Kosmos.Fixture { mock() }
+var Kosmos.notificationElement: NotificationShadeElement by
+ Kosmos.Fixture {
+ NotificationShadeElement(shadeInteractor, testScope.backgroundScope.coroutineContext)
+ }
+var Kosmos.qsElement: QSShadeElement by
+ Kosmos.Fixture { QSShadeElement(shadeInteractor, testScope.backgroundScope.coroutineContext) }
val Kosmos.shadeExpandedStateInteractor by
- Kosmos.Fixture { ShadeExpandedStateInteractorImpl(shadeInteractor, testScope.backgroundScope) }
+ Kosmos.Fixture {
+ ShadeExpandedStateInteractorImpl(
+ shadeInteractor,
+ testScope.backgroundScope,
+ notificationElement,
+ qsElement,
+ )
+ }
val Kosmos.fakeShadeExpandedStateInteractor by Kosmos.Fixture { FakeShadeExpandedStateInteractor() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModelKosmos.kt
index 63cd440a8633..b26081c40c38 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModelKosmos.kt
@@ -20,15 +20,19 @@ import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.util.time.systemClock
import com.android.systemui.volume.dialog.domain.interactor.volumeDialogVisibilityInteractor
+import com.android.systemui.volume.dialog.shared.volumeDialogLogger
import com.android.systemui.volume.dialog.sliders.domain.interactor.volumeDialogSliderInteractor
+import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType
val Kosmos.volumeDialogSliderViewModel by
Kosmos.Fixture {
VolumeDialogSliderViewModel(
+ sliderType = volumeDialogSliderType,
interactor = volumeDialogSliderInteractor,
visibilityInteractor = volumeDialogVisibilityInteractor,
coroutineScope = applicationCoroutineScope,
volumeDialogSliderIconProvider = volumeDialogSliderIconProvider,
systemClock = systemClock,
+ logger = volumeDialogLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModelKosmos.kt
index 5531f7608b69..8fb60fdd5ab1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSlidersViewModelKosmos.kt
@@ -18,6 +18,7 @@ package com.android.systemui.volume.dialog.sliders.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.volume.dialog.shared.volumeDialogLogger
import com.android.systemui.volume.dialog.sliders.dagger.volumeDialogSliderComponentFactory
import com.android.systemui.volume.dialog.sliders.domain.interactor.volumeDialogSlidersInteractor
@@ -27,5 +28,6 @@ val Kosmos.volumeDialogSlidersViewModel by
applicationCoroutineScope,
volumeDialogSlidersInteractor,
volumeDialogSliderComponentFactory,
+ volumeDialogLogger,
)
}
diff --git a/ravenwood/texts/ravenwood-framework-policies.txt b/ravenwood/texts/ravenwood-framework-policies.txt
index 80126df1b8df..26b6fe3d82ad 100644
--- a/ravenwood/texts/ravenwood-framework-policies.txt
+++ b/ravenwood/texts/ravenwood-framework-policies.txt
@@ -52,7 +52,7 @@ class android.content.BroadcastReceiver keep
class android.content.Context keep
method <init> ()V keep
method getSystemService (Ljava/lang/Class;)Ljava/lang/Object; keep
-class android.content.pm.PackageManager keep
+class android.content.pm.PackageManager
method <init> ()V keep
class android.text.ClipboardManager keep
method <init> ()V keep
diff --git a/ravenwood/tools/hoststubgen/scripts/dump-jar b/ravenwood/tools/hoststubgen/scripts/dump-jar
index 998357b70dff..02a6d25378bd 100755
--- a/ravenwood/tools/hoststubgen/scripts/dump-jar
+++ b/ravenwood/tools/hoststubgen/scripts/dump-jar
@@ -89,14 +89,33 @@ filter_output() {
# - Some other transient lines
# - Sometimes the javap shows mysterious warnings, so remove them too.
#
- # `/PATTERN-1/,/PATTERN-2/{//!d}` is a trick to delete lines between two patterns, without
- # the start and the end lines.
+ # Most conversion are simple per-line deletion or simple inline replacement with a regex.
+ #
+ # But removing the constant pool is a bit tricky. It looks like this in the output:
+ #---------------------------
+ #Constant pool:
+ # #1 = Methodref #31.#88 // java/lang/Object."<init>":()V
+ # #2 = Class #89 // java/lang/UnsupportedOperationException
+ # :
+ #{ // Or something, I'm not sure if it always ends with a "{".
+ #---------------------------
+ # i.e. we want to delete all lines from "Constant pool:" as long as the first character
+ # is a space.
+ #
+ # If we simply use '/^Constant pool:/,/^[^ ]/d', then it'll delete the "Constant pool:"
+ # line and "{" line too, but again the last line might be important, so we don't want to
+ # delete it.
+ #
+ # So we instead, use '/^Constant pool:/,/^[^ ]/{/^ /d}', which mean:
+ # between lines matching '/^Constant pool:/' and '/^[^ ]/', delete lines that start with
+ # a space. (=='/^ /d').
+ #
sed -e 's/#[0-9][0-9]*/#x/g' \
-e 's/^\( *\)[0-9][0-9]*:/\1x:/' \
- -e '/^Constant pool:/,/^[^ ]/{//!d}' \
+ -e '/^Constant pool:/,/^[^ ]/{/^ /d}' \
-e '/^ *line *[0-9][0-9]*: *[0-9][0-9]*$/d' \
- -e '/SHA-256 checksum/d' \
- -e '/Last modified/d' \
+ -e '/^ *SHA-256 checksum/d' \
+ -e '/^ *Last modified/d' \
-e '/^Classfile jar/d' \
-e '/\[warning\]/d'
else
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index be1b6ca93869..c5500831e21a 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -413,8 +413,8 @@ class TextFileFilterPolicyParser {
}
private fun parseClass(fields: Array<String>) {
- if (fields.size < 3) {
- throw ParseException("Class ('c') expects 2 fields.")
+ if (fields.size <= 1) {
+ throw ParseException("Class ('c') expects 1 or 2 fields.")
}
val className = fields[1]
@@ -424,7 +424,9 @@ class TextFileFilterPolicyParser {
// :aidl, etc?
val classType = resolveSpecialClass(className)
- if (fields[2].startsWith("!")) {
+ val policyStr = if (fields.size > 2) { fields[2] } else { "" }
+
+ if (policyStr.startsWith("!")) {
if (classType != SpecialClass.NotSpecial) {
// We could support it, but not needed at least for now.
throw ParseException(
@@ -432,10 +434,10 @@ class TextFileFilterPolicyParser {
)
}
// It's a redirection class.
- val toClass = fields[2].substring(1)
+ val toClass = policyStr.substring(1)
processor.onRedirectionClass(className, toClass)
- } else if (fields[2].startsWith("~")) {
+ } else if (policyStr.startsWith("~")) {
if (classType != SpecialClass.NotSpecial) {
// We could support it, but not needed at least for now.
throw ParseException(
@@ -443,11 +445,23 @@ class TextFileFilterPolicyParser {
)
}
// It's a class-load hook
- val callback = fields[2].substring(1)
+ val callback = policyStr.substring(1)
processor.onClassLoadHook(className, callback)
} else {
- val policy = parsePolicy(fields[2])
+ // Special case: if it's a class directive with no policy, then it encloses
+ // members, but we don't apply any policy to the class itself.
+ // This is only allowed in a not-special case.
+ if (policyStr == "") {
+ if (classType == SpecialClass.NotSpecial && superClass == null) {
+ currentClassName = className
+ processor.onSimpleClassStart(className)
+ return
+ }
+ throw ParseException("Special class or subclass directive must have a policy")
+ }
+
+ val policy = parsePolicy(policyStr)
if (!policy.isUsableWithClasses) {
throw ParseException("Class can't have policy '$policy'")
}
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh b/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
index 8408a18142eb..23699fd1dba4 100755
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
@@ -37,8 +37,7 @@ SCRIPT_NAME="${0##*/}"
GOLDEN_DIR=${GOLDEN_DIR:-golden-output}
mkdir -p $GOLDEN_DIR
-# TODO(b/388562869) We shouldn't need `--ignore-matching-lines`, but the golden files may not have the "Constant pool:" lines.
-DIFF_CMD=${DIFF_CMD:-diff -u --ignore-blank-lines --ignore-space-change --ignore-matching-lines='^\(Constant.pool:\|{\)$'}
+DIFF_CMD=${DIFF_CMD:-./tiny-framework-dump-test.py run-diff}
update=0
three_way=0
@@ -63,12 +62,10 @@ done
shift $(($OPTIND - 1))
# Build the dump files, which are the input of this test.
-run ${BUILD_CMD:=m} dump-jar tiny-framework-dump-test
-
+run ${BUILD_CMD:-m} dump-jar tiny-framework-dump-test
# Get the path to the generate text files. (not the golden files.)
# We get them from $OUT/module-info.json
-
files=(
$(python3 -c '
import sys
@@ -78,7 +75,7 @@ import json
with open(sys.argv[1], "r") as f:
data = json.load(f)
- # Equivalent to: jq -r '.["tiny-framework-dump-test"]["installed"][]'
+ # Equivalent to: jq -r '.["tiny-framework-dump-test"]["installed"][]'
for path in data["tiny-framework-dump-test"]["installed"]:
if "golden-output" in path:
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
index c35d6d106c8d..761748265726 100755
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
@@ -28,9 +28,16 @@ GOLDEN_DIRS = [
# Run diff.
def run_diff(file1, file2):
- # TODO(b/388562869) We shouldn't need `--ignore-matching-lines`, but the golden files may not have the "Constant pool:" lines.
- command = ['diff', '-u', '--ignore-blank-lines',
+ command = ['diff', '-u',
+ '--ignore-blank-lines',
'--ignore-space-change',
+
+ # Ignore the class file version.
+ '--ignore-matching-lines=^ *\(major\|minor\) version:$',
+
+ # We shouldn't need `--ignore-matching-lines`, but somehow
+ # the golden files were generated without these lines for b/388562869,
+ # so let's just ignore them.
'--ignore-matching-lines=^\(Constant.pool:\|{\)$',
file1, file2]
print(' '.join(command))
@@ -85,4 +92,13 @@ class TestWithGoldenOutput(unittest.TestCase):
if __name__ == "__main__":
+ args = sys.argv
+
+ # This script is used by diff-and-update-golden.sh too.
+ if len(args) > 1 and args[1] == "run-diff":
+ if run_diff(args[2], args[3]):
+ sys.exit(0)
+ else:
+ sys.exit(1)
+
unittest.main(verbosity=2)
diff --git a/services/core/java/com/android/server/power/hint/TEST_MAPPING b/services/core/java/com/android/server/power/hint/TEST_MAPPING
index 545070050977..fd81277e9ba4 100644
--- a/services/core/java/com/android/server/power/hint/TEST_MAPPING
+++ b/services/core/java/com/android/server/power/hint/TEST_MAPPING
@@ -15,5 +15,22 @@
{"exclude-annotation": "org.junit.Ignore"}
]
}
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsSystemHealthTestCases",
+ "options": [
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
+ },
+ {
+ "name": "CtsOsTestCases",
+ "options": [
+ {"include-filter": "android.os.health.cts.HeadroomTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
+ }
]
} \ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d32c31f1c1c7..5435d8f164da 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -193,7 +193,6 @@ import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
@@ -2187,12 +2186,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
}
- /** Returns {@code true} if the screen rotation animation needs to wait for the window. */
- boolean shouldSyncRotationChange(WindowState w) {
- final AsyncRotationController controller = mAsyncRotationController;
- return controller == null || !controller.isAsync(w);
- }
-
void notifyInsetsChanged(Consumer<WindowState> dispatchInsetsChanged) {
if (mFixedRotationLaunchingApp != null) {
// The insets state of fixed rotation app is a rotated copy. Make sure the visibilities
@@ -2279,10 +2272,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
if (!shellTransitions) {
forAllWindows(w -> {
w.seamlesslyRotateIfAllowed(transaction, oldRotation, rotation, rotateSeamlessly);
- if (!rotateSeamlessly && w.mHasSurface) {
- ProtoLog.v(WM_DEBUG_ORIENTATION, "Set mOrientationChanging of %s", w);
- w.setOrientationChanging(true);
- }
}, true /* traverseTopToBottom */);
mPinnedTaskController.startSeamlessRotationIfNeeded(transaction, oldRotation, rotation);
if (!mDisplayRotation.hasSeamlessRotatingWindow()) {
@@ -5083,15 +5072,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
Slog.w(TAG_WM, "Window freeze timeout expired.");
mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
- forAllWindows(w -> {
- if (!w.getOrientationChanging()) {
- return;
- }
- w.orientationChangeTimedOut();
- w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
- - mWmService.mDisplayFreezeTime);
- Slog.w(TAG_WM, "Force clearing orientation change: " + w);
- }, true /* traverseTopToBottom */);
mWmService.mWindowPlacerLocked.performSurfacePlacement();
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 04f09d5fe627..7a3eb67bf94e 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -205,7 +205,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// For seamless rotation cases this always stays true, as the windows complete their orientation
// changes 1 by 1 without disturbing global state.
boolean mOrientationChangeComplete = true;
- boolean mWallpaperActionPending = false;
private final Handler mHandler;
@@ -1100,10 +1099,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
}
- if ((bulkUpdateParams & SET_WALLPAPER_ACTION_PENDING) != 0) {
- mWallpaperActionPending = true;
- }
-
return doRequest;
}
diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
index 3eb13c52cca6..5e1d7928e96d 100644
--- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
+++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
@@ -231,8 +231,13 @@ class SnapshotPersistQueue {
if (next.isReady(mUserManagerInternal)) {
isReadyToWrite = true;
next.onDequeuedLocked();
- } else {
+ } else if (!mShutdown) {
mWriteQueue.addLast(next);
+ } else {
+ // User manager is locked and device is shutting down, skip writing
+ // this item.
+ next.onDequeuedLocked();
+ next = null;
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index dd6e15b74a58..bf23e757fee5 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -6407,11 +6407,6 @@ public class WindowManagerService extends IWindowManager.Stub
if (mFrozenDisplayId != INVALID_DISPLAY && mFrozenDisplayId == w.getDisplayId()
&& mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
ProtoLog.v(WM_DEBUG_ORIENTATION, "Changing surface while display frozen: %s", w);
- // WindowsState#reportResized won't tell invisible requested window to redraw,
- // so do not set it as changing orientation to avoid affecting draw state.
- if (w.isVisibleRequested()) {
- w.setOrientationChanging(true);
- }
if (mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_NONE) {
mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
// XXX should probably keep timeout from
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 85e3d89730a3..da58470edc1e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -148,7 +148,6 @@ import static com.android.server.wm.WindowManagerService.MY_PID;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
-import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
@@ -592,27 +591,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
/** Completely remove from window manager after exit animation? */
boolean mRemoveOnExit;
- /**
- * Set when the orientation is changing and this window has not yet
- * been updated for the new orientation.
- */
- private boolean mOrientationChanging;
-
/** The time when the window was last requested to redraw for orientation change. */
private long mOrientationChangeRedrawRequestTime;
/**
- * Sometimes in addition to the mOrientationChanging
- * flag we report that the orientation is changing
- * due to a mismatch in current and reported configuration.
- *
- * In the case of timeout we still need to make sure we
- * leave the orientation changing state though, so we
- * use this as a special time out escape hatch.
- */
- private boolean mOrientationChangeTimedOut;
-
- /**
* The orientation during the last visible call to relayout. If our
* current orientation is different, the window can't be ready
* to be shown.
@@ -1497,8 +1479,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Reset the drawn state if the window need to redraw for the change, so the transition
// can wait until it has finished drawing to start.
- if ((configChanged || getOrientationChanging() || dragResizingChanged)
- && isVisibleRequested()) {
+ if ((configChanged || dragResizingChanged) && isVisibleRequested()) {
winAnimator.mDrawState = DRAW_PENDING;
if (mActivityRecord != null) {
mActivityRecord.clearAllDrawn();
@@ -1512,15 +1493,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
ProtoLog.v(WM_DEBUG_RESIZE, "Resizing window %s", this);
mWmService.mResizingWindows.add(this);
}
- } else if (getOrientationChanging()) {
- if (isDrawn()) {
- ProtoLog.v(WM_DEBUG_ORIENTATION,
- "Orientation not waiting for draw in %s, surfaceController %s", this,
- winAnimator.mSurfaceControl);
- setOrientationChanging(false);
- mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
- - mWmService.mDisplayFreezeTime);
- }
}
}
@@ -1528,46 +1500,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return !mWindowFrames.mFrame.equals(mWindowFrames.mLastFrame);
}
- boolean getOrientationChanging() {
- if (mTransitionController.isShellTransitionsEnabled()) {
- // Shell transition doesn't use the methods for display frozen state.
- return false;
- }
- // In addition to the local state flag, we must also consider the difference in the last
- // reported configuration vs. the current state. If the client code has not been informed of
- // the change, logic dependent on having finished processing the orientation, such as
- // unfreezing, could be improperly triggered.
- // TODO(b/62846907): Checking against {@link mLastReportedConfiguration} could be flaky as
- // this is not necessarily what the client has processed yet. Find a
- // better indicator consistent with the client.
- return (mOrientationChanging || (isVisible()
- && getConfiguration().orientation != getLastReportedConfiguration().orientation))
- && !mSeamlesslyRotated
- && !mOrientationChangeTimedOut;
- }
-
- void setOrientationChanging(boolean changing) {
- mOrientationChangeTimedOut = false;
- if (mOrientationChanging == changing) {
- return;
- }
- mOrientationChanging = changing;
- if (changing) {
- mLastFreezeDuration = 0;
- if (mWmService.mRoot.mOrientationChangeComplete
- && mDisplayContent.shouldSyncRotationChange(this)) {
- mWmService.mRoot.mOrientationChangeComplete = false;
- }
- } else {
- // The orientation change is completed. If it was hidden by the animation, reshow it.
- mDisplayContent.finishAsyncRotation(mToken);
- }
- }
-
- void orientationChangeTimedOut() {
- mOrientationChangeTimedOut = true;
- }
-
@Override
void onDisplayChanged(DisplayContent dc) {
if (dc != null && mDisplayContent != null && dc != mDisplayContent
@@ -3355,12 +3287,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
mAppFreezing = false;
- if (mHasSurface && !getOrientationChanging()
- && mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
- ProtoLog.v(WM_DEBUG_ORIENTATION,
- "set mOrientationChanging of %s", this);
- setOrientationChanging(true);
- }
mLastFreezeDuration = 0;
setDisplayLayoutNeeded();
return true;
@@ -4266,9 +4192,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
+ " mDestroying=" + mDestroying
+ " mRemoved=" + mRemoved);
}
- if (getOrientationChanging() || mAppFreezing) {
- pw.println(prefix + "mOrientationChanging=" + mOrientationChanging
- + " configOrientationChanging="
+ if (mAppFreezing) {
+ pw.println(prefix + " configOrientationChanging="
+ (getLastReportedConfiguration().orientation != getConfiguration().orientation)
+ " mAppFreezing=" + mAppFreezing);
}
@@ -5356,7 +5281,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Send information to SurfaceFlinger about the priority of the current window.
updateFrameRateSelectionPriorityIfNeeded();
updateScaleIfNeeded();
- mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
+ mWinAnimator.prepareSurfaceLocked(getPendingTransaction());
applyDims();
}
super.prepareSurfaces();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index d973fb014e35..298580e4bb81 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -29,7 +29,6 @@ import static android.view.WindowManager.TRANSIT_OLD_NONE;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ANIM;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_DRAW;
-import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_ORIENTATION;
import static com.android.internal.protolog.WmProtoLogGroups.WM_DEBUG_STARTING_WINDOW;
import static com.android.internal.protolog.WmProtoLogGroups.WM_SHOW_SURFACE_ALLOC;
import static com.android.internal.protolog.WmProtoLogGroups.WM_SHOW_TRANSACTIONS;
@@ -417,56 +416,19 @@ class WindowStateAnimator {
}
}
- void computeShownFrameLocked() {
- if (mWin.mIsWallpaper && mService.mRoot.mWallpaperActionPending) {
- return;
- } else if (mWin.isDragResizeChanged()) {
- // This window is awaiting a relayout because user just started (or ended)
- // drag-resizing. The shown frame (which affects surface size and pos)
- // should not be updated until we get next finished draw with the new surface.
- // Otherwise one or two frames rendered with old settings would be displayed
- // with new geometry.
- return;
- }
-
- if (DEBUG) {
- Slog.v(TAG, "computeShownFrameLocked: " + this
- + " not attached, mAlpha=" + mAlpha);
- }
-
- mShownAlpha = mAlpha;
- }
-
void prepareSurfaceLocked(SurfaceControl.Transaction t) {
final WindowState w = mWin;
if (!hasSurface()) {
-
- // There is no need to wait for an animation change if our window is gone for layout
- // already as we'll never be visible.
- if (w.getOrientationChanging() && w.isGoneForLayout()) {
- ProtoLog.v(WM_DEBUG_ORIENTATION, "Orientation change skips hidden %s", w);
- w.setOrientationChanging(false);
- }
return;
}
- computeShownFrameLocked();
+ mShownAlpha = mAlpha;
if (!w.isOnScreen()) {
hide(t, "prepareSurfaceLocked");
if (!w.mIsWallpaper || !mService.mFlags.mEnsureWallpaperInTransitions) {
mWallpaperControllerLocked.hideWallpapers(w);
}
-
- // If we are waiting for this window to handle an orientation change. If this window is
- // really hidden (gone for layout), there is no point in still waiting for it.
- // Note that this does introduce a potential glitch if the window becomes unhidden
- // before it has drawn for the new orientation.
- if (w.getOrientationChanging() && w.isGoneForLayout()) {
- w.setOrientationChanging(false);
- ProtoLog.v(WM_DEBUG_ORIENTATION,
- "Orientation change skips hidden %s", w);
- }
} else if (mLastAlpha != mShownAlpha
|| mLastHidden) {
mLastAlpha = mShownAlpha;
@@ -483,20 +445,6 @@ class WindowStateAnimator {
}
}
}
-
- if (w.getOrientationChanging()) {
- if (!w.isDrawn()) {
- if (w.mDisplayContent.shouldSyncRotationChange(w)) {
- w.mWmService.mRoot.mOrientationChangeComplete = false;
- mAnimator.mLastWindowFreezeSource = w;
- }
- ProtoLog.v(WM_DEBUG_ORIENTATION,
- "Orientation continue waiting for draw in %s", w);
- } else {
- w.setOrientationChanging(false);
- ProtoLog.v(WM_DEBUG_ORIENTATION, "Orientation change complete in %s", w);
- }
- }
}
private void showRobustly(SurfaceControl.Transaction t) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index dff718a4b7d5..a34b5115faf9 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -127,7 +127,6 @@ class WindowSurfacePlacer {
mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
loopCount--;
} while (mTraversalScheduled && loopCount > 0);
- mService.mRoot.mWallpaperActionPending = false;
}
private void performSurfacePlacementLoop() {
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 9528467f7ad1..39206dcf21ef 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest2.java
@@ -952,7 +952,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(99, si.getExtras().getInt("x"));
}
- public void testShortcutInfoSaveAndLoad() throws InterruptedException {
+ public void disabled_testShortcutInfoSaveAndLoad() throws InterruptedException {
mRunningUsers.put(USER_11, true);
setCaller(CALLING_PACKAGE_1, USER_11);
@@ -1065,7 +1065,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
dumpUserFile(USER_11);
}
- public void testShortcutInfoSaveAndLoad_maskableBitmap() throws InterruptedException {
+ public void disabled_testShortcutInfoSaveAndLoad_maskableBitmap() throws InterruptedException {
mRunningUsers.put(USER_11, true);
setCaller(CALLING_PACKAGE_1, USER_11);
@@ -1134,7 +1134,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
dumpUserFile(USER_11);
}
- public void testShortcutInfoSaveAndLoad_resId() throws InterruptedException {
+ public void disabled_testShortcutInfoSaveAndLoad_resId() throws InterruptedException {
mRunningUsers.put(USER_11, true);
setCaller(CALLING_PACKAGE_1, USER_11);
@@ -1211,7 +1211,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(1, si.getRank());
}
- public void testShortcutInfoSaveAndLoad_uri() throws InterruptedException {
+ public void disabled_testShortcutInfoSaveAndLoad_uri() throws InterruptedException {
mRunningUsers.put(USER_11, true);
setCaller(CALLING_PACKAGE_1, USER_11);
@@ -1299,7 +1299,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals("uri_maskable", si.getIconUri());
}
- public void testShortcutInfoSaveAndLoad_forBackup() {
+ public void disabled_testShortcutInfoSaveAndLoad_forBackup() {
setCaller(CALLING_PACKAGE_1, USER_10);
final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
@@ -1368,7 +1368,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(0, si.getRank());
}
- public void testShortcutInfoSaveAndLoad_forBackup_resId() {
+ public void disabled_testShortcutInfoSaveAndLoad_forBackup_resId() {
setCaller(CALLING_PACKAGE_1, USER_10);
final Icon res32x32 = Icon.createWithResource(mClientContext, R.drawable.black_32x32);
@@ -1438,7 +1438,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertEquals(0, si.getRank());
}
- public void testShortcutInfoSaveAndLoad_forBackup_uri() {
+ public void disabled_testShortcutInfoSaveAndLoad_forBackup_uri() {
setCaller(CALLING_PACKAGE_1, USER_10);
final Icon uriIcon = Icon.createWithContentUri("test_uri");
@@ -1547,7 +1547,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
});
}
- public void testShortcutInfoSaveAndLoad_intents() {
+ public void disabled_testShortcutInfoSaveAndLoad_intents() {
checkShortcutInfoSaveAndLoad_intents(new Intent(Intent.ACTION_VIEW));
mInjectedCurrentTimeMillis += INTERVAL; // reset throttling.
@@ -1789,7 +1789,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
assertFalse(mManager.setDynamicShortcuts(list(si2)));
}
- public void testThrottling_localeChanges() {
+ public void disabled_testThrottling_localeChanges() {
prepareCrossProfileDataSet();
dumpsysOnLogcat("Before save & load");
@@ -2078,7 +2078,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
}
- public void testThrottling_resetByInternalCall() throws Exception {
+ public void disabled_testThrottling_resetByInternalCall() throws Exception {
prepareCrossProfileDataSet();
dumpsysOnLogcat("Before save & load");
@@ -2173,7 +2173,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
});
}
- public void testReportShortcutUsed() {
+ public void disabled_testReportShortcutUsed() {
mRunningUsers.put(USER_11, true);
runWithCaller(CALLING_PACKAGE_1, USER_11, () -> {
@@ -2322,7 +2322,7 @@ public class ShortcutManagerTest2 extends BaseShortcutManagerTest {
getTestContext().getPackageName()));
}
- public void testDumpCheckin() throws IOException {
+ public void disabled_testDumpCheckin() throws IOException {
prepareCrossProfileDataSet();
// prepareCrossProfileDataSet() doesn't set any icons, so do set here.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index dfd10ec86a20..d76a907ba010 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1583,42 +1583,6 @@ public class DisplayContentTests extends WindowTestsBase {
is(Configuration.ORIENTATION_PORTRAIT));
}
- @Test
- public void testHybridRotationAnimation() {
- final DisplayContent displayContent = mDefaultDisplay;
- final WindowState statusBar = newWindowBuilder("statusBar", TYPE_STATUS_BAR).build();
- final WindowState navBar = newWindowBuilder("navBar", TYPE_NAVIGATION_BAR).build();
- final WindowState app = newWindowBuilder("app", TYPE_BASE_APPLICATION).build();
- final WindowState[] windows = { statusBar, navBar, app };
- makeWindowVisible(windows);
- final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
- displayPolicy.addWindowLw(statusBar, statusBar.mAttrs);
- displayPolicy.addWindowLw(navBar, navBar.mAttrs);
- final ScreenRotationAnimation rotationAnim = new ScreenRotationAnimation(displayContent,
- displayContent.getRotation());
- spyOn(rotationAnim);
- // Assume that the display rotation is changed so it is frozen in preparation for animation.
- doReturn(true).when(rotationAnim).hasScreenshot();
- displayContent.getDisplayRotation().setRotation((displayContent.getRotation() + 1) % 4);
- displayContent.setRotationAnimation(rotationAnim);
- // The fade rotation animation also starts to hide some non-app windows.
- assertNotNull(displayContent.getAsyncRotationController());
- assertTrue(statusBar.isAnimating(PARENTS, ANIMATION_TYPE_TOKEN_TRANSFORM));
-
- for (WindowState w : windows) {
- w.setOrientationChanging(true);
- }
- // The display only waits for the app window to unfreeze.
- assertFalse(displayContent.shouldSyncRotationChange(statusBar));
- assertFalse(displayContent.shouldSyncRotationChange(navBar));
- assertTrue(displayContent.shouldSyncRotationChange(app));
- // If all windows animated by fade rotation animation have done the orientation change,
- // the animation controller should be cleared.
- statusBar.setOrientationChanging(false);
- navBar.setOrientationChanging(false);
- assertNull(displayContent.getAsyncRotationController());
- }
-
@SetupWindows(addWindows = { W_ACTIVITY, W_WALLPAPER, W_STATUS_BAR, W_NAVIGATION_BAR,
W_INPUT_METHOD, W_NOTIFICATION_SHADE })
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index ab9abfc4a876..f6f473b4964d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -855,7 +855,6 @@ public class WindowStateTests extends WindowTestsBase {
startingApp.updateResizingWindowIfNeeded();
assertTrue(mWm.mResizingWindows.contains(startingApp));
assertTrue(startingApp.isDrawn());
- assertFalse(startingApp.getOrientationChanging());
// Even if the display is frozen, invisible requested window should not be affected.
mWm.startFreezingDisplay(0, 0, mDisplayContent);
@@ -873,7 +872,6 @@ public class WindowStateTests extends WindowTestsBase {
win.updateResizingWindowIfNeeded();
assertThat(mWm.mResizingWindows).contains(win);
- assertTrue(win.getOrientationChanging());
mWm.mResizingWindows.remove(win);
spyOn(win.mClient);
@@ -892,7 +890,6 @@ public class WindowStateTests extends WindowTestsBase {
// Even "resized" throws remote exception, it is still considered as reported. So the window
// shouldn't be resized again (which may block unfreeze in real case).
assertThat(mWm.mResizingWindows).doesNotContain(win);
- assertFalse(win.getOrientationChanging());
}
@Test