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

Commit 94d03ec5 authored by Caitlin Shkuratov's avatar Caitlin Shkuratov Committed by Android (Google) Code Review
Browse files

Merge changes Ib150382b,If0ff461b,Ie58ebbc9 into main

* changes:
  [SB][Chips] Fix chip visibility problems with RootMod=on, ChipsMod=off.
  [SB][Chips] Hide chips on lockscreen when ChipsModernization is enabled
  [SB] Rework chip visibility & notification icon visibility with HUNs.
parents bdb2c65f aa680d7d
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationSt
import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
import com.android.systemui.statusbar.phone.domain.interactor.IsAreaDark
import com.android.systemui.statusbar.pipeline.battery.ui.viewmodel.BatteryViewModel
import com.android.systemui.statusbar.pipeline.shared.ui.model.ChipsVisibilityModel
import com.android.systemui.statusbar.pipeline.shared.ui.model.SystemInfoCombinedVisibilityModel
import com.android.systemui.statusbar.pipeline.shared.ui.model.VisibilityModel
import kotlinx.coroutines.flow.Flow
@@ -52,7 +53,10 @@ class FakeHomeStatusBarViewModel(
    override val primaryOngoingActivityChip: MutableStateFlow<OngoingActivityChipModel> =
        MutableStateFlow(OngoingActivityChipModel.Inactive())

    override val ongoingActivityChips = MutableStateFlow(MultipleOngoingActivityChipsModel())
    override val ongoingActivityChips =
        MutableStateFlow(
            ChipsVisibilityModel(MultipleOngoingActivityChipsModel(), areChipsAllowed = false)
        )

    override val ongoingActivityChipsLegacy =
        MutableStateFlow(MultipleOngoingActivityChipsModelLegacy())
+276 −5
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ import com.android.systemui.statusbar.chips.mediaprojection.domain.model.MediaPr
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.shareToAppChipViewModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsCallChip
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsScreenRecordChip
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsShareToAppChip
import com.android.systemui.statusbar.core.StatusBarRootModernization
@@ -88,6 +89,7 @@ import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher
import com.android.systemui.statusbar.phone.data.repository.fakeDarkIconRepository
import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.addOngoingCallState
import com.android.systemui.statusbar.pipeline.shared.domain.interactor.setHomeStatusBarIconBlockList
import com.android.systemui.statusbar.pipeline.shared.domain.interactor.setHomeStatusBarInteractorShowOperatorName
import com.android.systemui.statusbar.pipeline.shared.ui.model.VisibilityModel
@@ -716,7 +718,8 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
        }

    @Test
    fun canShowOngoingActivityChips_statusBarNotHidden_noSecureCamera_hun_false() =
    @DisableFlags(StatusBarNoHunBehavior.FLAG_NAME)
    fun canShowOngoingActivityChips_statusBarNotHidden_noSecureCamera_hunBySystem_noHunFlagOff_false() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.canShowOngoingActivityChips)

@@ -725,13 +728,201 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
            headsUpNotificationRepository.setNotifications(
                UnconfinedFakeHeadsUpRowRepository(
                    key = "key",
                    pinnedStatus = MutableStateFlow(PinnedStatus.PinnedByUser),
                    pinnedStatus = MutableStateFlow(PinnedStatus.PinnedBySystem),
                )
            )

            assertThat(latest).isFalse()
        }

    @Test
    @DisableFlags(StatusBarNoHunBehavior.FLAG_NAME)
    fun canShowOngoingActivityChips_statusBarNotHidden_noSecureCamera_hunByUser_noHunFlagOff_true() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.canShowOngoingActivityChips)

            transitionKeyguardToGone()

            headsUpNotificationRepository.setNotifications(
                UnconfinedFakeHeadsUpRowRepository(
                    key = "key",
                    pinnedStatus = MutableStateFlow(PinnedStatus.PinnedByUser),
                )
            )

            assertThat(latest).isTrue()
        }

    @Test
    @EnableFlags(StatusBarNoHunBehavior.FLAG_NAME)
    fun canShowOngoingActivityChips_statusBarNotHidden_noSecureCamera_hunBySystem_noHunFlagOn_true() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.canShowOngoingActivityChips)

            transitionKeyguardToGone()

            headsUpNotificationRepository.setNotifications(
                UnconfinedFakeHeadsUpRowRepository(
                    key = "key",
                    pinnedStatus = MutableStateFlow(PinnedStatus.PinnedBySystem),
                )
            )

            assertThat(latest).isTrue()
        }

    @Test
    @EnableFlags(StatusBarNoHunBehavior.FLAG_NAME)
    fun canShowOngoingActivityChips_statusBarNotHidden_noSecureCamera_hunByUser_noHunFlagOn_true() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.canShowOngoingActivityChips)

            transitionKeyguardToGone()

            headsUpNotificationRepository.setNotifications(
                UnconfinedFakeHeadsUpRowRepository(
                    key = "key",
                    pinnedStatus = MutableStateFlow(PinnedStatus.PinnedByUser),
                )
            )

            assertThat(latest).isTrue()
        }

    @Test
    @EnableChipsModernization
    fun ongoingActivityChips_statusBarHidden_noSecureCamera_noHun_notAllowed() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.ongoingActivityChips)

            // home status bar not allowed
            kosmos.sceneContainerRepository.snapToScene(Scenes.Lockscreen)
            kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(false, taskInfo = null)

            assertThat(latest!!.areChipsAllowed).isFalse()
        }

    @Test
    @EnableChipsModernization
    fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_noHun_isAllowed() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.ongoingActivityChips)

            transitionKeyguardToGone()

            assertThat(latest!!.areChipsAllowed).isTrue()
        }

    @Test
    @EnableChipsModernization
    fun ongoingActivityChips_statusBarNotHidden_secureCamera_noHun_notAllowed() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.ongoingActivityChips)

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

            assertThat(latest!!.areChipsAllowed).isFalse()
        }

    @Test
    @DisableFlags(StatusBarNoHunBehavior.FLAG_NAME)
    @EnableChipsModernization
    fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunBySystem_noHunFlagOff_notAllowed() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.ongoingActivityChips)

            transitionKeyguardToGone()

            headsUpNotificationRepository.setNotifications(
                UnconfinedFakeHeadsUpRowRepository(
                    key = "key",
                    pinnedStatus = MutableStateFlow(PinnedStatus.PinnedBySystem),
                )
            )

            assertThat(latest!!.areChipsAllowed).isFalse()
        }

    @Test
    @DisableFlags(StatusBarNoHunBehavior.FLAG_NAME)
    @EnableChipsModernization
    fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunByUser_noHunFlagOff_isAllowed() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.ongoingActivityChips)

            transitionKeyguardToGone()

            headsUpNotificationRepository.setNotifications(
                UnconfinedFakeHeadsUpRowRepository(
                    key = "key",
                    pinnedStatus = MutableStateFlow(PinnedStatus.PinnedByUser),
                )
            )

            assertThat(latest!!.areChipsAllowed).isTrue()
        }

    @Test
    @EnableFlags(StatusBarNoHunBehavior.FLAG_NAME)
    @EnableChipsModernization
    fun ongoingActivityChips_tatusBarNotHidden_noSecureCamera_hunBySystem_noHunFlagOn_isAllowed() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.ongoingActivityChips)

            transitionKeyguardToGone()

            headsUpNotificationRepository.setNotifications(
                UnconfinedFakeHeadsUpRowRepository(
                    key = "key",
                    pinnedStatus = MutableStateFlow(PinnedStatus.PinnedBySystem),
                )
            )

            assertThat(latest!!.areChipsAllowed).isTrue()
        }

    @Test
    @EnableFlags(StatusBarNoHunBehavior.FLAG_NAME)
    @EnableChipsModernization
    fun ongoingActivityChips_statusBarNotHidden_noSecureCamera_hunByUser_noHunFlagOn_isAllowed() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.ongoingActivityChips)

            transitionKeyguardToGone()

            headsUpNotificationRepository.setNotifications(
                UnconfinedFakeHeadsUpRowRepository(
                    key = "key",
                    pinnedStatus = MutableStateFlow(PinnedStatus.PinnedByUser),
                )
            )

            assertThat(latest!!.areChipsAllowed).isTrue()
        }

    @Test
    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
    @EnableChipsModernization
    fun ongoingActivityChips_followsChipsViewModel() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.ongoingActivityChips)
            transitionKeyguardToGone()

            screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording

            assertIsScreenRecordChip(latest!!.chips.active[0])

            addOngoingCallState(key = "call")

            assertIsScreenRecordChip(latest!!.chips.active[0])
            assertIsCallChip(latest!!.chips.active[1], "call")
        }

    @Test
    fun isClockVisible_allowedByDisableFlags_visible() =
        kosmos.runTest {
@@ -892,7 +1083,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {

    @Test
    @EnableChipsModernization
    fun isNotificationIconContainerVisible_anyChipShowing_ChipsModernizationOn() =
    fun isNotificationIconContainerVisible_anyChipShowing_chipsModernizationOn() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.isNotificationIconContainerVisible)
            transitionKeyguardToGone()
@@ -909,7 +1100,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
    @Test
    @DisableFlags(StatusBarRootModernization.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME)
    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
    fun isNotificationIconContainerVisible_anyChipShowing_PromotedNotifsOn() =
    fun isNotificationIconContainerVisible_anyChipShowing_promotedNotifsOn() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.isNotificationIconContainerVisible)
            transitionKeyguardToGone()
@@ -929,7 +1120,7 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
        StatusBarRootModernization.FLAG_NAME,
        StatusBarChipsModernization.FLAG_NAME,
    )
    fun isNotificationIconContainerVisible_anyChipShowing_ChipsModernizationAndPromotedNotifsOff() =
    fun isNotificationIconContainerVisible_anyChipShowing_chipsModernizationAndPromotedNotifsOff() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.isNotificationIconContainerVisible)
            transitionKeyguardToGone()
@@ -943,6 +1134,86 @@ class HomeStatusBarViewModelImplTest : SysuiTestCase() {
            assertThat(latest!!.visibility).isEqualTo(View.VISIBLE)
        }

    @DisableFlags(StatusBarNoHunBehavior.FLAG_NAME)
    fun isNotificationIconContainerVisible_hasChipButAlsoHun_hunBySystem_noHunFlagOff_visible() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.isNotificationIconContainerVisible)
            transitionKeyguardToGone()

            // Chip
            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording

            // HUN, PinnedBySystem
            headsUpNotificationRepository.setNotifications(
                UnconfinedFakeHeadsUpRowRepository(
                    key = "key",
                    pinnedStatus = MutableStateFlow(PinnedStatus.PinnedBySystem),
                )
            )

            assertThat(latest!!.visibility).isEqualTo(View.VISIBLE)
        }

    @DisableFlags(StatusBarNoHunBehavior.FLAG_NAME)
    fun isNotificationIconContainerVisible_hasChipButAlsoHun_hunByUser_noHunFlagOff_gone() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.isNotificationIconContainerVisible)
            transitionKeyguardToGone()

            // Chip
            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording

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

            assertThat(latest!!.visibility).isEqualTo(View.GONE)
        }

    @EnableFlags(StatusBarNoHunBehavior.FLAG_NAME)
    fun isNotificationIconContainerVisible_hasChipButAlsoHun_hunBySystem_noHunFlagOn_gone() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.isNotificationIconContainerVisible)
            transitionKeyguardToGone()

            // Chip
            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording

            // HUN, PinnedBySystem
            headsUpNotificationRepository.setNotifications(
                UnconfinedFakeHeadsUpRowRepository(
                    key = "key",
                    pinnedStatus = MutableStateFlow(PinnedStatus.PinnedBySystem),
                )
            )

            assertThat(latest!!.visibility).isEqualTo(View.GONE)
        }

    @EnableFlags(StatusBarNoHunBehavior.FLAG_NAME)
    fun isNotificationIconContainerVisible_hasChipButAlsoHun_hunByUser_noHunFlagOn_gone() =
        kosmos.runTest {
            val latest by collectLastValue(underTest.isNotificationIconContainerVisible)
            transitionKeyguardToGone()

            // Chip
            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording

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

            assertThat(latest!!.visibility).isEqualTo(View.GONE)
        }

    @Test
    fun isSystemInfoVisible_allowedByDisableFlags_visible() =
        kosmos.runTest {
+99 −91
Original line number Diff line number Diff line
@@ -47,7 +47,8 @@ import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernizat
import com.android.systemui.statusbar.pipeline.shared.ui.model.VisibilityModel
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch

/**
@@ -153,7 +154,13 @@ constructor(
                        OngoingActivityChipBinder.createBinding(primaryChipView)

                    launch {
                        viewModel.primaryOngoingActivityChip.collect { primaryChipModel ->
                        combine(
                                viewModel.primaryOngoingActivityChip,
                                viewModel.canShowOngoingActivityChips,
                                ::Pair,
                            )
                            .distinctUntilChanged()
                            .collect { (primaryChipModel, areChipsAllowed) ->
                                OngoingActivityChipBinder.bind(
                                    primaryChipModel,
                                    primaryChipViewBinding,
@@ -161,13 +168,11 @@ constructor(
                                )

                                if (StatusBarRootModernization.isEnabled) {
                                launch {
                                    bindLegacyPrimaryOngoingActivityChipWithVisibility(
                                        viewModel,
                                        areChipsAllowed,
                                        primaryChipModel,
                                        primaryChipViewBinding,
                                    )
                                }
                                } else {
                                    when (primaryChipModel) {
                                        is OngoingActivityChipModel.Active ->
@@ -199,7 +204,13 @@ constructor(
                            view.requireViewById(R.id.ongoing_activity_chip_secondary)
                        )
                    launch {
                        viewModel.ongoingActivityChipsLegacy.collectLatest { chips ->
                        combine(
                                viewModel.ongoingActivityChipsLegacy,
                                viewModel.canShowOngoingActivityChips,
                                ::Pair,
                            )
                            .distinctUntilChanged()
                            .collect { (chips, areChipsAllowed) ->
                                OngoingActivityChipBinder.bind(
                                    chips.primary,
                                    primaryChipViewBinding,
@@ -210,16 +221,13 @@ constructor(
                                    secondaryChipViewBinding,
                                    iconViewStore,
                                )

                                if (StatusBarRootModernization.isEnabled) {
                                launch {
                                    bindOngoingActivityChipsWithVisibility(
                                        viewModel,
                                        areChipsAllowed,
                                        chips,
                                        primaryChipViewBinding,
                                        secondaryChipViewBinding,
                                    )
                                }
                                } else {
                                    listener?.onOngoingActivityStatusChanged(
                                        hasPrimaryOngoingActivity =
@@ -230,7 +238,9 @@ constructor(
                                        shouldAnimate = true,
                                    )
                                }

                            }
                    }
                    launch {
                        viewModel.contentArea.collect { _ ->
                            OngoingActivityChipBinder.resetPrimaryChipWidthRestrictions(
                                primaryChipViewBinding,
@@ -244,7 +254,6 @@ constructor(
                        }
                    }
                }
                }

                if (SceneContainerFlag.isEnabled) {
                    listener?.let { listener ->
@@ -314,13 +323,12 @@ constructor(
    }

    /** Bind the (legacy) single primary ongoing activity chip with the status bar visibility */
    private suspend fun bindLegacyPrimaryOngoingActivityChipWithVisibility(
        viewModel: HomeStatusBarViewModel,
    private fun bindLegacyPrimaryOngoingActivityChipWithVisibility(
        areChipsAllowed: Boolean,
        primaryChipModel: OngoingActivityChipModel,
        primaryChipViewBinding: OngoingActivityChipViewBinding,
    ) {
        viewModel.canShowOngoingActivityChips.collectLatest { visible ->
            if (!visible) {
        if (!areChipsAllowed) {
            primaryChipViewBinding.rootView.hide(shouldAnimateChange = false)
        } else {
            when (primaryChipModel) {
@@ -337,25 +345,20 @@ constructor(
            }
        }
    }
    }

    /** Bind the primary/secondary chips along with the home status bar's visibility */
    private suspend fun bindOngoingActivityChipsWithVisibility(
        viewModel: HomeStatusBarViewModel,
    private fun bindOngoingActivityChipsWithVisibility(
        areChipsAllowed: Boolean,
        chips: MultipleOngoingActivityChipsModelLegacy,
        primaryChipViewBinding: OngoingActivityChipViewBinding,
        secondaryChipViewBinding: OngoingActivityChipViewBinding,
    ) {
        viewModel.canShowOngoingActivityChips.collectLatest { canShow ->
            if (!canShow) {
        if (!areChipsAllowed) {
            primaryChipViewBinding.rootView.hide(shouldAnimateChange = false)
            secondaryChipViewBinding.rootView.hide(shouldAnimateChange = false)
        } else {
            primaryChipViewBinding.rootView.adjustVisibility(chips.primary.toVisibilityModel())
                secondaryChipViewBinding.rootView.adjustVisibility(
                    chips.secondary.toVisibilityModel()
                )
            }
            secondaryChipViewBinding.rootView.adjustVisibility(chips.secondary.toVisibilityModel())
        }
    }

@@ -428,10 +431,15 @@ constructor(
    // See CollapsedStatusBarFragment#hide.
    private fun View.hide(state: Int = View.INVISIBLE, shouldAnimateChange: Boolean) {
        animate().cancel()
        if (visibility == View.INVISIBLE || visibility == View.GONE) {

        if (
            (visibility == View.INVISIBLE && state == View.INVISIBLE) ||
                (visibility == View.GONE && state == View.GONE)
        ) {
            return
        }
        if (!shouldAnimateChange) {
        val isAlreadyHidden = visibility == View.INVISIBLE || visibility == View.GONE
        if (!shouldAnimateChange || isAlreadyHidden) {
            alpha = 0f
            visibility = state
            return
+7 −5
Original line number Diff line number Diff line
@@ -196,16 +196,18 @@ fun StatusBarRoot(

                                setContent {
                                    PlatformTheme {
                                        val chips by
                                        val chipsVisibilityModel by
                                            statusBarViewModel.ongoingActivityChips
                                                .collectAsStateWithLifecycle()
                                        if (chipsVisibilityModel.areChipsAllowed) {
                                            OngoingActivityChips(
                                            chips = chips,
                                                chips = chipsVisibilityModel.chips,
                                                iconViewStore = iconViewStore,
                                            )
                                        }
                                    }
                                }
                            }

                        // Add the composable container for ongoingActivityChips before the
                        // notification_icon_area to maintain the same ordering for ongoing activity
+27 −0
Original line number Diff line number Diff line
/*
 * 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.statusbar.pipeline.shared.ui.model

import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel

data class ChipsVisibilityModel(
    val chips: MultipleOngoingActivityChipsModel,
    /**
     * True if the chips are allowed to be shown and false otherwise (e.g. if we're on lockscreen).
     */
    val areChipsAllowed: Boolean,
)
Loading