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

Commit 4ac5391a authored by dakinola's avatar dakinola
Browse files

Update ShadeDisplaysRepository to listen to shade display dev option

Making it so that when the shade display positon is changed by developer option, or adb command, the corresponding policy is propagagted to the repository

Bug: 379278693
Test: atest ShadeDisplaysRepositoryTest
Test: atest ShadePrimaryDisplayCommandTest
Test: manual testing with logs
Flag: com.android.systemui.shade_window_goes_around
Change-Id: I1804850c2e0c800efdc1b58fd8fd3f0685803bd9
parent a7972e8f
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -683,9 +683,9 @@

    <!-- Values for showing shade on external display for developers -->
    <string-array name="shade_display_awareness_values" >
        <item>device-display</item>
        <item>external-display</item>
        <item>focus-based</item>
        <item>default_display</item>
        <item>any_external_display</item>
        <item>status_bar_latest_touch</item>
    </string-array>

</resources>
+51 −30
Original line number Diff line number Diff line
@@ -16,17 +16,20 @@

package com.android.systemui.shade.data.repository

import android.provider.Settings.Global.DEVELOPMENT_SHADE_DISPLAY_AWARENESS
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.coroutines.collectValues
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.shade.display.ShadeDisplayPolicy
import com.android.systemui.shade.display.SpecificDisplayIdPolicy
import com.android.systemui.shade.display.AnyExternalShadeDisplayPolicy
import com.android.systemui.shade.display.DefaultDisplayShadePolicy
import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
import com.android.systemui.testKosmos
import com.android.systemui.util.settings.fakeGlobalSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -36,54 +39,72 @@ import org.junit.runner.RunWith
class ShadeDisplaysRepositoryTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val defaultPolicy = SpecificDisplayIdPolicy(0)

    private val shadeDisplaysRepository =
        ShadeDisplaysRepositoryImpl(defaultPolicy, testScope.backgroundScope)
    private val globalSettings = kosmos.fakeGlobalSettings
    private val displayRepository = kosmos.displayRepository
    private val defaultPolicy = DefaultDisplayShadePolicy()
    private val policies = kosmos.shadeDisplayPolicies

    private val underTest =
        ShadeDisplaysRepositoryImpl(
            globalSettings,
            defaultPolicy,
            testScope.backgroundScope,
            policies,
        )

    @Test
    fun policy_changing_propagatedFromTheLatestPolicy() =
        testScope.runTest {
            val displayIds by collectValues(shadeDisplaysRepository.displayId)
            val policy1 = MutablePolicy()
            val policy2 = MutablePolicy()
            val displayIds by collectValues(underTest.displayId)

            assertThat(displayIds).containsExactly(0)

            shadeDisplaysRepository.policy.value = policy1
            globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, "any_external_display")

            policy1.sendDisplayId(1)
            displayRepository.addDisplay(displayId = 1)

            assertThat(displayIds).containsExactly(0, 1)

            policy1.sendDisplayId(2)
            displayRepository.addDisplay(displayId = 2)

            assertThat(displayIds).containsExactly(0, 1, 2)
            assertThat(displayIds).containsExactly(0, 1)

            shadeDisplaysRepository.policy.value = policy2
            displayRepository.removeDisplay(displayId = 1)

            assertThat(displayIds).containsExactly(0, 1, 2, 0)
            assertThat(displayIds).containsExactly(0, 1, 2)

            policy1.sendDisplayId(4)
            globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, "default_display")

            // Changes to the first policy don't affect the output now
            assertThat(displayIds).containsExactly(0, 1, 2, 0)
        }

    @Test
    fun policy_updatesBasedOnSettingValue_defaultDisplay() =
        testScope.runTest {
            val policy by collectLastValue(underTest.policy)

            policy2.sendDisplayId(5)
            globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, "default_display")

            assertThat(displayIds).containsExactly(0, 1, 2, 0, 5)
            assertThat(policy).isInstanceOf(DefaultDisplayShadePolicy::class.java)
        }

    private class MutablePolicy : ShadeDisplayPolicy {
        fun sendDisplayId(id: Int) {
            _displayId.value = id
    @Test
    fun policy_updatesBasedOnSettingValue_anyExternal() =
        testScope.runTest {
            val policy by collectLastValue(underTest.policy)

            globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, "any_external_display")

            assertThat(policy).isInstanceOf(AnyExternalShadeDisplayPolicy::class.java)
        }

        private val _displayId = MutableStateFlow(0)
        override val name: String
            get() = "mutable_policy"
    @Test
    fun policy_updatesBasedOnSettingValue_focusBased() =
        testScope.runTest {
            val policy by collectLastValue(underTest.policy)

            globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, "status_bar_latest_touch")

        override val displayId: StateFlow<Int>
            get() = _displayId
            assertThat(policy).isInstanceOf(StatusBarTouchShadeDisplayPolicy::class.java)
        }
}
+14 −24
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import com.android.systemui.shade.ShadePrimaryDisplayCommand
import com.android.systemui.shade.display.ShadeDisplayPolicy
import com.android.systemui.statusbar.commandline.commandRegistry
import com.android.systemui.testKosmos
import com.android.systemui.util.settings.fakeGlobalSettings
import com.google.common.truth.StringSubject
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
@@ -44,18 +45,17 @@ import org.junit.runner.RunWith
class ShadePrimaryDisplayCommandTest : SysuiTestCase() {
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val testScope = kosmos.testScope
    private val globalSettings = kosmos.fakeGlobalSettings
    private val commandRegistry = kosmos.commandRegistry
    private val displayRepository = kosmos.displayRepository
    private val defaultPolicy = kosmos.defaultShadeDisplayPolicy
    private val policy1 = makePolicy("policy_1")
    private val shadeDisplaysRepository = kosmos.shadeDisplaysRepository
    private val policies = kosmos.shadeDisplayPolicies
    private val pw = PrintWriter(StringWriter())

    private val policies =
        setOf(defaultPolicy, policy1, makePolicy("policy_2"), makePolicy("policy_3"))

    private val underTest =
        ShadePrimaryDisplayCommand(
            globalSettings,
            commandRegistry,
            displayRepository,
            shadeDisplaysRepository,
@@ -68,21 +68,6 @@ class ShadePrimaryDisplayCommandTest : SysuiTestCase() {
        underTest.start()
    }

    @Test
    fun commandDisplayOverride_updatesDisplayId() =
        testScope.runTest {
            val displayId by collectLastValue(shadeDisplaysRepository.displayId)
            assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)

            val newDisplayId = 2
            commandRegistry.onShellCommand(
                pw,
                arrayOf("shade_display_override", newDisplayId.toString()),
            )

            assertThat(displayId).isEqualTo(newDisplayId)
        }

    @Test
    fun commandShadeDisplayOverride_resetsDisplayId() =
        testScope.runTest {
@@ -90,9 +75,10 @@ class ShadePrimaryDisplayCommandTest : SysuiTestCase() {
            assertThat(displayId).isEqualTo(Display.DEFAULT_DISPLAY)

            val newDisplayId = 2
            displayRepository.addDisplay(displayId = newDisplayId)
            commandRegistry.onShellCommand(
                pw,
                arrayOf("shade_display_override", newDisplayId.toString()),
                arrayOf("shade_display_override", "any_external_display"),
            )
            assertThat(displayId).isEqualTo(newDisplayId)

@@ -108,7 +94,10 @@ class ShadePrimaryDisplayCommandTest : SysuiTestCase() {
            val newDisplayId = 2
            displayRepository.addDisplay(displayId = newDisplayId)

            commandRegistry.onShellCommand(pw, arrayOf("shade_display_override", "any_external"))
            commandRegistry.onShellCommand(
                pw,
                arrayOf("shade_display_override", "any_external_display"),
            )

            assertThat(displayId).isEqualTo(newDisplayId)
        }
@@ -127,13 +116,14 @@ class ShadePrimaryDisplayCommandTest : SysuiTestCase() {
        }

    @Test
    fun policies_setsSpecificPolicy() =
    fun policies_setsNewPolicy() =
        testScope.runTest {
            val policy by collectLastValue(shadeDisplaysRepository.policy)
            val newPolicy = policies.last().name

            commandRegistry.onShellCommand(pw, arrayOf("shade_display_override", policy1.name))
            commandRegistry.onShellCommand(pw, arrayOf("shade_display_override", newPolicy))

            assertThat(policy!!.name).isEqualTo(policy1.name)
            assertThat(policy!!.name).isEqualTo(newPolicy)
        }

    private fun makePolicy(policyName: String): ShadeDisplayPolicy {
+10 −42
Original line number Diff line number Diff line
@@ -16,23 +16,23 @@

package com.android.systemui.shade

import android.view.Display
import android.provider.Settings.Global.DEVELOPMENT_SHADE_DISPLAY_AWARENESS
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.display.data.repository.DisplayRepository
import com.android.systemui.shade.data.repository.MutableShadeDisplaysRepository
import com.android.systemui.shade.display.ShadeDisplayPolicy
import com.android.systemui.shade.display.SpecificDisplayIdPolicy
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.util.settings.GlobalSettings
import java.io.PrintWriter
import javax.inject.Inject
import kotlin.text.toIntOrNull

@SysUISingleton
class ShadePrimaryDisplayCommand
@Inject
constructor(
    private val globalSettings: GlobalSettings,
    private val commandRegistry: CommandRegistry,
    private val displaysRepository: DisplayRepository,
    private val positionRepository: MutableShadeDisplaysRepository,
@@ -45,7 +45,7 @@ constructor(
    }

    override fun help(pw: PrintWriter) {
        pw.println("shade_display_override (<displayId>|<policyName>) ")
        pw.println("shade_display_override <policyName> ")
        pw.println("Set the display which is holding the shade, or the policy that defines it.")
        pw.println()
        pw.println("shade_display_override policies")
@@ -56,9 +56,6 @@ constructor(
        pw.println()
        pw.println("shade_display_override (list|status) ")
        pw.println("Lists available displays and which has the shade")
        pw.println()
        pw.println("shade_display_override any_external")
        pw.println("Moves the shade to the first not-default display available")
    }

    override fun execute(pw: PrintWriter, args: List<String>) {
@@ -74,28 +71,24 @@ constructor(
        fun execute() {
            when (val command = args.getOrNull(0)?.lowercase()) {
                "reset" -> reset()
                "policies" -> printPolicies()
                "list",
                "status" -> printStatus()
                "policies" -> printPolicies()
                "any_external" -> anyExternal()
                null -> help(pw)
                else -> parsePolicy(command)
            }
        }

        private fun parsePolicy(policyIdentifier: String) {
            val displayId = policyIdentifier.toIntOrNull()
            when {
                displayId != null -> changeDisplay(displayId = displayId)
                policies.any { it.name == policyIdentifier } -> {
                    positionRepository.policy.value = policies.first { it.name == policyIdentifier }
                }
                else -> help(pw)
            if (policies.any { it.name == policyIdentifier }) {
                globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, policyIdentifier)
            } else {
                help(pw)
            }
        }

        private fun reset() {
            positionRepository.policy.value = defaultPolicy
            globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, defaultPolicy.name)
            pw.println("Reset shade display policy to default policy: ${defaultPolicy.name}")
        }

@@ -117,30 +110,5 @@ constructor(
                pw.println(if (currentPolicyName == it.name) " (Current policy)" else "")
            }
        }

        private fun anyExternal() {
            val anyExternalDisplay =
                displaysRepository.displays.value.firstOrNull {
                    it.displayId != Display.DEFAULT_DISPLAY
                }
            if (anyExternalDisplay == null) {
                pw.println("No external displays available.")
                return
            }
            setDisplay(anyExternalDisplay.displayId)
        }

        private fun changeDisplay(displayId: Int) {
            if (displayId < 0) {
                pw.println("Error: display id should be positive integer")
            }

            setDisplay(displayId)
        }

        private fun setDisplay(id: Int) {
            positionRepository.policy.value = SpecificDisplayIdPolicy(id)
            pw.println("New shade primary display id is $id")
        }
    }
}
+28 −5
Original line number Diff line number Diff line
@@ -16,17 +16,22 @@

package com.android.systemui.shade.data.repository

import android.provider.Settings.Global.DEVELOPMENT_SHADE_DISPLAY_AWARENESS
import android.view.Display
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.shade.display.ShadeDisplayPolicy
import com.android.systemui.util.settings.GlobalSettings
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn

/** Source of truth for the display currently holding the shade. */
@@ -38,7 +43,7 @@ interface ShadeDisplaysRepository {
/** Allows to change the policy that determines in which display the Shade window is visible. */
interface MutableShadeDisplaysRepository : ShadeDisplaysRepository {
    /** Updates the policy to select where the shade is visible. */
    val policy: MutableStateFlow<ShadeDisplayPolicy>
    val policy: StateFlow<ShadeDisplayPolicy>
}

/** Keeps the policy and propagates the display id for the shade from it. */
@@ -46,9 +51,27 @@ interface MutableShadeDisplaysRepository : ShadeDisplaysRepository {
@OptIn(ExperimentalCoroutinesApi::class)
class ShadeDisplaysRepositoryImpl
@Inject
constructor(defaultPolicy: ShadeDisplayPolicy, @Background bgScope: CoroutineScope) :
    MutableShadeDisplaysRepository {
    override val policy = MutableStateFlow<ShadeDisplayPolicy>(defaultPolicy)
constructor(
    globalSettings: GlobalSettings,
    defaultPolicy: ShadeDisplayPolicy,
    @Background bgScope: CoroutineScope,
    policies: Set<@JvmSuppressWildcards ShadeDisplayPolicy>,
) : MutableShadeDisplaysRepository {

    override val policy: StateFlow<ShadeDisplayPolicy> =
        globalSettings
            .observerFlow(DEVELOPMENT_SHADE_DISPLAY_AWARENESS)
            .onStart { emit(Unit) }
            .map {
                val current = globalSettings.getString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS)
                for (policy in policies) {
                    if (policy.name == current) return@map policy
                }
                globalSettings.putString(DEVELOPMENT_SHADE_DISPLAY_AWARENESS, defaultPolicy.name)
                return@map defaultPolicy
            }
            .distinctUntilChanged()
            .stateIn(bgScope, SharingStarted.WhileSubscribed(), defaultPolicy)

    override val displayId: StateFlow<Int> =
        policy
Loading