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

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

Merge "Move shade to the default display when the keyguard is visible" into main

parents 5ddbc132 0825ca60
Loading
Loading
Loading
Loading
+54 −1
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.display.data.repository.display
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.testKosmos
@@ -38,17 +39,31 @@ import org.junit.runner.RunWith
class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val testScope = kosmos.testScope
    private val keyguardRepository = kosmos.fakeKeyguardRepository
    private val displayRepository = kosmos.displayRepository
    val underTest = StatusBarTouchShadeDisplayPolicy(displayRepository, testScope.backgroundScope)

    private fun createUnderTest(
        shadeOnDefaultDisplayWhenLocked: Boolean = false
    ): StatusBarTouchShadeDisplayPolicy {
        return StatusBarTouchShadeDisplayPolicy(
            displayRepository,
            keyguardRepository,
            testScope.backgroundScope,
            shadeOnDefaultDisplayWhenLocked = shadeOnDefaultDisplayWhenLocked,
        )
    }

    @Test
    fun displayId_defaultToDefaultDisplay() {
        val underTest = createUnderTest()

        assertThat(underTest.displayId.value).isEqualTo(Display.DEFAULT_DISPLAY)
    }

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

            displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
@@ -60,6 +75,7 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
    @Test
    fun onStatusBarTouched_notExistentDisplay_displayIdNotUpdated() =
        testScope.runTest {
            val underTest = createUnderTest()
            val displayIds by collectValues(underTest.displayId)
            assertThat(displayIds).isEqualTo(listOf(Display.DEFAULT_DISPLAY))

@@ -72,6 +88,7 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {
    @Test
    fun onStatusBarTouched_afterDisplayRemoved_goesBackToDefaultDisplay() =
        testScope.runTest {
            val underTest = createUnderTest()
            val displayId by collectLastValue(underTest.displayId)

            displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
@@ -83,4 +100,40 @@ class StatusBarTouchShadeDisplayPolicyTest : SysuiTestCase() {

            assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)
        }

    @Test
    fun onStatusBarTouched_afterKeyguardVisible_goesBackToDefaultDisplay() =
        testScope.runTest {
            val underTest = createUnderTest(shadeOnDefaultDisplayWhenLocked = true)
            val displayId by collectLastValue(underTest.displayId)

            displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
            underTest.onStatusBarTouched(2)

            assertThat(displayId).isEqualTo(2)

            keyguardRepository.setKeyguardShowing(true)

            assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)
        }

    @Test
    fun onStatusBarTouched_afterKeyguardHides_goesBackToPreviousDisplay() =
        testScope.runTest {
            val underTest = createUnderTest(shadeOnDefaultDisplayWhenLocked = true)
            val displayId by collectLastValue(underTest.displayId)

            displayRepository.addDisplays(display(id = 2, type = TYPE_EXTERNAL))
            underTest.onStatusBarTouched(2)

            assertThat(displayId).isEqualTo(2)

            keyguardRepository.setKeyguardShowing(true)

            assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)

            keyguardRepository.setKeyguardShowing(false)

            assertThat(displayId).isEqualTo(2)
        }
}
+15 −0
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import javax.inject.Provider
import javax.inject.Qualifier

/**
 * Module responsible for managing display-specific components and resources for the notification
@@ -237,9 +238,23 @@ object ShadeDisplayAwareModule {
            CoreStartable.NOP
        }
    }

    @Provides
    @ShadeOnDefaultDisplayWhenLocked
    fun provideShadeOnDefaultDisplayWhenLocked(): Boolean = true
}

@Module
internal interface OptionalShadeDisplayAwareBindings {
    @BindsOptionalOf fun bindOptionalOfWindowRootView(): WindowRootView
}

/**
 * Annotates the boolean value that defines whether the shade window should go back to the default
 * display when the keyguard is visible.
 *
 * As of today (Dec 2024), This is a configuration parameter provided in the dagger graph as the
 * final policy around keyguard display is still under discussion, and will be evaluated based on
 * how well this solution behaves from the performance point of view.
 */
@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class ShadeOnDefaultDisplayWhenLocked
+28 −7
Original line number Diff line number Diff line
@@ -22,34 +22,55 @@ import com.android.app.tracing.coroutines.launchTraced
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DisplayRepository
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.shade.ShadeOnDefaultDisplayWhenLocked
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn

/**
 * Moves the shade on the last display that received a status bar touch.
 *
 * If the display is removed, falls back to the default one.
 * If the display is removed, falls back to the default one. When [shadeOnDefaultDisplayWhenLocked]
 * is true, the shade falls back to the default display when the keyguard is visible.
 */
@SysUISingleton
class StatusBarTouchShadeDisplayPolicy
@Inject
constructor(displayRepository: DisplayRepository, @Background val backgroundScope: CoroutineScope) :
    ShadeDisplayPolicy {
    override val name: String
        get() = "status_bar_latest_touch"
constructor(
    displayRepository: DisplayRepository,
    keyguardRepository: KeyguardRepository,
    @Background val backgroundScope: CoroutineScope,
    @ShadeOnDefaultDisplayWhenLocked val shadeOnDefaultDisplayWhenLocked: Boolean,
) : ShadeDisplayPolicy {
    override val name: String = "status_bar_latest_touch"

    private val currentDisplayId = MutableStateFlow(Display.DEFAULT_DISPLAY)
    private val availableDisplayIds: StateFlow<Set<Int>> = displayRepository.displayIds

    override val displayId: StateFlow<Int>
        get() = currentDisplayId
    override val displayId: StateFlow<Int> =
        if (shadeOnDefaultDisplayWhenLocked) {
            keyguardRepository.isKeyguardShowing
                .combine(currentDisplayId) { isKeyguardShowing, currentDisplayId ->
                    if (isKeyguardShowing) {
                        Display.DEFAULT_DISPLAY
                    } else {
                        currentDisplayId
                    }
                }
                .stateIn(backgroundScope, SharingStarted.WhileSubscribed(), currentDisplayId.value)
        } else {
            currentDisplayId
        }

    private var removalListener: Job? = null