Loading packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt +9 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.systemui.media.controls.ui.controller import android.content.Context import android.content.res.Configuration import android.graphics.Rect import android.view.View import android.view.ViewGroup import androidx.annotation.VisibleForTesting Loading Loading @@ -125,6 +126,7 @@ constructor( /** single pane media container placed at the top of the notifications list */ var singlePaneContainer: MediaContainerView? = null private set private var splitShadeContainer: ViewGroup? = null /** Loading Loading @@ -185,6 +187,13 @@ constructor( } } fun isWithinMediaViewBounds(x: Int, y: Int): Boolean { val bounds = Rect() mediaHost.hostView.getBoundsOnScreen(bounds) return bounds.contains(x, y) } fun refreshMediaPosition(reason: String) { val currentState = statusBarStateController.state Loading packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt +10 −1 Original line number Diff line number Diff line Loading @@ -55,10 +55,12 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.media.controls.ui.controller.KeyguardMediaController import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.SceneDataSourceDelegator import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf import com.android.systemui.util.kotlin.collectFlow Loading Loading @@ -88,6 +90,8 @@ constructor( private val communalContent: CommunalContent, @Communal private val dataSourceDelegator: SceneDataSourceDelegator, private val notificationStackScrollLayoutController: NotificationStackScrollLayoutController, private val keyguardMediaController: KeyguardMediaController, private val lockscreenSmartspaceController: LockscreenSmartspaceController ) : LifecycleOwner { private class CommunalWrapper(context: Context) : FrameLayout(context) { Loading Loading @@ -445,7 +449,12 @@ constructor( // the touch. if ( !hubShowing && !notificationStackScrollLayoutController.isBelowLastNotification(ev.x, ev.y) (!notificationStackScrollLayoutController.isBelowLastNotification(ev.x, ev.y) || keyguardMediaController.isWithinMediaViewBounds(ev.x.toInt(), ev.y.toInt()) || lockscreenSmartspaceController.isWithinSmartspaceBounds( ev.x.toInt(), ev.y.toInt() )) ) { return false } Loading packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt +16 −2 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.content.ContentResolver import android.content.Context import android.content.Intent import android.database.ContentObserver import android.graphics.Rect import android.net.Uri import android.os.Handler import android.os.UserHandle Loading Loading @@ -570,6 +571,20 @@ constructor( plugin?.unregisterListener(listener) } fun isWithinSmartspaceBounds(x: Int, y: Int): Boolean { smartspaceViews.forEach { val bounds = Rect() with(it as View) { this.getBoundsOnScreen(bounds) if (bounds.contains(x, y)) { return true } } } return false } private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean { if (isDateWeatherDecoupled && t.featureType == SmartspaceTarget.FEATURE_WEATHER) { return false Loading Loading @@ -705,4 +720,3 @@ constructor( } } } packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt +36 −2 Original line number Diff line number Diff line Loading @@ -57,9 +57,11 @@ import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.media.controls.controller.keyguardMediaController import com.android.systemui.res.R import com.android.systemui.scene.shared.model.sceneDataSourceDelegator import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.statusbar.lockscreen.lockscreenSmartspaceController import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat Loading Loading @@ -134,7 +136,9 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { ambientTouchComponentFactory, communalContent, kosmos.sceneDataSourceDelegator, kosmos.notificationStackScrollLayoutController kosmos.notificationStackScrollLayoutController, kosmos.keyguardMediaController, kosmos.lockscreenSmartspaceController ) } testableLooper = TestableLooper.get(this) Loading Loading @@ -178,7 +182,9 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { ambientTouchComponentFactory, communalContent, kosmos.sceneDataSourceDelegator, kosmos.notificationStackScrollLayoutController kosmos.notificationStackScrollLayoutController, kosmos.keyguardMediaController, kosmos.lockscreenSmartspaceController ) // First call succeeds. Loading @@ -205,6 +211,8 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { communalContent, kosmos.sceneDataSourceDelegator, kosmos.notificationStackScrollLayoutController, kosmos.keyguardMediaController, kosmos.lockscreenSmartspaceController ) assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.INITIALIZED) Loading @@ -226,6 +234,8 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { communalContent, kosmos.sceneDataSourceDelegator, kosmos.notificationStackScrollLayoutController, kosmos.keyguardMediaController, kosmos.lockscreenSmartspaceController ) // Only initView without attaching a view as we don't want the flows to start collecting Loading Loading @@ -598,6 +608,30 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } } @Test fun fullScreenSwipeGesture_doNotProcessTouchesInUmo() = with(kosmos) { testScope.runTest { // Communal is closed. goToScene(CommunalScenes.Blank) whenever(keyguardMediaController.isWithinMediaViewBounds(any(), any())) .thenReturn(true) assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse() } } @Test fun fullScreenSwipeGesture_doNotProcessTouchesInSmartspace() = with(kosmos) { testScope.runTest { // Communal is closed. goToScene(CommunalScenes.Blank) whenever(lockscreenSmartspaceController.isWithinSmartspaceBounds(any(), any())) .thenReturn(true) assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse() } } @Test fun onTouchEvent_hubOpen_touchesDispatched() = with(kosmos) { Loading packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/controller/KeyguardMediaController.kt 0 → 100644 +23 −0 Original line number Diff line number Diff line /* * 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.media.controls.controller import com.android.systemui.kosmos.Kosmos import com.android.systemui.media.controls.ui.controller.KeyguardMediaController import com.android.systemui.util.mockito.mock val Kosmos.keyguardMediaController by Kosmos.Fixture { mock<KeyguardMediaController>() } Loading
packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaController.kt +9 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.systemui.media.controls.ui.controller import android.content.Context import android.content.res.Configuration import android.graphics.Rect import android.view.View import android.view.ViewGroup import androidx.annotation.VisibleForTesting Loading Loading @@ -125,6 +126,7 @@ constructor( /** single pane media container placed at the top of the notifications list */ var singlePaneContainer: MediaContainerView? = null private set private var splitShadeContainer: ViewGroup? = null /** Loading Loading @@ -185,6 +187,13 @@ constructor( } } fun isWithinMediaViewBounds(x: Int, y: Int): Boolean { val bounds = Rect() mediaHost.hostView.getBoundsOnScreen(bounds) return bounds.contains(x, y) } fun refreshMediaPosition(reason: String) { val currentState = statusBarStateController.state Loading
packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt +10 −1 Original line number Diff line number Diff line Loading @@ -55,10 +55,12 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac import com.android.systemui.keyguard.shared.model.Edge import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.media.controls.ui.controller.KeyguardMediaController import com.android.systemui.res.R import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.scene.shared.model.SceneDataSourceDelegator import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf import com.android.systemui.util.kotlin.collectFlow Loading Loading @@ -88,6 +90,8 @@ constructor( private val communalContent: CommunalContent, @Communal private val dataSourceDelegator: SceneDataSourceDelegator, private val notificationStackScrollLayoutController: NotificationStackScrollLayoutController, private val keyguardMediaController: KeyguardMediaController, private val lockscreenSmartspaceController: LockscreenSmartspaceController ) : LifecycleOwner { private class CommunalWrapper(context: Context) : FrameLayout(context) { Loading Loading @@ -445,7 +449,12 @@ constructor( // the touch. if ( !hubShowing && !notificationStackScrollLayoutController.isBelowLastNotification(ev.x, ev.y) (!notificationStackScrollLayoutController.isBelowLastNotification(ev.x, ev.y) || keyguardMediaController.isWithinMediaViewBounds(ev.x.toInt(), ev.y.toInt()) || lockscreenSmartspaceController.isWithinSmartspaceBounds( ev.x.toInt(), ev.y.toInt() )) ) { return false } Loading
packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt +16 −2 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.content.ContentResolver import android.content.Context import android.content.Intent import android.database.ContentObserver import android.graphics.Rect import android.net.Uri import android.os.Handler import android.os.UserHandle Loading Loading @@ -570,6 +571,20 @@ constructor( plugin?.unregisterListener(listener) } fun isWithinSmartspaceBounds(x: Int, y: Int): Boolean { smartspaceViews.forEach { val bounds = Rect() with(it as View) { this.getBoundsOnScreen(bounds) if (bounds.contains(x, y)) { return true } } } return false } private fun filterSmartspaceTarget(t: SmartspaceTarget): Boolean { if (isDateWeatherDecoupled && t.featureType == SmartspaceTarget.FEATURE_WEATHER) { return false Loading Loading @@ -705,4 +720,3 @@ constructor( } } }
packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt +36 −2 Original line number Diff line number Diff line Loading @@ -57,9 +57,11 @@ import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.media.controls.controller.keyguardMediaController import com.android.systemui.res.R import com.android.systemui.scene.shared.model.sceneDataSourceDelegator import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.statusbar.lockscreen.lockscreenSmartspaceController import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat Loading Loading @@ -134,7 +136,9 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { ambientTouchComponentFactory, communalContent, kosmos.sceneDataSourceDelegator, kosmos.notificationStackScrollLayoutController kosmos.notificationStackScrollLayoutController, kosmos.keyguardMediaController, kosmos.lockscreenSmartspaceController ) } testableLooper = TestableLooper.get(this) Loading Loading @@ -178,7 +182,9 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { ambientTouchComponentFactory, communalContent, kosmos.sceneDataSourceDelegator, kosmos.notificationStackScrollLayoutController kosmos.notificationStackScrollLayoutController, kosmos.keyguardMediaController, kosmos.lockscreenSmartspaceController ) // First call succeeds. Loading @@ -205,6 +211,8 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { communalContent, kosmos.sceneDataSourceDelegator, kosmos.notificationStackScrollLayoutController, kosmos.keyguardMediaController, kosmos.lockscreenSmartspaceController ) assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.INITIALIZED) Loading @@ -226,6 +234,8 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { communalContent, kosmos.sceneDataSourceDelegator, kosmos.notificationStackScrollLayoutController, kosmos.keyguardMediaController, kosmos.lockscreenSmartspaceController ) // Only initView without attaching a view as we don't want the flows to start collecting Loading Loading @@ -598,6 +608,30 @@ class GlanceableHubContainerControllerTest : SysuiTestCase() { } } @Test fun fullScreenSwipeGesture_doNotProcessTouchesInUmo() = with(kosmos) { testScope.runTest { // Communal is closed. goToScene(CommunalScenes.Blank) whenever(keyguardMediaController.isWithinMediaViewBounds(any(), any())) .thenReturn(true) assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse() } } @Test fun fullScreenSwipeGesture_doNotProcessTouchesInSmartspace() = with(kosmos) { testScope.runTest { // Communal is closed. goToScene(CommunalScenes.Blank) whenever(lockscreenSmartspaceController.isWithinSmartspaceBounds(any(), any())) .thenReturn(true) assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse() } } @Test fun onTouchEvent_hubOpen_touchesDispatched() = with(kosmos) { Loading
packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/controller/KeyguardMediaController.kt 0 → 100644 +23 −0 Original line number Diff line number Diff line /* * 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.media.controls.controller import com.android.systemui.kosmos.Kosmos import com.android.systemui.media.controls.ui.controller.KeyguardMediaController import com.android.systemui.util.mockito.mock val Kosmos.keyguardMediaController by Kosmos.Fixture { mock<KeyguardMediaController>() }