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

Commit 1bc3ef6e authored by Ale Nijamkin's avatar Ale Nijamkin Committed by Android (Google) Code Review
Browse files

Merge "[flexiglass] Adds unit/integration tests for notifications." into main

parents 57927ea7 022fd95e
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -144,8 +144,7 @@ private fun SceneScope.LockscreenScene(
            modifier = Modifier.fillMaxSize(),
        )

        val notificationStackPosition by
            viewModel.keyguardRoot.notificationPositionOnLockscreen.collectAsState()
        val notificationStackPosition by viewModel.keyguardRoot.notificationBounds.collectAsState()

        Layout(
            modifier = Modifier.fillMaxSize(),
+5 −5
Original line number Diff line number Diff line
@@ -72,8 +72,8 @@ object Notifications {
@Composable
fun SceneScope.HeadsUpNotificationSpace(
    viewModel: NotificationsPlaceholderViewModel,
    isPeekFromBottom: Boolean = false,
    modifier: Modifier = Modifier,
    isPeekFromBottom: Boolean = false,
) {
    NotificationPlaceholder(
        viewModel = viewModel,
@@ -149,11 +149,11 @@ private fun SceneScope.NotificationPlaceholder(
    form: Form,
    modifier: Modifier = Modifier,
) {
    val key = Notifications.Elements.NotificationPlaceholder
    val elementKey = Notifications.Elements.NotificationPlaceholder
    Box(
        modifier =
            modifier
                .element(key)
                .element(elementKey)
                .debugBackground(viewModel)
                .onSizeChanged { size: IntSize ->
                    debugLog(viewModel) { "STACK onSizeChanged: size=$size" }
@@ -166,7 +166,7 @@ private fun SceneScope.NotificationPlaceholder(
                            " bounds=${coordinates.boundsInWindow()}"
                    }
                    val boundsInWindow = coordinates.boundsInWindow()
                    viewModel.setPlaceholderPositionInWindow(
                    viewModel.onBoundsChanged(
                        top = boundsInWindow.top,
                        bottom = boundsInWindow.bottom,
                    )
@@ -176,7 +176,7 @@ private fun SceneScope.NotificationPlaceholder(
            animateSharedFloatAsState(
                value = if (form == Form.HunFromTop) 0f else 1f,
                key = SharedExpansionValue,
                element = key
                element = elementKey
            )
        debugLog(viewModel) { "STACK composed: expansion=$animatedExpansion" }
        if (viewModel.isPlaceholderTextVisible) {
+107 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.
 */

@file:OptIn(ExperimentalCoroutinesApi::class)

package com.android.systemui.statusbar.notification

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
import com.android.systemui.flags.featureFlagsClassic
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags
import com.android.systemui.scene.shared.flag.sceneContainerFlags
import com.android.systemui.scene.shared.model.ObservableTransitionState
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationStackAppearanceViewModel
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class NotificationStackAppearanceIntegrationTest : SysuiTestCase() {

    private val kosmos =
        testKosmos().apply {
            sceneContainerFlags = FakeSceneContainerFlags(enabled = true)
            featureFlagsClassic.apply {
                set(Flags.FULL_SCREEN_USER_SWITCHER, false)
                set(Flags.NSSL_DEBUG_LINES, false)
            }
        }
    private val testScope = kosmos.testScope
    private val placeholderViewModel = kosmos.notificationsPlaceholderViewModel
    private val appearanceViewModel = kosmos.notificationStackAppearanceViewModel
    private val sceneInteractor = kosmos.sceneInteractor

    @Test
    fun updateBounds() =
        testScope.runTest {
            val bounds by collectLastValue(appearanceViewModel.stackBounds)

            val top = 200f
            val bottom = 550f
            placeholderViewModel.onBoundsChanged(top, bottom)
            assertThat(bounds).isEqualTo(NotificationContainerBounds(top = top, bottom = bottom))
        }

    @Test
    fun updateShadeExpansion() =
        testScope.runTest {
            val expandFraction by collectLastValue(appearanceViewModel.expandFraction)
            assertThat(expandFraction).isEqualTo(0f)

            val transitionState =
                MutableStateFlow<ObservableTransitionState>(
                    ObservableTransitionState.Idle(scene = SceneKey.Lockscreen)
                )
            sceneInteractor.setTransitionState(transitionState)
            sceneInteractor.changeScene(SceneModel(SceneKey.Shade), "reason")
            val transitionProgress = MutableStateFlow(0f)
            transitionState.value =
                ObservableTransitionState.Transition(
                    fromScene = SceneKey.Lockscreen,
                    toScene = SceneKey.Shade,
                    progress = transitionProgress,
                    isInitiatedByUserInput = false,
                    isUserInputOngoing = flowOf(false),
                )
            val steps = 10
            repeat(steps) { repetition ->
                val progress = (1f / steps) * (repetition + 1)
                transitionProgress.value = progress
                runCurrent()
                assertThat(expandFraction).isWithin(0.01f).of(progress)
            }

            sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "reason")
            assertThat(expandFraction).isWithin(0.01f).of(1f)
        }
}
+73 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.statusbar.notification.stack.domain.interactor

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class NotificationStackAppearanceInteractorTest : SysuiTestCase() {

    private val kosmos = Kosmos()
    private val testScope = kosmos.testScope
    private val underTest = kosmos.notificationStackAppearanceInteractor

    @Test
    fun stackBounds() =
        testScope.runTest {
            val stackBounds by collectLastValue(underTest.stackBounds)

            val bounds1 =
                NotificationContainerBounds(
                    top = 100f,
                    bottom = 200f,
                    isAnimated = true,
                )
            underTest.setStackBounds(bounds1)
            assertThat(stackBounds).isEqualTo(bounds1)

            val bounds2 =
                NotificationContainerBounds(
                    top = 200f,
                    bottom = 300f,
                    isAnimated = false,
                )
            underTest.setStackBounds(bounds2)
            assertThat(stackBounds).isEqualTo(bounds2)
        }

    @Test(expected = IllegalStateException::class)
    fun setStackBounds_withImproperBounds_throwsException() =
        testScope.runTest {
            underTest.setStackBounds(
                NotificationContainerBounds(
                    top = 100f,
                    bottom = 99f,
                )
            )
        }
}
+30 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.common.shared.model

/** Models the bounds of the notification container. */
data class NotificationContainerBounds(
    /** The position of the top of the container in its window coordinate system, in pixels. */
    val top: Float = 0f,
    /** The position of the bottom of the container in its window coordinate system, in pixels. */
    val bottom: Float = 0f,
    /** Whether any modifications to top/bottom should be smoothly animated. */
    val isAnimated: Boolean = false,
) {
    /** The current height of the notification container. */
    val height: Float = bottom - top
}
Loading