Merge "[flexiglass] Reimplement closing guts on outside touch in flexiglass" into main
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index d5874d1..e17cb31 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -16,6 +16,8 @@
package com.android.systemui.scene.ui.composable
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Text
@@ -28,6 +30,7 @@
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.pointer.pointerInput
import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayout
@@ -100,7 +103,13 @@
}
Box(
- modifier = Modifier.fillMaxSize(),
+ modifier =
+ Modifier.fillMaxSize().pointerInput(Unit) {
+ awaitEachGesture {
+ awaitFirstDown(false)
+ viewModel.onSceneContainerUserInputStarted()
+ }
+ },
) {
SceneTransitionLayout(state = state, modifier = modifier.fillMaxSize()) {
sceneByKey.forEach { (sceneKey, composableScene) ->
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index e3a69a9..35cefa6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -401,10 +401,10 @@
underTest.setVisible(false, "reason")
val isVisible by collectLastValue(underTest.isVisible)
assertThat(isVisible).isFalse()
- underTest.onRemoteUserInteractionStarted("reason")
+ underTest.onRemoteUserInputStarted("reason")
assertThat(isVisible).isTrue()
- underTest.onUserInteractionFinished()
+ underTest.onUserInputFinished()
assertThat(isVisible).isFalse()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index f856c55..832e7b1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -237,7 +237,7 @@
sceneInteractor.setVisible(false, "reason")
runCurrent()
assertThat(underTest.isVisible).isFalse()
- sceneInteractor.onRemoteUserInteractionStarted("reason")
+ sceneInteractor.onRemoteUserInputStarted("reason")
runCurrent()
assertThat(underTest.isVisible).isTrue()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt
index 1356e93..06a2c5a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt
@@ -21,6 +21,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds
import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimRounding
@@ -84,4 +85,48 @@
)
)
}
+
+ @Test
+ fun shouldCloseGuts_userInputOngoing_currentGestureInGuts() =
+ testScope.runTest {
+ val shouldCloseGuts by collectLastValue(underTest.shouldCloseGuts)
+
+ kosmos.sceneInteractor.onSceneContainerUserInputStarted()
+ underTest.setCurrentGestureInGuts(true)
+
+ assertThat(shouldCloseGuts).isFalse()
+ }
+
+ @Test
+ fun shouldCloseGuts_userInputOngoing_currentGestureNotInGuts() =
+ testScope.runTest {
+ val shouldCloseGuts by collectLastValue(underTest.shouldCloseGuts)
+
+ kosmos.sceneInteractor.onSceneContainerUserInputStarted()
+ underTest.setCurrentGestureInGuts(false)
+
+ assertThat(shouldCloseGuts).isTrue()
+ }
+
+ @Test
+ fun shouldCloseGuts_userInputNotOngoing_currentGestureInGuts() =
+ testScope.runTest {
+ val shouldCloseGuts by collectLastValue(underTest.shouldCloseGuts)
+
+ kosmos.sceneInteractor.onUserInputFinished()
+ underTest.setCurrentGestureInGuts(true)
+
+ assertThat(shouldCloseGuts).isFalse()
+ }
+
+ @Test
+ fun shouldCloseGuts_userInputNotOngoing_currentGestureNotInGuts() =
+ testScope.runTest {
+ val shouldCloseGuts by collectLastValue(underTest.shouldCloseGuts)
+
+ kosmos.sceneInteractor.onUserInputFinished()
+ underTest.setCurrentGestureInGuts(false)
+
+ assertThat(shouldCloseGuts).isFalse()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index a37a722..fe5cbb1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -230,7 +230,7 @@
// If scene framework is enabled, set the scene container window to
// visible and let the touch "slip" into that window.
if (SceneContainerFlag.isEnabled()) {
- mSceneInteractor.get().onRemoteUserInteractionStarted("launcher swipe");
+ mSceneInteractor.get().onRemoteUserInputStarted("launcher swipe");
} else {
mShadeViewControllerLazy.get().startInputFocusTransfer();
}
@@ -266,7 +266,7 @@
if (SceneContainerFlag.isEnabled()) {
int action = event.getActionMasked();
if (action == ACTION_DOWN) {
- mSceneInteractor.get().onRemoteUserInteractionStarted(
+ mSceneInteractor.get().onRemoteUserInputStarted(
"trackpad swipe");
} else if (action == ACTION_UP) {
mSceneInteractor.get().changeScene(
diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
index 3e2c630..beb6816 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
@@ -56,7 +56,10 @@
*
* For more information see the logic in `SceneInteractor` that mutates this.
*/
- val isRemoteUserInteractionOngoing = MutableStateFlow(false)
+ val isRemoteUserInputOngoing = MutableStateFlow(false)
+
+ /** Whether there's ongoing user input on the scene container Composable hierarchy */
+ val isSceneContainerUserInputOngoing = MutableStateFlow(false)
private val defaultTransitionState = ObservableTransitionState.Idle(config.initialSceneKey)
private val _transitionState = MutableStateFlow<Flow<ObservableTransitionState>?>(null)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 1b9c346..4c404e2 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -148,11 +148,11 @@
val isVisible: StateFlow<Boolean> =
combine(
repository.isVisible,
- repository.isRemoteUserInteractionOngoing,
+ repository.isRemoteUserInputOngoing,
) { isVisible, isRemoteUserInteractionOngoing ->
isVisibleInternal(
raw = isVisible,
- isRemoteUserInteractionOngoing = isRemoteUserInteractionOngoing,
+ isRemoteUserInputOngoing = isRemoteUserInteractionOngoing,
)
}
.stateIn(
@@ -162,8 +162,13 @@
)
/** Whether there's an ongoing remotely-initiated user interaction. */
- val isRemoteUserInteractionOngoing: StateFlow<Boolean> =
- repository.isRemoteUserInteractionOngoing
+ val isRemoteUserInteractionOngoing: StateFlow<Boolean> = repository.isRemoteUserInputOngoing
+
+ /**
+ * Whether there's an ongoing user interaction started in the scene container Compose hierarchy.
+ */
+ val isSceneContainerUserInputOngoing: StateFlow<Boolean> =
+ repository.isSceneContainerUserInputOngoing
/**
* The amount of transition into or out of the given [scene].
@@ -284,7 +289,7 @@
* Please do not call this from outside of the scene framework. If you are trying to force the
* visibility to visible or invisible, prefer making changes to the existing caller of this
* method or to upstream state used to calculate [isVisible]; for an example of the latter,
- * please see [onRemoteUserInteractionStarted] and [onUserInteractionFinished].
+ * please see [onRemoteUserInputStarted] and [onUserInputFinished].
*/
fun setVisible(isVisible: Boolean, loggingReason: String) {
val wasVisible = repository.isVisible.value
@@ -301,6 +306,16 @@
}
/**
+ * Notifies that a scene container user interaction has begun.
+ *
+ * This is a user interaction that originates within the Composable hierarchy of the scene
+ * container.
+ */
+ fun onSceneContainerUserInputStarted() {
+ repository.isSceneContainerUserInputOngoing.value = true
+ }
+
+ /**
* Notifies that a remote user interaction has begun.
*
* This is a user interaction that originates outside of the UI of the scene container and
@@ -311,18 +326,19 @@
* then rerouted by window manager to System UI. While the user interaction definitely continues
* within the System UI process and code, it also originates remotely.
*/
- fun onRemoteUserInteractionStarted(loggingReason: String) {
- logger.logRemoteUserInteractionStarted(loggingReason)
- repository.isRemoteUserInteractionOngoing.value = true
+ fun onRemoteUserInputStarted(loggingReason: String) {
+ logger.logRemoteUserInputStarted(loggingReason)
+ repository.isRemoteUserInputOngoing.value = true
}
/**
* Notifies that the current user interaction (internally or remotely started, see
- * [onRemoteUserInteractionStarted]) has finished.
+ * [onSceneContainerUserInputStarted] and [onRemoteUserInputStarted]) has finished.
*/
- fun onUserInteractionFinished() {
- logger.logUserInteractionFinished()
- repository.isRemoteUserInteractionOngoing.value = false
+ fun onUserInputFinished() {
+ logger.logUserInputFinished()
+ repository.isSceneContainerUserInputOngoing.value = false
+ repository.isRemoteUserInputOngoing.value = false
}
/**
@@ -351,9 +367,9 @@
private fun isVisibleInternal(
raw: Boolean = repository.isVisible.value,
- isRemoteUserInteractionOngoing: Boolean = repository.isRemoteUserInteractionOngoing.value,
+ isRemoteUserInputOngoing: Boolean = repository.isRemoteUserInputOngoing.value,
): Boolean {
- return raw || isRemoteUserInteractionOngoing
+ return raw || isRemoteUserInputOngoing
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
index 94c94e2..045a887 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
@@ -115,7 +115,7 @@
)
}
- fun logRemoteUserInteractionStarted(
+ fun logRemoteUserInputStarted(
reason: String,
) {
logBuffer.log(
@@ -126,7 +126,7 @@
)
}
- fun logUserInteractionFinished() {
+ fun logUserInputFinished() {
logBuffer.log(
tag = TAG,
level = LogLevel.INFO,
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index 9dfb745..8b4b77f 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -31,6 +31,7 @@
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -97,7 +98,9 @@
}
/**
- * Notifies that a [MotionEvent] is first seen at the top of the scene container UI.
+ * Notifies that a [MotionEvent] is first seen at the top of the scene container UI. This
+ * includes gestures on [SharedNotificationContainer] as well as the Composable scene container
+ * hierarchy.
*
* Call this before the [MotionEvent] starts to propagate through the UI hierarchy.
*/
@@ -108,11 +111,21 @@
event.actionMasked == MotionEvent.ACTION_UP ||
event.actionMasked == MotionEvent.ACTION_CANCEL
) {
- sceneInteractor.onUserInteractionFinished()
+ sceneInteractor.onUserInputFinished()
}
}
/**
+ * Notifies that a scene container user interaction has begun.
+ *
+ * This is a user interaction that has reached the Composable hierarchy of the scene container,
+ * rather than being handled by [SharedNotificationContainer].
+ */
+ fun onSceneContainerUserInputStarted() {
+ sceneInteractor.onSceneContainerUserInputStarted()
+ }
+
+ /**
* Notifies that a [MotionEvent] that was previously sent to [onMotionEvent] has passed through
* the scene container UI.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index d0c51bc2..bf00a39 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -1255,6 +1255,11 @@
}
@Override
+ public void closeGutsOnSceneTouch() {
+ mController.closeControlsDueToOutsideTouch();
+ }
+
+ @Override
public void setSyntheticScrollConsumer(@Nullable Consumer<Float> consumer) {
mScrollViewFields.setSyntheticScrollConsumer(consumer);
}
@@ -1265,6 +1270,11 @@
}
@Override
+ public void setCurrentGestureInGutsConsumer(@Nullable Consumer<Boolean> consumer) {
+ mScrollViewFields.setCurrentGestureInGutsConsumer(consumer);
+ }
+
+ @Override
public void setHeadsUpHeightConsumer(@Nullable Consumer<Float> consumer) {
mScrollViewFields.setHeadsUpHeightConsumer(consumer);
}
@@ -3548,33 +3558,41 @@
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
- if (SceneContainerFlag.isEnabled() && mIsBeingDragged) {
+ if (SceneContainerFlag.isEnabled()) {
int action = ev.getActionMasked();
- boolean isUpOrCancel = action == ACTION_UP || action == ACTION_CANCEL;
- if (mSendingTouchesToSceneFramework) {
- MotionEvent adjustedEvent = MotionEvent.obtain(ev);
- adjustedEvent.setLocation(ev.getRawX(), ev.getRawY());
- mController.sendTouchToSceneFramework(adjustedEvent);
- mScrollViewFields.sendCurrentGestureOverscroll(
- getExpandedInThisMotion() && !isUpOrCancel);
- adjustedEvent.recycle();
- } else if (!isUpOrCancel) {
- // if this is the first touch being sent to the scene framework,
- // convert it into a synthetic DOWN event.
- mSendingTouchesToSceneFramework = true;
- MotionEvent downEvent = MotionEvent.obtain(ev);
- downEvent.setAction(MotionEvent.ACTION_DOWN);
- downEvent.setLocation(ev.getRawX(), ev.getRawY());
- mController.sendTouchToSceneFramework(downEvent);
- mScrollViewFields.sendCurrentGestureOverscroll(getExpandedInThisMotion());
- downEvent.recycle();
+ boolean isTouchInGuts = mController.isTouchInGutsView(ev);
+ if (action == MotionEvent.ACTION_DOWN && !isTouchInGuts) {
+ mController.closeControlsDueToOutsideTouch();
}
+ if (mIsBeingDragged) {
+ boolean isUpOrCancel = action == ACTION_UP || action == ACTION_CANCEL;
+ if (mSendingTouchesToSceneFramework) {
+ MotionEvent adjustedEvent = MotionEvent.obtain(ev);
+ adjustedEvent.setLocation(ev.getRawX(), ev.getRawY());
+ mScrollViewFields.sendCurrentGestureOverscroll(
+ getExpandedInThisMotion() && !isUpOrCancel);
+ mController.sendTouchToSceneFramework(adjustedEvent);
+ adjustedEvent.recycle();
+ } else if (!isUpOrCancel) {
+ // if this is the first touch being sent to the scene framework,
+ // convert it into a synthetic DOWN event.
+ mSendingTouchesToSceneFramework = true;
+ MotionEvent downEvent = MotionEvent.obtain(ev);
+ downEvent.setAction(MotionEvent.ACTION_DOWN);
+ downEvent.setLocation(ev.getRawX(), ev.getRawY());
+ mScrollViewFields.sendCurrentGestureInGuts(isTouchInGuts);
+ mScrollViewFields.sendCurrentGestureOverscroll(getExpandedInThisMotion());
+ mController.sendTouchToSceneFramework(downEvent);
+ downEvent.recycle();
+ }
- if (isUpOrCancel) {
- mScrollViewFields.sendCurrentGestureOverscroll(false);
- setIsBeingDragged(false);
+ if (isUpOrCancel) {
+ mScrollViewFields.sendCurrentGestureInGuts(false);
+ mScrollViewFields.sendCurrentGestureOverscroll(false);
+ setIsBeingDragged(false);
+ }
+ return false;
}
- return false;
}
return TouchLogger.logDispatchTouch(TAG, ev, super.dispatchTouchEvent(ev));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index c25b30d..4e73529 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -1690,7 +1690,7 @@
mVisibilityProvider.obtain(entry, true));
}
- public void closeControlsIfOutsideTouch(MotionEvent ev) {
+ private View getGutsView() {
NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
View translatingParentView = mSwipeHelper.getTranslatingParentView();
@@ -1703,15 +1703,35 @@
// Checking menu
view = translatingParentView;
}
+ return view;
+ }
+
+ public void closeControlsIfOutsideTouch(MotionEvent ev) {
+ SceneContainerFlag.assertInLegacyMode();
+ View view = getGutsView();
if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
// Touch was outside visible guts / menu notification, close what's visible
- mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
- false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
- false /* resetMenu */);
- mSwipeHelper.resetExposedMenuView(true /* animate */, true /* force */);
+ closeAndSaveGuts();
}
}
+ void closeControlsDueToOutsideTouch() {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
+ closeAndSaveGuts();
+ }
+
+ private void closeAndSaveGuts() {
+ mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
+ false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
+ false /* resetMenu */);
+ mSwipeHelper.resetExposedMenuView(true /* animate */, true /* force */);
+ }
+
+ boolean isTouchInGutsView(MotionEvent event) {
+ View view = getGutsView();
+ return NotificationSwipeHelper.isTouchInView(event, view);
+ }
+
public void clearSilentNotifications() {
FooterViewRefactor.assertInLegacyMode();
// Leave the shade open if there will be other notifs left over to clear
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
index 383d8b3..aa39539 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ScrollViewFields.kt
@@ -53,6 +53,11 @@
*/
var currentGestureOverscrollConsumer: Consumer<Boolean>? = null
/**
+ * When a gesture is on open notification guts, which means scene container should not close the
+ * guts off of this gesture, we can notify the placeholder through here.
+ */
+ var currentGestureInGutsConsumer: Consumer<Boolean>? = null
+ /**
* Any time the heads up height is recalculated, it should be updated here to be used by the
* placeholder
*/
@@ -66,6 +71,10 @@
fun sendCurrentGestureOverscroll(isCurrentGestureOverscroll: Boolean) =
currentGestureOverscrollConsumer?.accept(isCurrentGestureOverscroll)
+ /** send [isCurrentGestureInGuts] to the [currentGestureInGutsConsumer], if present. */
+ fun sendCurrentGestureInGuts(isCurrentGestureInGuts: Boolean) =
+ currentGestureInGutsConsumer?.accept(isCurrentGestureInGuts)
+
/** send the [headsUpHeight] to the [headsUpHeightConsumer], if present. */
fun sendHeadsUpHeight(headsUpHeight: Float) = headsUpHeightConsumer?.accept(headsUpHeight)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
index f6d9351..4907d44 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
@@ -39,4 +39,7 @@
* consumed part of the gesture.
*/
val isCurrentGestureOverscroll = MutableStateFlow(false)
+
+ /** Whether the current touch gesture is on any open notification guts. */
+ val isCurrentGestureInGuts = MutableStateFlow(false)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
index 8557afc..756cd87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
@@ -18,6 +18,7 @@
package com.android.systemui.statusbar.notification.stack.domain.interactor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.statusbar.notification.stack.data.repository.NotificationPlaceholderRepository
@@ -39,6 +40,7 @@
constructor(
private val viewHeightRepository: NotificationViewHeightRepository,
private val placeholderRepository: NotificationPlaceholderRepository,
+ sceneInteractor: SceneInteractor,
shadeInteractor: ShadeInteractor,
) {
/** The bounds of the notification stack in the current scene. */
@@ -93,6 +95,15 @@
val isCurrentGestureOverscroll: Flow<Boolean> =
viewHeightRepository.isCurrentGestureOverscroll.asStateFlow()
+ /** Whether we should close any notification guts that are currently open. */
+ val shouldCloseGuts: Flow<Boolean> =
+ combine(
+ sceneInteractor.isSceneContainerUserInputOngoing,
+ viewHeightRepository.isCurrentGestureInGuts
+ ) { isUserInputOngoing, isCurrentGestureInGuts ->
+ isUserInputOngoing && !isCurrentGestureInGuts
+ }
+
/** Sets the alpha to apply to the NSSL for the brightness mirror */
fun setAlphaForBrightnessMirror(alpha: Float) {
placeholderRepository.alphaForBrightnessMirror.value = alpha
@@ -119,6 +130,10 @@
viewHeightRepository.isCurrentGestureOverscroll.value = isOverscroll
}
+ fun setCurrentGestureInGuts(isInGuts: Boolean) {
+ viewHeightRepository.isCurrentGestureInGuts.value = isInGuts
+ }
+
fun setConstrainedAvailableSpace(height: Int) {
placeholderRepository.constrainedAvailableSpace.value = height
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
index 1289cec..235b4da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
@@ -71,6 +71,9 @@
/** Set a consumer for current gesture overscroll events */
fun setCurrentGestureOverscrollConsumer(consumer: Consumer<Boolean>?)
+ /** Set a consumer for current gesture in guts events */
+ fun setCurrentGestureInGutsConsumer(consumer: Consumer<Boolean>?)
+
/** Set a consumer for heads up height changed events */
fun setHeadsUpHeightConsumer(consumer: Consumer<Float>?)
@@ -92,6 +95,12 @@
/** Gets the inset for HUNs when they are not visible */
fun getHeadsUpInset(): Int
+ /**
+ * Signals that any open Notification guts should be closed, as scene container is handling
+ * touch events.
+ */
+ fun closeGutsOnSceneTouch()
+
/** Adds a listener to be notified, when the stack height might have changed. */
fun addStackHeightChangedListener(runnable: Runnable)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index c044f6f..3cc6e81 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -36,6 +36,7 @@
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
/** Binds the [NotificationScrollView]. */
@@ -98,13 +99,18 @@
.filter { it }
.collect { view.setStackTop(-(view.getHeadsUpInset().toFloat())) }
}
+ launch {
+ viewModel.shouldCloseGuts.filter { it }.collect { view.closeGutsOnSceneTouch() }
+ }
launchAndDispose {
view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer)
view.setCurrentGestureOverscrollConsumer(viewModel.currentGestureOverscrollConsumer)
+ view.setCurrentGestureInGutsConsumer(viewModel.currentGestureInGutsConsumer)
DisposableHandle {
view.setSyntheticScrollConsumer(null)
view.setCurrentGestureOverscrollConsumer(null)
+ view.setCurrentGestureInGutsConsumer(null)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index b2045fe..3999578 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -136,6 +136,9 @@
val qsExpandFraction: Flow<Float> =
shadeInteractor.qsExpansion.dumpWhileCollecting("qsExpandFraction")
+ /** Whether we should close any open notification guts. */
+ val shouldCloseGuts: Flow<Boolean> = stackAppearanceInteractor.shouldCloseGuts
+
val shouldResetStackTop: Flow<Boolean> =
sceneInteractor.transitionState
.mapNotNull { state -> state is Idle && state.currentScene == Scenes.Gone }
@@ -202,6 +205,10 @@
val currentGestureOverscrollConsumer: (Boolean) -> Unit =
stackAppearanceInteractor::setCurrentGestureOverscroll
+ /** Receives whether the current touch gesture is inside any open guts. */
+ val currentGestureInGutsConsumer: (Boolean) -> Unit =
+ stackAppearanceInteractor::setCurrentGestureInGuts
+
/** Whether the notification stack is scrollable or not. */
val isScrollable: Flow<Boolean> =
sceneInteractor.currentScene
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt
index 230ddf9..48c2cc7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManagerTest.kt
@@ -56,11 +56,11 @@
runCurrent()
assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse()
- sceneRepository.isRemoteUserInteractionOngoing.value = true
+ sceneRepository.isRemoteUserInputOngoing.value = true
runCurrent()
assertThat(underTest.shouldMakeEntireScreenTouchable()).isTrue()
- sceneRepository.isRemoteUserInteractionOngoing.value = false
+ sceneRepository.isRemoteUserInputOngoing.value = false
runCurrent()
assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse()
}
@@ -71,7 +71,7 @@
testScope.runTest {
assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse()
- sceneRepository.isRemoteUserInteractionOngoing.value = true
+ sceneRepository.isRemoteUserInputOngoing.value = true
runCurrent()
assertThat(underTest.shouldMakeEntireScreenTouchable()).isFalse()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorKosmos.kt
index dbfd9de..2772d36 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorKosmos.kt
@@ -18,6 +18,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.notification.stack.data.repository.notificationPlaceholderRepository
import com.android.systemui.statusbar.notification.stack.data.repository.notificationViewHeightRepository
@@ -26,6 +27,7 @@
NotificationStackAppearanceInteractor(
viewHeightRepository = notificationViewHeightRepository,
placeholderRepository = notificationPlaceholderRepository,
+ sceneInteractor = sceneInteractor,
shadeInteractor = shadeInteractor,
)
}