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

Commit 7a55c865 authored by Ahmed Mehfooz's avatar Ahmed Mehfooz Committed by Android (Google) Code Review
Browse files

Merge "Add support for keeping status bar visible in immersive mode" into main

parents eb114442 e649b388
Loading
Loading
Loading
Loading
+127 −8
Original line number Original line Diff line number Diff line
@@ -30,11 +30,13 @@ import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.runTest
@@ -72,7 +74,7 @@ class OngoingCallInteractorTest : SysuiTestCase() {
                                whenTime = 1000L,
                                whenTime = 1000L,
                                callType = CallType.Ongoing,
                                callType = CallType.Ongoing,
                                statusBarChipIcon = testIconView,
                                statusBarChipIcon = testIconView,
                                contentIntent = testIntent
                                contentIntent = testIntent,
                            )
                            )
                        )
                        )
                    }
                    }
@@ -95,7 +97,9 @@ class OngoingCallInteractorTest : SysuiTestCase() {
                    .apply {
                    .apply {
                        addIndividualNotif(
                        addIndividualNotif(
                            activeNotificationModel(
                            activeNotificationModel(
                                key = "notif1", whenTime = 1000L, callType = CallType.Ongoing
                                key = "notif1",
                                whenTime = 1000L,
                                callType = CallType.Ongoing,
                            )
                            )
                        )
                        )
                    }
                    }
@@ -114,7 +118,9 @@ class OngoingCallInteractorTest : SysuiTestCase() {
                    .apply {
                    .apply {
                        addIndividualNotif(
                        addIndividualNotif(
                            activeNotificationModel(
                            activeNotificationModel(
                                key = "notif1", whenTime = 1000L, callType = CallType.Ongoing
                                key = "notif1",
                                whenTime = 1000L,
                                callType = CallType.Ongoing,
                            )
                            )
                        )
                        )
                    }
                    }
@@ -138,7 +144,7 @@ class OngoingCallInteractorTest : SysuiTestCase() {
                                key = "notif1",
                                key = "notif1",
                                whenTime = 1000L,
                                whenTime = 1000L,
                                callType = CallType.Ongoing,
                                callType = CallType.Ongoing,
                                uid = UID
                                uid = UID,
                            )
                            )
                        )
                        )
                    }
                    }
@@ -161,7 +167,7 @@ class OngoingCallInteractorTest : SysuiTestCase() {
                                key = "notif1",
                                key = "notif1",
                                whenTime = 1000L,
                                whenTime = 1000L,
                                callType = CallType.Ongoing,
                                callType = CallType.Ongoing,
                                uid = UID
                                uid = UID,
                            )
                            )
                        )
                        )
                    }
                    }
@@ -185,13 +191,12 @@ class OngoingCallInteractorTest : SysuiTestCase() {
                                key = "notif1",
                                key = "notif1",
                                whenTime = 1000L,
                                whenTime = 1000L,
                                callType = CallType.Ongoing,
                                callType = CallType.Ongoing,
                                uid = UID
                                uid = UID,
                            )
                            )
                        )
                        )
                    }
                    }
                    .build()
                    .build()
            assertThat(latest)
            assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
                .isInstanceOf(OngoingCallModel.InCall::class.java)


            // App becomes visible
            // App becomes visible
            kosmos.activityManagerRepository.fake.setIsAppVisible(UID, true)
            kosmos.activityManagerRepository.fake.setIsAppVisible(UID, true)
@@ -202,6 +207,120 @@ class OngoingCallInteractorTest : SysuiTestCase() {
            assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
            assertThat(latest).isInstanceOf(OngoingCallModel.InCall::class.java)
        }
        }


    @Test
    fun ongoingCallNotification_setsRequiresStatusBarVisibleTrue() =
        kosmos.runTest {
            val ongoingCallState by collectLastValue(underTest.ongoingCallState)

            val requiresStatusBarVisibleInRepository by
                collectLastValue(
                    kosmos.fakeStatusBarModeRepository.defaultDisplay
                        .ongoingProcessRequiresStatusBarVisible
                )
            val requiresStatusBarVisibleInWindowController by
                collectLastValue(
                    kosmos.fakeStatusBarWindowControllerStore.defaultDisplay
                        .ongoingProcessRequiresStatusBarVisible
                )
            repository.activeNotifications.value =
                ActiveNotificationsStore.Builder()
                    .apply {
                        addIndividualNotif(
                            activeNotificationModel(
                                key = "notif1",
                                whenTime = 1000L,
                                callType = CallType.Ongoing,
                                uid = UID,
                            )
                        )
                    }
                    .build()

            assertThat(ongoingCallState).isInstanceOf(OngoingCallModel.InCall::class.java)
            assertThat(requiresStatusBarVisibleInRepository).isTrue()
            assertThat(requiresStatusBarVisibleInWindowController).isTrue()
        }

    @Test
    fun notificationRemoved_setsRequiresStatusBarVisibleFalse() =
        kosmos.runTest {
            val ongoingCallState by collectLastValue(underTest.ongoingCallState)

            val requiresStatusBarVisibleInRepository by
                collectLastValue(
                    kosmos.fakeStatusBarModeRepository.defaultDisplay
                        .ongoingProcessRequiresStatusBarVisible
                )
            val requiresStatusBarVisibleInWindowController by
                collectLastValue(
                    kosmos.fakeStatusBarWindowControllerStore.defaultDisplay
                        .ongoingProcessRequiresStatusBarVisible
                )

            repository.activeNotifications.value =
                ActiveNotificationsStore.Builder()
                    .apply {
                        addIndividualNotif(
                            activeNotificationModel(
                                key = "notif1",
                                whenTime = 1000L,
                                callType = CallType.Ongoing,
                                uid = UID,
                            )
                        )
                    }
                    .build()

            repository.activeNotifications.value = ActiveNotificationsStore()

            assertThat(ongoingCallState).isInstanceOf(OngoingCallModel.NoCall::class.java)
            assertThat(requiresStatusBarVisibleInRepository).isFalse()
            assertThat(requiresStatusBarVisibleInWindowController).isFalse()
        }

    @Test
    fun ongoingCallNotification_appBecomesVisible_setsRequiresStatusBarVisibleFalse() =
        kosmos.runTest {
            val ongoingCallState by collectLastValue(underTest.ongoingCallState)

            val requiresStatusBarVisibleInRepository by
                collectLastValue(
                    kosmos.fakeStatusBarModeRepository.defaultDisplay
                        .ongoingProcessRequiresStatusBarVisible
                )
            val requiresStatusBarVisibleInWindowController by
                collectLastValue(
                    kosmos.fakeStatusBarWindowControllerStore.defaultDisplay
                        .ongoingProcessRequiresStatusBarVisible
                )

            kosmos.activityManagerRepository.fake.startingIsAppVisibleValue = false
            repository.activeNotifications.value =
                ActiveNotificationsStore.Builder()
                    .apply {
                        addIndividualNotif(
                            activeNotificationModel(
                                key = "notif1",
                                whenTime = 1000L,
                                callType = CallType.Ongoing,
                                uid = UID,
                            )
                        )
                    }
                    .build()

            assertThat(ongoingCallState).isInstanceOf(OngoingCallModel.InCall::class.java)
            assertThat(requiresStatusBarVisibleInRepository).isTrue()
            assertThat(requiresStatusBarVisibleInWindowController).isTrue()

            kosmos.activityManagerRepository.fake.setIsAppVisible(UID, true)

            assertThat(ongoingCallState)
                .isInstanceOf(OngoingCallModel.InCallWithVisibleApp::class.java)
            assertThat(requiresStatusBarVisibleInRepository).isFalse()
            assertThat(requiresStatusBarVisibleInWindowController).isFalse()
        }

    companion object {
    companion object {
        private const val UID = 885
        private const val UID = 885
    }
    }
+34 −2
Original line number Original line Diff line number Diff line
@@ -37,6 +37,7 @@ import com.android.systemui.statusbar.phone.BoundsPair
import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator
import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator
import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent
import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent
import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import dagger.assisted.Assisted
import dagger.assisted.Assisted
@@ -89,6 +90,9 @@ interface StatusBarModePerDisplayRepository : OnStatusBarViewInitializedListener
    /** The current mode of the status bar. */
    /** The current mode of the status bar. */
    val statusBarMode: StateFlow<StatusBarMode>
    val statusBarMode: StateFlow<StatusBarMode>


    /** Whether the status bar is forced to be visible because of an ongoing call */
    val ongoingProcessRequiresStatusBarVisible: StateFlow<Boolean>

    /**
    /**
     * Requests for the status bar to be shown transiently.
     * Requests for the status bar to be shown transiently.
     *
     *
@@ -110,6 +114,12 @@ interface StatusBarModePerDisplayRepository : OnStatusBarViewInitializedListener
     * if needed.
     * if needed.
     */
     */
    fun stop()
    fun stop()

    /**
     * Called when an ongoing process needs to prevent the status bar from being hidden in any
     * state.
     */
    fun setOngoingProcessRequiresStatusBarVisible(requiredVisible: Boolean)
}
}


class StatusBarModePerDisplayRepositoryImpl
class StatusBarModePerDisplayRepositoryImpl
@@ -195,6 +205,16 @@ constructor(
        statusBarBoundsProvider.addChangeListener(listener)
        statusBarBoundsProvider.addChangeListener(listener)
    }
    }


    private val _ongoingProcessRequiresStatusBarVisible = MutableStateFlow(false)
    override val ongoingProcessRequiresStatusBarVisible =
        _ongoingProcessRequiresStatusBarVisible.asStateFlow()

    override fun setOngoingProcessRequiresStatusBarVisible(
        requiredVisible: Boolean
    ) {
        _ongoingProcessRequiresStatusBarVisible.value = requiredVisible
    }

    override val isInFullscreenMode: StateFlow<Boolean> =
    override val isInFullscreenMode: StateFlow<Boolean> =
        _originalStatusBarAttributes
        _originalStatusBarAttributes
            .map { params ->
            .map { params ->
@@ -235,16 +255,28 @@ constructor(
                isTransientShown,
                isTransientShown,
                isInFullscreenMode,
                isInFullscreenMode,
                ongoingCallRepository.ongoingCallState,
                ongoingCallRepository.ongoingCallState,
            ) { modifiedAttributes, isTransientShown, isInFullscreenMode, ongoingCallState ->
                _ongoingProcessRequiresStatusBarVisible,
            ) {
                modifiedAttributes,
                isTransientShown,
                isInFullscreenMode,
                ongoingCallStateLegacy,
                ongoingProcessRequiresStatusBarVisible ->
                if (modifiedAttributes == null) {
                if (modifiedAttributes == null) {
                    null
                    null
                } else {
                } else {
                    val hasOngoingCall =
                        if (StatusBarChipsModernization.isEnabled) {
                            ongoingProcessRequiresStatusBarVisible
                        } else {
                            ongoingCallStateLegacy is OngoingCallModel.InCall
                        }
                    val statusBarMode =
                    val statusBarMode =
                        toBarMode(
                        toBarMode(
                            modifiedAttributes.appearance,
                            modifiedAttributes.appearance,
                            isTransientShown,
                            isTransientShown,
                            isInFullscreenMode,
                            isInFullscreenMode,
                            hasOngoingCall = ongoingCallState is OngoingCallModel.InCall,
                            hasOngoingCall,
                        )
                        )
                    StatusBarAppearance(
                    StatusBarAppearance(
                        statusBarMode,
                        statusBarMode,
+76 −37
Original line number Original line Diff line number Diff line
@@ -21,17 +21,22 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.Logger
import com.android.systemui.log.core.Logger
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import javax.inject.Inject
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.stateIn


/**
/**
@@ -47,7 +52,9 @@ import kotlinx.coroutines.flow.stateIn
@SysUISingleton
@SysUISingleton
class OngoingCallInteractor @Inject constructor(
class OngoingCallInteractor @Inject constructor(
    @Application private val scope: CoroutineScope,
    @Application private val scope: CoroutineScope,
    activityManagerRepository: ActivityManagerRepository,
    private val activityManagerRepository: ActivityManagerRepository,
    private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
    private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
    activeNotificationsInteractor: ActiveNotificationsInteractor,
    activeNotificationsInteractor: ActiveNotificationsInteractor,
    @OngoingCallLog private val logBuffer: LogBuffer,
    @OngoingCallLog private val logBuffer: LogBuffer,
) {
) {
@@ -58,22 +65,45 @@ class OngoingCallInteractor @Inject constructor(
     */
     */
    val ongoingCallState: StateFlow<OngoingCallModel> =
    val ongoingCallState: StateFlow<OngoingCallModel> =
        activeNotificationsInteractor.ongoingCallNotification
        activeNotificationsInteractor.ongoingCallNotification
            .flatMapLatest { notificationModel ->
            .flatMapLatest { notification ->
                when (notificationModel) {
                createOngoingCallStateFlow(
                    null -> {
                    notification = notification
                )
            }
            .onEach { state ->
                setStatusBarRequiredForOngoingCall(state)
            }
            .stateIn(
                scope = scope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = OngoingCallModel.NoCall
            )

    private fun createOngoingCallStateFlow(
        notification: ActiveNotificationModel?
    ): Flow<OngoingCallModel> {
        if (notification == null) {
            logger.d("No active call notification - hiding chip")
            logger.d("No active call notification - hiding chip")
                        flowOf(OngoingCallModel.NoCall)
            return flowOf(OngoingCallModel.NoCall)
        }
        }


                    else -> combine(
        return combine(
                        flowOf(notificationModel),
            flowOf(notification),
            activityManagerRepository.createIsAppVisibleFlow(
            activityManagerRepository.createIsAppVisibleFlow(
                            creationUid = notificationModel.uid,
                creationUid = notification.uid,
                logger = logger,
                logger = logger,
                identifyingLogTag = TAG,
                identifyingLogTag = TAG,
                        ),
            )
        ) { model, isVisible ->
        ) { model, isVisible ->
                        when {
            deriveOngoingCallState(model, isVisible)
        }
    }

    private fun deriveOngoingCallState(
        model: ActiveNotificationModel,
        isVisible: Boolean
    ): OngoingCallModel {
        return when {
            isVisible -> {
            isVisible -> {
                logger.d({ "Call app is visible: uid=$int1" }) {
                logger.d({ "Call app is visible: uid=$int1" }) {
                    int1 = model.uid
                    int1 = model.uid
@@ -90,14 +120,23 @@ class OngoingCallInteractor @Inject constructor(
                    startTimeMs = model.whenTime,
                    startTimeMs = model.whenTime,
                    notificationIconView = model.statusBarChipIconView,
                    notificationIconView = model.statusBarChipIconView,
                    intent = model.contentIntent,
                    intent = model.contentIntent,
                                    notificationKey = model.key,
                    notificationKey = model.key
                )
                )
            }
            }
        }
        }
    }
    }

    private fun setStatusBarRequiredForOngoingCall(state: OngoingCallModel) {
        val statusBarRequired = state is OngoingCallModel.InCall
        // TODO(b/382808183): Create a single repository that can be utilized in
        //  `statusBarModeRepositoryStore` and `statusBarWindowControllerStore` so we do not need
        //  two separate calls to force the status bar to stay visible.
        statusBarModeRepositoryStore.defaultDisplay.setOngoingProcessRequiresStatusBarVisible(
            statusBarRequired
        )
        statusBarWindowControllerStore.defaultDisplay
            .setOngoingProcessRequiresStatusBarVisible(statusBarRequired)
    }
    }
            }
            .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingCallModel.NoCall)


    companion object {
    companion object {
        private val TAG = "OngoingCall"
        private val TAG = "OngoingCall"
+4 −0
Original line number Original line Diff line number Diff line
@@ -47,6 +47,7 @@ class FakeStatusBarModePerDisplayRepository : StatusBarModePerDisplayRepository
    override val isInFullscreenMode = MutableStateFlow(false)
    override val isInFullscreenMode = MutableStateFlow(false)
    override val statusBarAppearance = MutableStateFlow<StatusBarAppearance?>(null)
    override val statusBarAppearance = MutableStateFlow<StatusBarAppearance?>(null)
    override val statusBarMode = MutableStateFlow(StatusBarMode.TRANSPARENT)
    override val statusBarMode = MutableStateFlow(StatusBarMode.TRANSPARENT)
    override val ongoingProcessRequiresStatusBarVisible = MutableStateFlow(false)


    override fun showTransient() {
    override fun showTransient() {
        isTransientShown.value = true
        isTransientShown.value = true
@@ -59,6 +60,9 @@ class FakeStatusBarModePerDisplayRepository : StatusBarModePerDisplayRepository
    override fun start() {}
    override fun start() {}


    override fun stop() {}
    override fun stop() {}
    override fun setOngoingProcessRequiresStatusBarVisible(requiredVisible: Boolean) {
        ongoingProcessRequiresStatusBarVisible.value = requiredVisible
    }


    override fun onStatusBarViewInitialized(component: HomeStatusBarComponent) {}
    override fun onStatusBarViewInitialized(component: HomeStatusBarComponent) {}


+4 −0
Original line number Original line Diff line number Diff line
@@ -20,7 +20,9 @@ import com.android.systemui.activity.data.repository.activityManagerRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore


val Kosmos.ongoingCallInteractor: OngoingCallInteractor by
val Kosmos.ongoingCallInteractor: OngoingCallInteractor by
    Kosmos.Fixture {
    Kosmos.Fixture {
@@ -28,6 +30,8 @@ val Kosmos.ongoingCallInteractor: OngoingCallInteractor by
          scope = applicationCoroutineScope,
          scope = applicationCoroutineScope,
          activeNotificationsInteractor = activeNotificationsInteractor,
          activeNotificationsInteractor = activeNotificationsInteractor,
          activityManagerRepository = activityManagerRepository,
          activityManagerRepository = activityManagerRepository,
          statusBarModeRepositoryStore = fakeStatusBarModeRepository,
          statusBarWindowControllerStore = fakeStatusBarWindowControllerStore,
          logBuffer = logcatLogBuffer("OngoingCallInteractorKosmos"),
          logBuffer = logcatLogBuffer("OngoingCallInteractorKosmos"),
      )
      )
    }
    }
Loading