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

Commit 14c94d32 authored by Michal Brzezinski's avatar Michal Brzezinski
Browse files

Sticky keys UI: implementing repository and view model

Adding StickyKeysRepository which registers listener in InputManager and passes data to flow listeners.
StickyKeyIndicatorViewModel is the only layer above (which is likely to change soon when more input data is needed), domain layer seemed a bit of an overkill for now.

Classes are not used from anywhere yet so code is not active or flagged.

Test: StickyKeysIndicatorViewModelTest
Bug: 313855932
Flag: None

Change-Id: I5493d866947d8553f8b40f7fb26324eafaf312a6
parent 8b737250
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -354,6 +354,8 @@

    <uses-permission android:name="android.permission.MONITOR_KEYBOARD_BACKLIGHT" />

    <uses-permission android:name="android.permission.MONITOR_STICKY_MODIFIER_STATE" />

    <!-- Listen to (dis-)connection of external displays and enable / disable them. -->
    <uses-permission android:name="android.permission.MANAGE_DISPLAYS" />

+7 −0
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package com.android.systemui.keyboard

import com.android.systemui.keyboard.data.repository.KeyboardRepository
import com.android.systemui.keyboard.data.repository.KeyboardRepositoryImpl
import com.android.systemui.keyboard.stickykeys.data.repository.StickyKeysRepository
import com.android.systemui.keyboard.stickykeys.data.repository.StickyKeysRepositoryImpl
import dagger.Binds
import dagger.Module

@@ -27,4 +29,9 @@ abstract class KeyboardModule {

    @Binds
    abstract fun bindKeyboardRepository(repository: KeyboardRepositoryImpl): KeyboardRepository

    @Binds
    abstract fun bindStickyKeysRepository(
        repository: StickyKeysRepositoryImpl
    ): StickyKeysRepository
}
+37 −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.keyboard.stickykeys

import com.android.systemui.keyboard.stickykeys.shared.model.Locked
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.KeyboardLog
import javax.inject.Inject

private const val TAG = "stickyKeys"

class StickyKeysLogger @Inject constructor(@KeyboardLog private val buffer: LogBuffer) {
    fun logNewStickyKeysReceived(linkedHashMap: Map<ModifierKey, Locked>) {
        buffer.log(
            TAG,
            LogLevel.VERBOSE,
            { str1 = linkedHashMap.toString() },
            { "new sticky keys state received: $str1" }
        )
    }
}
 No newline at end of file
+92 −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.keyboard.stickykeys.data.repository

import android.hardware.input.InputManager
import android.hardware.input.InputManager.StickyModifierStateListener
import android.hardware.input.StickyModifierState
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyboard.stickykeys.StickyKeysLogger
import com.android.systemui.keyboard.stickykeys.shared.model.Locked
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.ALT
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.ALT_GR
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.CTRL
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.META
import com.android.systemui.keyboard.stickykeys.shared.model.ModifierKey.SHIFT
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import javax.inject.Inject

interface StickyKeysRepository {
    val stickyKeys: Flow<LinkedHashMap<ModifierKey, Locked>>
    val settingEnabled: Flow<Boolean>
}

class StickyKeysRepositoryImpl
@Inject
constructor(
    private val inputManager: InputManager,
    @Background private val backgroundDispatcher: CoroutineDispatcher,
    private val stickyKeysLogger: StickyKeysLogger,
) : StickyKeysRepository {

    override val stickyKeys: Flow<LinkedHashMap<ModifierKey, Locked>> =
        conflatedCallbackFlow {
                val listener = StickyModifierStateListener { stickyModifierState ->
                    trySendWithFailureLogging(stickyModifierState, TAG)
                }
                // after registering, InputManager calls listener with the current value
                inputManager.registerStickyModifierStateListener(Runnable::run, listener)
                awaitClose { inputManager.unregisterStickyModifierStateListener(listener) }
            }
            .map { toStickyKeysMap(it) }
            .onEach { stickyKeysLogger.logNewStickyKeysReceived(it) }
            .flowOn(backgroundDispatcher)

    // TODO(b/319837892): Implement reading actual setting
    override val settingEnabled: StateFlow<Boolean> = MutableStateFlow(true)

    private fun toStickyKeysMap(state: StickyModifierState): LinkedHashMap<ModifierKey, Locked> {
        val keys = linkedMapOf<ModifierKey, Locked>()
        state.apply {
            if (isAltGrModifierOn) keys[ALT_GR] = Locked(false)
            if (isAltGrModifierLocked) keys[ALT_GR] = Locked(true)
            if (isAltModifierOn) keys[ALT] = Locked(false)
            if (isAltModifierLocked) keys[ALT] = Locked(true)
            if (isCtrlModifierOn) keys[CTRL] = Locked(false)
            if (isCtrlModifierLocked) keys[CTRL] = Locked(true)
            if (isMetaModifierOn) keys[META] = Locked(false)
            if (isMetaModifierLocked) keys[META] = Locked(true)
            if (isShiftModifierOn) keys[SHIFT] = Locked(false)
            if (isShiftModifierLocked) keys[SHIFT] = Locked(true)
        }
        return keys
    }

    companion object {
        const val TAG = "StickyKeysRepositoryImpl"
    }
}
+28 −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.keyboard.stickykeys.shared.model

@JvmInline
value class Locked(val locked: Boolean)

enum class ModifierKey(val text: String) {
    ALT("ALT LEFT"),
    ALT_GR("ALT RIGHT"),
    CTRL("CTRL"),
    META("META"),
    SHIFT("SHIFT"),
}
Loading