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

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

Merge "Introduce AnyExternalShadeDisplayPolicy" into main

parents bae6c332 964fb064
Loading
Loading
Loading
Loading
+84 −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.shade.display

import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.display.data.repository.display
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.test.runTest
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class AnyExternalShadeDisplayPolicyTest : SysuiTestCase() {
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val testScope = kosmos.testScope
    private val displayRepository = kosmos.displayRepository
    val underTest = AnyExternalShadeDisplayPolicy(displayRepository, testScope.backgroundScope)

    @Test
    fun displayId_ignoresUnwantedTypes() =
        testScope.runTest {
            val displayId by collectLastValue(underTest.displayId)

            displayRepository.addDisplays(
                display(id = 0, type = Display.TYPE_INTERNAL),
                display(id = 1, type = Display.TYPE_UNKNOWN),
                display(id = 2, type = Display.TYPE_VIRTUAL),
                display(id = 3, type = Display.TYPE_EXTERNAL),
            )

            assertThat(displayId).isEqualTo(3)
        }

    @Test
    fun displayId_onceRemoved_goesToNextDisplay() =
        testScope.runTest {
            val displayId by collectLastValue(underTest.displayId)

            displayRepository.addDisplays(
                display(id = 0, type = Display.TYPE_INTERNAL),
                display(id = 2, type = Display.TYPE_EXTERNAL),
                display(id = 3, type = Display.TYPE_EXTERNAL),
            )

            assertThat(displayId).isEqualTo(2)

            displayRepository.removeDisplay(2)

            assertThat(displayId).isEqualTo(3)
        }

    @Test
    fun displayId_onlyDefaultDisplay_defaultDisplay() =
        testScope.runTest {
            val displayId by collectLastValue(underTest.displayId)

            displayRepository.addDisplays(display(id = 0, type = Display.TYPE_INTERNAL))

            assertThat(displayId).isEqualTo(0)
        }
}
+57 −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.shade.display

import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DisplayRepository
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn

/**
 * Returns an external display if one exists, otherwise the default display.
 *
 * If there are multiple external displays, the one with minimum display ID is returned.
 */
@SysUISingleton
class AnyExternalShadeDisplayPolicy
@Inject
constructor(displayRepository: DisplayRepository, @Background bgScope: CoroutineScope) :
    ShadeDisplayPolicy {
    override val name: String
        get() = "any_external_display"

    override val displayId: StateFlow<Int> =
        displayRepository.displays
            .map { displays ->
                displays
                    .filter { it.displayId != DEFAULT_DISPLAY && it.type in ALLOWED_DISPLAY_TYPES }
                    .minOfOrNull { it.displayId } ?: DEFAULT_DISPLAY
            }
            .stateIn(bgScope, SharingStarted.WhileSubscribed(), DEFAULT_DISPLAY)

    private companion object {
        val ALLOWED_DISPLAY_TYPES =
            setOf(Display.TYPE_EXTERNAL, Display.TYPE_OVERLAY, Display.TYPE_WIFI)
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -35,5 +35,11 @@ interface ShadeDisplayPolicyModule {
    @Binds
    fun provideDefaultPolicyToSet(impl: DefaultShadeDisplayPolicy): ShadeDisplayPolicy

    @IntoSet
    @Binds
    fun provideAnyExternalShadeDisplayPolicyToSet(
        impl: AnyExternalShadeDisplayPolicy
    ): ShadeDisplayPolicy

    @Binds fun provideDefaultPolicy(impl: DefaultShadeDisplayPolicy): ShadeDisplayPolicy
}
+4 −0
Original line number Diff line number Diff line
@@ -57,6 +57,10 @@ class FakeDisplayRepository @Inject constructor() : DisplayRepository {
        addDisplay(display(type, id = displayId))
    }

    suspend fun addDisplays(vararg displays: Display) {
        displays.forEach { addDisplay(it) }
    }

    suspend fun addDisplay(display: Display) {
        flow.value += display
        displayAdditionEventFlow.emit(display)