1 /* <lambda>null2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18 package com.android.systemui.keyboard.data.repository 19 20 import android.hardware.input.InputManager 21 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging 22 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow 23 import com.android.systemui.dagger.SysUISingleton 24 import com.android.systemui.dagger.qualifiers.Application 25 import com.android.systemui.dagger.qualifiers.Background 26 import com.android.systemui.keyboard.shared.model.BacklightModel 27 import javax.inject.Inject 28 import kotlinx.coroutines.CoroutineDispatcher 29 import kotlinx.coroutines.CoroutineScope 30 import kotlinx.coroutines.channels.awaitClose 31 import kotlinx.coroutines.flow.Flow 32 import kotlinx.coroutines.flow.SharingStarted 33 import kotlinx.coroutines.flow.distinctUntilChanged 34 import kotlinx.coroutines.flow.flowOn 35 import kotlinx.coroutines.flow.map 36 import kotlinx.coroutines.flow.shareIn 37 38 interface KeyboardRepository { 39 val keyboardConnected: Flow<Boolean> 40 val backlight: Flow<BacklightModel> 41 } 42 43 @SysUISingleton 44 class KeyboardRepositoryImpl 45 @Inject 46 constructor( 47 @Application private val applicationScope: CoroutineScope, 48 @Background private val backgroundDispatcher: CoroutineDispatcher, 49 private val inputManager: InputManager, 50 ) : KeyboardRepository { 51 52 private val connectedDeviceIds: Flow<Set<Int>> = <lambda>null53 conflatedCallbackFlow { 54 fun send(element: Set<Int>) = trySendWithFailureLogging(element, TAG) 55 56 var connectedKeyboards = inputManager.inputDeviceIds.toSet() 57 val listener = 58 object : InputManager.InputDeviceListener { 59 override fun onInputDeviceAdded(deviceId: Int) { 60 connectedKeyboards = connectedKeyboards + deviceId 61 send(connectedKeyboards) 62 } 63 64 override fun onInputDeviceChanged(deviceId: Int) = Unit 65 66 override fun onInputDeviceRemoved(deviceId: Int) { 67 connectedKeyboards = connectedKeyboards - deviceId 68 send(connectedKeyboards) 69 } 70 } 71 send(connectedKeyboards) 72 inputManager.registerInputDeviceListener(listener, /* handler= */ null) 73 awaitClose { inputManager.unregisterInputDeviceListener(listener) } 74 } 75 .shareIn( 76 scope = applicationScope, 77 started = SharingStarted.Lazily, 78 replay = 1, 79 ) 80 81 override val keyboardConnected: Flow<Boolean> = 82 connectedDeviceIds <lambda>null83 .map { it.any { deviceId -> isPhysicalFullKeyboard(deviceId) } } 84 .distinctUntilChanged() 85 .flowOn(backgroundDispatcher) 86 87 override val backlight: Flow<BacklightModel> = <lambda>null88 conflatedCallbackFlow { 89 // TODO(b/268645734) register BacklightListener 90 } 91 isPhysicalFullKeyboardnull92 private fun isPhysicalFullKeyboard(deviceId: Int): Boolean { 93 val device = inputManager.getInputDevice(deviceId) 94 return !device.isVirtual && device.isFullKeyboard 95 } 96 97 companion object { 98 const val TAG = "KeyboardRepositoryImpl" 99 } 100 } 101