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

Commit 5c7f65fc authored by Michał Brzeziński's avatar Michał Brzeziński Committed by Android (Google) Code Review
Browse files

Merge "Adding connectedKeyboards flow to KeyboardRepository" into main

parents d35c124c 61bcfb4c
Loading
Loading
Loading
Loading
+66 −6
Original line number Diff line number Diff line
@@ -145,7 +145,7 @@ class KeyboardRepositoryTest : SysuiTestCase() {

            fakeInputManager.addPhysicalKeyboard(
                PHYSICAL_NOT_FULL_KEYBOARD_ID,
                isFullKeyboard = false
                isFullKeyboard = false,
            )
            assertThat(isKeyboardConnected).isFalse()

@@ -223,7 +223,7 @@ class KeyboardRepositoryTest : SysuiTestCase() {
            backlightListenerCaptor.value.onBacklightChanged(
                current = 1,
                max = 5,
                triggeredByKeyPress = false
                triggeredByKeyPress = false,
            )
            assertThat(backlight).isNull()
        }
@@ -239,7 +239,7 @@ class KeyboardRepositoryTest : SysuiTestCase() {
            backlightListenerCaptor.value.onBacklightChanged(
                current = 1,
                max = 5,
                triggeredByKeyPress = true
                triggeredByKeyPress = true,
            )
            assertThat(backlight).isNotNull()
        }
@@ -318,15 +318,75 @@ class KeyboardRepositoryTest : SysuiTestCase() {
        }
    }

    @Test
    fun connectedKeyboards_emitsAllKeyboards() {
        testScope.runTest {
            val firstKeyboard = Keyboard(vendorId = 1, productId = 1)
            val secondKeyboard = Keyboard(vendorId = 2, productId = 2)
            captureDeviceListener()
            val keyboards by collectLastValueImmediately(underTest.connectedKeyboards)

            fakeInputManager.addPhysicalKeyboard(
                PHYSICAL_FULL_KEYBOARD_ID,
                vendorId = firstKeyboard.vendorId,
                productId = firstKeyboard.productId,
            )
            assertThat(keyboards)
                .containsExactly(Keyboard(firstKeyboard.vendorId, firstKeyboard.productId))

            fakeInputManager.addPhysicalKeyboard(
                ANOTHER_PHYSICAL_FULL_KEYBOARD_ID,
                vendorId = secondKeyboard.vendorId,
                productId = secondKeyboard.productId,
            )
            assertThat(keyboards)
                .containsExactly(
                    Keyboard(firstKeyboard.vendorId, firstKeyboard.productId),
                    Keyboard(secondKeyboard.vendorId, secondKeyboard.productId),
                )
        }
    }

    @Test
    fun connectedKeyboards_emitsOnlyFullPhysicalKeyboards() {
        testScope.runTest {
            captureDeviceListener()
            val keyboards by collectLastValueImmediately(underTest.connectedKeyboards)

            fakeInputManager.addPhysicalKeyboard(PHYSICAL_FULL_KEYBOARD_ID)
            fakeInputManager.addDevice(VIRTUAL_FULL_KEYBOARD_ID, SOURCE_KEYBOARD)
            fakeInputManager.addPhysicalKeyboard(
                PHYSICAL_NOT_FULL_KEYBOARD_ID,
                isFullKeyboard = false,
            )

            assertThat(keyboards).hasSize(1)
        }
    }

    @Test
    fun connectedKeyboards_emitsOnlyConnectedKeyboards() {
        testScope.runTest {
            captureDeviceListener()
            val keyboards by collectLastValueImmediately(underTest.connectedKeyboards)

            fakeInputManager.addPhysicalKeyboard(PHYSICAL_FULL_KEYBOARD_ID)
            fakeInputManager.addPhysicalKeyboard(ANOTHER_PHYSICAL_FULL_KEYBOARD_ID)
            fakeInputManager.removeDevice(ANOTHER_PHYSICAL_FULL_KEYBOARD_ID)

            assertThat(keyboards).hasSize(1)
        }
    }

    private fun KeyboardBacklightListener.onBacklightChanged(
        current: Int,
        max: Int,
        triggeredByKeyPress: Boolean = true
        triggeredByKeyPress: Boolean = true,
    ) {
        onKeyboardBacklightChanged(
            /* deviceId= */ 0,
            TestBacklightState(current, max),
            triggeredByKeyPress
            triggeredByKeyPress,
        )
    }

@@ -343,7 +403,7 @@ class KeyboardRepositoryTest : SysuiTestCase() {

    private class TestBacklightState(
        private val brightnessLevel: Int,
        private val maxBrightnessLevel: Int
        private val maxBrightnessLevel: Int,
    ) : KeyboardBacklightState() {
        override fun getBrightnessLevel() = brightnessLevel

+7 −11
Original line number Diff line number Diff line
@@ -38,7 +38,7 @@ class InputDeviceRepository
constructor(
    @Background private val backgroundHandler: Handler,
    @Background private val backgroundScope: CoroutineScope,
    private val inputManager: InputManager
    private val inputManager: InputManager,
) {

    sealed interface DeviceChange
@@ -50,11 +50,11 @@ constructor(
    data object FreshStart : DeviceChange

    /**
     * Emits collection of all currently connected keyboards and what was the last [DeviceChange].
     * It emits collection so that every new subscriber to this SharedFlow can get latest state of
     * all keyboards. Otherwise we might get into situation where subscriber timing on
     * initialization matter and later subscriber will only get latest device and will miss all
     * previous devices.
     * Emits collection of all currently connected input devices and what was the last
     * [DeviceChange]. It emits collection so that every new subscriber to this SharedFlow can get
     * latest state of all input devices. Otherwise we might get into situation where subscriber
     * timing on initialization matter and later subscriber will only get latest device and will
     * miss all previous devices.
     */
    // TODO(b/351984587): Replace with StateFlow
    @SuppressLint("SharedFlowCreation")
@@ -79,11 +79,7 @@ constructor(
                inputManager.registerInputDeviceListener(listener, backgroundHandler)
                awaitClose { inputManager.unregisterInputDeviceListener(listener) }
            }
            .shareIn(
                scope = backgroundScope,
                started = SharingStarted.Lazily,
                replay = 1,
            )
            .shareIn(scope = backgroundScope, started = SharingStarted.Lazily, replay = 1)

    private fun <T> SendChannel<T>.sendWithLogging(element: T) {
        trySendWithFailureLogging(element, TAG)
+2 −0
Original line number Diff line number Diff line
@@ -50,6 +50,8 @@ class CommandLineKeyboardRepository @Inject constructor(commandRegistry: Command
    private val _newlyConnectedKeyboard: MutableStateFlow<Keyboard?> = MutableStateFlow(null)
    override val newlyConnectedKeyboard: Flow<Keyboard> = _newlyConnectedKeyboard.filterNotNull()

    override val connectedKeyboards: Flow<Set<Keyboard>> = MutableStateFlow(emptySet())

    init {
        Log.i(TAG, "initializing shell command $COMMAND")
        commandRegistry.registerCommand(COMMAND) { KeyboardCommand() }
+11 −1
Original line number Diff line number Diff line
@@ -61,6 +61,9 @@ interface KeyboardRepository {
     */
    val newlyConnectedKeyboard: Flow<Keyboard>

    /** Emits set of currently connected keyboards */
    val connectedKeyboards: Flow<Set<Keyboard>>

    /**
     * Emits [BacklightModel] whenever user changes backlight level from keyboard press. Can only
     * happen when physical keyboard is connected
@@ -74,7 +77,7 @@ class KeyboardRepositoryImpl
constructor(
    @Background private val backgroundDispatcher: CoroutineDispatcher,
    private val inputManager: InputManager,
    inputDeviceRepository: InputDeviceRepository
    inputDeviceRepository: InputDeviceRepository,
) : KeyboardRepository {

    @FlowPreview
@@ -93,6 +96,13 @@ constructor(
            .mapNotNull { deviceIdToKeyboard(it) }
            .flowOn(backgroundDispatcher)

    override val connectedKeyboards: Flow<Set<Keyboard>> =
        inputDeviceRepository.deviceChange
            .map { (deviceIds, _) -> deviceIds }
            .map { deviceIds -> deviceIds.filter { isPhysicalFullKeyboard(it) } }
            .distinctUntilChanged()
            .map { deviceIds -> deviceIds.mapNotNull { deviceIdToKeyboard(it) }.toSet() }

    override val isAnyKeyboardConnected: Flow<Boolean> =
        inputDeviceRepository.deviceChange
            .map { (ids, _) -> ids.any { id -> isPhysicalFullKeyboard(id) } }
+18 −3
Original line number Diff line number Diff line
@@ -19,8 +19,10 @@ package com.android.systemui.keyboard.data.repository

import com.android.systemui.keyboard.data.model.Keyboard
import com.android.systemui.keyboard.shared.model.BacklightModel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.filterNotNull

class FakeKeyboardRepository : KeyboardRepository {
@@ -32,8 +34,14 @@ class FakeKeyboardRepository : KeyboardRepository {
    // filtering to make sure backlight doesn't have default initial value
    override val backlight: Flow<BacklightModel> = _backlightState.filterNotNull()

    private val _newlyConnectedKeyboard: MutableStateFlow<Keyboard?> = MutableStateFlow(null)
    override val newlyConnectedKeyboard: Flow<Keyboard> = _newlyConnectedKeyboard.filterNotNull()
    // implemented as channel because original implementation is modeling events: it doesn't hold
    // state so it won't always emit once connected. And it's bad if some tests depend on that
    // incorrect behaviour.
    private val _newlyConnectedKeyboard: Channel<Keyboard> = Channel()
    override val newlyConnectedKeyboard: Flow<Keyboard> = _newlyConnectedKeyboard.consumeAsFlow()

    private val _connectedKeyboards: MutableStateFlow<Set<Keyboard>> = MutableStateFlow(setOf())
    override val connectedKeyboards: Flow<Set<Keyboard>> = _connectedKeyboards

    fun setBacklight(state: BacklightModel) {
        _backlightState.value = state
@@ -43,7 +51,14 @@ class FakeKeyboardRepository : KeyboardRepository {
        _isAnyKeyboardConnected.value = connected
    }

    fun setConnectedKeyboards(keyboards: Set<Keyboard>) {
        _connectedKeyboards.value = keyboards
        _isAnyKeyboardConnected.value = keyboards.isNotEmpty()
    }

    fun setNewlyConnectedKeyboard(keyboard: Keyboard) {
        _newlyConnectedKeyboard.value = keyboard
        _newlyConnectedKeyboard.trySend(keyboard)
        _connectedKeyboards.value += keyboard
        _isAnyKeyboardConnected.value = true
    }
}