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

Commit 64739dcd authored by Evan Laird's avatar Evan Laird Committed by Android (Google) Code Review
Browse files

Merge changes If79b8f20,I91a033c1,I8927fcfd,I41f0fe27 into main

* changes:
  [sb] get rid of recomposition on the StatusBarRoot
  [sb] StatusBarVisibilityChangeListener is now optional
  [sb] OngoingActivityChipsViewModel to use bg scope
  [sb] Move flows to bgDispatcher
parents 91d57642 a270a476
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -35,7 +35,7 @@ class FakeHomeStatusBarViewBinder : HomeStatusBarViewBinder {
        viewModel: HomeStatusBarViewModel,
        systemEventChipAnimateIn: ((View) -> Unit)?,
        systemEventChipAnimateOut: ((View) -> Unit)?,
        listener: StatusBarVisibilityChangeListener,
        listener: StatusBarVisibilityChangeListener?,
    ) {
        this.listener = listener
    }
+0 −2
Original line number Diff line number Diff line
@@ -52,8 +52,6 @@ class FakeHomeStatusBarViewModel(

    override val isHomeStatusBarAllowedByScene = MutableStateFlow(false)

    override val shouldHomeStatusBarBeVisible = MutableStateFlow(false)

    override val shouldShowOperatorNameView = MutableStateFlow(false)

    override val isClockVisible =
+0 −95
Original line number Diff line number Diff line
@@ -88,16 +88,13 @@ import com.android.systemui.statusbar.pipeline.shared.domain.interactor.setHomeS
import com.android.systemui.statusbar.pipeline.shared.ui.model.VisibilityModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class HomeStatusBarViewModelImplTest : SysuiTestCase() {
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
@@ -650,98 +647,6 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
            assertThat(latest).isTrue()
        }

    @Test
    fun shouldHomeStatusBarBeVisible_keyguardNotGone_noHun_false() =
        kosmos.runTest {
            // Do not transition from keyguard. i.e., we don't call transitionKeyguardToGone()

            // Nothing disabled
            fakeDisableFlagsRepository.disableFlags.value =
                DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)

            val latest by collectLastValue(underTest.shouldHomeStatusBarBeVisible)
            assertThat(latest).isFalse()
        }

    @Test
    fun shouldHomeStatusBarBeVisible_keyguardNotGone_hun_true() =
        kosmos.runTest {
            // Keyguard gone
            transitionKeyguardToGone()

            // Nothing disabled
            fakeDisableFlagsRepository.disableFlags.value =
                DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)

            // there is an active HUN
            headsUpNotificationRepository.setNotifications(
                UnconfinedFakeHeadsUpRowRepository(
                    key = "key",
                    pinnedStatus = MutableStateFlow(PinnedStatus.PinnedByUser),
                )
            )

            val latest by collectLastValue(underTest.shouldHomeStatusBarBeVisible)
            assertThat(latest).isTrue()
        }

    @Test
    fun shouldHomeStatusBarBeVisible_keyguardGone_noHun_notInCamera_true() =
        kosmos.runTest {
            // Keyguard gone
            transitionKeyguardToGone()

            // Nothing disabled
            fakeDisableFlagsRepository.disableFlags.value =
                DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)

            val latest by collectLastValue(underTest.shouldHomeStatusBarBeVisible)
            assertThat(latest).isTrue()
        }

    @Test
    fun shouldHomeStatusBarBeVisible_keyguardGone_hun_notInCamera_true() =
        kosmos.runTest {
            // Keyguard gone
            transitionKeyguardToGone()

            // Nothing disabled
            fakeDisableFlagsRepository.disableFlags.value =
                DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)

            // there is an active HUN
            headsUpNotificationRepository.setNotifications(
                UnconfinedFakeHeadsUpRowRepository(
                    key = "key",
                    pinnedStatus = MutableStateFlow(PinnedStatus.PinnedByUser),
                )
            )

            val latest by collectLastValue(underTest.shouldHomeStatusBarBeVisible)
            assertThat(latest).isTrue()
        }

    @Test
    fun shouldHomeStatusBarBeVisible_keyguardGone_noHun_inCamera_false() =
        kosmos.runTest {
            // Keyguard gone
            transitionKeyguardToGone()

            // Nothing disabled
            fakeDisableFlagsRepository.disableFlags.value =
                DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)

            fakeKeyguardTransitionRepository.sendTransitionSteps(
                from = KeyguardState.LOCKSCREEN,
                to = KeyguardState.OCCLUDED,
                testScope = testScope,
            )
            kosmos.keyguardInteractor.onCameraLaunchDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)

            val latest by collectLastValue(underTest.shouldHomeStatusBarBeVisible)
            assertThat(latest).isFalse()
        }

    @Test
    fun isClockVisible_allowedByDisableFlags_visible() =
        kosmos.runTest {
+2 −2
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@ import android.content.res.Configuration
import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.chips.StatusBarChipLogTags.pad
@@ -56,7 +56,7 @@ import kotlinx.coroutines.flow.stateIn
class OngoingActivityChipsViewModel
@Inject
constructor(
    @Application scope: CoroutineScope,
    @Background scope: CoroutineScope,
    screenRecordChipViewModel: ScreenRecordChipViewModel,
    shareToAppChipViewModel: ShareToAppChipViewModel,
    castToOtherDeviceChipViewModel: CastToOtherDeviceChipViewModel,
+46 −25
Original line number Diff line number Diff line
@@ -67,7 +67,7 @@ interface HomeStatusBarViewBinder {
        viewModel: HomeStatusBarViewModel,
        systemEventChipAnimateIn: ((View) -> Unit)?,
        systemEventChipAnimateOut: ((View) -> Unit)?,
        listener: StatusBarVisibilityChangeListener,
        listener: StatusBarVisibilityChangeListener?,
    )
}

@@ -83,8 +83,22 @@ constructor(
        viewModel: HomeStatusBarViewModel,
        systemEventChipAnimateIn: ((View) -> Unit)?,
        systemEventChipAnimateOut: ((View) -> Unit)?,
        listener: StatusBarVisibilityChangeListener,
        listener: StatusBarVisibilityChangeListener?,
    ) {
        // Set some top-level views to gone before we get started
        val primaryChipView: View = view.requireViewById(R.id.ongoing_activity_chip_primary)
        val systemInfoView = view.requireViewById<View>(R.id.status_bar_end_side_content)
        val clockView = view.requireViewById<View>(R.id.clock)
        val notificationIconsArea = view.requireViewById<View>(R.id.notificationIcons)

        // CollapsedStatusBarFragment doesn't need this
        if (StatusBarRootModernization.isEnabled) {
            primaryChipView.isVisible = false
            systemInfoView.isVisible = false
            clockView.isVisible = false
            notificationIconsArea.isVisible = false
        }

        view.repeatWhenAttached {
            repeatOnLifecycle(Lifecycle.State.CREATED) {
                val iconViewStore =
@@ -95,17 +109,21 @@ constructor(
                    } else {
                        null
                    }
                listener?.let { listener ->
                    launch {
                        viewModel.isTransitioningFromLockscreenToOccluded.collect {
                            listener.onStatusBarVisibilityMaybeChanged()
                        }
                    }
                }

                listener?.let { listener ->
                    launch {
                        viewModel.transitionFromLockscreenToDreamStartedEvent.collect {
                            listener.onTransitionFromLockscreenToDreamStarted()
                        }
                    }
                }

                if (NotificationsLiveDataStoreRefactor.isEnabled) {
                    val lightsOutView: View = view.requireViewById(R.id.notification_lights_out)
@@ -129,9 +147,7 @@ constructor(

                if (!StatusBarNotifChips.isEnabled && !StatusBarChipsModernization.isEnabled) {
                    val primaryChipViewBinding =
                        OngoingActivityChipBinder.createBinding(
                            view.requireViewById(R.id.ongoing_activity_chip_primary)
                        )
                        OngoingActivityChipBinder.createBinding(primaryChipView)
                    launch {
                        viewModel.primaryOngoingActivityChip.collect { primaryChipModel ->
                            OngoingActivityChipBinder.bind(
@@ -155,14 +171,14 @@ constructor(
                            } else {
                                when (primaryChipModel) {
                                    is OngoingActivityChipModel.Shown ->
                                        listener.onOngoingActivityStatusChanged(
                                        listener?.onOngoingActivityStatusChanged(
                                            hasPrimaryOngoingActivity = true,
                                            hasSecondaryOngoingActivity = false,
                                            shouldAnimate = true,
                                        )

                                    is OngoingActivityChipModel.Hidden ->
                                        listener.onOngoingActivityStatusChanged(
                                        listener?.onOngoingActivityStatusChanged(
                                            hasPrimaryOngoingActivity = false,
                                            hasSecondaryOngoingActivity = false,
                                            shouldAnimate = primaryChipModel.shouldAnimate,
@@ -177,9 +193,7 @@ constructor(
                    // Create view bindings here so we don't keep re-fetching child views each time
                    // the chip model changes.
                    val primaryChipViewBinding =
                        OngoingActivityChipBinder.createBinding(
                            view.requireViewById(R.id.ongoing_activity_chip_primary)
                        )
                        OngoingActivityChipBinder.createBinding(primaryChipView)
                    val secondaryChipViewBinding =
                        OngoingActivityChipBinder.createBinding(
                            view.requireViewById(R.id.ongoing_activity_chip_secondary)
@@ -205,7 +219,7 @@ constructor(
                                    chips.secondary.toVisibilityModel()
                                )
                            } else {
                                listener.onOngoingActivityStatusChanged(
                                listener?.onOngoingActivityStatusChanged(
                                    hasPrimaryOngoingActivity =
                                        chips.primary is OngoingActivityChipModel.Shown,
                                    hasSecondaryOngoingActivity =
@@ -231,15 +245,21 @@ constructor(
                }

                if (SceneContainerFlag.isEnabled) {
                    listener?.let { listener ->
                        launch {
                            viewModel.isHomeStatusBarAllowedByScene.collect {
                                listener.onIsHomeStatusBarAllowedBySceneChanged(it)
                            }
                        }
                    }
                }

                if (StatusBarRootModernization.isEnabled) {
                    // TODO(b/393445203): figure out the best story for this stub view. This crashes
                    // if we move it up to the top of [bind]
                    val operatorNameView = view.requireViewById<View>(R.id.operator_name_frame)
                    operatorNameView.isVisible = false

                    StatusBarOperatorNameViewBinder.bind(
                        operatorNameView,
                        viewModel.operatorNameViewModel,
@@ -251,19 +271,14 @@ constructor(
                        }
                    }

                    val clockView = view.requireViewById<View>(R.id.clock)
                    launch { viewModel.isClockVisible.collect { clockView.adjustVisibility(it) } }

                    val notificationIconsArea = view.requireViewById<View>(R.id.notificationIcons)
                    launch {
                        viewModel.isNotificationIconContainerVisible.collect {
                            notificationIconsArea.adjustVisibility(it)
                        }
                    }

                    val systemInfoView =
                        view.requireViewById<View>(R.id.status_bar_end_side_content)
                    // TODO(b/364360986): Also handle operator name view.
                    launch {
                        viewModel.systemInfoCombinedVis.collect { (baseVis, animState) ->
                            // Broadly speaking, the baseVis controls the view.visibility, and
@@ -352,6 +367,9 @@ constructor(

    // See CollapsedStatusBarFragment#hide.
    private fun View.hide(state: Int = View.INVISIBLE, shouldAnimateChange: Boolean) {
        if (visibility == View.INVISIBLE || visibility == View.GONE) {
            return
        }
        val v = this
        v.animate().cancel()
        if (!shouldAnimateChange) {
@@ -370,6 +388,9 @@ constructor(

    // See CollapsedStatusBarFragment#show.
    private fun View.show(shouldAnimateChange: Boolean) {
        if (visibility == View.VISIBLE) {
            return
        }
        val v = this
        v.animate().cancel()
        v.visibility = View.VISIBLE
Loading