@@ -5,9 +5,11 @@ import android.content.SharedPreferences
5
5
import android.hardware.input.InputManager
6
6
import android.view.InputDevice
7
7
import android.view.KeyEvent
8
+ import androidx.core.content.edit
8
9
import com.fredporciuncula.flow.preferences.FlowSharedPreferences
9
10
import com.swordfish.lemuroid.app.shared.input.lemuroiddevice.getLemuroidInputDevice
10
- import com.swordfish.lemuroid.app.shared.settings.GameMenuShortcut
11
+ import com.swordfish.lemuroid.app.shared.settings.GameShortcut
12
+ import com.swordfish.lemuroid.app.shared.settings.GameShortcutType
11
13
import dagger.Lazy
12
14
import kotlinx.coroutines.Dispatchers
13
15
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -18,10 +20,12 @@ import kotlinx.coroutines.flow.flatMapLatest
18
20
import kotlinx.coroutines.flow.flowOf
19
21
import kotlinx.coroutines.flow.flowOn
20
22
import kotlinx.coroutines.flow.map
23
+ import kotlinx.coroutines.flow.mapNotNull
21
24
import kotlinx.coroutines.flow.onCompletion
22
25
import kotlinx.coroutines.flow.onSubscription
23
26
import kotlinx.coroutines.withContext
24
27
import kotlinx.serialization.builtins.MapSerializer
28
+ import kotlinx.serialization.builtins.PairSerializer
25
29
import kotlinx.serialization.json.Json
26
30
27
31
@OptIn(ExperimentalCoroutinesApi ::class )
@@ -38,29 +42,17 @@ class InputDeviceManager(
38
42
fun getInputBindingsObservable (): Flow <(InputDevice ? ) -> Map <InputKey , RetroKey >> {
39
43
return getEnabledInputsObservable()
40
44
.flatMapLatest { devices ->
41
- val allDeviceBindingsFlows =
42
- devices.map { device ->
43
- getBindingsFlow(device).map { device to it }
44
- }
45
- combine(allDeviceBindingsFlows) { allDeviceBindings ->
46
- allDeviceBindings.associate { (inputKey, retroKey) -> inputKey to retroKey }
47
- }
45
+ val allDeviceBindingsFlows = devices.map { device -> getBindingsFlow(device).map { device to it } }
46
+ combine(allDeviceBindingsFlows) { it.toMap() }
48
47
}
49
48
.map { bindings -> { bindings[it] ? : mapOf () } }
50
49
}
51
50
52
- fun getInputMenuShortCutObservable (): Flow <GameMenuShortcut ? > {
51
+ fun getGameShortcutsObservable (): Flow <Map < InputDevice , List < GameShortcut >> > {
53
52
return getEnabledInputsObservable()
54
- .map { devices ->
55
- val device = devices.firstOrNull()
56
- device
57
- ?.let {
58
- sharedPreferences.getString(
59
- computeGameMenuShortcutPreference(it),
60
- GameMenuShortcut .getDefault(it)?.name,
61
- )
62
- }
63
- ?.let { GameMenuShortcut .findByName(device, it) }
53
+ .flatMapLatest { devices ->
54
+ val allShortcutFlows = devices.map { device -> getShortcutBindingsFlow(device).map { device to it } }
55
+ combine(allShortcutFlows) { it.toMap() }
64
56
}
65
57
}
66
58
@@ -81,6 +73,25 @@ class InputDeviceManager(
81
73
.flowOn(Dispatchers .IO )
82
74
}
83
75
76
+ private fun getShortcutBindingsFlow (device : InputDevice ): Flow <List <GameShortcut >> {
77
+ val flows =
78
+ GameShortcutType .values().map { type ->
79
+ flowSharedPreferences.getString(computeGameShortcutPreference(device, type))
80
+ .asFlow()
81
+ .mapNotNull { preference ->
82
+ if (preference.isEmpty()) return @mapNotNull GameShortcut .getDefault(device, type)
83
+ val decoded = runCatching { Json .decodeFromString(bindingsComboSerializer, preference) }
84
+ val combo = decoded.getOrNull() ? : return @mapNotNull GameShortcut .getDefault(device, type)
85
+ GameShortcut (type = type, keys = setOf (combo.first.keyCode, combo.second.keyCode))
86
+ }
87
+ }
88
+ return if (flows.isEmpty()) {
89
+ flowOf(emptyList())
90
+ } else {
91
+ combine(flows) { it.toList() }
92
+ }.flowOn(Dispatchers .IO )
93
+ }
94
+
84
95
suspend fun getCurrentBindings (inputDevice : InputDevice ): Map <InputKey , RetroKey > {
85
96
return withContext(Dispatchers .IO ) {
86
97
val preference =
@@ -124,6 +135,18 @@ class InputDeviceManager(
124
135
.commit()
125
136
}
126
137
138
+ suspend fun updateShortcutBinding (
139
+ inputDevice : InputDevice ,
140
+ shortcutType : GameShortcutType ,
141
+ inputKeys : Pair <InputKey , InputKey >,
142
+ ) = withContext(Dispatchers .IO ) {
143
+ sharedPreferences.edit(commit = true ) {
144
+ val key = computeGameShortcutPreference(inputDevice, shortcutType)
145
+ val value = Json .encodeToString(bindingsComboSerializer, inputKeys)
146
+ putString(key, value)
147
+ }
148
+ }
149
+
127
150
suspend fun resetAllBindings () =
128
151
withContext(Dispatchers .IO ) {
129
152
val editor = sharedPreferences.edit()
@@ -208,6 +231,7 @@ class InputDeviceManager(
208
231
private const val GAME_PAD_ENABLED_PREFERENCE_BASE_KEY = " pref_key_gamepad_enabled"
209
232
210
233
private val bindingsMapSerializer = MapSerializer (InputKey .serializer(), RetroKey .serializer())
234
+ private val bindingsComboSerializer = PairSerializer (InputKey .serializer(), InputKey .serializer())
211
235
212
236
private fun getSharedPreferencesId (inputDevice : InputDevice ) = inputDevice.descriptor
213
237
@@ -221,8 +245,10 @@ class InputDeviceManager(
221
245
fun computeEnabledGamePadPreference (inputDevice : InputDevice ) =
222
246
" ${GAME_PAD_ENABLED_PREFERENCE_BASE_KEY } _${getSharedPreferencesId(inputDevice)} "
223
247
224
- fun computeGameMenuShortcutPreference (inputDevice : InputDevice ) =
225
- " ${GAME_PAD_BINDING_PREFERENCE_BASE_KEY } _${getSharedPreferencesId(inputDevice)} _gamemenu"
248
+ fun computeGameShortcutPreference (
249
+ inputDevice : InputDevice ,
250
+ type : GameShortcutType ,
251
+ ) = " ${GAME_PAD_BINDING_PREFERENCE_BASE_KEY } _${getSharedPreferencesId(inputDevice)} _shortcut_$type ."
226
252
227
253
fun computeKeyBindingGamePadPreference (inputDevice : InputDevice ) =
228
254
" ${GAME_PAD_BINDING_PREFERENCE_BASE_KEY } _${getSharedPreferencesId(inputDevice)} "
0 commit comments