Loading packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt +31 −2 Original line number Diff line number Diff line Loading @@ -31,10 +31,14 @@ import androidx.compose.foundation.layout.requiredHeight import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.boundsInWindow import androidx.compose.ui.layout.onPlaced import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.ContentScope Loading @@ -59,6 +63,8 @@ import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.ui.composable.Overlay import com.android.systemui.shade.ui.composable.CollapsedShadeHeader import com.android.systemui.shade.ui.composable.OverlayShade import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel import com.android.systemui.statusbar.phone.ui.StatusBarIconController Loading Loading @@ -96,13 +102,37 @@ constructor( override fun ContentScope.Content(modifier: Modifier) { val viewModel = rememberViewModel("QuickSettingsShadeOverlay") { contentViewModelFactory.create() } val panelCornerRadius = with(LocalDensity.current) { OverlayShade.Dimensions.PanelCornerRadius.toPx().toInt() } // set the bounds to null when the QuickSettings overlay disappears DisposableEffect(Unit) { onDispose { viewModel.onPanelShapeChanged(null) } } OverlayShade( panelAlignment = Alignment.TopEnd, modifier = modifier, onScrimClicked = viewModel::onScrimClicked, ) { Column { Column( modifier = Modifier.onPlaced { coordinates -> val boundsInWindow = coordinates.boundsInWindow() val shadeScrimBounds = ShadeScrimBounds( left = boundsInWindow.left, top = boundsInWindow.top, right = boundsInWindow.right, bottom = boundsInWindow.bottom, ) val shape = ShadeScrimShape( bounds = shadeScrimBounds, topRadius = 0, bottomRadius = panelCornerRadius, ) viewModel.onPanelShapeChanged(shape) } ) { if (viewModel.showHeader) { CollapsedShadeHeader( viewModelFactory = viewModel.shadeHeaderViewModelFactory, Loading @@ -112,7 +142,6 @@ constructor( statusBarIconController = statusBarIconController, ) } ShadeBody(viewModel = viewModel.quickSettingsContainerViewModel) } Loading packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt +20 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,9 @@ import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.data.repository.shadeRepository import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shade.shared.flag.DualShade import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi Loading Loading @@ -143,6 +146,23 @@ class QuickSettingsShadeOverlayContentViewModelTest : SysuiTestCase() { assertThat(underTest.showHeader).isFalse() } @Test fun onPanelShapeChanged() = testScope.runTest { val actual by collectLastValue(kosmos.notificationStackAppearanceInteractor.qsPanelShape) val expected = ShadeScrimShape( bounds = ShadeScrimBounds(left = 10f, top = 0f, right = 710f, bottom = 600f), topRadius = 0, bottomRadius = 100, ) underTest.onPanelShapeChanged(expected) assertThat(expected).isEqualTo(actual) } private fun TestScope.lockDevice() { val currentScene by collectLastValue(sceneInteractor.currentScene) kosmos.powerInteractor.setAsleepForTest() Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt +4 −3 Original line number Diff line number Diff line Loading @@ -74,7 +74,8 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { testScope.runTest { val radius = MutableStateFlow(32) val leftOffset = MutableStateFlow(0) val shape by collectLastValue(scrollViewModel.shadeScrimShape(radius, leftOffset)) val shape by collectLastValue(scrollViewModel.notificationScrimShape(radius, leftOffset)) // When: receive scrim bounds placeholderViewModel.onScrimBoundsChanged( Loading @@ -87,7 +88,7 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { bounds = ShadeScrimBounds(left = 0f, top = 200f, right = 100f, bottom = 550f), topRadius = 32, bottomRadius = 0 bottomRadius = 0, ) ) Loading @@ -104,7 +105,7 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { bounds = ShadeScrimBounds(left = 10f, top = 200f, right = 100f, bottom = 550f), topRadius = 24, bottomRadius = 0 bottomRadius = 0, ) ) Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt +36 −20 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ 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 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest Loading @@ -40,27 +41,38 @@ class NotificationStackAppearanceInteractorTest : SysuiTestCase() { private val underTest = kosmos.notificationStackAppearanceInteractor @Test fun stackBounds() = fun stackNotificationScrimBounds() = testScope.runTest { val stackBounds by collectLastValue(underTest.shadeScrimBounds) val stackBounds by collectLastValue(underTest.notificationShadeScrimBounds) val bounds1 = ShadeScrimBounds( top = 100f, bottom = 200f, ) underTest.setShadeScrimBounds(bounds1) val bounds1 = ShadeScrimBounds(top = 100f, bottom = 200f) underTest.setNotificationShadeScrimBounds(bounds1) assertThat(stackBounds).isEqualTo(bounds1) val bounds2 = ShadeScrimBounds( top = 200f, bottom = 300f, ) underTest.setShadeScrimBounds(bounds2) val bounds2 = ShadeScrimBounds(top = 200f, bottom = 300f) underTest.setNotificationShadeScrimBounds(bounds2) assertThat(stackBounds).isEqualTo(bounds2) } @Test fun setQsPanelShape() = testScope.runTest { val actual by collectLastValue(underTest.qsPanelShape) val expected1 = ShadeScrimShape( bounds = ShadeScrimBounds(top = 0f, bottom = 100f), topRadius = 0, bottomRadius = 10, ) underTest.setQsPanelShape(expected1) assertThat(actual).isEqualTo(expected1) val expected2 = expected1.copy(topRadius = 10) underTest.setQsPanelShape(expected2) assertThat(expected2).isEqualTo(actual) } @Test fun stackRounding() = testScope.runTest { Loading @@ -76,13 +88,17 @@ class NotificationStackAppearanceInteractorTest : SysuiTestCase() { } @Test(expected = IllegalStateException::class) fun setStackBounds_withImproperBounds_throwsException() = fun stackNotificationScrimBounds_withImproperBounds_throwsException() = testScope.runTest { underTest.setShadeScrimBounds( ShadeScrimBounds( top = 100f, bottom = 99f, ) underTest.setNotificationShadeScrimBounds(ShadeScrimBounds(top = 100f, bottom = 99f)) } @Test(expected = IllegalStateException::class) fun setQsPanelShape_withImproperBounds_throwsException() = testScope.runTest { val invalidBounds = ShadeScrimBounds(top = 0f, bottom = -10f) underTest.setQsPanelShape( ShadeScrimShape(bounds = invalidBounds, topRadius = 10, bottomRadius = 10) ) } Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt +4 −2 Original line number Diff line number Diff line Loading @@ -38,12 +38,14 @@ class NotificationsPlaceholderViewModelTest : SysuiTestCase() { private val underTest by lazy { kosmos.notificationsPlaceholderViewModel } @Test fun onBoundsChanged() = fun onScrimBoundsChanged() = kosmos.testScope.runTest { val bounds = ShadeScrimBounds(left = 5f, top = 15f, right = 25f, bottom = 35f) underTest.onScrimBoundsChanged(bounds) val stackBounds by collectLastValue(kosmos.notificationStackAppearanceInteractor.shadeScrimBounds) collectLastValue( kosmos.notificationStackAppearanceInteractor.notificationShadeScrimBounds ) assertThat(stackBounds).isEqualTo(bounds) } } Loading
packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeOverlay.kt +31 −2 Original line number Diff line number Diff line Loading @@ -31,10 +31,14 @@ import androidx.compose.foundation.layout.requiredHeight import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.boundsInWindow import androidx.compose.ui.layout.onPlaced import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.scene.ContentScope Loading @@ -59,6 +63,8 @@ import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.ui.composable.Overlay import com.android.systemui.shade.ui.composable.CollapsedShadeHeader import com.android.systemui.shade.ui.composable.OverlayShade import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel import com.android.systemui.statusbar.phone.ui.StatusBarIconController Loading Loading @@ -96,13 +102,37 @@ constructor( override fun ContentScope.Content(modifier: Modifier) { val viewModel = rememberViewModel("QuickSettingsShadeOverlay") { contentViewModelFactory.create() } val panelCornerRadius = with(LocalDensity.current) { OverlayShade.Dimensions.PanelCornerRadius.toPx().toInt() } // set the bounds to null when the QuickSettings overlay disappears DisposableEffect(Unit) { onDispose { viewModel.onPanelShapeChanged(null) } } OverlayShade( panelAlignment = Alignment.TopEnd, modifier = modifier, onScrimClicked = viewModel::onScrimClicked, ) { Column { Column( modifier = Modifier.onPlaced { coordinates -> val boundsInWindow = coordinates.boundsInWindow() val shadeScrimBounds = ShadeScrimBounds( left = boundsInWindow.left, top = boundsInWindow.top, right = boundsInWindow.right, bottom = boundsInWindow.bottom, ) val shape = ShadeScrimShape( bounds = shadeScrimBounds, topRadius = 0, bottomRadius = panelCornerRadius, ) viewModel.onPanelShapeChanged(shape) } ) { if (viewModel.showHeader) { CollapsedShadeHeader( viewModelFactory = viewModel.shadeHeaderViewModelFactory, Loading @@ -112,7 +142,6 @@ constructor( statusBarIconController = statusBarIconController, ) } ShadeBody(viewModel = viewModel.quickSettingsContainerViewModel) } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayContentViewModelTest.kt +20 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,9 @@ import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.shade.data.repository.shadeRepository import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shade.shared.flag.DualShade import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimBounds import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi Loading Loading @@ -143,6 +146,23 @@ class QuickSettingsShadeOverlayContentViewModelTest : SysuiTestCase() { assertThat(underTest.showHeader).isFalse() } @Test fun onPanelShapeChanged() = testScope.runTest { val actual by collectLastValue(kosmos.notificationStackAppearanceInteractor.qsPanelShape) val expected = ShadeScrimShape( bounds = ShadeScrimBounds(left = 10f, top = 0f, right = 710f, bottom = 600f), topRadius = 0, bottomRadius = 100, ) underTest.onPanelShapeChanged(expected) assertThat(expected).isEqualTo(actual) } private fun TestScope.lockDevice() { val currentScene by collectLastValue(sceneInteractor.currentScene) kosmos.powerInteractor.setAsleepForTest() Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt +4 −3 Original line number Diff line number Diff line Loading @@ -74,7 +74,8 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { testScope.runTest { val radius = MutableStateFlow(32) val leftOffset = MutableStateFlow(0) val shape by collectLastValue(scrollViewModel.shadeScrimShape(radius, leftOffset)) val shape by collectLastValue(scrollViewModel.notificationScrimShape(radius, leftOffset)) // When: receive scrim bounds placeholderViewModel.onScrimBoundsChanged( Loading @@ -87,7 +88,7 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { bounds = ShadeScrimBounds(left = 0f, top = 200f, right = 100f, bottom = 550f), topRadius = 32, bottomRadius = 0 bottomRadius = 0, ) ) Loading @@ -104,7 +105,7 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { bounds = ShadeScrimBounds(left = 10f, top = 200f, right = 100f, bottom = 550f), topRadius = 24, bottomRadius = 0 bottomRadius = 0, ) ) Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractorTest.kt +36 −20 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ 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 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest Loading @@ -40,27 +41,38 @@ class NotificationStackAppearanceInteractorTest : SysuiTestCase() { private val underTest = kosmos.notificationStackAppearanceInteractor @Test fun stackBounds() = fun stackNotificationScrimBounds() = testScope.runTest { val stackBounds by collectLastValue(underTest.shadeScrimBounds) val stackBounds by collectLastValue(underTest.notificationShadeScrimBounds) val bounds1 = ShadeScrimBounds( top = 100f, bottom = 200f, ) underTest.setShadeScrimBounds(bounds1) val bounds1 = ShadeScrimBounds(top = 100f, bottom = 200f) underTest.setNotificationShadeScrimBounds(bounds1) assertThat(stackBounds).isEqualTo(bounds1) val bounds2 = ShadeScrimBounds( top = 200f, bottom = 300f, ) underTest.setShadeScrimBounds(bounds2) val bounds2 = ShadeScrimBounds(top = 200f, bottom = 300f) underTest.setNotificationShadeScrimBounds(bounds2) assertThat(stackBounds).isEqualTo(bounds2) } @Test fun setQsPanelShape() = testScope.runTest { val actual by collectLastValue(underTest.qsPanelShape) val expected1 = ShadeScrimShape( bounds = ShadeScrimBounds(top = 0f, bottom = 100f), topRadius = 0, bottomRadius = 10, ) underTest.setQsPanelShape(expected1) assertThat(actual).isEqualTo(expected1) val expected2 = expected1.copy(topRadius = 10) underTest.setQsPanelShape(expected2) assertThat(expected2).isEqualTo(actual) } @Test fun stackRounding() = testScope.runTest { Loading @@ -76,13 +88,17 @@ class NotificationStackAppearanceInteractorTest : SysuiTestCase() { } @Test(expected = IllegalStateException::class) fun setStackBounds_withImproperBounds_throwsException() = fun stackNotificationScrimBounds_withImproperBounds_throwsException() = testScope.runTest { underTest.setShadeScrimBounds( ShadeScrimBounds( top = 100f, bottom = 99f, ) underTest.setNotificationShadeScrimBounds(ShadeScrimBounds(top = 100f, bottom = 99f)) } @Test(expected = IllegalStateException::class) fun setQsPanelShape_withImproperBounds_throwsException() = testScope.runTest { val invalidBounds = ShadeScrimBounds(top = 0f, bottom = -10f) underTest.setQsPanelShape( ShadeScrimShape(bounds = invalidBounds, topRadius = 10, bottomRadius = 10) ) } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModelTest.kt +4 −2 Original line number Diff line number Diff line Loading @@ -38,12 +38,14 @@ class NotificationsPlaceholderViewModelTest : SysuiTestCase() { private val underTest by lazy { kosmos.notificationsPlaceholderViewModel } @Test fun onBoundsChanged() = fun onScrimBoundsChanged() = kosmos.testScope.runTest { val bounds = ShadeScrimBounds(left = 5f, top = 15f, right = 25f, bottom = 35f) underTest.onScrimBoundsChanged(bounds) val stackBounds by collectLastValue(kosmos.notificationStackAppearanceInteractor.shadeScrimBounds) collectLastValue( kosmos.notificationStackAppearanceInteractor.notificationShadeScrimBounds ) assertThat(stackBounds).isEqualTo(bounds) } }