diff options
20 files changed, 362 insertions, 498 deletions
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 619b4280d954..aa0d474ba41c 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 @@ -204,7 +204,7 @@ fun SceneContainer( SceneTransitionLayout( state = state, modifier = modifier.fillMaxSize(), - swipeSourceDetector = viewModel.edgeDetector, + swipeSourceDetector = viewModel.swipeSourceDetector, ) { sceneByKey.forEach { (sceneKey, scene) -> scene( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt index b66e2fe13e8a..47ca4b14a26f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenUserActionsViewModelTest.kt @@ -41,7 +41,7 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.TransitionKeys -import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge +import com.android.systemui.scene.ui.viewmodel.SceneContainerArea import com.android.systemui.shade.data.repository.shadeRepository import com.android.systemui.shade.domain.interactor.disableDualShade import com.android.systemui.shade.domain.interactor.enableDualShade @@ -275,20 +275,20 @@ class LockscreenUserActionsViewModelTest : SysuiTestCase() { assertThat(downDestination?.transitionKey).isNull() } - val downFromTopRightDestination = + val downFromEndHalfDestination = userActions?.get( Swipe.Down( - fromSource = SceneContainerEdge.TopRight, + fromSource = SceneContainerArea.EndHalf, pointerCount = if (downWithTwoPointers) 2 else 1, ) ) when { - !isShadeTouchable -> assertThat(downFromTopRightDestination).isNull() - downWithTwoPointers -> assertThat(downFromTopRightDestination).isNull() + !isShadeTouchable -> assertThat(downFromEndHalfDestination).isNull() + downWithTwoPointers -> assertThat(downFromEndHalfDestination).isNull() else -> { - assertThat(downFromTopRightDestination) + assertThat(downFromEndHalfDestination) .isEqualTo(ShowOverlay(Overlays.QuickSettingsShade)) - assertThat(downFromTopRightDestination?.transitionKey).isNull() + assertThat(downFromEndHalfDestination?.transitionKey).isNull() } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt index 52b9e47e6d3d..52a0a5445002 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt @@ -30,7 +30,7 @@ import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn import com.android.systemui.scene.shared.model.Overlays -import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge +import com.android.systemui.scene.ui.viewmodel.SceneContainerArea import com.android.systemui.shade.ui.viewmodel.notificationsShadeOverlayActionsViewModel import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat @@ -71,13 +71,13 @@ class NotificationsShadeOverlayActionsViewModelTest : SysuiTestCase() { } @Test - fun downFromTopRight_switchesToQuickSettingsShade() = + fun downFromTopEnd_switchesToQuickSettingsShade() = testScope.runTest { val actions by collectLastValue(underTest.actions) underTest.activateIn(this) val action = - (actions?.get(Swipe.Down(fromSource = SceneContainerEdge.TopRight)) as? ShowOverlay) + (actions?.get(Swipe.Down(fromSource = SceneContainerArea.EndHalf)) as? ShowOverlay) assertThat(action?.overlay).isEqualTo(Overlays.QuickSettingsShade) val overlaysToHide = action?.hideCurrentOverlays as? HideCurrentOverlays.Some assertThat(overlaysToHide).isNotNull() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt index df2dd99c779e..b98059a1fe90 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt @@ -31,7 +31,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.lifecycle.activateIn import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel import com.android.systemui.scene.shared.model.Overlays -import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge +import com.android.systemui.scene.ui.viewmodel.SceneContainerArea import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest @@ -84,13 +84,14 @@ class QuickSettingsShadeOverlayActionsViewModelTest : SysuiTestCase() { } @Test - fun downFromTopLeft_switchesToNotificationsShade() = + fun downFromTopStart_switchesToNotificationsShade() = testScope.runTest { val actions by collectLastValue(underTest.actions) underTest.activateIn(this) val action = - (actions?.get(Swipe.Down(fromSource = SceneContainerEdge.TopLeft)) as? ShowOverlay) + (actions?.get(Swipe.Down(fromSource = SceneContainerArea.StartHalf)) + as? ShowOverlay) assertThat(action?.overlay).isEqualTo(Overlays.NotificationsShade) val overlaysToHide = action?.hideCurrentOverlays as? HideCurrentOverlays.Some assertThat(overlaysToHide).isNotNull() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetectorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetectorTest.kt new file mode 100644 index 000000000000..a09e5cd9de9b --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetectorTest.kt @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2025 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.scene.ui.viewmodel + +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.EndEdge +import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.EndHalf +import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.BottomEdge +import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.LeftEdge +import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.LeftHalf +import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.RightEdge +import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.RightHalf +import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.StartEdge +import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.StartHalf +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class SceneContainerSwipeDetectorTest : SysuiTestCase() { + + private val edgeSize = 40 + private val screenWidth = 800 + private val screenHeight = 600 + + private val underTest = SceneContainerSwipeDetector(edgeSize = edgeSize.dp) + + @Test + fun source_noEdge_detectsLeftHalf() { + val detectedEdge = swipeVerticallyFrom(x = screenWidth / 2 - 1, y = screenHeight / 2) + assertThat(detectedEdge).isEqualTo(LeftHalf) + } + + @Test + fun source_swipeVerticallyOnTopLeft_detectsLeftHalf() { + val detectedEdge = swipeVerticallyFrom(x = 1, y = edgeSize - 1) + assertThat(detectedEdge).isEqualTo(LeftHalf) + } + + @Test + fun source_swipeHorizontallyOnTopLeft_detectsLeftEdge() { + val detectedEdge = swipeHorizontallyFrom(x = 1, y = edgeSize - 1) + assertThat(detectedEdge).isEqualTo(LeftEdge) + } + + @Test + fun source_swipeVerticallyOnTopRight_detectsRightHalf() { + val detectedEdge = swipeVerticallyFrom(x = screenWidth - 1, y = edgeSize - 1) + assertThat(detectedEdge).isEqualTo(RightHalf) + } + + @Test + fun source_swipeHorizontallyOnTopRight_detectsRightEdge() { + val detectedEdge = swipeHorizontallyFrom(x = screenWidth - 1, y = edgeSize - 1) + assertThat(detectedEdge).isEqualTo(RightEdge) + } + + @Test + fun source_swipeVerticallyToLeftOfSplit_detectsLeftHalf() { + val detectedEdge = swipeVerticallyFrom(x = (screenWidth / 2) - 1, y = edgeSize - 1) + assertThat(detectedEdge).isEqualTo(LeftHalf) + } + + @Test + fun source_swipeVerticallyToRightOfSplit_detectsRightHalf() { + val detectedEdge = swipeVerticallyFrom(x = (screenWidth / 2) + 1, y = edgeSize - 1) + assertThat(detectedEdge).isEqualTo(RightHalf) + } + + @Test + fun source_swipeVerticallyOnBottom_detectsBottomEdge() { + val detectedEdge = + swipeVerticallyFrom(x = screenWidth / 3, y = screenHeight - (edgeSize / 2)) + assertThat(detectedEdge).isEqualTo(BottomEdge) + } + + @Test + fun source_swipeHorizontallyOnBottom_detectsLeftHalf() { + val detectedEdge = + swipeHorizontallyFrom(x = screenWidth / 3, y = screenHeight - (edgeSize - 1)) + assertThat(detectedEdge).isEqualTo(LeftHalf) + } + + @Test + fun source_swipeHorizontallyOnLeft_detectsLeftEdge() { + val detectedEdge = swipeHorizontallyFrom(x = edgeSize - 1, y = screenHeight / 2) + assertThat(detectedEdge).isEqualTo(LeftEdge) + } + + @Test + fun source_swipeVerticallyOnLeft_detectsLeftHalf() { + val detectedEdge = swipeVerticallyFrom(x = edgeSize - 1, y = screenHeight / 2) + assertThat(detectedEdge).isEqualTo(LeftHalf) + } + + @Test + fun source_swipeHorizontallyOnRight_detectsRightEdge() { + val detectedEdge = + swipeHorizontallyFrom(x = screenWidth - edgeSize + 1, y = screenHeight / 2) + assertThat(detectedEdge).isEqualTo(RightEdge) + } + + @Test + fun source_swipeVerticallyOnRight_detectsRightHalf() { + val detectedEdge = swipeVerticallyFrom(x = screenWidth - edgeSize + 1, y = screenHeight / 2) + assertThat(detectedEdge).isEqualTo(RightHalf) + } + + @Test + fun resolve_startEdgeInLtr_resolvesLeftEdge() { + val resolvedEdge = StartEdge.resolve(LayoutDirection.Ltr) + assertThat(resolvedEdge).isEqualTo(LeftEdge) + } + + @Test + fun resolve_startEdgeInRtl_resolvesRightEdge() { + val resolvedEdge = StartEdge.resolve(LayoutDirection.Rtl) + assertThat(resolvedEdge).isEqualTo(RightEdge) + } + + @Test + fun resolve_endEdgeInLtr_resolvesRightEdge() { + val resolvedEdge = EndEdge.resolve(LayoutDirection.Ltr) + assertThat(resolvedEdge).isEqualTo(RightEdge) + } + + @Test + fun resolve_endEdgeInRtl_resolvesLeftEdge() { + val resolvedEdge = EndEdge.resolve(LayoutDirection.Rtl) + assertThat(resolvedEdge).isEqualTo(LeftEdge) + } + + @Test + fun resolve_startHalfInLtr_resolvesLeftHalf() { + val resolvedEdge = StartHalf.resolve(LayoutDirection.Ltr) + assertThat(resolvedEdge).isEqualTo(LeftHalf) + } + + @Test + fun resolve_startHalfInRtl_resolvesRightHalf() { + val resolvedEdge = StartHalf.resolve(LayoutDirection.Rtl) + assertThat(resolvedEdge).isEqualTo(RightHalf) + } + + @Test + fun resolve_endHalfInLtr_resolvesRightHalf() { + val resolvedEdge = EndHalf.resolve(LayoutDirection.Ltr) + assertThat(resolvedEdge).isEqualTo(RightHalf) + } + + @Test + fun resolve_endHalfInRtl_resolvesLeftHalf() { + val resolvedEdge = EndHalf.resolve(LayoutDirection.Rtl) + assertThat(resolvedEdge).isEqualTo(LeftHalf) + } + + private fun swipeVerticallyFrom(x: Int, y: Int): SceneContainerArea.Resolved? { + return swipeFrom(x, y, Orientation.Vertical) + } + + private fun swipeHorizontallyFrom(x: Int, y: Int): SceneContainerArea.Resolved? { + return swipeFrom(x, y, Orientation.Horizontal) + } + + private fun swipeFrom(x: Int, y: Int, orientation: Orientation): SceneContainerArea.Resolved? { + return underTest.source( + layoutSize = IntSize(width = screenWidth, height = screenHeight), + position = IntOffset(x, y), + density = Density(1f), + orientation = orientation, + ) + } +} 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 30d9f73d7441..adaebbd27986 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 @@ -48,6 +48,7 @@ import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest @@ -55,6 +56,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) @EnableSceneContainer @@ -324,7 +326,7 @@ class SceneContainerViewModelTest : SysuiTestCase() { kosmos.enableSingleShade() assertThat(shadeMode).isEqualTo(ShadeMode.Single) - assertThat(underTest.edgeDetector).isEqualTo(DefaultEdgeDetector) + assertThat(underTest.swipeSourceDetector).isEqualTo(DefaultEdgeDetector) } @Test @@ -334,26 +336,28 @@ class SceneContainerViewModelTest : SysuiTestCase() { kosmos.enableSplitShade() assertThat(shadeMode).isEqualTo(ShadeMode.Split) - assertThat(underTest.edgeDetector).isEqualTo(DefaultEdgeDetector) + assertThat(underTest.swipeSourceDetector).isEqualTo(DefaultEdgeDetector) } @Test - fun edgeDetector_dualShade_narrowScreen_usesSplitEdgeDetector() = + fun edgeDetector_dualShade_narrowScreen_usesSceneContainerSwipeDetector() = testScope.runTest { val shadeMode by collectLastValue(kosmos.shadeMode) kosmos.enableDualShade(wideLayout = false) assertThat(shadeMode).isEqualTo(ShadeMode.Dual) - assertThat(underTest.edgeDetector).isEqualTo(kosmos.splitEdgeDetector) + assertThat(underTest.swipeSourceDetector) + .isInstanceOf(SceneContainerSwipeDetector::class.java) } @Test - fun edgeDetector_dualShade_wideScreen_usesSplitEdgeDetector() = + fun edgeDetector_dualShade_wideScreen_usesSceneContainerSwipeDetector() = testScope.runTest { val shadeMode by collectLastValue(kosmos.shadeMode) kosmos.enableDualShade(wideLayout = true) assertThat(shadeMode).isEqualTo(ShadeMode.Dual) - assertThat(underTest.edgeDetector).isEqualTo(kosmos.splitEdgeDetector) + assertThat(underTest.swipeSourceDetector) + .isInstanceOf(SceneContainerSwipeDetector::class.java) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SplitEdgeDetectorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SplitEdgeDetectorTest.kt deleted file mode 100644 index 3d76d280b2cc..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SplitEdgeDetectorTest.kt +++ /dev/null @@ -1,274 +0,0 @@ -/* - * 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.scene.ui.viewmodel - -import androidx.compose.foundation.gestures.Orientation -import androidx.compose.ui.unit.Density -import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.unit.IntSize -import androidx.compose.ui.unit.LayoutDirection -import androidx.compose.ui.unit.dp -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge.End -import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge.Resolved.Bottom -import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge.Resolved.Left -import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge.Resolved.Right -import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge.Resolved.TopLeft -import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge.Resolved.TopRight -import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge.Start -import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge.TopEnd -import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge.TopStart -import com.google.common.truth.Truth.assertThat -import kotlin.test.assertFailsWith -import org.junit.Test -import org.junit.runner.RunWith - -@SmallTest -@RunWith(AndroidJUnit4::class) -class SplitEdgeDetectorTest : SysuiTestCase() { - - private val edgeSize = 40 - private val screenWidth = 800 - private val screenHeight = 600 - - private var edgeSplitFraction = 0.7f - - private val underTest = - SplitEdgeDetector( - topEdgeSplitFraction = { edgeSplitFraction }, - edgeSize = edgeSize.dp, - ) - - @Test - fun source_noEdge_detectsNothing() { - val detectedEdge = - swipeVerticallyFrom( - x = screenWidth / 2, - y = screenHeight / 2, - ) - assertThat(detectedEdge).isNull() - } - - @Test - fun source_swipeVerticallyOnTopLeft_detectsTopLeft() { - val detectedEdge = - swipeVerticallyFrom( - x = 1, - y = edgeSize - 1, - ) - assertThat(detectedEdge).isEqualTo(TopLeft) - } - - @Test - fun source_swipeHorizontallyOnTopLeft_detectsLeft() { - val detectedEdge = - swipeHorizontallyFrom( - x = 1, - y = edgeSize - 1, - ) - assertThat(detectedEdge).isEqualTo(Left) - } - - @Test - fun source_swipeVerticallyOnTopRight_detectsTopRight() { - val detectedEdge = - swipeVerticallyFrom( - x = screenWidth - 1, - y = edgeSize - 1, - ) - assertThat(detectedEdge).isEqualTo(TopRight) - } - - @Test - fun source_swipeHorizontallyOnTopRight_detectsRight() { - val detectedEdge = - swipeHorizontallyFrom( - x = screenWidth - 1, - y = edgeSize - 1, - ) - assertThat(detectedEdge).isEqualTo(Right) - } - - @Test - fun source_swipeVerticallyToLeftOfSplit_detectsTopLeft() { - val detectedEdge = - swipeVerticallyFrom( - x = (screenWidth * edgeSplitFraction).toInt() - 1, - y = edgeSize - 1, - ) - assertThat(detectedEdge).isEqualTo(TopLeft) - } - - @Test - fun source_swipeVerticallyToRightOfSplit_detectsTopRight() { - val detectedEdge = - swipeVerticallyFrom( - x = (screenWidth * edgeSplitFraction).toInt() + 1, - y = edgeSize - 1, - ) - assertThat(detectedEdge).isEqualTo(TopRight) - } - - @Test - fun source_edgeSplitFractionUpdatesDynamically() { - val middleX = (screenWidth * 0.5f).toInt() - val topY = 0 - - // Split closer to the right; middle of screen is considered "left". - edgeSplitFraction = 0.6f - assertThat(swipeVerticallyFrom(x = middleX, y = topY)).isEqualTo(TopLeft) - - // Split closer to the left; middle of screen is considered "right". - edgeSplitFraction = 0.4f - assertThat(swipeVerticallyFrom(x = middleX, y = topY)).isEqualTo(TopRight) - - // Illegal fraction. - edgeSplitFraction = 1.2f - assertFailsWith<IllegalArgumentException> { swipeVerticallyFrom(x = middleX, y = topY) } - - // Illegal fraction. - edgeSplitFraction = -0.3f - assertFailsWith<IllegalArgumentException> { swipeVerticallyFrom(x = middleX, y = topY) } - } - - @Test - fun source_swipeVerticallyOnBottom_detectsBottom() { - val detectedEdge = - swipeVerticallyFrom( - x = screenWidth / 3, - y = screenHeight - (edgeSize / 2), - ) - assertThat(detectedEdge).isEqualTo(Bottom) - } - - @Test - fun source_swipeHorizontallyOnBottom_detectsNothing() { - val detectedEdge = - swipeHorizontallyFrom( - x = screenWidth / 3, - y = screenHeight - (edgeSize - 1), - ) - assertThat(detectedEdge).isNull() - } - - @Test - fun source_swipeHorizontallyOnLeft_detectsLeft() { - val detectedEdge = - swipeHorizontallyFrom( - x = edgeSize - 1, - y = screenHeight / 2, - ) - assertThat(detectedEdge).isEqualTo(Left) - } - - @Test - fun source_swipeVerticallyOnLeft_detectsNothing() { - val detectedEdge = - swipeVerticallyFrom( - x = edgeSize - 1, - y = screenHeight / 2, - ) - assertThat(detectedEdge).isNull() - } - - @Test - fun source_swipeHorizontallyOnRight_detectsRight() { - val detectedEdge = - swipeHorizontallyFrom( - x = screenWidth - edgeSize + 1, - y = screenHeight / 2, - ) - assertThat(detectedEdge).isEqualTo(Right) - } - - @Test - fun source_swipeVerticallyOnRight_detectsNothing() { - val detectedEdge = - swipeVerticallyFrom( - x = screenWidth - edgeSize + 1, - y = screenHeight / 2, - ) - assertThat(detectedEdge).isNull() - } - - @Test - fun resolve_startInLtr_resolvesLeft() { - val resolvedEdge = Start.resolve(LayoutDirection.Ltr) - assertThat(resolvedEdge).isEqualTo(Left) - } - - @Test - fun resolve_startInRtl_resolvesRight() { - val resolvedEdge = Start.resolve(LayoutDirection.Rtl) - assertThat(resolvedEdge).isEqualTo(Right) - } - - @Test - fun resolve_endInLtr_resolvesRight() { - val resolvedEdge = End.resolve(LayoutDirection.Ltr) - assertThat(resolvedEdge).isEqualTo(Right) - } - - @Test - fun resolve_endInRtl_resolvesLeft() { - val resolvedEdge = End.resolve(LayoutDirection.Rtl) - assertThat(resolvedEdge).isEqualTo(Left) - } - - @Test - fun resolve_topStartInLtr_resolvesTopLeft() { - val resolvedEdge = TopStart.resolve(LayoutDirection.Ltr) - assertThat(resolvedEdge).isEqualTo(TopLeft) - } - - @Test - fun resolve_topStartInRtl_resolvesTopRight() { - val resolvedEdge = TopStart.resolve(LayoutDirection.Rtl) - assertThat(resolvedEdge).isEqualTo(TopRight) - } - - @Test - fun resolve_topEndInLtr_resolvesTopRight() { - val resolvedEdge = TopEnd.resolve(LayoutDirection.Ltr) - assertThat(resolvedEdge).isEqualTo(TopRight) - } - - @Test - fun resolve_topEndInRtl_resolvesTopLeft() { - val resolvedEdge = TopEnd.resolve(LayoutDirection.Rtl) - assertThat(resolvedEdge).isEqualTo(TopLeft) - } - - private fun swipeVerticallyFrom(x: Int, y: Int): SceneContainerEdge.Resolved? { - return swipeFrom(x, y, Orientation.Vertical) - } - - private fun swipeHorizontallyFrom(x: Int, y: Int): SceneContainerEdge.Resolved? { - return swipeFrom(x, y, Orientation.Horizontal) - } - - private fun swipeFrom(x: Int, y: Int, orientation: Orientation): SceneContainerEdge.Resolved? { - return underTest.source( - layoutSize = IntSize(width = screenWidth, height = screenHeight), - position = IntOffset(x, y), - density = Density(1f), - orientation = orientation, - ) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt index 1b9251061f3d..9319961f5b68 100644 --- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt @@ -24,7 +24,7 @@ import com.android.compose.animation.scene.UserActionResult.HideOverlay import com.android.compose.animation.scene.UserActionResult.ShowOverlay import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays import com.android.systemui.scene.shared.model.Overlays -import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge +import com.android.systemui.scene.ui.viewmodel.SceneContainerArea import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -38,7 +38,7 @@ class NotificationsShadeOverlayActionsViewModel @AssistedInject constructor() : mapOf( Swipe.Up to HideOverlay(Overlays.NotificationsShade), Back to HideOverlay(Overlays.NotificationsShade), - Swipe.Down(fromSource = SceneContainerEdge.TopRight) to + Swipe.Down(fromSource = SceneContainerArea.EndHalf) to ShowOverlay( Overlays.QuickSettingsShade, hideCurrentOverlays = HideCurrentOverlays.Some(Overlays.NotificationsShade), diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt index 5bc26f50f70f..52c4e2fac6d5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt @@ -25,7 +25,7 @@ import com.android.compose.animation.scene.UserActionResult.ShowOverlay import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel import com.android.systemui.scene.shared.model.Overlays -import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge +import com.android.systemui.scene.ui.viewmodel.SceneContainerArea import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -47,7 +47,7 @@ constructor(private val editModeViewModel: EditModeViewModel) : UserActionsViewM put(Back, HideOverlay(Overlays.QuickSettingsShade)) } put( - Swipe.Down(fromSource = SceneContainerEdge.TopLeft), + Swipe.Down(fromSource = SceneContainerArea.StartHalf), ShowOverlay( Overlays.NotificationsShade, hideCurrentOverlays = diff --git a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt index a4949ad66109..caa61617505f 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt @@ -16,7 +16,6 @@ package com.android.systemui.scene -import androidx.compose.ui.unit.dp import com.android.systemui.CoreStartable import com.android.systemui.notifications.ui.composable.NotificationsShadeSessionModule import com.android.systemui.scene.domain.SceneDomainModule @@ -30,8 +29,6 @@ import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.composable.SceneContainerTransitions -import com.android.systemui.scene.ui.viewmodel.SplitEdgeDetector -import com.android.systemui.shade.domain.interactor.ShadeInteractor import dagger.Binds import dagger.Module import dagger.Provides @@ -99,15 +96,5 @@ interface KeyguardlessSceneContainerFrameworkModule { transitionsBuilder = SceneContainerTransitions(), ) } - - @Provides - fun splitEdgeDetector(shadeInteractor: ShadeInteractor): SplitEdgeDetector { - return SplitEdgeDetector( - topEdgeSplitFraction = shadeInteractor::getTopEdgeSplitFraction, - // TODO(b/338577208): This should be 60dp at the top in the dual-shade UI. Better to - // replace this constant with dynamic window insets. - edgeSize = 40.dp, - ) - } } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt index a018283c3953..ea11d202b119 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt @@ -16,7 +16,6 @@ package com.android.systemui.scene -import androidx.compose.ui.unit.dp import com.android.systemui.CoreStartable import com.android.systemui.notifications.ui.composable.NotificationsShadeSessionModule import com.android.systemui.scene.domain.SceneDomainModule @@ -30,8 +29,6 @@ import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.ui.composable.SceneContainerTransitions -import com.android.systemui.scene.ui.viewmodel.SplitEdgeDetector -import com.android.systemui.shade.domain.interactor.ShadeInteractor import dagger.Binds import dagger.Module import dagger.Provides @@ -121,15 +118,5 @@ interface SceneContainerFrameworkModule { transitionsBuilder = SceneContainerTransitions(), ) } - - @Provides - fun splitEdgeDetector(shadeInteractor: ShadeInteractor): SplitEdgeDetector { - return SplitEdgeDetector( - topEdgeSplitFraction = shadeInteractor::getTopEdgeSplitFraction, - // TODO(b/338577208): This should be 60dp at the top in the dual-shade UI. Better to - // replace this constant with dynamic window insets. - edgeSize = 40.dp, - ) - } } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetector.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetector.kt new file mode 100644 index 000000000000..ede453dbe6b3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetector.kt @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2025 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.scene.ui.viewmodel + +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.LayoutDirection +import com.android.compose.animation.scene.Edge +import com.android.compose.animation.scene.FixedSizeEdgeDetector +import com.android.compose.animation.scene.SwipeSource +import com.android.compose.animation.scene.SwipeSourceDetector + +/** Identifies an area of the [SceneContainer] to detect swipe gestures on. */ +sealed class SceneContainerArea(private val resolveArea: (LayoutDirection) -> Resolved) : + SwipeSource { + data object StartEdge : + SceneContainerArea( + resolveArea = { + if (it == LayoutDirection.Ltr) Resolved.LeftEdge else Resolved.RightEdge + } + ) + + data object StartHalf : + SceneContainerArea( + resolveArea = { + if (it == LayoutDirection.Ltr) Resolved.LeftHalf else Resolved.RightHalf + } + ) + + data object EndEdge : + SceneContainerArea( + resolveArea = { + if (it == LayoutDirection.Ltr) Resolved.RightEdge else Resolved.LeftEdge + } + ) + + data object EndHalf : + SceneContainerArea( + resolveArea = { + if (it == LayoutDirection.Ltr) Resolved.RightHalf else Resolved.LeftHalf + } + ) + + override fun resolve(layoutDirection: LayoutDirection): Resolved { + return resolveArea(layoutDirection) + } + + sealed interface Resolved : SwipeSource.Resolved { + data object LeftEdge : Resolved + + data object LeftHalf : Resolved + + data object BottomEdge : Resolved + + data object RightEdge : Resolved + + data object RightHalf : Resolved + } +} + +/** + * A [SwipeSourceDetector] that detects edges similarly to [FixedSizeEdgeDetector], but additionally + * detects the left and right halves of the screen (besides the edges). + * + * Corner cases (literally): A vertical swipe on the top-left corner of the screen will be resolved + * to [SceneContainerArea.Resolved.LeftHalf], whereas a horizontal swipe in the same position will + * be resolved to [SceneContainerArea.Resolved.LeftEdge]. The behavior is similar on the top-right + * corner of the screen. + * + * Callers who need to detect the start and end edges based on the layout direction (LTR vs RTL) + * should subscribe to [SceneContainerArea.StartEdge] and [SceneContainerArea.EndEdge] instead. + * These will be resolved at runtime to [SceneContainerArea.Resolved.LeftEdge] and + * [SceneContainerArea.Resolved.RightEdge] appropriately. Similarly, [SceneContainerArea.StartHalf] + * and [SceneContainerArea.EndHalf] will be resolved appropriately to + * [SceneContainerArea.Resolved.LeftHalf] and [SceneContainerArea.Resolved.RightHalf]. + * + * @param edgeSize The fixed size of each edge. + */ +class SceneContainerSwipeDetector(val edgeSize: Dp) : SwipeSourceDetector { + + private val fixedEdgeDetector = FixedSizeEdgeDetector(edgeSize) + + override fun source( + layoutSize: IntSize, + position: IntOffset, + density: Density, + orientation: Orientation, + ): SceneContainerArea.Resolved { + val fixedEdge = fixedEdgeDetector.source(layoutSize, position, density, orientation) + return when (fixedEdge) { + Edge.Resolved.Left -> SceneContainerArea.Resolved.LeftEdge + Edge.Resolved.Bottom -> SceneContainerArea.Resolved.BottomEdge + Edge.Resolved.Right -> SceneContainerArea.Resolved.RightEdge + else -> { + // Note: This intentionally includes Edge.Resolved.Top. At the moment, we don't need + // to detect swipes on the top edge, and consider them part of the right/left half. + if (position.x < layoutSize.width * 0.5f) { + SceneContainerArea.Resolved.LeftHalf + } else { + SceneContainerArea.Resolved.RightHalf + } + } + } + } +} 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 498ad1d5e49a..01bcc2400933 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 @@ -19,6 +19,7 @@ package com.android.systemui.scene.ui.viewmodel import android.view.MotionEvent import android.view.View import androidx.compose.runtime.getValue +import androidx.compose.ui.unit.dp import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.animation.scene.ContentKey import com.android.compose.animation.scene.DefaultEdgeDetector @@ -64,7 +65,6 @@ constructor( private val powerInteractor: PowerInteractor, shadeModeInteractor: ShadeModeInteractor, private val remoteInputInteractor: RemoteInputInteractor, - private val splitEdgeDetector: SplitEdgeDetector, private val logger: SceneLogger, hapticsViewModelFactory: SceneContainerHapticsViewModel.Factory, val lightRevealScrim: LightRevealScrimViewModel, @@ -89,16 +89,20 @@ constructor( val hapticsViewModel: SceneContainerHapticsViewModel = hapticsViewModelFactory.create(view) /** - * The [SwipeSourceDetector] to use for defining which edges of the screen can be defined in the + * The [SwipeSourceDetector] to use for defining which areas of the screen can be defined in the * [UserAction]s for this container. */ - val edgeDetector: SwipeSourceDetector by + val swipeSourceDetector: SwipeSourceDetector by hydrator.hydratedStateOf( - traceName = "edgeDetector", + traceName = "swipeSourceDetector", initialValue = DefaultEdgeDetector, source = shadeModeInteractor.shadeMode.map { - if (it is ShadeMode.Dual) splitEdgeDetector else DefaultEdgeDetector + if (it is ShadeMode.Dual) { + SceneContainerSwipeDetector(edgeSize = 40.dp) + } else { + DefaultEdgeDetector + } }, ) diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SplitEdgeDetector.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SplitEdgeDetector.kt deleted file mode 100644 index f88bcb57a27d..000000000000 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SplitEdgeDetector.kt +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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.scene.ui.viewmodel - -import androidx.compose.foundation.gestures.Orientation -import androidx.compose.ui.unit.Density -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.unit.IntSize -import androidx.compose.ui.unit.LayoutDirection -import com.android.compose.animation.scene.Edge -import com.android.compose.animation.scene.FixedSizeEdgeDetector -import com.android.compose.animation.scene.SwipeSource -import com.android.compose.animation.scene.SwipeSourceDetector - -/** - * The edge of a [SceneContainer]. It differs from a standard [Edge] by splitting the top edge into - * top-left and top-right. - */ -enum class SceneContainerEdge(private val resolveEdge: (LayoutDirection) -> Resolved) : - SwipeSource { - TopLeft(resolveEdge = { Resolved.TopLeft }), - TopRight(resolveEdge = { Resolved.TopRight }), - TopStart( - resolveEdge = { if (it == LayoutDirection.Ltr) Resolved.TopLeft else Resolved.TopRight } - ), - TopEnd( - resolveEdge = { if (it == LayoutDirection.Ltr) Resolved.TopRight else Resolved.TopLeft } - ), - Bottom(resolveEdge = { Resolved.Bottom }), - Left(resolveEdge = { Resolved.Left }), - Right(resolveEdge = { Resolved.Right }), - Start(resolveEdge = { if (it == LayoutDirection.Ltr) Resolved.Left else Resolved.Right }), - End(resolveEdge = { if (it == LayoutDirection.Ltr) Resolved.Right else Resolved.Left }); - - override fun resolve(layoutDirection: LayoutDirection): Resolved { - return resolveEdge(layoutDirection) - } - - enum class Resolved : SwipeSource.Resolved { - TopLeft, - TopRight, - Bottom, - Left, - Right, - } -} - -/** - * A [SwipeSourceDetector] that detects edges similarly to [FixedSizeEdgeDetector], except that the - * top edge is split in two: top-left and top-right. The split point between the two is dynamic and - * may change during runtime. - * - * Callers who need to detect the start and end edges based on the layout direction (LTR vs RTL) - * should subscribe to [SceneContainerEdge.TopStart] and [SceneContainerEdge.TopEnd] instead. These - * will be resolved at runtime to [SceneContainerEdge.Resolved.TopLeft] and - * [SceneContainerEdge.Resolved.TopRight] appropriately. Similarly, [SceneContainerEdge.Start] and - * [SceneContainerEdge.End] will be resolved appropriately to [SceneContainerEdge.Resolved.Left] and - * [SceneContainerEdge.Resolved.Right]. - * - * @param topEdgeSplitFraction A function which returns the fraction between [0..1] (i.e., - * percentage) of screen width to consider the split point between "top-left" and "top-right" - * edges. It is called on each source detection event. - * @param edgeSize The fixed size of each edge. - */ -class SplitEdgeDetector( - val topEdgeSplitFraction: () -> Float, - val edgeSize: Dp, -) : SwipeSourceDetector { - - private val fixedEdgeDetector = FixedSizeEdgeDetector(edgeSize) - - override fun source( - layoutSize: IntSize, - position: IntOffset, - density: Density, - orientation: Orientation, - ): SceneContainerEdge.Resolved? { - val fixedEdge = - fixedEdgeDetector.source( - layoutSize, - position, - density, - orientation, - ) - return when (fixedEdge) { - Edge.Resolved.Top -> { - val topEdgeSplitFraction = topEdgeSplitFraction() - require(topEdgeSplitFraction in 0f..1f) { - "topEdgeSplitFraction must return a value between 0.0 and 1.0" - } - val isLeftSide = position.x < layoutSize.width * topEdgeSplitFraction - if (isLeftSide) SceneContainerEdge.Resolved.TopLeft - else SceneContainerEdge.Resolved.TopRight - } - Edge.Resolved.Left -> SceneContainerEdge.Resolved.Left - Edge.Resolved.Bottom -> SceneContainerEdge.Resolved.Bottom - Edge.Resolved.Right -> SceneContainerEdge.Resolved.Right - null -> null - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt b/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt index b155ada87efd..1f534a5c191a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/display/StatusBarTouchShadeDisplayPolicy.kt @@ -111,10 +111,7 @@ constructor( statusbarWidth: Int, ): ShadeElement { val xPercentage = motionEvent.x / statusbarWidth - val threshold = shadeInteractor.get().getTopEdgeSplitFraction() - return if (xPercentage < threshold) { - notificationElement.get() - } else qsShadeElement.get() + return if (xPercentage < 0.5f) notificationElement.get() else qsShadeElement.get() } private fun monitorDisplayRemovals(): Job { diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt index c8ce316c41dd..6d68796454eb 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt @@ -16,7 +16,6 @@ package com.android.systemui.shade.domain.interactor -import androidx.annotation.FloatRange import com.android.compose.animation.scene.TransitionKey import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow @@ -66,16 +65,6 @@ interface ShadeInteractor : BaseShadeInteractor { * wide as the entire screen. */ val isShadeLayoutWide: StateFlow<Boolean> - - /** - * The fraction between [0..1] (i.e., percentage) of screen width to consider the threshold - * between "top-left" and "top-right" for the purposes of dual-shade invocation. - * - * Note that this fraction only determines the *split* between the absolute left and right - * directions. In RTL layouts, the "top-start" edge will resolve to "top-right", and "top-end" - * will resolve to "top-left". - */ - @FloatRange(from = 0.0, to = 1.0) fun getTopEdgeSplitFraction(): Float } /** ShadeInteractor methods with implementations that differ between non-empty impls. */ diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt index b1129a94d833..77e6a833c153 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorEmptyImpl.kt @@ -48,8 +48,6 @@ class ShadeInteractorEmptyImpl @Inject constructor() : ShadeInteractor { override val isExpandToQsEnabled: Flow<Boolean> = inactiveFlowBoolean override val isShadeLayoutWide: StateFlow<Boolean> = inactiveFlowBoolean - override fun getTopEdgeSplitFraction(): Float = 0.5f - override fun expandNotificationsShade(loggingReason: String, transitionKey: TransitionKey?) {} override fun expandQuickSettingsShade(loggingReason: String, transitionKey: TransitionKey?) {} diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt index c6752f867183..cf3b08c041be 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt @@ -20,10 +20,11 @@ import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.Swipe import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult +import com.android.compose.animation.scene.UserActionResult.ShowOverlay import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.TransitionKeys.ToSplitShade -import com.android.systemui.scene.ui.viewmodel.SceneContainerEdge +import com.android.systemui.scene.ui.viewmodel.SceneContainerArea /** Returns collection of [UserAction] to [UserActionResult] pairs for opening the single shade. */ fun singleShadeActions( @@ -66,11 +67,10 @@ fun splitShadeActions(): Array<Pair<UserAction, UserActionResult>> { /** Returns collection of [UserAction] to [UserActionResult] pairs for opening the dual shade. */ fun dualShadeActions(): Array<Pair<UserAction, UserActionResult>> { - val notifShadeUserActionResult = UserActionResult.ShowOverlay(Overlays.NotificationsShade) - val qsShadeuserActionResult = UserActionResult.ShowOverlay(Overlays.QuickSettingsShade) return arrayOf( - Swipe.Down to notifShadeUserActionResult, - Swipe.Down(fromSource = SceneContainerEdge.TopRight) to qsShadeuserActionResult, + Swipe.Down to ShowOverlay(Overlays.NotificationsShade), + Swipe.Down(fromSource = SceneContainerArea.EndHalf) to + ShowOverlay(Overlays.QuickSettingsShade), ) } 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 825e0143800b..f0350acd83ca 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 @@ -20,7 +20,6 @@ import com.android.systemui.scene.ui.FakeOverlay import com.android.systemui.scene.ui.composable.ConstantSceneContainerTransitionsBuilder 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.shade.domain.interactor.shadeModeInteractor import com.android.systemui.statusbar.domain.interactor.remoteInputInteractor @@ -99,7 +98,6 @@ val Kosmos.sceneContainerViewModelFactory by Fixture { powerInteractor = powerInteractor, shadeModeInteractor = shadeModeInteractor, remoteInputInteractor = remoteInputInteractor, - splitEdgeDetector = splitEdgeDetector, logger = sceneLogger, hapticsViewModelFactory = sceneContainerHapticsViewModelFactory, view = view, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/viewmodel/SplitEdgeDetectorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/viewmodel/SplitEdgeDetectorKosmos.kt deleted file mode 100644 index e0b529261c4d..000000000000 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/viewmodel/SplitEdgeDetectorKosmos.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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.scene.ui.viewmodel - -import androidx.compose.ui.unit.dp -import com.android.systemui.kosmos.Kosmos -import com.android.systemui.shade.domain.interactor.shadeInteractor - -var Kosmos.splitEdgeDetector: SplitEdgeDetector by - Kosmos.Fixture { - SplitEdgeDetector( - topEdgeSplitFraction = shadeInteractor::getTopEdgeSplitFraction, - edgeSize = 40.dp, - ) - } |