Loading packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt +33 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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>() Loading Loading @@ -233,6 +237,35 @@ class SceneContainerViewModelTest : SysuiTestCase() { assertThat(kosmos.fakePowerRepository.userTouchRegistered).isTrue() } @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 { Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryImplTest.kt +1 −1 Original line number Diff line number Diff line Loading @@ -50,7 +50,7 @@ class RemoteInputRepositoryImplTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) testScope = TestScope() underTest = RemoteInputRepositoryImpl(remoteInputManager) underTest = RemoteInputRepositoryImpl(testScope.backgroundScope, remoteInputManager) } @Test Loading packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt +5 −0 Original line number Diff line number Diff line Loading @@ -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" } Loading packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt +26 −1 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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, Loading Loading @@ -101,6 +102,10 @@ constructor( this@SceneContainerViewModel.onMotionEvent(motionEvent) } override fun onEmptySpaceMotionEvent(motionEvent: MotionEvent) { this@SceneContainerViewModel.onEmptySpaceMotionEvent(motionEvent) } override fun onMotionEventComplete() { this@SceneContainerViewModel.onMotionEventComplete() } Loading Loading @@ -146,6 +151,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. * Loading Loading @@ -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. Loading packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +2 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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()) { Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt +33 −0 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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>() Loading Loading @@ -233,6 +237,35 @@ class SceneContainerViewModelTest : SysuiTestCase() { assertThat(kosmos.fakePowerRepository.userTouchRegistered).isTrue() } @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 { Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/RemoteInputRepositoryImplTest.kt +1 −1 Original line number Diff line number Diff line Loading @@ -50,7 +50,7 @@ class RemoteInputRepositoryImplTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) testScope = TestScope() underTest = RemoteInputRepositoryImpl(remoteInputManager) underTest = RemoteInputRepositoryImpl(testScope.backgroundScope, remoteInputManager) } @Test Loading
packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt +5 −0 Original line number Diff line number Diff line Loading @@ -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" } Loading
packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt +26 −1 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading @@ -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, Loading Loading @@ -101,6 +102,10 @@ constructor( this@SceneContainerViewModel.onMotionEvent(motionEvent) } override fun onEmptySpaceMotionEvent(motionEvent: MotionEvent) { this@SceneContainerViewModel.onEmptySpaceMotionEvent(motionEvent) } override fun onMotionEventComplete() { this@SceneContainerViewModel.onMotionEventComplete() } Loading Loading @@ -146,6 +151,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. * Loading Loading @@ -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. Loading
packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +2 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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()) { Loading