Skip to content

Commit c8e6229

Browse files
authored
Merge pull request #1671 from keymapperorg/develop
Version 3.0.1
2 parents aed2cff + 5d95314 commit c8e6229

File tree

111 files changed

+813
-1103
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

111 files changed

+813
-1103
lines changed

CHANGELOG.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,31 @@
1+
## [3.0.1](https://github.com/sds100/KeyMapper/releases/tag/v3.0.1)
2+
3+
#### 28 April 2025
4+
5+
## Added
6+
7+
- #1652 Bring back the menu button to show input method picker.
8+
- #1657 Turn on repeat by default for volume actions.
9+
10+
## Changed
11+
12+
- #1654 The Key Mapper keyboard is now required again for Text actions because the accessibility service API does not work in all situations.
13+
- #1653 Hide the export/import menu buttons in groups.
14+
- #1553 Hide double press option for side key and fingerprint gesture triggers because it is misleading. Double activations can be done with sequence triggers instead.
15+
- #1669 Change quick settings tile text.
16+
17+
## Bug fixes
18+
19+
- Inputting key events with Shizuku does not crash the app if a Key Mapper keyboard is being used at the same time. And latency when inputting key events has been improved in some apps.
20+
- #1646 disabling Bluetooth clears the list of connected devices.
21+
- #1655 do not crash when restoring key map groups.
22+
- #1649 show purchase verification failed error if no network connection.
23+
- #1648 caching purchases works so you can use floating buttons and assistant trigger without an internet connection.
24+
- #1658 floating buttons appear in the wrong place in portrait if saved in landscape.
25+
- #1659 Use trigger does not work if the screen orientation changes when re-entering the app.
26+
- #1668 Crashes when floating menu does not fit in the display height.
27+
- #1667 Hold down mode UI is missing from 2.8.
28+
129
## [3.0.0](https://github.com/sds100/KeyMapper/releases/tag/v3.0.0)
230

331
_See the changes from previous 3.0 Beta releases._

app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ dependencies {
221221
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
222222
implementation "androidx.room:room-runtime:$room_version"
223223
implementation "androidx.viewpager2:viewpager2:1.1.0"
224-
implementation "androidx.datastore:datastore-preferences:1.1.4"
224+
implementation "androidx.datastore:datastore-preferences:1.2.0-alpha01"
225225
implementation "androidx.core:core-splashscreen:1.0.1"
226226
implementation "androidx.activity:activity-compose:1.10.1"
227227
implementation "androidx.navigation:navigation-compose:2.8.9"

app/src/free/java/io/github/sds100/keymapper/purchasing/PurchasingManagerImpl.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ class PurchasingManagerImpl(
1414
private val coroutineScope: CoroutineScope,
1515
) : PurchasingManager {
1616
override val onCompleteProductPurchase: MutableSharedFlow<ProductId> = MutableSharedFlow()
17-
override val purchases: Flow<State<Set<ProductId>>> = MutableStateFlow(State.Data(emptySet()))
17+
override val purchases: Flow<State<Result<Set<ProductId>>>> =
18+
MutableStateFlow(State.Data(Error.PurchasingNotImplemented))
1819

1920
override suspend fun launchPurchasingFlow(product: ProductId): Result<Unit> {
2021
return Error.PurchasingNotImplemented
@@ -27,4 +28,6 @@ class PurchasingManagerImpl(
2728
override suspend fun isPurchased(product: ProductId): Result<Boolean> {
2829
return Error.PurchasingNotImplemented
2930
}
31+
32+
override fun refresh() {}
3033
}

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@
254254
android:name=".system.tiles.ToggleMappingsTile"
255255
android:exported="true"
256256
android:icon="@drawable/ic_tile_pause"
257-
android:label="@string/tile_pause"
257+
android:label="@string/tile_pause_title"
258258
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
259259
tools:targetApi="24">
260260
<intent-filter>

app/src/main/java/io/github/sds100/keymapper/UseCases.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ object UseCases {
142142
ServiceLocator.intentAdapter(ctx),
143143
getActionError(ctx),
144144
keyMapperImeMessenger(ctx, keyEventRelayService),
145-
ShizukuInputEventInjector(),
145+
ShizukuInputEventInjector(coroutineScope = ServiceLocator.appCoroutineScope(ctx)),
146146
ServiceLocator.packageManagerAdapter(ctx),
147147
ServiceLocator.appShortcutAdapter(ctx),
148148
ServiceLocator.popupMessageAdapter(ctx),
@@ -179,11 +179,12 @@ object UseCases {
179179
ServiceLocator.audioAdapter(ctx),
180180
keyMapperImeMessenger(ctx, keyEventRelayService),
181181
service,
182-
ShizukuInputEventInjector(),
182+
ShizukuInputEventInjector(ServiceLocator.appCoroutineScope(ctx)),
183183
ServiceLocator.popupMessageAdapter(ctx),
184184
ServiceLocator.permissionAdapter(ctx),
185185
ServiceLocator.resourceProvider(ctx),
186186
ServiceLocator.vibratorAdapter(ctx),
187+
ServiceLocator.appCoroutineScope(ctx),
187188
)
188189

189190
fun rerouteKeyEvents(ctx: Context, keyEventRelayService: KeyEventRelayServiceWrapper) = RerouteKeyEventsUseCaseImpl(

app/src/main/java/io/github/sds100/keymapper/about/AboutFragment.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class AboutFragment : Fragment() {
5757
onBackPressed()
5858
}
5959

60-
version = Constants.VERSION
60+
version = "${Constants.VERSION} ${Constants.VERSION_CODE}"
6161
}
6262
}
6363

app/src/main/java/io/github/sds100/keymapper/actions/ActionOptionsBottomSheet.kt

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import androidx.compose.runtime.Composable
3030
import androidx.compose.runtime.rememberCoroutineScope
3131
import androidx.compose.ui.Alignment
3232
import androidx.compose.ui.Modifier
33+
import androidx.compose.ui.platform.LocalContext
3334
import androidx.compose.ui.platform.LocalDensity
3435
import androidx.compose.ui.platform.LocalUriHandler
3536
import androidx.compose.ui.res.stringResource
@@ -44,6 +45,7 @@ import io.github.sds100.keymapper.util.ui.SliderStepSizes
4445
import io.github.sds100.keymapper.util.ui.compose.CheckBoxText
4546
import io.github.sds100.keymapper.util.ui.compose.RadioButtonText
4647
import io.github.sds100.keymapper.util.ui.compose.SliderOptionText
48+
import io.github.sds100.keymapper.util.ui.compose.openUriSafe
4749
import kotlinx.coroutines.launch
4850

4951
@OptIn(ExperimentalMaterial3Api::class)
@@ -63,6 +65,7 @@ fun ActionOptionsBottomSheet(
6365
dragHandle = {},
6466
) {
6567
val uriHandler = LocalUriHandler.current
68+
val ctx = LocalContext.current
6669
val helpUrl = stringResource(R.string.url_keymap_action_options_guide)
6770
val scope = rememberCoroutineScope()
6871

@@ -80,7 +83,7 @@ fun ActionOptionsBottomSheet(
8083
modifier = Modifier
8184
.align(Alignment.TopEnd)
8285
.padding(horizontal = 8.dp),
83-
onClick = { uriHandler.openUri(helpUrl) },
86+
onClick = { uriHandler.openUriSafe(ctx, helpUrl) },
8487
) {
8588
Icon(
8689
imageVector = Icons.AutoMirrored.Rounded.HelpOutline,
@@ -261,7 +264,36 @@ fun ActionOptionsBottomSheet(
261264
)
262265
}
263266

264-
Spacer(Modifier.height(8.dp))
267+
if (state.showHoldDownMode) {
268+
Spacer(Modifier.height(8.dp))
269+
270+
Text(
271+
modifier = Modifier.padding(horizontal = 16.dp),
272+
text = stringResource(R.string.hold_down_until_trigger_is_dot_dot_dot),
273+
style = MaterialTheme.typography.titleSmall,
274+
)
275+
276+
FlowRow(
277+
modifier = Modifier
278+
.fillMaxWidth()
279+
.padding(horizontal = 8.dp),
280+
horizontalArrangement = Arrangement.spacedBy(8.dp),
281+
) {
282+
RadioButtonText(
283+
isSelected = state.holdDownMode == HoldDownMode.TRIGGER_RELEASED,
284+
text = stringResource(R.string.stop_holding_down_when_trigger_released),
285+
onSelected = { callback.onSelectHoldDownMode(HoldDownMode.TRIGGER_RELEASED) },
286+
)
287+
288+
RadioButtonText(
289+
isSelected = state.holdDownMode == HoldDownMode.TRIGGER_PRESSED_AGAIN,
290+
text = stringResource(R.string.stop_holding_down_trigger_pressed_again),
291+
onSelected = { callback.onSelectHoldDownMode(HoldDownMode.TRIGGER_PRESSED_AGAIN) },
292+
)
293+
294+
Spacer(Modifier.width(8.dp))
295+
}
296+
}
265297

266298
if (state.showHoldDown) {
267299
Spacer(Modifier.height(8.dp))
@@ -352,6 +384,7 @@ interface ActionOptionsBottomSheetCallback {
352384
fun onRepeatDelayChanged(delay: Int) = run { }
353385
fun onHoldDownCheckedChange(checked: Boolean) = run { }
354386
fun onHoldDownDurationChanged(duration: Int) = run { }
387+
fun onSelectHoldDownMode(holdDownMode: HoldDownMode) = run { }
355388
fun onDelayBeforeNextActionChanged(delay: Int) = run { }
356389
fun onMultiplierChanged(multiplier: Int) = run { }
357390
}

app/src/main/java/io/github/sds100/keymapper/actions/ActionUtils.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -781,8 +781,7 @@ fun ActionData.canBeHeldDown(): Boolean = when (this) {
781781

782782
fun ActionData.canUseImeToPerform(): Boolean = when (this) {
783783
is ActionData.InputKeyEvent -> !useShell
784-
// Android 13+ can use the accessibility service to input text.
785-
is ActionData.Text -> Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU
784+
is ActionData.Text -> true
786785
is ActionData.MoveCursorToEnd -> true
787786
else -> false
788787
}

app/src/main/java/io/github/sds100/keymapper/actions/ConfigActionsViewModel.kt

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,15 @@ class ConfigActionsViewModel(
219219
actionOptionsUid.value?.let { uid -> config.setActionHoldDownDuration(uid, duration) }
220220
}
221221

222+
override fun onSelectHoldDownMode(holdDownMode: HoldDownMode) {
223+
actionOptionsUid.value?.let { uid ->
224+
config.setActionStopHoldingDownWhenTriggerPressedAgain(
225+
uid,
226+
holdDownMode == HoldDownMode.TRIGGER_PRESSED_AGAIN,
227+
)
228+
}
229+
}
230+
222231
override fun onDelayBeforeNextActionChanged(delay: Int) {
223232
actionOptionsUid.value?.let { uid -> config.setDelayBeforeNextAction(uid, delay) }
224233
}
@@ -230,7 +239,10 @@ class ConfigActionsViewModel(
230239
override fun onSelectRepeatMode(repeatMode: RepeatMode) {
231240
actionOptionsUid.value?.let { uid ->
232241
when (repeatMode) {
233-
RepeatMode.TRIGGER_RELEASED -> config.setActionStopRepeatingWhenTriggerReleased(uid)
242+
RepeatMode.TRIGGER_RELEASED -> config.setActionStopRepeatingWhenTriggerReleased(
243+
uid,
244+
)
245+
234246
RepeatMode.LIMIT_REACHED -> config.setActionStopRepeatingWhenLimitReached(uid)
235247
RepeatMode.TRIGGER_PRESSED_AGAIN -> config.setActionStopRepeatingWhenTriggerPressedAgain(
236248
uid,
@@ -416,7 +428,8 @@ class ConfigActionsViewModel(
416428
return ConfigActionsState.Empty(shortcuts = shortcuts)
417429
}
418430

419-
val actions = createListItems(keyMap, showDeviceDescriptors, errorSnapshot, shortcuts.size)
431+
val actions =
432+
createListItems(keyMap, showDeviceDescriptors, errorSnapshot, shortcuts.size)
420433

421434
return ConfigActionsState.Loaded(
422435
actions = actions,
@@ -542,7 +555,9 @@ class ConfigActionsViewModel(
542555
holdDownDuration = action.holdDownDuration ?: defaultHoldDownDuration,
543556
defaultHoldDownDuration = defaultHoldDownDuration,
544557

545-
showHoldDownMode = keyMap.isStopHoldingDownActionWhenTriggerPressedAgainAllowed(action),
558+
showHoldDownMode = keyMap.isStopHoldingDownActionWhenTriggerPressedAgainAllowed(
559+
action,
560+
),
546561
holdDownMode = if (action.stopHoldDownWhenTriggerPressedAgain) {
547562
HoldDownMode.TRIGGER_PRESSED_AGAIN
548563
} else {

0 commit comments

Comments
 (0)