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

Commit 964fb064 authored by Nicolo' Mazzucato's avatar Nicolo' Mazzucato
Browse files

Introduce AnyExternalShadeDisplayPolicy

This policy sets the shade to stay on the non-internal display with minimum display id. Note that only some display types are considered.

It is possible to select this policy with

"adb shell cmd statusbar shade_display_override any_external_display"

(This only works if the flag is on)

Bug: 362719719
Bug: 380444270
Test: ShadePrimaryDisplayCommandTest, ShadeDisplaysRepositoryTest
Flag: com.android.systemui.shade_window_goes_around
Change-Id: Ie3df9a0a03ddc77339a547b9e3bf58361d1b89ef
parent 3fa30d35
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)