Loading packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt +138 −1 Original line number Diff line number Diff line Loading @@ -23,15 +23,16 @@ import android.view.Display import android.view.Display.DEFAULT_DISPLAY import android.view.Display.TYPE_EXTERNAL import android.view.Display.TYPE_INTERNAL import android.view.IWindowManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.FlowValue import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.statusbar.CommandQueue import com.android.systemui.util.mockito.kotlinArgumentCaptor import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.utils.os.FakeHandler import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.TestScope Loading @@ -46,6 +47,7 @@ import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.kotlin.eq import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper Loading @@ -53,7 +55,11 @@ import org.mockito.kotlin.eq class DisplayRepositoryTest : SysuiTestCase() { private val displayManager = mock<DisplayManager>() private val commandQueue = mock<CommandQueue>() private val windowManager = mock<IWindowManager>() private val displayListener = kotlinArgumentCaptor<DisplayManager.DisplayListener>() private val commandQueueCallbacks = kotlinArgumentCaptor<CommandQueue.Callbacks>() private val connectedDisplayListener = kotlinArgumentCaptor<DisplayManager.DisplayListener>() private val testHandler = FakeHandler(Looper.getMainLooper()) Loading @@ -67,6 +73,8 @@ class DisplayRepositoryTest : SysuiTestCase() { private val displayRepository: DisplayRepositoryImpl by lazy { DisplayRepositoryImpl( displayManager, commandQueue, windowManager, testHandler, TestScope(UnconfinedTestDispatcher()), UnconfinedTestDispatcher(), Loading Loading @@ -513,6 +521,115 @@ class DisplayRepositoryTest : SysuiTestCase() { assertThat(displayRepository.getDisplay(2)).isNull() } @Test fun displayIdsWithSystemDecorations_onStart_emitsDisplaysWithSystemDecorations() = testScope.runTest { setDisplays(0, 1, 2) whenever(windowManager.shouldShowSystemDecors(0)).thenReturn(true) whenever(windowManager.shouldShowSystemDecors(1)).thenReturn(false) whenever(windowManager.shouldShowSystemDecors(2)).thenReturn(true) val displayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue() assertThat(displayIdsWithSystemDecorations).containsExactly(0, 2) } @Test fun displayIdsWithSystemDecorations_systemDecorationAdded_emitsIncludingNewDisplayIds() = testScope.runTest { setDisplays(0) whenever(windowManager.shouldShowSystemDecors(0)).thenReturn(true) val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue() sendOnDisplayAddSystemDecorations(2) sendOnDisplayAddSystemDecorations(3) assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(0, 2, 3) } @Test fun displayIdsWithSystemDecorations_systemDecorationAdded_emitsToNewSubscribers() = testScope.runTest { setDisplays(0) whenever(windowManager.shouldShowSystemDecors(0)).thenReturn(true) val priorDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue() sendOnDisplayAddSystemDecorations(1) assertThat(priorDisplayIdsWithSystemDecorations).containsExactly(0, 1) val lastDisplayIdsWithSystemDecorations by collectLastValue(displayRepository.displayIdsWithSystemDecorations) assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(0, 1) } @Test fun displayIdsWithSystemDecorations_systemDecorationRemoved_doesNotEmitRemovedDisplayId() = testScope.runTest { val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue() sendOnDisplayAddSystemDecorations(1) sendOnDisplayAddSystemDecorations(2) sendOnDisplayRemoveSystemDecorations(2) assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1) } @Test fun displayIdsWithSystemDecorations_systemDecorationsRemoved_nonExistentDisplay_noEffect() = testScope.runTest { val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue() sendOnDisplayAddSystemDecorations(1) sendOnDisplayRemoveSystemDecorations(2) assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1) } @Test fun displayIdsWithSystemDecorations_displayRemoved_doesNotEmitRemovedDisplayId() = testScope.runTest { val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue() sendOnDisplayAddSystemDecorations(1) sendOnDisplayAddSystemDecorations(2) sendOnDisplayRemoved(2) assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1) } @Test fun displayIdsWithSystemDecorations_displayRemoved_nonExistentDisplay_noEffect() = testScope.runTest { val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue() sendOnDisplayAddSystemDecorations(1) sendOnDisplayRemoved(2) assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1) } @Test fun displayIdsWithSystemDecorations_onFlowCollection_commandQueueCallbackRegistered() = testScope.runTest { val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue() assertThat(lastDisplayIdsWithSystemDecorations).isEmpty() verify(commandQueue, times(1)).addCallback(any()) } @Test fun displayIdsWithSystemDecorations_afterFlowCollection_commandQueueCallbackUnregistered() { testScope.runTest { val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue() assertThat(lastDisplayIdsWithSystemDecorations).isEmpty() verify(commandQueue, times(1)).addCallback(any()) } verify(commandQueue, times(1)).removeCallback(any()) } private fun Iterable<Display>.ids(): List<Int> = map { it.displayId } private fun Iterable<Set<Display>>.toIdSets(): List<Set<Int>> = map { it.ids().toSet() } Loading Loading @@ -550,6 +667,14 @@ class DisplayRepositoryTest : SysuiTestCase() { return flowValue } // Wrapper to capture the displayListener and commandQueueCallbacks. private fun TestScope.latestDisplayIdsWithSystemDecorationsValue(): FlowValue<Set<Int>?> { val flowValue = collectLastValue(displayRepository.displayIdsWithSystemDecorations) captureAddedRemovedListener() captureCommandQueueCallbacks() return flowValue } private fun captureAddedRemovedListener() { verify(displayManager) .registerDisplayListener( Loading @@ -563,6 +688,10 @@ class DisplayRepositoryTest : SysuiTestCase() { ) } private fun captureCommandQueueCallbacks() { verify(commandQueue).addCallback(commandQueueCallbacks.capture()) } private fun sendOnDisplayAdded(id: Int, displayType: Int) { val mockDisplay = display(id = id, type = displayType) whenever(displayManager.getDisplay(eq(id))).thenReturn(mockDisplay) Loading Loading @@ -592,6 +721,14 @@ class DisplayRepositoryTest : SysuiTestCase() { connectedDisplayListener.value.onDisplayChanged(id) } private fun sendOnDisplayRemoveSystemDecorations(id: Int) { commandQueueCallbacks.value.onDisplayRemoveSystemDecorations(id) } private fun sendOnDisplayAddSystemDecorations(id: Int) { commandQueueCallbacks.value.onDisplayAddSystemDecorations(id) } private fun setDisplays(displays: List<Display>) { whenever(displayManager.displays).thenReturn(displays.toTypedArray()) displays.forEach { display -> Loading packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt +61 −2 Original line number Diff line number Diff line Loading @@ -26,14 +26,16 @@ import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_REMOVED import android.os.Handler import android.util.Log import android.view.Display import android.view.IWindowManager import com.android.app.tracing.FlowTracing.traceEach import com.android.app.tracing.traceSection import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.display.data.DisplayEvent import com.android.systemui.statusbar.CommandQueue import com.android.systemui.util.Compile import com.android.systemui.util.kotlin.pairwiseBy import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope Loading @@ -43,6 +45,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter Loading @@ -50,12 +53,13 @@ import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.scan import kotlinx.coroutines.flow.stateIn /** Provides a [Flow] of [Display] as returned by [DisplayManager]. */ /** Repository for providing access to display related information and events. */ interface DisplayRepository { /** Display change event indicating a change to the given displayId has occurred. */ val displayChangeEvent: Flow<Int> Loading @@ -66,6 +70,9 @@ interface DisplayRepository { /** Display removal event indicating a display has been removed. */ val displayRemovalEvent: Flow<Int> /** A [StateFlow] that maintains a set of display IDs that should have system decorations. */ val displayIdsWithSystemDecorations: StateFlow<Set<Int>> /** * Provides the current set of displays. * Loading Loading @@ -124,6 +131,8 @@ class DisplayRepositoryImpl @Inject constructor( private val displayManager: DisplayManager, private val commandQueue: CommandQueue, private val windowManager: IWindowManager, @Background backgroundHandler: Handler, @Background bgApplicationScope: CoroutineScope, @Background backgroundCoroutineDispatcher: CoroutineDispatcher, Loading Loading @@ -426,6 +435,56 @@ constructor( .map { it.resultSet } } private val decorationEvents: Flow<Event> = callbackFlow { val callback = object : CommandQueue.Callbacks { override fun onDisplayAddSystemDecorations(displayId: Int) { trySend(Event.Add(displayId)) } override fun onDisplayRemoveSystemDecorations(displayId: Int) { trySend(Event.Remove(displayId)) } } commandQueue.addCallback(callback) awaitClose { commandQueue.removeCallback(callback) } } private val initialDisplayIdsWithDecorations: Set<Int> = displayIds.value.filter { windowManager.shouldShowSystemDecors(it) }.toSet() /** * A [StateFlow] that maintains a set of display IDs that should have system decorations. * * Updates to the set are triggered by: * - Adding displays via [CommandQueue.Callbacks.onDisplayAddSystemDecorations]. * - Removing displays via [CommandQueue.Callbacks.onDisplayRemoveSystemDecorations]. * - Removing displays via [displayRemovalEvent] emissions. * * The set is initialized with displays that qualify for system decorations based on * [WindowManager.shouldShowSystemDecors]. */ override val displayIdsWithSystemDecorations: StateFlow<Set<Int>> = merge(decorationEvents, displayRemovalEvent.map { Event.Remove(it) }) .scan(initialDisplayIdsWithDecorations) { displayIds: Set<Int>, event: Event -> when (event) { is Event.Add -> displayIds + event.displayId is Event.Remove -> displayIds - event.displayId } } .distinctUntilChanged() .stateIn( scope = bgApplicationScope, started = SharingStarted.WhileSubscribed(), initialValue = initialDisplayIdsWithDecorations, ) private sealed class Event(val displayId: Int) { class Add(displayId: Int) : Event(displayId) class Remove(displayId: Int) : Event(displayId) } private companion object { const val TAG = "DisplayRepository" val DEBUG = Log.isLoggable(TAG, Log.DEBUG) || Compile.IS_DEBUG Loading packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt +2 −0 Original line number Diff line number Diff line Loading @@ -104,6 +104,8 @@ class FakeDisplayRepository @Inject constructor() : DisplayRepository { private val _displayChangeEvent = MutableSharedFlow<Int>(replay = 1) override val displayChangeEvent: Flow<Int> = _displayChangeEvent override val displayIdsWithSystemDecorations: StateFlow<Set<Int>> = MutableStateFlow(emptySet()) suspend fun emitDisplayChangeEvent(displayId: Int) = _displayChangeEvent.emit(displayId) fun setDefaultDisplayOff(defaultDisplayOff: Boolean) { Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt +138 −1 Original line number Diff line number Diff line Loading @@ -23,15 +23,16 @@ import android.view.Display import android.view.Display.DEFAULT_DISPLAY import android.view.Display.TYPE_EXTERNAL import android.view.Display.TYPE_INTERNAL import android.view.IWindowManager import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.FlowValue import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.statusbar.CommandQueue import com.android.systemui.util.mockito.kotlinArgumentCaptor import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.utils.os.FakeHandler import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.TestScope Loading @@ -46,6 +47,7 @@ import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.kotlin.eq import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper Loading @@ -53,7 +55,11 @@ import org.mockito.kotlin.eq class DisplayRepositoryTest : SysuiTestCase() { private val displayManager = mock<DisplayManager>() private val commandQueue = mock<CommandQueue>() private val windowManager = mock<IWindowManager>() private val displayListener = kotlinArgumentCaptor<DisplayManager.DisplayListener>() private val commandQueueCallbacks = kotlinArgumentCaptor<CommandQueue.Callbacks>() private val connectedDisplayListener = kotlinArgumentCaptor<DisplayManager.DisplayListener>() private val testHandler = FakeHandler(Looper.getMainLooper()) Loading @@ -67,6 +73,8 @@ class DisplayRepositoryTest : SysuiTestCase() { private val displayRepository: DisplayRepositoryImpl by lazy { DisplayRepositoryImpl( displayManager, commandQueue, windowManager, testHandler, TestScope(UnconfinedTestDispatcher()), UnconfinedTestDispatcher(), Loading Loading @@ -513,6 +521,115 @@ class DisplayRepositoryTest : SysuiTestCase() { assertThat(displayRepository.getDisplay(2)).isNull() } @Test fun displayIdsWithSystemDecorations_onStart_emitsDisplaysWithSystemDecorations() = testScope.runTest { setDisplays(0, 1, 2) whenever(windowManager.shouldShowSystemDecors(0)).thenReturn(true) whenever(windowManager.shouldShowSystemDecors(1)).thenReturn(false) whenever(windowManager.shouldShowSystemDecors(2)).thenReturn(true) val displayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue() assertThat(displayIdsWithSystemDecorations).containsExactly(0, 2) } @Test fun displayIdsWithSystemDecorations_systemDecorationAdded_emitsIncludingNewDisplayIds() = testScope.runTest { setDisplays(0) whenever(windowManager.shouldShowSystemDecors(0)).thenReturn(true) val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue() sendOnDisplayAddSystemDecorations(2) sendOnDisplayAddSystemDecorations(3) assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(0, 2, 3) } @Test fun displayIdsWithSystemDecorations_systemDecorationAdded_emitsToNewSubscribers() = testScope.runTest { setDisplays(0) whenever(windowManager.shouldShowSystemDecors(0)).thenReturn(true) val priorDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue() sendOnDisplayAddSystemDecorations(1) assertThat(priorDisplayIdsWithSystemDecorations).containsExactly(0, 1) val lastDisplayIdsWithSystemDecorations by collectLastValue(displayRepository.displayIdsWithSystemDecorations) assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(0, 1) } @Test fun displayIdsWithSystemDecorations_systemDecorationRemoved_doesNotEmitRemovedDisplayId() = testScope.runTest { val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue() sendOnDisplayAddSystemDecorations(1) sendOnDisplayAddSystemDecorations(2) sendOnDisplayRemoveSystemDecorations(2) assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1) } @Test fun displayIdsWithSystemDecorations_systemDecorationsRemoved_nonExistentDisplay_noEffect() = testScope.runTest { val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue() sendOnDisplayAddSystemDecorations(1) sendOnDisplayRemoveSystemDecorations(2) assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1) } @Test fun displayIdsWithSystemDecorations_displayRemoved_doesNotEmitRemovedDisplayId() = testScope.runTest { val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue() sendOnDisplayAddSystemDecorations(1) sendOnDisplayAddSystemDecorations(2) sendOnDisplayRemoved(2) assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1) } @Test fun displayIdsWithSystemDecorations_displayRemoved_nonExistentDisplay_noEffect() = testScope.runTest { val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue() sendOnDisplayAddSystemDecorations(1) sendOnDisplayRemoved(2) assertThat(lastDisplayIdsWithSystemDecorations).containsExactly(1) } @Test fun displayIdsWithSystemDecorations_onFlowCollection_commandQueueCallbackRegistered() = testScope.runTest { val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue() assertThat(lastDisplayIdsWithSystemDecorations).isEmpty() verify(commandQueue, times(1)).addCallback(any()) } @Test fun displayIdsWithSystemDecorations_afterFlowCollection_commandQueueCallbackUnregistered() { testScope.runTest { val lastDisplayIdsWithSystemDecorations by latestDisplayIdsWithSystemDecorationsValue() assertThat(lastDisplayIdsWithSystemDecorations).isEmpty() verify(commandQueue, times(1)).addCallback(any()) } verify(commandQueue, times(1)).removeCallback(any()) } private fun Iterable<Display>.ids(): List<Int> = map { it.displayId } private fun Iterable<Set<Display>>.toIdSets(): List<Set<Int>> = map { it.ids().toSet() } Loading Loading @@ -550,6 +667,14 @@ class DisplayRepositoryTest : SysuiTestCase() { return flowValue } // Wrapper to capture the displayListener and commandQueueCallbacks. private fun TestScope.latestDisplayIdsWithSystemDecorationsValue(): FlowValue<Set<Int>?> { val flowValue = collectLastValue(displayRepository.displayIdsWithSystemDecorations) captureAddedRemovedListener() captureCommandQueueCallbacks() return flowValue } private fun captureAddedRemovedListener() { verify(displayManager) .registerDisplayListener( Loading @@ -563,6 +688,10 @@ class DisplayRepositoryTest : SysuiTestCase() { ) } private fun captureCommandQueueCallbacks() { verify(commandQueue).addCallback(commandQueueCallbacks.capture()) } private fun sendOnDisplayAdded(id: Int, displayType: Int) { val mockDisplay = display(id = id, type = displayType) whenever(displayManager.getDisplay(eq(id))).thenReturn(mockDisplay) Loading Loading @@ -592,6 +721,14 @@ class DisplayRepositoryTest : SysuiTestCase() { connectedDisplayListener.value.onDisplayChanged(id) } private fun sendOnDisplayRemoveSystemDecorations(id: Int) { commandQueueCallbacks.value.onDisplayRemoveSystemDecorations(id) } private fun sendOnDisplayAddSystemDecorations(id: Int) { commandQueueCallbacks.value.onDisplayAddSystemDecorations(id) } private fun setDisplays(displays: List<Display>) { whenever(displayManager.displays).thenReturn(displays.toTypedArray()) displays.forEach { display -> Loading
packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt +61 −2 Original line number Diff line number Diff line Loading @@ -26,14 +26,16 @@ import android.hardware.display.DisplayManager.EVENT_TYPE_DISPLAY_REMOVED import android.os.Handler import android.util.Log import android.view.Display import android.view.IWindowManager import com.android.app.tracing.FlowTracing.traceEach import com.android.app.tracing.traceSection import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.display.data.DisplayEvent import com.android.systemui.statusbar.CommandQueue import com.android.systemui.util.Compile import com.android.systemui.util.kotlin.pairwiseBy import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope Loading @@ -43,6 +45,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter Loading @@ -50,12 +53,13 @@ import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.scan import kotlinx.coroutines.flow.stateIn /** Provides a [Flow] of [Display] as returned by [DisplayManager]. */ /** Repository for providing access to display related information and events. */ interface DisplayRepository { /** Display change event indicating a change to the given displayId has occurred. */ val displayChangeEvent: Flow<Int> Loading @@ -66,6 +70,9 @@ interface DisplayRepository { /** Display removal event indicating a display has been removed. */ val displayRemovalEvent: Flow<Int> /** A [StateFlow] that maintains a set of display IDs that should have system decorations. */ val displayIdsWithSystemDecorations: StateFlow<Set<Int>> /** * Provides the current set of displays. * Loading Loading @@ -124,6 +131,8 @@ class DisplayRepositoryImpl @Inject constructor( private val displayManager: DisplayManager, private val commandQueue: CommandQueue, private val windowManager: IWindowManager, @Background backgroundHandler: Handler, @Background bgApplicationScope: CoroutineScope, @Background backgroundCoroutineDispatcher: CoroutineDispatcher, Loading Loading @@ -426,6 +435,56 @@ constructor( .map { it.resultSet } } private val decorationEvents: Flow<Event> = callbackFlow { val callback = object : CommandQueue.Callbacks { override fun onDisplayAddSystemDecorations(displayId: Int) { trySend(Event.Add(displayId)) } override fun onDisplayRemoveSystemDecorations(displayId: Int) { trySend(Event.Remove(displayId)) } } commandQueue.addCallback(callback) awaitClose { commandQueue.removeCallback(callback) } } private val initialDisplayIdsWithDecorations: Set<Int> = displayIds.value.filter { windowManager.shouldShowSystemDecors(it) }.toSet() /** * A [StateFlow] that maintains a set of display IDs that should have system decorations. * * Updates to the set are triggered by: * - Adding displays via [CommandQueue.Callbacks.onDisplayAddSystemDecorations]. * - Removing displays via [CommandQueue.Callbacks.onDisplayRemoveSystemDecorations]. * - Removing displays via [displayRemovalEvent] emissions. * * The set is initialized with displays that qualify for system decorations based on * [WindowManager.shouldShowSystemDecors]. */ override val displayIdsWithSystemDecorations: StateFlow<Set<Int>> = merge(decorationEvents, displayRemovalEvent.map { Event.Remove(it) }) .scan(initialDisplayIdsWithDecorations) { displayIds: Set<Int>, event: Event -> when (event) { is Event.Add -> displayIds + event.displayId is Event.Remove -> displayIds - event.displayId } } .distinctUntilChanged() .stateIn( scope = bgApplicationScope, started = SharingStarted.WhileSubscribed(), initialValue = initialDisplayIdsWithDecorations, ) private sealed class Event(val displayId: Int) { class Add(displayId: Int) : Event(displayId) class Remove(displayId: Int) : Event(displayId) } private companion object { const val TAG = "DisplayRepository" val DEBUG = Log.isLoggable(TAG, Log.DEBUG) || Compile.IS_DEBUG Loading
packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt +2 −0 Original line number Diff line number Diff line Loading @@ -104,6 +104,8 @@ class FakeDisplayRepository @Inject constructor() : DisplayRepository { private val _displayChangeEvent = MutableSharedFlow<Int>(replay = 1) override val displayChangeEvent: Flow<Int> = _displayChangeEvent override val displayIdsWithSystemDecorations: StateFlow<Set<Int>> = MutableStateFlow(emptySet()) suspend fun emitDisplayChangeEvent(displayId: Int) = _displayChangeEvent.emit(displayId) fun setDefaultDisplayOff(defaultDisplayOff: Boolean) { Loading