diff options
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 |