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

Commit c19dece6 authored by Marcello Galhardo's avatar Marcello Galhardo Committed by Automerger Merge Worker
Browse files

Merge "Hide Notes Quick Affordance when Direct Boot user is locked" into udc-dev am: 5c039f3d

parents 62a61a3a 5c039f3d
Loading
Loading
Loading
Loading
+57 −26
Original line number Diff line number Diff line
@@ -18,6 +18,11 @@ package com.android.systemui.notetask.quickaffordance

import android.content.Context
import android.hardware.input.InputSettings
import android.os.Build
import android.os.UserManager
import android.util.Log
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
@@ -39,6 +44,7 @@ import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach

class NoteTaskQuickAffordanceConfig
@Inject
@@ -46,6 +52,8 @@ constructor(
    context: Context,
    private val controller: NoteTaskController,
    private val stylusManager: StylusManager,
    private val keyguardMonitor: KeyguardUpdateMonitor,
    private val userManager: UserManager,
    private val lazyRepository: Lazy<KeyguardQuickAffordanceRepository>,
    @NoteTaskEnabledKey private val isEnabled: Boolean,
) : KeyguardQuickAffordanceConfig {
@@ -61,10 +69,19 @@ constructor(
    // Due to a dependency cycle with KeyguardQuickAffordanceRepository, we need to lazily access
    // the repository when lockScreenState is accessed for the first time.
    override val lockScreenState by lazy {
        val stylusEverUsedFlow = createStylusEverUsedFlow(context, stylusManager)
        val configSelectedFlow = createConfigSelectedFlow(lazyRepository.get(), key)
        combine(configSelectedFlow, stylusEverUsedFlow) { isSelected, isStylusEverUsed ->
            if (isEnabled && (isSelected || isStylusEverUsed)) {
        val repository = lazyRepository.get()
        val configSelectedFlow = repository.createConfigSelectedFlow(key)
        val stylusEverUsedFlow = stylusManager.createStylusEverUsedFlow(context)
        val userUnlockedFlow = userManager.createUserUnlockedFlow(keyguardMonitor)
        combine(userUnlockedFlow, stylusEverUsedFlow, configSelectedFlow) {
                isUserUnlocked,
                isStylusEverUsed,
                isConfigSelected ->
                logDebug { "lockScreenState:isUserUnlocked=$isUserUnlocked" }
                logDebug { "lockScreenState:isStylusEverUsed=$isStylusEverUsed" }
                logDebug { "lockScreenState:isConfigSelected=$isConfigSelected" }

                if (isEnabled && isUserUnlocked && (isConfigSelected || isStylusEverUsed)) {
                    val contentDescription = ContentDescription.Resource(pickerNameResourceId)
                    val icon = Icon.Resource(pickerIconResourceId, contentDescription)
                    LockScreenState.Visible(icon)
@@ -72,6 +89,7 @@ constructor(
                    LockScreenState.Hidden
                }
            }
            .onEach { state -> logDebug { "lockScreenState=$state" } }
    }

    override suspend fun getPickerScreenState() =
@@ -82,15 +100,24 @@ constructor(
        }

    override fun onTriggered(expandable: Expandable?): OnTriggeredResult {
        controller.showNoteTask(
            entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE,
        )
        controller.showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
        return OnTriggeredResult.Handled
    }
}

private fun createStylusEverUsedFlow(context: Context, stylusManager: StylusManager) =
    callbackFlow {
private fun UserManager.createUserUnlockedFlow(monitor: KeyguardUpdateMonitor) = callbackFlow {
    trySendBlocking(isUserUnlocked)
    val callback =
        object : KeyguardUpdateMonitorCallback() {
            override fun onUserUnlocked() {
                trySendBlocking(isUserUnlocked)
            }
        }
    monitor.registerCallback(callback)
    awaitClose { monitor.removeCallback(callback) }
}

private fun StylusManager.createStylusEverUsedFlow(context: Context) = callbackFlow {
    trySendBlocking(InputSettings.isStylusEverUsed(context))
    val callback =
        object : StylusManager.StylusCallback {
@@ -98,11 +125,15 @@ private fun createStylusEverUsedFlow(context: Context, stylusManager: StylusMana
                trySendBlocking(InputSettings.isStylusEverUsed(context))
            }
        }
        stylusManager.registerCallback(callback)
        awaitClose { stylusManager.unregisterCallback(callback) }
    registerCallback(callback)
    awaitClose { unregisterCallback(callback) }
}

private fun createConfigSelectedFlow(repository: KeyguardQuickAffordanceRepository, key: String) =
    repository.selections.map { selected ->
private fun KeyguardQuickAffordanceRepository.createConfigSelectedFlow(key: String) =
    selections.map { selected ->
        selected.values.flatten().any { selectedConfig -> selectedConfig.key == key }
    }

private inline fun Any.logDebug(message: () -> String) {
    if (Build.IS_DEBUGGABLE) Log.d(this::class.java.simpleName, message())
}
+95 −22
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
package com.android.systemui.notetask.quickaffordance

import android.hardware.input.InputSettings
import android.os.UserManager
import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import com.android.dx.mockito.inline.extended.ExtendedMockito
@@ -33,6 +34,7 @@ import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepo
import com.android.systemui.notetask.NoteTaskController
import com.android.systemui.notetask.NoteTaskEntryPoint
import com.android.systemui.stylus.StylusManager
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -55,6 +57,7 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {
    @Mock lateinit var controller: NoteTaskController
    @Mock lateinit var stylusManager: StylusManager
    @Mock lateinit var repository: KeyguardQuickAffordanceRepository
    @Mock lateinit var userManager: UserManager

    private lateinit var mockitoSession: MockitoSession

@@ -66,12 +69,6 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {
                .mockStatic(InputSettings::class.java)
                .strictness(Strictness.LENIENT)
                .startMocking()

        whenever(InputSettings.isStylusEverUsed(mContext)).then { true }
        whenever(repository.selections).then {
            val map = mapOf("" to listOf(createUnderTest()))
            MutableStateFlow(map)
        }
    }

    @After
@@ -84,6 +81,8 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {
            context = context,
            controller = controller,
            stylusManager = stylusManager,
            userManager = userManager,
            keyguardMonitor = mock(),
            lazyRepository = { repository },
            isEnabled = isEnabled,
        )
@@ -98,47 +97,101 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {
                )
        )

    // region lockScreenState
    @Test
    fun lockScreenState_stylusUsed_noCustomShortcutSelected_shouldEmitVisible() = runTest {
        val underTest = createUnderTest()
    fun lockScreenState_stylusUsed_userUnlocked_isSelected_shouldEmitVisible() = runTest {
        TestConfig()
            .setStylusEverUsed(true)
            .setUserUnlocked(true)
            .setConfigSelections(mock<NoteTaskQuickAffordanceConfig>())

        val underTest = createUnderTest()
        val actual by collectLastValue(underTest.lockScreenState)

        assertThat(actual).isEqualTo(createLockScreenStateVisible())
    }

    @Test
    fun lockScreenState_noStylusEverUsed_noCustomShortcutSelected_shouldEmitVisible() = runTest {
        whenever(InputSettings.isStylusEverUsed(mContext)).then { false }
    fun lockScreenState_stylusUnused_userUnlocked_isSelected_shouldEmitHidden() = runTest {
        TestConfig()
            .setStylusEverUsed(false)
            .setUserUnlocked(true)
            .setConfigSelections(mock<NoteTaskQuickAffordanceConfig>())

        val underTest = createUnderTest()
        val actual by collectLastValue(underTest.lockScreenState)

        assertThat(actual).isEqualTo(LockScreenState.Hidden)
    }

    @Test
    fun lockScreenState_stylusUsed_userLocked_isSelected_shouldEmitHidden() = runTest {
        TestConfig()
            .setStylusEverUsed(true)
            .setUserUnlocked(false)
            .setConfigSelections(mock<NoteTaskQuickAffordanceConfig>())

        val underTest = createUnderTest()
        val actual by collectLastValue(underTest.lockScreenState)

        assertThat(actual).isEqualTo(LockScreenState.Hidden)
    }

    @Test
    fun lockScreenState_stylusUsed_userUnlocked_noSelected_shouldEmitVisible() = runTest {
        TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections()

        val underTest = createUnderTest()
        val actual by collectLastValue(underTest.lockScreenState)

        assertThat(actual).isEqualTo(createLockScreenStateVisible())
    }

    @Test
    fun lockScreenState_stylusUsed_customShortcutSelected_shouldEmitVisible() = runTest {
        whenever(repository.selections).then {
            val map = mapOf<String, List<KeyguardQuickAffordanceConfig>>()
            MutableStateFlow(map)
    fun lockScreenState_stylusUnused_userUnlocked_noSelected_shouldEmitHidden() = runTest {
        TestConfig().setStylusEverUsed(false).setUserUnlocked(true).setConfigSelections()

        val underTest = createUnderTest()
        val actual by collectLastValue(underTest.lockScreenState)

        assertThat(actual).isEqualTo(LockScreenState.Hidden)
    }

    @Test
    fun lockScreenState_stylusUsed_userLocked_noSelected_shouldEmitHidden() = runTest {
        TestConfig().setStylusEverUsed(true).setUserUnlocked(false).setConfigSelections()

        val underTest = createUnderTest()
        val actual by collectLastValue(underTest.lockScreenState)

        assertThat(actual).isEqualTo(LockScreenState.Hidden)
    }

    @Test
    fun lockScreenState_stylusUsed_userUnlocked_customSelections_shouldEmitVisible() = runTest {
        TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections(mock())

        val underTest = createUnderTest()
        val actual by collectLastValue(underTest.lockScreenState)

        assertThat(actual).isEqualTo(createLockScreenStateVisible())
    }

    @Test
    fun lockScreenState_noIsStylusEverUsed_noCustomShortcutSelected_shouldEmitHidden() = runTest {
        whenever(InputSettings.isStylusEverUsed(mContext)).then { false }
        whenever(repository.selections).then {
            val map = mapOf<String, List<KeyguardQuickAffordanceConfig>>()
            MutableStateFlow(map)
        }
    fun lockScreenState_stylusUnused_userUnlocked_customSelections_shouldEmitHidden() = runTest {
        TestConfig().setStylusEverUsed(false).setUserUnlocked(true).setConfigSelections(mock())

        val underTest = createUnderTest()
        val actual by collectLastValue(underTest.lockScreenState)

        assertThat(actual).isEqualTo(LockScreenState.Hidden)
    }

    @Test
    fun lockScreenState_stylusUsed_userLocked_customSelections_shouldEmitHidden() = runTest {
        TestConfig().setStylusEverUsed(true).setUserUnlocked(false).setConfigSelections(mock())

        val underTest = createUnderTest()
        val actual by collectLastValue(underTest.lockScreenState)

        assertThat(actual).isEqualTo(LockScreenState.Hidden)
@@ -146,12 +199,14 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {

    @Test
    fun lockScreenState_isNotEnabled_shouldEmitHidden() = runTest {
        val underTest = createUnderTest(isEnabled = false)
        TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections()

        val underTest = createUnderTest(isEnabled = false)
        val actual by collectLastValue(underTest.lockScreenState)

        assertThat(actual).isEqualTo(LockScreenState.Hidden)
    }
    // endregion

    @Test
    fun onTriggered_shouldLaunchNoteTask() {
@@ -161,4 +216,22 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {

        verify(controller).showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE)
    }

    private inner class TestConfig {

        fun setStylusEverUsed(value: Boolean) = also {
            whenever(InputSettings.isStylusEverUsed(mContext)).thenReturn(value)
        }

        fun setUserUnlocked(value: Boolean) = also {
            whenever(userManager.isUserUnlocked).thenReturn(value)
        }

        fun setConfigSelections(vararg values: KeyguardQuickAffordanceConfig) = also {
            val slotKey = "bottom-right"
            val configSnapshots = values.toList()
            val map = mapOf(slotKey to configSnapshots)
            whenever(repository.selections).thenReturn(MutableStateFlow(map))
        }
    }
}