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

Commit a270a476 authored by Evan Laird's avatar Evan Laird
Browse files

[sb] get rid of recomposition on the StatusBarRoot

StatusBarRoot was using AndroidView, and a single top-level `visible`
flow to determine whether or not to hide the entirety of
PhoneStatusBarView.

This was ultimately pretty inefficient, and was not what the previous
fragment was doing. This change does two main things:

1. Use the HomeStatusBarViewBinder to set initial visibilities to GONE,
   and relies on the bindings themselves to adjust as-needed
2. Removes the need for the `update` block in the AndroidView that
   contains PhoneStatusBarView

Test: sysui tests
Test: manually kiling sysui
Bug: 393610814
Flag: EXEMPT bugfix
Change-Id: If79b8f20cedeee4ecf19ace69d41f7966cab03f4
parent c0effb99
Loading
Loading
Loading
Loading
+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 −92
Original line number Diff line number Diff line
@@ -647,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 {
+26 −11
Original line number Diff line number Diff line
@@ -85,6 +85,20 @@ constructor(
        systemEventChipAnimateOut: ((View) -> Unit)?,
        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 =
@@ -133,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(
@@ -181,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)
@@ -245,7 +255,11 @@ constructor(
                }

                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,
@@ -257,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
@@ -358,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) {
@@ -376,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
+13 −28
Original line number Diff line number Diff line
@@ -25,16 +25,13 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.isVisible
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.theme.PlatformTheme
import com.android.keyguard.AlphaOptimizedLinearLayout
import com.android.systemui.plugins.DarkIconDispatcher
@@ -130,9 +127,6 @@ fun StatusBarRoot(
        }

        Row(Modifier.fillMaxSize()) {
            val scope = rememberCoroutineScope()
            val visible =
                statusBarViewModel.shouldHomeStatusBarBeVisible.collectAsStateWithLifecycle(false)
            AndroidView(
                factory = { context ->
                    val inflater = LayoutInflater.from(context)
@@ -247,15 +241,12 @@ fun StatusBarRoot(
                        endSideContent.addView(composeView, 0)
                    }

                    scope.launch {
                    notificationIconsBinder.bindWhileAttached(
                        notificationIconContainer,
                        context.displayId,
                    )
                    }

                    // This binder handles everything else
                    scope.launch {
                    statusBarViewBinder.bind(
                        context.displayId,
                        phoneStatusBarView,
@@ -264,15 +255,9 @@ fun StatusBarRoot(
                        eventAnimationInteractor::animateStatusBarContentForChipExit,
                        listener = null,
                    )
                    }
                    onViewCreated(phoneStatusBarView)
                    phoneStatusBarView
                },
                update = { view ->
                    // Show or hide the entire status bar. This is important so that we aren't
                    // visible when first inflated
                    view.isVisible = visible.value
                },
                }
            )
        }
    }
+1 −4
Original line number Diff line number Diff line
@@ -87,9 +87,6 @@ import kotlinx.coroutines.flow.stateIn
 * so that it's all in one place and easily testable outside of the fragment.
 */
interface HomeStatusBarViewModel {
    /** Should the entire status bar be hidden? */
    val shouldHomeStatusBarBeVisible: Flow<Boolean>

    /**
     * True if the device is currently transitioning from lockscreen to occluded and false
     * otherwise.
@@ -303,7 +300,7 @@ constructor(
            isHomeScreenStatusBarAllowedLegacy
        }

    override val shouldHomeStatusBarBeVisible =
    private val shouldHomeStatusBarBeVisible =
        combine(
                isHomeStatusBarAllowed,
                keyguardInteractor.isSecureCameraActive,