Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 48a40537 authored by Danny Burakov's avatar Danny Burakov Committed by Android (Google) Code Review
Browse files

Merge "[flexiglass] Single source of truth for notifications horizon. position." into main

parents 3e0afde8 f0d7aafb
Loading
Loading
Loading
Loading
+12 −10
Original line number Diff line number Diff line
@@ -192,7 +192,7 @@ constructor(
        ) {
            OverlayShade(
                panelElement = QuickSettingsShade.Elements.Panel,
                alignmentOnWideScreens = Alignment.TopEnd,
                alignmentOnWideScreens = Alignment.End,
                enableTransparency = contentViewModel.isTransparencyEnabled,
                onScrimClicked = contentViewModel::onScrimClicked,
                onBackgroundPlaced = { bounds, topCornerRadius, bottomCornerRadius ->
@@ -233,15 +233,17 @@ constructor(
                },
                stackScrollView = notificationStackScrollView.get(),
                viewModel = hunPlaceholderViewModel,
                modifier = Modifier.onGloballyPositioned { layoutCoordinates ->
                modifier =
                    Modifier.onGloballyPositioned { layoutCoordinates ->
                        val bounds = layoutCoordinates.boundsInWindow().toAndroidRectF()
                        if (bounds.height() > 0) {
                        // HUN gesture area must extend from the top of the screen for animations
                            // HUN gesture area must extend from the top of the screen for
                            // animations
                            bounds.top = 0f
                            bounds.bottom += headsUpInset
                            notificationStackScrollView.get().updateDrawBounds(bounds)
                        }
                }
                    },
            )
        }
    }
+12 −5
Original line number Diff line number Diff line
@@ -68,7 +68,7 @@ import kotlin.math.min
@Composable
fun ContentScope.OverlayShade(
    panelElement: ElementKey,
    alignmentOnWideScreens: Alignment,
    alignmentOnWideScreens: Alignment.Horizontal,
    enableTransparency: Boolean,
    onScrimClicked: () -> Unit,
    modifier: Modifier = Modifier,
@@ -81,13 +81,20 @@ fun ContentScope.OverlayShade(
    val isFullWidth = isFullWidthShade()
    val panelSpec = rememberShadeExpansionMotion(isFullWidth)
    val panelCornerRadiusPx = with(LocalDensity.current) { panelSpec.radius.toPx() }
    val panelAlignment =
        when {
            isFullWidth -> Alignment.TopCenter
            alignmentOnWideScreens == Alignment.End -> Alignment.TopEnd
            else -> Alignment.TopStart
        }

    Box(modifier) {
        Scrim(showBackgroundColor = enableTransparency, onClicked = onScrimClicked)

        Box(
            modifier =
                Modifier.fillMaxSize().panelContainerPadding(isFullWidth, alignmentOnWideScreens),
            contentAlignment = if (isFullWidth) Alignment.TopCenter else alignmentOnWideScreens,
            contentAlignment = panelAlignment,
        ) {
            val gestureContext = rememberGestureContext()
            Panel(
@@ -189,7 +196,7 @@ private fun getHalfScreenWidth(): Dp {
@Composable
private fun Modifier.panelContainerPadding(
    isFullWidthPanel: Boolean,
    alignment: Alignment,
    alignment: Alignment.Horizontal,
): Modifier {
    if (isFullWidthPanel) {
        return this
@@ -200,8 +207,8 @@ private fun Modifier.panelContainerPadding(

    val (startPadding, endPadding) =
        when (alignment) {
            Alignment.TopStart -> horizontalPaddingDp to halfScreenWidth
            Alignment.TopEnd -> halfScreenWidth to horizontalPaddingDp
            Alignment.Start -> horizontalPaddingDp to halfScreenWidth
            Alignment.End -> halfScreenWidth to horizontalPaddingDp
            else -> horizontalPaddingDp to horizontalPaddingDp
        }
    val paddings = PaddingValues(start = startPadding, end = endPadding)
+66 −41
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.android.systemui.notifications.ui.viewmodel
import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS
import android.content.res.Configuration
import android.content.res.mainResources
import android.content.testableContext
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper
@@ -34,10 +33,12 @@ import com.android.systemui.authentication.data.repository.FakeAuthenticationRep
import com.android.systemui.authentication.domain.interactor.AuthenticationResult
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.desktop.domain.interactor.DesktopInteractor
import com.android.systemui.desktop.domain.interactor.desktopInteractor
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.ui.transitions.blurConfig
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.backgroundScope
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
@@ -67,6 +68,10 @@ import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModelFactory
import com.android.systemui.statusbar.core.StatusBarForDesktop
import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
import com.android.systemui.statusbar.disableflags.domain.interactor.disableFlagsInteractor
import com.android.systemui.statusbar.notification.stack.data.repository.notificationPlaceholderRepository
import com.android.systemui.statusbar.notification.stack.data.repository.notificationViewHeightRepository
import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModelFactory
import com.android.systemui.statusbar.policy.configurationController
import com.android.systemui.testKosmos
@@ -99,6 +104,7 @@ class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() {
    fun showHeader_desktopStatusBarDisabled_true() =
        kosmos.runTest {
            setUseDesktopStatusBar(false)

            assertThat(underTest.showHeader).isTrue()
        }

@@ -107,6 +113,7 @@ class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() {
    fun showHeader_desktopStatusBarEnabled_statusBarForDesktopEnabled_false() =
        kosmos.runTest {
            setUseDesktopStatusBar(true)

            assertThat(underTest.showHeader).isFalse()
        }

@@ -115,30 +122,40 @@ class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() {
    fun showHeader_desktopStatusBarEnabled_statusBarForDesktopDisabled_true() =
        kosmos.runTest {
            setUseDesktopStatusBar(true)

            assertThat(underTest.showHeader).isTrue()
        }

    @Test
    @DisableFlags(StatusBarForDesktop.FLAG_NAME)
    fun alignmentOnWideScreens_statusBarForDesktopDisabled_topStart() =
    fun alignmentOnWideScreens_statusBarForDesktopDisabled_start() =
        kosmos.runTest {
            assertThat(createTestInstance().alignmentOnWideScreens).isEqualTo(Alignment.TopStart)
            setUseDesktopStatusBar(false)

            assertThat(underTest.alignmentOnWideScreens).isEqualTo(Alignment.Start)
        }

    @Test
    @EnableFlags(StatusBarForDesktop.FLAG_NAME)
    fun alignmentOnWideScreens_configDisabled_statusBarForDesktopEnabled_topStart() =
    fun alignmentOnWideScreens_configDisabled_statusBarForDesktopEnabled_start() =
        kosmos.runTest {
            overrideConfig(R.bool.config_notificationShadeOnTopEnd, false)
            assertThat(createTestInstance().alignmentOnWideScreens).isEqualTo(Alignment.TopStart)
            enableDualShade(wideLayout = true)
            overrideResource(R.bool.config_notificationShadeOnTopEnd, false)

            val underTest = createTestInstance().apply { activateIn(testScope) }
            assertThat(underTest.alignmentOnWideScreens).isEqualTo(Alignment.Start)
        }

    @Test
    @EnableFlags(StatusBarForDesktop.FLAG_NAME)
    fun alignmentOnWideScreens_configEnabled_statusBarForDesktopEnabled_topEnd() =
    fun alignmentOnWideScreens_configEnabled_statusBarForDesktopEnabled_end() =
        kosmos.runTest {
            overrideConfig(R.bool.config_notificationShadeOnTopEnd, true)
            assertThat(createTestInstance().alignmentOnWideScreens).isEqualTo(Alignment.TopEnd)
            enableDualShade(wideLayout = true)
            overrideResource(R.bool.config_notificationShadeOnTopEnd, true)
            notificationStackAppearanceInteractor.notificationStackHorizontalAlignment

            val underTest = createTestInstance().apply { activateIn(testScope) }
            assertThat(underTest.alignmentOnWideScreens).isEqualTo(Alignment.End)
        }

    @Test
@@ -306,12 +323,12 @@ class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() {
            val currentScene by collectLastValue(sceneInteractor.currentScene)
            val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)

            kosmos.enableDualShade()
            kosmos.shadeInteractor.expandNotificationsShade("test")
            enableDualShade()
            shadeInteractor.expandNotificationsShade("test")
            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
            assertThat(currentOverlays).contains(Overlays.NotificationsShade)

            kosmos.enableSingleShade()
            enableSingleShade()
            assertThat(currentScene).isEqualTo(Scenes.Shade)
            assertThat(currentOverlays).doesNotContain(Overlays.NotificationsShade)
        }
@@ -370,15 +387,37 @@ class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() {
        assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
    }

    private fun Kosmos.setUseDesktopStatusBar(enable: Boolean) {
        overrideConfig(R.bool.config_useDesktopStatusBar, enable)
        configurationController.onConfigurationChanged(Configuration())
    private suspend fun Kosmos.unlockDevice() {
        val currentScene by collectLastValue(sceneInteractor.currentScene)
        powerInteractor.setAwakeForTest()
        assertThat(authenticationInteractor.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
            .isEqualTo(AuthenticationResult.SUCCEEDED)

        assertThat(currentScene).isEqualTo(Scenes.Gone)
    }

    private fun Kosmos.overrideConfig(configId: Int, value: Boolean) {
        testableContext.orCreateTestableResources.addOverride(configId, value)
    @Test
    fun onShadeBoundsChanged_forwardsToShadeOverlayInteractor() =
        kosmos.runTest {
            var shadeBounds: android.graphics.Rect? = null
            shadeInteractor.addShadeOverlayBoundsListener { shadeBounds = it }
            assertThat(shadeBounds).isNull()

            val bounds = Rect(0f, 0f, 100f, 100f)
            val expectedShadeBounds = android.graphics.Rect(0, 0, 100, 100)
            underTest.onShadeOverlayBoundsChanged(bounds)

            assertThat(shadeBounds).isEqualTo(expectedShadeBounds)
        }

    private fun Kosmos.setUseDesktopStatusBar(enable: Boolean) {
        enableDualShade(wideLayout = true)
        overrideResource(R.bool.config_useDesktopStatusBar, enable)
        configurationController.onConfigurationChanged(Configuration())
    }

    // TODO(441100057): Remove once DesktopInteractor.isNotificationShadeOnTopEnd supports runtime
    //  config updates.
    private fun Kosmos.createTestInstance(): NotificationsShadeOverlayContentViewModel {
        val desktopInteractor =
            DesktopInteractor(
@@ -390,38 +429,24 @@ class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() {
            mainDispatcher = testDispatcher,
            shadeHeaderViewModelFactory = shadeHeaderViewModelFactory,
            notificationsPlaceholderViewModelFactory = notificationsPlaceholderViewModelFactory,
            notificationStackAppearanceInteractor =
                NotificationStackAppearanceInteractor(
                    applicationScope = applicationCoroutineScope,
                    viewHeightRepository = notificationViewHeightRepository,
                    placeholderRepository = notificationPlaceholderRepository,
                    sceneInteractor = sceneInteractor,
                    shadeModeInteractor = shadeModeInteractor,
                    desktopInteractor = desktopInteractor,
                ),
            sceneInteractor = sceneInteractor,
            shadeInteractor = shadeInteractor,
            shadeModeInteractor = shadeModeInteractor,
            disableFlagsInteractor = disableFlagsInteractor,
            mediaCarouselInteractor = mediaCarouselInteractor,
            blurConfig = blurConfig,
            windowRootViewBlurInteractor = windowRootViewBlurInteractor,
            desktopInteractor = desktopInteractor,
            blurConfig = blurConfig,
            mediaViewModelFactory = mediaViewModelFactory,
        )
    }

    private suspend fun Kosmos.unlockDevice() {
        val currentScene by collectLastValue(sceneInteractor.currentScene)
        powerInteractor.setAwakeForTest()
        assertThat(authenticationInteractor.authenticate(FakeAuthenticationRepository.DEFAULT_PIN))
            .isEqualTo(AuthenticationResult.SUCCEEDED)

        assertThat(currentScene).isEqualTo(Scenes.Gone)
    }

    @Test
    fun onShadeBoundsChanged_forwardsToShadeOverlayInteractor() =
        kosmos.runTest {
            var shadeBounds: android.graphics.Rect? = null
            shadeInteractor.addShadeOverlayBoundsListener { shadeBounds = it }
            assertThat(shadeBounds).isNull()

            val bounds = Rect(0f, 0f, 100f, 100f)
            val expectedShadeBounds = android.graphics.Rect(0, 0, 100, 100)
            underTest.onShadeOverlayBoundsChanged(bounds)

            assertThat(shadeBounds).isEqualTo(expectedShadeBounds)
        }
}
+86 −3
Original line number Diff line number Diff line
@@ -16,20 +16,29 @@

package com.android.systemui.statusbar.notification.stack.domain.interactor

import android.platform.test.annotations.EnableFlags
import androidx.compose.ui.Alignment
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_STATUS_BAR_FOR_DESKTOP
import com.android.systemui.SysuiTestCase
import com.android.systemui.desktop.domain.interactor.enableUsingDesktopStatusBar
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.domain.interactor.enableDualShade
import com.android.systemui.shade.domain.interactor.enableSingleShade
import com.android.systemui.shade.domain.interactor.enableSplitShade
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 org.junit.Assert
import org.junit.Assert.assertThrows
import org.junit.Test
import org.junit.runner.RunWith
@@ -38,7 +47,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class NotificationStackAppearanceInteractorTest : SysuiTestCase() {

    private val kosmos = testKosmos()
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val Kosmos.underTest by Kosmos.Fixture { notificationStackAppearanceInteractor }

    @Test
@@ -93,7 +102,9 @@ class NotificationStackAppearanceInteractorTest : SysuiTestCase() {
    fun stackNotificationScrimBounds_withImproperBounds_throwsException() =
        kosmos.runTest {
            assertThrows(IllegalStateException::class.java) {
                underTest.setNotificationShadeScrimBounds(ShadeScrimBounds(top = 100f, bottom = 99f))
                underTest.setNotificationShadeScrimBounds(
                    ShadeScrimBounds(top = 100f, bottom = 99f)
                )
            }
        }

@@ -151,4 +162,76 @@ class NotificationStackAppearanceInteractorTest : SysuiTestCase() {

            assertThat(shouldCloseGuts).isFalse()
        }

    @Test
    fun notificationStackHorizontalAlignment_singleShade_centeredHorizontally() =
        kosmos.runTest {
            val alignment by collectLastValue(underTest.notificationStackHorizontalAlignment)

            enableSingleShade(wideLayout = true)

            assertThat(alignment).isEqualTo(Alignment.CenterHorizontally)
        }

    @Test
    fun notificationStackHorizontalAlignment_splitShade_endAligned() =
        kosmos.runTest {
            val alignment by collectLastValue(underTest.notificationStackHorizontalAlignment)

            enableSplitShade()

            assertThat(alignment).isEqualTo(Alignment.End)
        }

    @Test
    @EnableSceneContainer
    fun notificationStackHorizontalAlignment_dualShadeNarrow_centeredHorizontally() =
        kosmos.runTest {
            val alignment by collectLastValue(underTest.notificationStackHorizontalAlignment)

            enableDualShade(wideLayout = false)

            assertThat(alignment).isEqualTo(Alignment.CenterHorizontally)
        }

    @Test
    @EnableSceneContainer
    fun notificationStackHorizontalAlignment_dualShadeWide_startAligned() =
        kosmos.runTest {
            val alignment by collectLastValue(underTest.notificationStackHorizontalAlignment)

            enableDualShade(wideLayout = true)

            assertThat(alignment).isEqualTo(Alignment.Start)
        }

    @Test
    @EnableSceneContainer
    @EnableFlags(FLAG_STATUS_BAR_FOR_DESKTOP)
    fun notificationStackHorizontalAlignment_desktopWithTopEndConfig_endAligned() =
        kosmos.runTest {
            overrideResource(R.bool.config_notificationShadeOnTopEnd, true)

            val alignment by collectLastValue(underTest.notificationStackHorizontalAlignment)

            enableUsingDesktopStatusBar()
            enableDualShade(wideLayout = true)

            assertThat(alignment).isEqualTo(Alignment.End)
        }

    @Test
    @EnableSceneContainer
    @EnableFlags(FLAG_STATUS_BAR_FOR_DESKTOP)
    fun notificationStackHorizontalAlignment_desktopWithoutTopEndConfig_startAligned() =
        kosmos.runTest {
            overrideResource(R.bool.config_notificationShadeOnTopEnd, false)

            val alignment by collectLastValue(underTest.notificationStackHorizontalAlignment)

            enableUsingDesktopStatusBar()
            enableDualShade(wideLayout = true)

            assertThat(alignment).isEqualTo(Alignment.Start)
        }
}
+21 −145

File changed.

Preview size limit exceeded, changes collapsed.

Loading