Skip to content

Commit b5ccba2

Browse files
committed
#1818 set up input method for key event actions
1 parent f595517 commit b5ccba2

11 files changed

Lines changed: 173 additions & 84 deletions

File tree

base/src/main/java/io/github/sds100/keymapper/base/actions/ActionsScreen.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ fun ActionsScreen(modifier: Modifier = Modifier, viewModel: ConfigActionsViewMod
8181
onChooseInputMethodClick = viewModel::onChooseImeClick,
8282
onDoneClick = viewModel::dismissFixKeyEventActionBottomSheet,
8383
onSelectProMode = viewModel::onSelectProMode,
84-
onSelectInputMethod = viewModel::onSelectInputMethod
84+
onSelectInputMethod = viewModel::onSelectInputMethod,
85+
onAutoSwitchImeCheckedChange = viewModel::onAutoSwitchImeCheckedChange,
8586
)
8687
}
8788

base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionBottomSheet.kt

Lines changed: 105 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ import io.github.sds100.keymapper.base.R
4444
import io.github.sds100.keymapper.base.compose.KeyMapperTheme
4545
import io.github.sds100.keymapper.base.compose.LocalCustomColorsPalette
4646
import io.github.sds100.keymapper.base.trigger.AccessibilityServiceRequirementRow
47+
import io.github.sds100.keymapper.base.trigger.InputMethodRequirementRow
4748
import io.github.sds100.keymapper.base.trigger.ProModeStatus
49+
import io.github.sds100.keymapper.base.utils.ui.compose.CheckBoxText
4850
import io.github.sds100.keymapper.base.utils.ui.compose.HeaderText
4951
import io.github.sds100.keymapper.base.utils.ui.compose.icons.KeyMapperIcons
5052
import io.github.sds100.keymapper.base.utils.ui.compose.icons.ProModeIcon
@@ -63,6 +65,7 @@ fun FixKeyEventActionBottomSheet(
6365
onEnableInputMethodClick: () -> Unit = {},
6466
onChooseInputMethodClick: () -> Unit = {},
6567
onDoneClick: () -> Unit = {},
68+
onAutoSwitchImeCheckedChange: (Boolean) -> Unit = {},
6669
) {
6770
ModalBottomSheet(
6871
modifier = modifier,
@@ -96,7 +99,7 @@ fun FixKeyEventActionBottomSheet(
9699
FixKeyEventActionOptionCard(
97100
onClick = onSelectInputMethod,
98101
selected = state is FixKeyEventActionState.InputMethod,
99-
title = stringResource(R.string.trigger_setup_input_method_title),
102+
title = stringResource(R.string.fix_key_event_action_input_method_title),
100103
icon = Icons.Rounded.Keyboard,
101104
) {
102105
val annotatedText = buildAnnotatedString {
@@ -111,64 +114,74 @@ fun FixKeyEventActionBottomSheet(
111114
Placeholder(
112115
width = MaterialTheme.typography.bodyLarge.fontSize,
113116
height = MaterialTheme.typography.bodyLarge.fontSize,
114-
placeholderVerticalAlign = PlaceholderVerticalAlign.TextCenter
115-
)
117+
placeholderVerticalAlign = PlaceholderVerticalAlign.TextCenter,
118+
),
116119
) {
117120
Icon(
118121
imageVector = Icons.Rounded.Remove,
119122
contentDescription = null,
120-
tint = MaterialTheme.colorScheme.error
123+
tint = MaterialTheme.colorScheme.error,
121124
)
122-
}
123-
)
125+
},
126+
),
124127
)
125128
Text(
126129
annotatedText,
127130
inlineContent = inlineContent,
128-
style = MaterialTheme.typography.bodyMedium
131+
style = MaterialTheme.typography.bodyMedium,
129132
)
130133
}
131134

132-
// TODO show disabled state on < Android Q
135+
val isProModeUnsupported = state.proModeStatus == ProModeStatus.UNSUPPORTED
136+
133137
FixKeyEventActionOptionCard(
134138
onClick = onSelectProMode,
135139
selected = state is FixKeyEventActionState.ProMode,
136140
title = stringResource(R.string.pro_mode_app_bar_title),
137141
icon = KeyMapperIcons.ProModeIcon,
142+
enabled = !isProModeUnsupported,
138143
) {
139-
val annotatedText = buildAnnotatedString {
140-
appendInlineContent("icon", "[icon]")
141-
append(" ")
142-
append(stringResource(R.string.fix_key_event_action_pro_mode_text))
143-
}
144-
val inlineContent = mapOf(
145-
Pair(
146-
"icon",
147-
InlineTextContent(
148-
Placeholder(
149-
width = MaterialTheme.typography.bodyLarge.fontSize,
150-
height = MaterialTheme.typography.bodyLarge.fontSize,
151-
placeholderVerticalAlign = PlaceholderVerticalAlign.TextCenter
152-
)
153-
) {
154-
Icon(
155-
imageVector = Icons.Rounded.Add,
156-
contentDescription = null,
157-
tint = LocalCustomColorsPalette.current.green
158-
)
159-
}
144+
if (isProModeUnsupported) {
145+
Text(
146+
stringResource(R.string.trigger_setup_pro_mode_unsupported),
147+
style = MaterialTheme.typography.bodyMedium,
148+
color = MaterialTheme.colorScheme.error,
160149
)
161-
)
162-
Text(
163-
annotatedText,
164-
inlineContent = inlineContent,
165-
style = MaterialTheme.typography.bodyMedium
166-
)
150+
} else {
151+
val annotatedText = buildAnnotatedString {
152+
appendInlineContent("icon", "[icon]")
153+
append(" ")
154+
append(stringResource(R.string.fix_key_event_action_pro_mode_text))
155+
}
156+
val inlineContent = mapOf(
157+
Pair(
158+
"icon",
159+
InlineTextContent(
160+
Placeholder(
161+
width = MaterialTheme.typography.bodyLarge.fontSize,
162+
height = MaterialTheme.typography.bodyLarge.fontSize,
163+
placeholderVerticalAlign = PlaceholderVerticalAlign.TextCenter,
164+
),
165+
) {
166+
Icon(
167+
imageVector = Icons.Rounded.Add,
168+
contentDescription = null,
169+
tint = LocalCustomColorsPalette.current.green,
170+
)
171+
},
172+
),
173+
)
174+
Text(
175+
annotatedText,
176+
inlineContent = inlineContent,
177+
style = MaterialTheme.typography.bodyMedium,
178+
)
179+
}
167180
}
168181

169182
Text(
170183
stringResource(R.string.fix_key_event_action_change_in_settings_caption),
171-
style = MaterialTheme.typography.labelMedium
184+
style = MaterialTheme.typography.labelMedium,
172185
)
173186
}
174187

@@ -177,16 +190,31 @@ fun FixKeyEventActionBottomSheet(
177190
AccessibilityServiceRequirementRow(
178191
modifier = Modifier.fillMaxWidth(),
179192
isServiceEnabled = state.isAccessibilityServiceEnabled,
180-
onClick = onEnableAccessibilityServiceClick
193+
onClick = onEnableAccessibilityServiceClick,
181194
)
182195

183196
when (state) {
184197
is FixKeyEventActionState.InputMethod -> {
198+
InputMethodRequirementRow(
199+
modifier = Modifier.fillMaxWidth(),
200+
isChosen = state.isChosen,
201+
isEnabled = state.isEnabled,
202+
enablingRequiresUserInput = state.enablingRequiresUserInput,
203+
onEnableClick = onEnableInputMethodClick,
204+
onChooseClick = onChooseInputMethodClick,
205+
)
185206

207+
HeaderText(text = stringResource(R.string.fix_key_event_action_options_title))
208+
209+
CheckBoxText(
210+
modifier = Modifier.fillMaxWidth(),
211+
text = stringResource(R.string.fix_key_event_action_auto_switch_ime_text),
212+
isChecked = state.isAutoSwitchImeEnabled,
213+
onCheckedChange = onAutoSwitchImeCheckedChange,
214+
)
186215
}
187216

188217
is FixKeyEventActionState.ProMode -> {
189-
190218
}
191219
}
192220

@@ -204,21 +232,26 @@ private fun FixKeyEventActionOptionCard(
204232
selected: Boolean,
205233
title: String,
206234
icon: ImageVector,
207-
content: @Composable ColumnScope.() -> Unit
235+
enabled: Boolean = true,
236+
content: @Composable ColumnScope.() -> Unit,
208237
) {
209-
OutlinedCard(modifier = modifier.fillMaxWidth(), onClick = onClick) {
238+
OutlinedCard(
239+
modifier = modifier.fillMaxWidth(),
240+
onClick = onClick,
241+
enabled = enabled,
242+
) {
210243
Row(
211244
modifier = Modifier
212245
.fillMaxWidth()
213-
.padding(start = 16.dp, top = 16.dp, end = 8.dp, bottom = 16.dp)
246+
.padding(start = 16.dp, top = 16.dp, end = 8.dp, bottom = 16.dp),
214247
) {
215248
Column(modifier = Modifier.weight(1f)) {
216249
Row(verticalAlignment = Alignment.CenterVertically) {
217250
Icon(icon, contentDescription = null)
218251
Spacer(modifier = Modifier.width(8.dp))
219252
Text(
220253
text = title,
221-
style = MaterialTheme.typography.titleMedium
254+
style = MaterialTheme.typography.titleMedium,
222255
)
223256
}
224257
Spacer(modifier = Modifier.height(8.dp))
@@ -227,7 +260,8 @@ private fun FixKeyEventActionOptionCard(
227260
RadioButton(
228261
modifier = Modifier.align(Alignment.CenterVertically),
229262
selected = selected,
230-
onClick = onClick
263+
onClick = onClick,
264+
enabled = enabled,
231265
)
232266
}
233267
}
@@ -251,6 +285,8 @@ private fun InputMethodPreview() {
251285
isChosen = true,
252286
enablingRequiresUserInput = true,
253287
isAccessibilityServiceEnabled = true,
288+
isAutoSwitchImeEnabled = true,
289+
proModeStatus = ProModeStatus.ENABLED,
254290
),
255291
)
256292
}
@@ -275,4 +311,29 @@ private fun ProModePreview() {
275311
),
276312
)
277313
}
278-
}
314+
}
315+
316+
@OptIn(ExperimentalMaterial3Api::class)
317+
@Preview
318+
@Composable
319+
private fun ProModeUnsupportedPreview() {
320+
KeyMapperTheme {
321+
val sheetState = SheetState(
322+
skipPartiallyExpanded = true,
323+
density = LocalDensity.current,
324+
initialValue = SheetValue.Expanded,
325+
)
326+
327+
FixKeyEventActionBottomSheet(
328+
sheetState = sheetState,
329+
state = FixKeyEventActionState.InputMethod(
330+
proModeStatus = ProModeStatus.UNSUPPORTED,
331+
isEnabled = true,
332+
isChosen = true,
333+
enablingRequiresUserInput = true,
334+
isAutoSwitchImeEnabled = false,
335+
isAccessibilityServiceEnabled = true,
336+
),
337+
)
338+
}
339+
}

base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionDelegate.kt

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import io.github.sds100.keymapper.base.utils.ui.DialogProvider
1212
import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
1313
import io.github.sds100.keymapper.base.utils.ui.ViewModelHelper
1414
import io.github.sds100.keymapper.common.utils.Constants
15+
import io.github.sds100.keymapper.common.utils.onFailure
1516
import io.github.sds100.keymapper.data.Keys
1617
import io.github.sds100.keymapper.data.PreferenceDefaults
1718
import io.github.sds100.keymapper.data.repositories.PreferenceRepository
@@ -31,6 +32,7 @@ import kotlinx.coroutines.flow.flowOf
3132
import kotlinx.coroutines.flow.map
3233
import kotlinx.coroutines.flow.stateIn
3334
import kotlinx.coroutines.launch
35+
import timber.log.Timber
3436
import javax.inject.Inject
3537
import javax.inject.Named
3638

@@ -84,27 +86,32 @@ class FixKeyEventActionDelegateImpl @Inject constructor(
8486
if (isProModeSelected) {
8587
combine(
8688
proModeStatus,
87-
controlAccessibilityServiceUseCase.serviceState
89+
controlAccessibilityServiceUseCase.serviceState,
8890
) { proModeStatus, serviceState ->
8991
FixKeyEventActionState.ProMode(
9092
proModeStatus = proModeStatus,
91-
isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED
93+
isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED,
9294
)
9395
}
9496
} else {
9597
combine(
98+
proModeStatus,
9699
setupInputMethodUseCase.isEnabled,
97100
setupInputMethodUseCase.isChosen,
98-
controlAccessibilityServiceUseCase.serviceState
99-
) { isEnabled, isChosen, serviceState ->
101+
controlAccessibilityServiceUseCase.serviceState,
102+
preferenceRepository.get(Keys.changeImeOnInputFocus),
103+
) { proModeStatus, isEnabled, isChosen, serviceState, changeImeOnInputFocus ->
100104
val enablingRequiresUserInput =
101-
Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
105+
Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU
102106

103107
FixKeyEventActionState.InputMethod(
104108
isEnabled = isEnabled,
105109
isChosen = isChosen,
106110
enablingRequiresUserInput = enablingRequiresUserInput,
107-
isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED
111+
isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED,
112+
proModeStatus = proModeStatus,
113+
isAutoSwitchImeEnabled = changeImeOnInputFocus
114+
?: PreferenceDefaults.CHANGE_IME_ON_INPUT_FOCUS,
108115
)
109116
}
110117
}
@@ -153,7 +160,9 @@ class FixKeyEventActionDelegateImpl @Inject constructor(
153160

154161
override fun onChooseImeClick() {
155162
viewModelScope.launch {
156-
setupInputMethodUseCase.chooseInputMethod()
163+
setupInputMethodUseCase.chooseInputMethod().onFailure {
164+
Timber.e("Failed to choose input method when fixing key event action. Error: $it")
165+
}
157166
}
158167
}
159168

@@ -164,6 +173,12 @@ class FixKeyEventActionDelegateImpl @Inject constructor(
164173
override fun onSelectInputMethod() {
165174
preferenceRepository.set(Keys.keyEventActionsUseSystemBridge, false)
166175
}
176+
177+
override fun onAutoSwitchImeCheckedChange(checked: Boolean) {
178+
viewModelScope.launch {
179+
preferenceRepository.set(Keys.changeImeOnInputFocus, checked)
180+
}
181+
}
167182
}
168183

169184
interface FixKeyEventActionDelegate {
@@ -175,7 +190,7 @@ interface FixKeyEventActionDelegate {
175190
fun onEnableProModeForKeyEventActionsClick()
176191
fun onEnableImeClick()
177192
fun onChooseImeClick()
178-
179193
fun onSelectProMode()
180194
fun onSelectInputMethod()
181-
}
195+
fun onAutoSwitchImeCheckedChange(checked: Boolean)
196+
}

base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionState.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import io.github.sds100.keymapper.base.trigger.ProModeStatus
44

55
sealed class FixKeyEventActionState {
66
abstract val isAccessibilityServiceEnabled: Boolean
7+
abstract val proModeStatus: ProModeStatus
78

89
data class InputMethod(
910
val isEnabled: Boolean,
@@ -13,11 +14,13 @@ sealed class FixKeyEventActionState {
1314
* show one button that both enables and chooses the input method.
1415
*/
1516
val enablingRequiresUserInput: Boolean,
16-
override val isAccessibilityServiceEnabled: Boolean
17+
val isAutoSwitchImeEnabled: Boolean,
18+
override val isAccessibilityServiceEnabled: Boolean,
19+
override val proModeStatus: ProModeStatus,
1720
) : FixKeyEventActionState()
1821

1922
data class ProMode(
20-
val proModeStatus: ProModeStatus,
21-
override val isAccessibilityServiceEnabled: Boolean
23+
override val isAccessibilityServiceEnabled: Boolean,
24+
override val proModeStatus: ProModeStatus,
2225
) : FixKeyEventActionState()
2326
}

base/src/main/java/io/github/sds100/keymapper/base/home/HomeKeyMapListScreen.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ fun HomeKeyMapListScreen(
144144
onChooseInputMethodClick = viewModel::onChooseImeClick,
145145
onDoneClick = viewModel::dismissFixKeyEventActionBottomSheet,
146146
onSelectProMode = viewModel::onSelectProMode,
147-
onSelectInputMethod = viewModel::onSelectInputMethod
147+
onSelectInputMethod = viewModel::onSelectInputMethod,
148+
onAutoSwitchImeCheckedChange = viewModel::onAutoSwitchImeCheckedChange,
148149
)
149150
}
150151

0 commit comments

Comments
 (0)