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

Commit 0924ffe6 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "[SB] Create StatusBarWindowStateRepository." into main

parents dd9bb062 1a910a9e
Loading
Loading
Loading
Loading
+18 −14
Original line number Diff line number Diff line
@@ -17,9 +17,9 @@
package com.android.systemui.statusbar.window

import android.app.StatusBarManager
import android.app.StatusBarManager.WindowVisibleState
import android.app.StatusBarManager.WINDOW_STATE_SHOWING
import android.app.StatusBarManager.WINDOW_STATUS_BAR
import android.app.StatusBarManager.WindowVisibleState
import android.app.StatusBarManager.windowStateToString
import android.util.Log
import com.android.systemui.dagger.SysUISingleton
@@ -31,19 +31,23 @@ import javax.inject.Inject
/**
 * A centralized class maintaining the state of the status bar window.
 *
 * @deprecated use
 *   [com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepository] instead.
 *
 * Classes that want to get updates about the status bar window state should subscribe to this class
 * via [addListener] and should NOT add their own callback on [CommandQueue].
 */
@SysUISingleton
class StatusBarWindowStateController @Inject constructor(
    @DisplayId private val thisDisplayId: Int,
    commandQueue: CommandQueue
) {
    private val commandQueueCallback = object : CommandQueue.Callbacks {
@Deprecated("Use StatusBarWindowRepository instead")
class StatusBarWindowStateController
@Inject
constructor(@DisplayId private val thisDisplayId: Int, commandQueue: CommandQueue) {
    private val commandQueueCallback =
        object : CommandQueue.Callbacks {
            override fun setWindowState(
                displayId: Int,
                @StatusBarManager.WindowType window: Int,
            @WindowVisibleState state: Int
                @WindowVisibleState state: Int,
            ) {
                this@StatusBarWindowStateController.setWindowState(displayId, window, state)
            }
@@ -71,7 +75,7 @@ class StatusBarWindowStateController @Inject constructor(
    private fun setWindowState(
        displayId: Int,
        @StatusBarManager.WindowType window: Int,
        @WindowVisibleState state: Int
        @WindowVisibleState state: Int,
    ) {
        if (displayId != thisDisplayId) {
            return
+86 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.window.data.repository

import android.app.StatusBarManager
import android.app.StatusBarManager.WINDOW_STATE_HIDDEN
import android.app.StatusBarManager.WINDOW_STATE_HIDING
import android.app.StatusBarManager.WINDOW_STATE_SHOWING
import android.app.StatusBarManager.WINDOW_STATUS_BAR
import android.app.StatusBarManager.WindowVisibleState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.DisplayId
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn

/**
 * A centralized class maintaining the state of the status bar window.
 *
 * Classes that want to get updates about the status bar window state should subscribe to
 * [windowState] and should NOT add their own callback on [CommandQueue].
 */
@SysUISingleton
class StatusBarWindowStateRepository
@Inject
constructor(
    private val commandQueue: CommandQueue,
    @DisplayId private val thisDisplayId: Int,
    @Application private val scope: CoroutineScope,
) {
    val windowState: StateFlow<StatusBarWindowState> =
        conflatedCallbackFlow {
                val callback =
                    object : CommandQueue.Callbacks {
                        override fun setWindowState(
                            displayId: Int,
                            @StatusBarManager.WindowType window: Int,
                            @WindowVisibleState state: Int,
                        ) {
                            // TODO(b/364360986): Log the window state changes.
                            if (displayId != thisDisplayId) {
                                return
                            }
                            if (window != WINDOW_STATUS_BAR) {
                                return
                            }
                            trySend(state.toWindowState())
                        }
                    }
                commandQueue.addCallback(callback)
                awaitClose { commandQueue.removeCallback(callback) }
            }
            // Use Eagerly because we always need to know about the status bar window state
            .stateIn(scope, SharingStarted.Eagerly, StatusBarWindowState.Hidden)

    @WindowVisibleState
    private fun Int.toWindowState(): StatusBarWindowState {
        return when (this) {
            WINDOW_STATE_SHOWING -> StatusBarWindowState.Showing
            WINDOW_STATE_HIDING -> StatusBarWindowState.Hiding
            WINDOW_STATE_HIDDEN -> StatusBarWindowState.Hidden
            else -> StatusBarWindowState.Hidden
        }
    }
}
+27 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.window.data.model

/**
 * Represents the state of the status bar *window* as a whole (as opposed to individual views within
 * the status bar).
 */
enum class StatusBarWindowState {
    Showing,
    Hiding,
    Hidden,
}
+112 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.window.data.repository

import android.app.StatusBarManager.WINDOW_NAVIGATION_BAR
import android.app.StatusBarManager.WINDOW_STATE_HIDDEN
import android.app.StatusBarManager.WINDOW_STATE_HIDING
import android.app.StatusBarManager.WINDOW_STATE_SHOWING
import android.app.StatusBarManager.WINDOW_STATUS_BAR
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.commandQueue
import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.mockito.Mockito.verify
import org.mockito.kotlin.argumentCaptor

@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
class StatusBarWindowStateRepositoryTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val commandQueue = kosmos.commandQueue
    private val underTest =
        StatusBarWindowStateRepository(commandQueue, DISPLAY_ID, testScope.backgroundScope)

    private val callback: CommandQueue.Callbacks
        get() {
            testScope.runCurrent()
            val callbackCaptor = argumentCaptor<CommandQueue.Callbacks>()
            verify(commandQueue).addCallback(callbackCaptor.capture())
            return callbackCaptor.firstValue
        }

    @Test
    fun windowState_notSameDisplayId_notUpdated() =
        testScope.runTest {
            val latest by collectLastValue(underTest.windowState)
            assertThat(latest).isEqualTo(StatusBarWindowState.Hidden)

            callback.setWindowState(DISPLAY_ID + 1, WINDOW_STATUS_BAR, WINDOW_STATE_SHOWING)

            assertThat(latest).isEqualTo(StatusBarWindowState.Hidden)
        }

    @Test
    fun windowState_notStatusBarWindow_notUpdated() =
        testScope.runTest {
            val latest by collectLastValue(underTest.windowState)
            assertThat(latest).isEqualTo(StatusBarWindowState.Hidden)

            callback.setWindowState(DISPLAY_ID, WINDOW_NAVIGATION_BAR, WINDOW_STATE_SHOWING)

            assertThat(latest).isEqualTo(StatusBarWindowState.Hidden)
        }

    @Test
    fun windowState_showing_updated() =
        testScope.runTest {
            val latest by collectLastValue(underTest.windowState)

            callback.setWindowState(DISPLAY_ID, WINDOW_STATUS_BAR, WINDOW_STATE_SHOWING)

            assertThat(latest).isEqualTo(StatusBarWindowState.Showing)
        }

    @Test
    fun windowState_hiding_updated() =
        testScope.runTest {
            val latest by collectLastValue(underTest.windowState)

            callback.setWindowState(DISPLAY_ID, WINDOW_STATUS_BAR, WINDOW_STATE_HIDING)

            assertThat(latest).isEqualTo(StatusBarWindowState.Hiding)
        }

    @Test
    fun windowState_hidden_updated() =
        testScope.runTest {
            val latest by collectLastValue(underTest.windowState)
            callback.setWindowState(DISPLAY_ID, WINDOW_STATUS_BAR, WINDOW_STATE_SHOWING)
            assertThat(latest).isEqualTo(StatusBarWindowState.Showing)

            callback.setWindowState(DISPLAY_ID, WINDOW_STATUS_BAR, WINDOW_STATE_HIDDEN)

            assertThat(latest).isEqualTo(StatusBarWindowState.Hidden)
        }
}

private const val DISPLAY_ID = 10