diff options
11 files changed, 134 insertions, 24 deletions
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 07f74367fd06..3f165a3bcb63 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -1052,6 +1052,16 @@ flag { } flag { + name: "glanceable_hub_back_gesture" + namespace: "systemui" + description: "Enables back gesture on the glanceable hub" + bug: "346331399" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "glanceable_hub_allow_keyguard_when_dreaming" namespace: "systemui" description: "Allows users to exit dream to keyguard with glanceable hub enabled" diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt index cc4e7752ab21..d0466313cf81 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt @@ -27,6 +27,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.android.compose.animation.scene.Back import com.android.compose.animation.scene.CommunalSwipeDetector import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.ElementKey @@ -41,6 +42,7 @@ import com.android.compose.animation.scene.SwipeDirection import com.android.compose.animation.scene.observableTransitionState import com.android.compose.animation.scene.transitions import com.android.compose.theme.LocalAndroidColorScheme +import com.android.systemui.Flags.glanceableHubBackGesture import com.android.systemui.communal.shared.model.CommunalBackgroundType import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.shared.model.CommunalTransitionKeys @@ -193,10 +195,17 @@ fun CommunalContainer( Box(modifier = Modifier.fillMaxSize()) } - scene( - CommunalScenes.Communal, - userActions = mapOf(Swipe(SwipeDirection.Right) to CommunalScenes.Blank) - ) { + val userActions = + if (glanceableHubBackGesture()) { + mapOf( + Swipe(SwipeDirection.Right) to CommunalScenes.Blank, + Back to CommunalScenes.Blank, + ) + } else { + mapOf(Swipe(SwipeDirection.Right) to CommunalScenes.Blank) + } + + scene(CommunalScenes.Communal, userActions = userActions) { CommunalScene( backgroundType = backgroundType, colors = colors, diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index a614fc118636..4ef1f93481f7 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -126,6 +126,8 @@ public class QuickStepContract { public static final long SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED = 1L << 33; // PiP animation is running public static final long SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING = 1L << 34; + // Communal hub is showing + public static final long SYSUI_STATE_COMMUNAL_HUB_SHOWING = 1L << 35; // Mask for SystemUiStateFlags to isolate SYSUI_STATE_AWAKE and // SYSUI_STATE_WAKEFULNESS_TRANSITION, to match WAKEFULNESS_* constants @@ -176,6 +178,7 @@ public class QuickStepContract { SYSUI_STATE_SHORTCUT_HELPER_SHOWING, SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED, SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING, + SYSUI_STATE_COMMUNAL_HUB_SHOWING, }) public @interface SystemUiStateFlags {} @@ -283,6 +286,9 @@ public class QuickStepContract { if ((flags & SYSUI_STATE_DISABLE_GESTURE_PIP_ANIMATING) != 0) { str.add("disable_gesture_pip_animating"); } + if ((flags & SYSUI_STATE_COMMUNAL_HUB_SHOWING) != 0) { + str.add("communal_hub_showing"); + } return str.toString(); } @@ -336,7 +342,8 @@ public class QuickStepContract { // the keyguard) if ((sysuiStateFlags & SYSUI_STATE_BOUNCER_SHOWING) != 0 || (sysuiStateFlags & SYSUI_STATE_DIALOG_SHOWING) != 0 - || (sysuiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0) { + || (sysuiStateFlags & SYSUI_STATE_VOICE_INTERACTION_WINDOW_SHOWING) != 0 + || (sysuiStateFlags & SYSUI_STATE_COMMUNAL_HUB_SHOWING) != 0) { return false; } if ((sysuiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0) { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java index 37e9dc1a9d48..7750f6bf4178 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java @@ -233,7 +233,7 @@ public class SystemActions implements CoreStartable, ConfigurationController.Con // NotificationShadeWindowController.registerCallback() only keeps weak references. mNotificationShadeCallback = (keyguardShowing, keyguardOccluded, keyguardGoingAway, bouncerShowing, mDozing, - panelExpanded, isDreaming) -> + panelExpanded, isDreaming, communalShowing) -> registerOrUnregisterDismissNotificationShadeAction(); mScreenshotHelper = new ScreenshotHelper(mContext); } diff --git a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt index 5084944685b6..42f66cca2522 100644 --- a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt +++ b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt @@ -18,12 +18,14 @@ package com.android.systemui.model import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey +import com.android.systemui.Flags.glanceableHubBackGesture import com.android.systemui.dagger.SysUISingleton import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING +import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_COMMUNAL_HUB_SHOWING import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED @@ -105,6 +107,10 @@ constructor( { it.scene == Scenes.Lockscreen && it.invisibleDueToOcclusion }, + SYSUI_STATE_COMMUNAL_HUB_SHOWING to + { + glanceableHubBackGesture() && it.scene == Scenes.Communal + } ) } diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index e07b057119a1..4e2e2272b5b0 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -26,11 +26,13 @@ import static android.view.MotionEvent.ACTION_UP; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; +import static com.android.systemui.Flags.glanceableHubBackGesture; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_AWAKE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_COMMUNAL_HUB_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE; @@ -792,7 +794,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded, boolean keyguardGoingAway, boolean bouncerShowing, boolean isDozing, - boolean panelExpanded, boolean isDreaming) { + boolean panelExpanded, boolean isDreaming, boolean communalShowing) { mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING, keyguardShowing && !keyguardOccluded) .setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED, @@ -802,6 +804,8 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis .setFlag(SYSUI_STATE_BOUNCER_SHOWING, bouncerShowing) .setFlag(SYSUI_STATE_DEVICE_DOZING, isDozing) .setFlag(SYSUI_STATE_DEVICE_DREAMING, isDreaming) + .setFlag(SYSUI_STATE_COMMUNAL_HUB_SHOWING, + glanceableHubBackGesture() && communalShowing) .commitUpdate(mContext.getDisplayId()); } diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt index ba4c29aad6a2..d870fe69463d 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt @@ -17,6 +17,7 @@ package com.android.systemui.shade import android.content.Context +import android.graphics.Insets import android.graphics.Rect import android.os.PowerManager import android.os.SystemClock @@ -25,6 +26,7 @@ import android.view.GestureDetector import android.view.MotionEvent import android.view.View import android.view.ViewGroup +import android.view.WindowInsets import android.widget.FrameLayout import androidx.activity.OnBackPressedDispatcher import androidx.activity.OnBackPressedDispatcherOwner @@ -37,6 +39,7 @@ import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.android.compose.theme.PlatformTheme import com.android.internal.annotations.VisibleForTesting +import com.android.systemui.Flags.glanceableHubBackGesture import com.android.systemui.ambient.touch.TouchMonitor import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent import com.android.systemui.communal.dagger.Communal @@ -259,15 +262,33 @@ constructor( // Run when the touch handling lifecycle is RESUMED, meaning the hub is visible and not // occluded. lifecycleRegistry.repeatOnLifecycle(Lifecycle.State.RESUMED) { - val exclusionRect = - Rect( - 0, - topEdgeSwipeRegionWidth, - containerView.right, - containerView.bottom - bottomEdgeSwipeRegionWidth - ) + // Avoid adding exclusion to right/left edges to allow back gestures. + val insets = + if (glanceableHubBackGesture()) { + containerView.rootWindowInsets.getInsets(WindowInsets.Type.systemGestures()) + } else { + Insets.NONE + } - containerView.systemGestureExclusionRects = listOf(exclusionRect) + containerView.systemGestureExclusionRects = + listOf( + // Only allow swipe up to bouncer and swipe down to shade in the very + // top/bottom to avoid conflicting with widgets in the hub grid. + Rect( + insets.left, + topEdgeSwipeRegionWidth, + containerView.right - insets.right, + containerView.bottom - bottomEdgeSwipeRegionWidth + ), + // Disable back gestures on the left side of the screen, to avoid + // conflicting with scene transitions. + Rect( + 0, + 0, + insets.right, + containerView.bottom, + ) + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java index f67d0c185e0f..bc5cf2a87925 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java @@ -644,7 +644,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW mCurrentState.bouncerShowing, mCurrentState.dozing, mCurrentState.shadeOrQsExpanded, - mCurrentState.dreaming); + mCurrentState.dreaming, + mCurrentState.communalVisible); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java index da91d6a05d6b..6ac7f11d3ad8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java @@ -26,5 +26,5 @@ public interface StatusBarWindowCallback { */ void onStateChanged(boolean keyguardShowing, boolean keyguardOccluded, boolean keyguardGoingAway, boolean bouncerShowing, boolean isDozing, - boolean panelExpanded, boolean isDreaming); + boolean panelExpanded, boolean isDreaming, boolean communalShowing); } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java index 8457bdb2d0ff..45799b255630 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java @@ -261,7 +261,7 @@ public class BubblesManager { // Store callback in a field so it won't get GC'd mStatusBarWindowCallback = (keyguardShowing, keyguardOccluded, keyguardGoingAway, bouncerShowing, isDozing, - panelExpanded, isDreaming) -> { + panelExpanded, isDreaming, communalShowing) -> { if (panelExpanded != mPanelExpanded) { mPanelExpanded = panelExpanded; mBubbles.onNotificationPanelExpandedChanged(panelExpanded); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt index b0213a4b6421..169511f827ef 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt @@ -16,19 +16,24 @@ package com.android.systemui.shade +import android.graphics.Insets import android.graphics.Rect import android.os.PowerManager +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.testing.ViewUtils import android.view.MotionEvent import android.view.View +import android.view.WindowInsets import android.widget.FrameLayout import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.test.filters.SmallTest import com.android.compose.animation.scene.SceneKey import com.android.systemui.Flags +import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_BACK_GESTURE import com.android.systemui.SysuiTestCase import com.android.systemui.ambient.touch.TouchHandler import com.android.systemui.ambient.touch.TouchMonitor @@ -66,6 +71,9 @@ import org.mockito.ArgumentMatchers.anyFloat import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.spy @ExperimentalCoroutinesApi @RunWith(AndroidTestingRunner::class) @@ -317,6 +325,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } @Test + @DisableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE) fun gestureExclusionZone_setAfterInit() = with(kosmos) { testScope.runTest { @@ -325,10 +334,41 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { assertThat(containerView.systemGestureExclusionRects) .containsExactly( Rect( - /* left */ 0, - /* top */ TOP_SWIPE_REGION_WIDTH, - /* right */ CONTAINER_WIDTH, - /* bottom */ CONTAINER_HEIGHT - BOTTOM_SWIPE_REGION_WIDTH + /* left= */ 0, + /* top= */ TOP_SWIPE_REGION_WIDTH, + /* right= */ CONTAINER_WIDTH, + /* bottom= */ CONTAINER_HEIGHT - BOTTOM_SWIPE_REGION_WIDTH + ), + Rect( + /* left= */ 0, + /* top= */ 0, + /* right= */ 0, + /* bottom= */ CONTAINER_HEIGHT + ) + ) + } + } + + @Test + @EnableFlags(FLAG_GLANCEABLE_HUB_BACK_GESTURE) + fun gestureExclusionZone_setAfterInit_backGestureEnabled() = + with(kosmos) { + testScope.runTest { + goToScene(CommunalScenes.Communal) + + assertThat(containerView.systemGestureExclusionRects) + .containsExactly( + Rect( + /* left= */ FAKE_INSETS.left, + /* top= */ TOP_SWIPE_REGION_WIDTH, + /* right= */ CONTAINER_WIDTH - FAKE_INSETS.right, + /* bottom= */ CONTAINER_HEIGHT - BOTTOM_SWIPE_REGION_WIDTH + ), + Rect( + /* left= */ 0, + /* top= */ 0, + /* right= */ FAKE_INSETS.right, + /* bottom= */ CONTAINER_HEIGHT ) ) } @@ -340,6 +380,9 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { testScope.runTest { goToScene(CommunalScenes.Communal) + // Exclusion rect is set. + assertThat(containerView.systemGestureExclusionRects).isNotEmpty() + // Shade shows up. shadeTestUtil.setQsExpansion(1.0f) testableLooper.processAllMessages() @@ -355,6 +398,9 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { testScope.runTest { goToScene(CommunalScenes.Communal) + // Exclusion rect is set. + assertThat(containerView.systemGestureExclusionRects).isNotEmpty() + // Bouncer is visible. fakeKeyguardBouncerRepository.setPrimaryShow(true) testableLooper.processAllMessages() @@ -371,7 +417,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { goToScene(CommunalScenes.Communal) // Exclusion rect is set. - assertThat(containerView.systemGestureExclusionRects).hasSize(1) + assertThat(containerView.systemGestureExclusionRects).isNotEmpty() // Leave the hub. goToScene(CommunalScenes.Blank) @@ -399,7 +445,12 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } private fun initAndAttachContainerView() { - containerView = View(context) + val mockInsets = + mock<WindowInsets> { + on { getInsets(WindowInsets.Type.systemGestures()) } doReturn FAKE_INSETS + } + + containerView = spy(View(context)) { on { rootWindowInsets } doReturn mockInsets } parentView = FrameLayout(context) @@ -422,6 +473,7 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { private const val RIGHT_SWIPE_REGION_WIDTH = 20 private const val TOP_SWIPE_REGION_WIDTH = 12 private const val BOTTOM_SWIPE_REGION_WIDTH = 14 + private val FAKE_INSETS = Insets.of(10, 20, 30, 50) /** * A touch down event right in the middle of the screen, to avoid being in any of the swipe |