summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt33
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryImplTest.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt5
9 files changed, 109 insertions, 7 deletions
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 b632a8aaad46..af895c82c975 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
@@ -21,6 +21,8 @@ package com.android.systemui.scene.ui.viewmodel
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.MotionEvent
+import android.view.MotionEvent.ACTION_DOWN
+import android.view.MotionEvent.ACTION_OUTSIDE
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -43,6 +45,7 @@ import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.shared.flag.DualShade
import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.statusbar.data.repository.fakeRemoteInputRepository
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -68,6 +71,7 @@ class SceneContainerViewModelTest : SysuiTestCase() {
private val fakeSceneDataSource by lazy { kosmos.fakeSceneDataSource }
private val fakeShadeRepository by lazy { kosmos.fakeShadeRepository }
private val sceneContainerConfig by lazy { kosmos.sceneContainerConfig }
+ private val fakeRemoteInputRepository by lazy { kosmos.fakeRemoteInputRepository }
private val falsingManager by lazy { kosmos.fakeFalsingManager }
private val view = mock<View>()
@@ -234,6 +238,35 @@ class SceneContainerViewModelTest : SysuiTestCase() {
}
@Test
+ fun userInputOnEmptySpace_insideEvent() =
+ testScope.runTest {
+ assertThat(fakeRemoteInputRepository.areRemoteInputsClosed).isFalse()
+ val insideMotionEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0f, 0f, 0)
+ underTest.onEmptySpaceMotionEvent(insideMotionEvent)
+ assertThat(fakeRemoteInputRepository.areRemoteInputsClosed).isFalse()
+ }
+
+ @Test
+ fun userInputOnEmptySpace_outsideEvent_remoteInputActive() =
+ testScope.runTest {
+ fakeRemoteInputRepository.isRemoteInputActive.value = true
+ assertThat(fakeRemoteInputRepository.areRemoteInputsClosed).isFalse()
+ val outsideMotionEvent = MotionEvent.obtain(0, 0, ACTION_OUTSIDE, 0f, 0f, 0)
+ underTest.onEmptySpaceMotionEvent(outsideMotionEvent)
+ assertThat(fakeRemoteInputRepository.areRemoteInputsClosed).isTrue()
+ }
+
+ @Test
+ fun userInputOnEmptySpace_outsideEvent_remoteInputInactive() =
+ testScope.runTest {
+ fakeRemoteInputRepository.isRemoteInputActive.value = false
+ assertThat(fakeRemoteInputRepository.areRemoteInputsClosed).isFalse()
+ val outsideMotionEvent = MotionEvent.obtain(0, 0, ACTION_OUTSIDE, 0f, 0f, 0)
+ underTest.onEmptySpaceMotionEvent(outsideMotionEvent)
+ assertThat(fakeRemoteInputRepository.areRemoteInputsClosed).isFalse()
+ }
+
+ @Test
fun remoteUserInteraction_keepsContainerVisible() =
testScope.runTest {
sceneInteractor.setVisible(false, "reason")
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryImplTest.kt
index f7a8858a2741..3b720ef30db7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryImplTest.kt
@@ -50,7 +50,7 @@ class RemoteInputRepositoryImplTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
testScope = TestScope()
- underTest = RemoteInputRepositoryImpl(remoteInputManager)
+ underTest = RemoteInputRepositoryImpl(testScope.backgroundScope, remoteInputManager)
}
@Test
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
index cd38ca6cadef..7d7cab41cf96 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
@@ -77,6 +77,11 @@ class SceneWindowRootView(context: Context, attrs: AttributeSet?) : WindowRootVi
}
}
+ override fun onTouchEvent(event: MotionEvent?): Boolean {
+ event?.let { motionEventHandler?.onEmptySpaceMotionEvent(it) }
+ return super.onTouchEvent(event)
+ }
+
companion object {
private const val TAG = "SceneWindowRootView"
}
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 82f65cf55211..32d5cb460cd8 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
@@ -39,6 +39,7 @@ import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.Overlay
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.statusbar.domain.interactor.RemoteInputInteractor
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -48,7 +49,6 @@ import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Models UI state for the scene container. */
class SceneContainerViewModel
@@ -58,6 +58,7 @@ constructor(
private val falsingInteractor: FalsingInteractor,
private val powerInteractor: PowerInteractor,
shadeInteractor: ShadeInteractor,
+ private val remoteInputInteractor: RemoteInputInteractor,
private val splitEdgeDetector: SplitEdgeDetector,
private val logger: SceneLogger,
hapticsViewModelFactory: SceneContainerHapticsViewModel.Factory,
@@ -101,6 +102,10 @@ constructor(
this@SceneContainerViewModel.onMotionEvent(motionEvent)
}
+ override fun onEmptySpaceMotionEvent(motionEvent: MotionEvent) {
+ this@SceneContainerViewModel.onEmptySpaceMotionEvent(motionEvent)
+ }
+
override fun onMotionEventComplete() {
this@SceneContainerViewModel.onMotionEventComplete()
}
@@ -147,6 +152,23 @@ constructor(
}
/**
+ * Notifies that a [MotionEvent] has propagated through the entire [SharedNotificationContainer]
+ * and Composable scene container hierarchy without being handled.
+ *
+ * Call this after the [MotionEvent] has finished propagating through the UI hierarchy.
+ */
+ fun onEmptySpaceMotionEvent(event: MotionEvent) {
+ // check if the touch is outside the window and if remote input is active.
+ // If true, close any active remote inputs.
+ if (
+ event.action == MotionEvent.ACTION_OUTSIDE &&
+ (remoteInputInteractor.isRemoteInputActive as StateFlow).value
+ ) {
+ remoteInputInteractor.closeRemoteInputs()
+ }
+ }
+
+ /**
* Notifies that a scene container user interaction has begun.
*
* This is a user interaction that has reached the Composable hierarchy of the scene container,
@@ -263,6 +285,9 @@ constructor(
/** Notifies that a [MotionEvent] has occurred. */
fun onMotionEvent(motionEvent: MotionEvent)
+ /** Notifies that a [MotionEvent] has occurred outside the root window. */
+ fun onEmptySpaceMotionEvent(motionEvent: MotionEvent)
+
/**
* Notifies that the previous [MotionEvent] reported by [onMotionEvent] has finished
* processing.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 9b1e782fcba4..fdc1c0e4dd22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -53,6 +53,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.dagger.CentralSurfacesDependenciesModule;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
@@ -686,6 +687,7 @@ public class NotificationRemoteInputManager implements CoreStartable {
}
public void checkRemoteInputOutside(MotionEvent event) {
+ SceneContainerFlag.assertInLegacyMode();
if (event.getAction() == MotionEvent.ACTION_OUTSIDE // touch outside the source bar
&& event.getX() == 0 && event.getY() == 0 // a touch outside both bars
&& isRemoteInputActive()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt
index 9af4b8c18c86..cd1e2ceeca00 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/RemoteInputRepository.kt
@@ -16,16 +16,21 @@
package com.android.systemui.statusbar.data.repository
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.NotificationRemoteInputManager
import com.android.systemui.statusbar.RemoteInputController
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import dagger.Binds
import dagger.Module
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.stateIn
/**
* Repository used for tracking the state of notification remote input (e.g. when the user presses
@@ -42,30 +47,50 @@ interface RemoteInputRepository {
val remoteInputRowBottomBound: Flow<Float?>
fun setRemoteInputRowBottomBound(bottom: Float?)
+
+ /** Close any active remote inputs */
+ fun closeRemoteInputs()
}
@SysUISingleton
class RemoteInputRepositoryImpl
@Inject
-constructor(private val notificationRemoteInputManager: NotificationRemoteInputManager) :
- RemoteInputRepository {
- override val isRemoteInputActive: Flow<Boolean> = conflatedCallbackFlow {
- trySend(false) // initial value is false
+constructor(
+ @Application applicationScope: CoroutineScope,
+ private val notificationRemoteInputManager: NotificationRemoteInputManager,
+) : RemoteInputRepository {
+ private val _isRemoteInputActive = conflatedCallbackFlow {
val callback =
object : RemoteInputController.Callback {
override fun onRemoteInputActive(active: Boolean) {
trySend(active)
}
}
+ trySend(notificationRemoteInputManager.isRemoteInputActive)
notificationRemoteInputManager.addControllerCallback(callback)
awaitClose { notificationRemoteInputManager.removeControllerCallback(callback) }
}
+ override val isRemoteInputActive =
+ if (SceneContainerFlag.isEnabled) {
+ _isRemoteInputActive.stateIn(
+ applicationScope,
+ SharingStarted.WhileSubscribed(),
+ notificationRemoteInputManager.isRemoteInputActive,
+ )
+ } else {
+ _isRemoteInputActive
+ }
+
override val remoteInputRowBottomBound = MutableStateFlow<Float?>(null)
override fun setRemoteInputRowBottomBound(bottom: Float?) {
remoteInputRowBottomBound.value = bottom
}
+
+ override fun closeRemoteInputs() {
+ notificationRemoteInputManager.closeRemoteInputs()
+ }
}
@Module
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt
index b83b0cc8d2c9..ea8c3e2cef91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/RemoteInputInteractor.kt
@@ -40,4 +40,9 @@ constructor(private val remoteInputRepository: RemoteInputRepository) {
fun setRemoteInputRowBottomBound(bottom: Float?) {
remoteInputRepository.setRemoteInputRowBottomBound(bottom)
}
+
+ /** Close any active remote inputs */
+ fun closeRemoteInputs() {
+ remoteInputRepository.closeRemoteInputs()
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
index 4f414d995aab..3300c96b87fd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneKosmos.kt
@@ -17,6 +17,7 @@ import com.android.systemui.scene.ui.viewmodel.SceneContainerHapticsViewModel
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.scene.ui.viewmodel.splitEdgeDetector
import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.domain.interactor.remoteInputInteractor
import kotlinx.coroutines.flow.MutableStateFlow
import org.mockito.kotlin.mock
@@ -87,6 +88,7 @@ val Kosmos.sceneContainerViewModelFactory by Fixture {
falsingInteractor = falsingInteractor,
powerInteractor = powerInteractor,
shadeInteractor = shadeInteractor,
+ remoteInputInteractor = remoteInputInteractor,
splitEdgeDetector = splitEdgeDetector,
logger = sceneLogger,
hapticsViewModelFactory = sceneContainerHapticsViewModelFactory,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt
index 91602c23ac17..375bede594b3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeRemoteInputRepository.kt
@@ -23,6 +23,11 @@ import kotlinx.coroutines.flow.flowOf
class FakeRemoteInputRepository : RemoteInputRepository {
override val isRemoteInputActive = MutableStateFlow(false)
override val remoteInputRowBottomBound: Flow<Float?> = flowOf(null)
+ var areRemoteInputsClosed: Boolean = false
override fun setRemoteInputRowBottomBound(bottom: Float?) {}
+
+ override fun closeRemoteInputs() {
+ areRemoteInputsClosed = true
+ }
}