Skip to content

Commit 1cc6575

Browse files
authored
Merge pull request #1621 from keymapperorg/develop
Version 3.0.0 Beta 4
2 parents f44187f + 9d661aa commit 1cc6575

33 files changed

+489
-151
lines changed

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
## [3.0 Beta 4](https://github.com/sds100/KeyMapper/releases/tag/v3.0.0-beta.4)
2+
3+
#### 2 April 2025
4+
5+
_See the changes from previous 3.0 Beta releases as well._
6+
7+
## Added
8+
9+
- #1620 enable Key Mapper Basic Input Method without user interaction on Android 13+.
10+
- #1619 Automatically select the non key mapper keyboard when the device is locked and wanting to type.
11+
12+
## Changed
13+
14+
- *Finally* renamed the theme settings after many years. @jambl3r.
15+
16+
## Bug fixes
17+
18+
- #1618, #1532, #1590 The Key Mapper keyboard is no longer required for Text actions.
19+
- Flashlight action works again on devices that do not support variable brightness
20+
121
## [3.0 Beta 3](https://github.com/sds100/KeyMapper/releases/tag/v3.0.0-beta.3)
222

323
_See the changes from previous 3.0 Beta releases as well._

app/build.gradle

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,6 @@ dependencies {
165165
def room_version = "2.6.1"
166166
def coroutinesVersion = "1.9.0"
167167
def nav_version = '2.8.9'
168-
def work_version = "2.10.0"
169168
def epoxy_version = "4.6.2"
170169
def splitties_version = "3.0.0"
171170
def multidex_version = "2.0.1"
@@ -211,7 +210,6 @@ dependencies {
211210
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.8.7"
212211
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.8.7"
213212
implementation "androidx.room:room-ktx:$room_version"
214-
implementation "androidx.work:work-runtime-ktx:$work_version"
215213
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
216214
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
217215
implementation "androidx.multidex:multidex:$multidex_version"

app/src/main/AndroidManifest.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
android:allowBackup="true"
8080
android:icon="@mipmap/ic_launcher"
8181
android:label="@string/app_name"
82+
android:directBootAware="true"
8283
android:roundIcon="@mipmap/ic_launcher_round"
8384
android:supportsRtl="true"
8485
android:theme="@style/AppTheme.NoActionBar"
@@ -174,6 +175,7 @@
174175
android:exported="true">
175176
<intent-filter>
176177
<action android:name="android.intent.action.BOOT_COMPLETED" />
178+
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
177179
</intent-filter>
178180
</receiver>
179181

@@ -231,8 +233,10 @@
231233
android:resource="@xml/config_accessibility_service" />
232234
</service>
233235

236+
<!-- IMPORTANT! Must be direct boot aware so it can automatically choose a proper keyboard when the user wants to unlock their phone. -->
234237
<service
235238
android:name=".system.inputmethod.KeyMapperImeService"
239+
android:directBootAware="true"
236240
android:exported="true"
237241
android:label="@string/ime_service_label"
238242
android:permission="android.permission.BIND_INPUT_METHOD">

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

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package io.github.sds100.keymapper
22

3+
import android.annotation.SuppressLint
34
import android.content.Intent
45
import android.os.Build
6+
import android.os.UserManager
7+
import android.util.Log
58
import androidx.appcompat.app.AppCompatDelegate
9+
import androidx.core.content.getSystemService
610
import androidx.lifecycle.Lifecycle
711
import androidx.lifecycle.LifecycleObserver
812
import androidx.lifecycle.OnLifecycleEvent
@@ -64,7 +68,10 @@ import java.util.Calendar
6468
/**
6569
* Created by sds100 on 19/05/2020.
6670
*/
71+
@SuppressLint("LogNotTimber")
6772
class KeyMapperApp : MultiDexApplication() {
73+
private val tag = KeyMapperApp::class.simpleName
74+
6875
val appCoroutineScope = MainScope()
6976

7077
val notificationAdapter by lazy { AndroidNotificationAdapter(this, appCoroutineScope) }
@@ -167,9 +174,16 @@ class KeyMapperApp : MultiDexApplication() {
167174

168175
private val processLifecycleOwner by lazy { ProcessLifecycleOwner.get() }
169176

177+
private val userManager: UserManager? by lazy { getSystemService<UserManager>() }
178+
179+
private val initLock: Any = Any()
180+
private var initialized = false
181+
170182
override fun onCreate() {
171183
val priorExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()
172184

185+
Log.i(tag, "KeyMapperApp: OnCreate")
186+
173187
Thread.setDefaultUncaughtExceptionHandler { thread, exception ->
174188
// log in a blocking manner and always log regardless of whether the setting is turned on
175189
val entry = LogEntryEntity(
@@ -188,9 +202,30 @@ class KeyMapperApp : MultiDexApplication() {
188202

189203
super.onCreate()
190204

191-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
192-
// DynamicColors.applyToActivitiesIfAvailable(this)
205+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && userManager?.isUserUnlocked == false) {
206+
Log.i(tag, "KeyMapperApp: Delay init because locked.")
207+
// If the device is still encrypted and locked do not initialize anything that
208+
// may potentially need the encrypted app storage like databases.
209+
return
210+
}
211+
212+
synchronized(initLock) {
213+
init()
214+
initialized = true
193215
}
216+
}
217+
218+
fun onBootUnlocked() {
219+
synchronized(initLock) {
220+
if (!initialized) {
221+
init()
222+
}
223+
initialized = true
224+
}
225+
}
226+
227+
private fun init() {
228+
Log.i(tag, "KeyMapperApp: Init")
194229

195230
ServiceLocator.settingsRepository(this).get(Keys.darkTheme)
196231
.map { it?.toIntOrNull() }

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -777,7 +777,8 @@ fun ActionData.canBeHeldDown(): Boolean = when (this) {
777777

778778
fun ActionData.canUseImeToPerform(): Boolean = when (this) {
779779
is ActionData.InputKeyEvent -> !useShell
780-
is ActionData.Text -> true
780+
// Android 13+ can use the accessibility service to input text.
781+
is ActionData.Text -> Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU
781782
is ActionData.MoveCursorToEnd -> true
782783
else -> false
783784
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,11 @@ class PerformActionsUseCaseImpl(
333333
}
334334

335335
is ActionData.Text -> {
336-
imeInputEventInjector.inputText(action.text)
336+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
337+
accessibilityService.inputText(action.text)
338+
} else {
339+
imeInputEventInjector.inputText(action.text)
340+
}
337341
result = Success(Unit)
338342
}
339343

app/src/main/java/io/github/sds100/keymapper/backup/BackupManager.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,8 @@ class BackupManagerImpl(
476476
modifiedGroup = RepositoryUtils.saveUniqueName(
477477
modifiedGroup,
478478
saveBlock = { renamedGroup ->
479-
if (siblings.any { sibling -> sibling.name == renamedGroup.name }) {
479+
// Do not rename the group with a (1) if it is the same UID. Just overwrite the name.
480+
if (siblings.any { sibling -> sibling.uid != renamedGroup.uid && sibling.name == renamedGroup.name }) {
480481
throw IllegalStateException("Non unique group name")
481482
}
482483
},

app/src/main/java/io/github/sds100/keymapper/groups/GroupRow.kt

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import io.github.sds100.keymapper.util.ui.compose.ComposeIconInfo
5151
fun GroupRow(
5252
modifier: Modifier = Modifier,
5353
groups: List<GroupListItemModel>,
54+
showNewGroup: Boolean = true,
5455
onNewGroupClick: () -> Unit = {},
5556
onGroupClick: (String) -> Unit = {},
5657
enabled: Boolean = true,
@@ -80,19 +81,21 @@ fun GroupRow(
8081
// Show new group button in the expand indicator if the new group button
8182
// in the flow row has overflowed.
8283
Row {
83-
NewGroupButton(
84-
onClick = onNewGroupClick,
85-
text = if (isSubgroups) {
86-
stringResource(R.string.home_new_subgroup_button)
87-
} else {
88-
stringResource(R.string.home_new_group_button)
89-
},
90-
icon = {
91-
Icon(imageVector = Icons.Rounded.Add, null)
92-
},
93-
showText = groups.isEmpty(),
94-
enabled = enabled,
95-
)
84+
if (showNewGroup) {
85+
NewGroupButton(
86+
onClick = onNewGroupClick,
87+
text = if (isSubgroups) {
88+
stringResource(R.string.home_new_subgroup_button)
89+
} else {
90+
stringResource(R.string.home_new_group_button)
91+
},
92+
icon = {
93+
Icon(imageVector = Icons.Rounded.Add, null)
94+
},
95+
showText = groups.isEmpty(),
96+
enabled = enabled,
97+
)
98+
}
9699

97100
Spacer(Modifier.width(8.dp))
98101

@@ -160,19 +163,21 @@ fun GroupRow(
160163
)
161164
}
162165

163-
NewGroupButton(
164-
onClick = onNewGroupClick,
165-
text = if (isSubgroups) {
166-
stringResource(R.string.home_new_subgroup_button)
167-
} else {
168-
stringResource(R.string.home_new_group_button)
169-
},
170-
icon = {
171-
Icon(imageVector = Icons.Rounded.Add, null)
172-
},
173-
showText = groups.isEmpty(),
174-
enabled = enabled,
175-
)
166+
if (showNewGroup) {
167+
NewGroupButton(
168+
onClick = onNewGroupClick,
169+
text = if (isSubgroups) {
170+
stringResource(R.string.home_new_subgroup_button)
171+
} else {
172+
stringResource(R.string.home_new_group_button)
173+
},
174+
icon = {
175+
Icon(imageVector = Icons.Rounded.Add, null)
176+
},
177+
showText = groups.isEmpty(),
178+
enabled = enabled,
179+
)
180+
}
176181
}
177182
}
178183
}

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import androidx.compose.ui.layout.onSizeChanged
4141
import androidx.compose.ui.platform.LocalContext
4242
import androidx.compose.ui.platform.LocalUriHandler
4343
import androidx.compose.ui.res.stringResource
44+
import androidx.compose.ui.tooling.preview.Devices
4445
import androidx.compose.ui.tooling.preview.Preview
4546
import androidx.compose.ui.unit.Dp
4647
import androidx.compose.ui.unit.dp
@@ -176,7 +177,7 @@ fun HomeKeyMapListScreen(
176177
)
177178
},
178179
appBarContent = {
179-
KeyMapAppBar(
180+
KeyMapListAppBar(
180181
state = state.appBarState,
181182
scrollBehavior = scrollBehavior,
182183
onSettingsClick = onSettingsClick,
@@ -499,7 +500,7 @@ private fun PreviewSelectingKeyMaps() {
499500
)
500501
},
501502
appBarContent = {
502-
KeyMapAppBar(state = appBarState)
503+
KeyMapListAppBar(state = appBarState)
503504
},
504505
selectionBottomSheet = {
505506
SelectionBottomSheet(
@@ -542,7 +543,7 @@ private fun PreviewKeyMapsRunning() {
542543
)
543544
},
544545
appBarContent = {
545-
KeyMapAppBar(state = appBarState)
546+
KeyMapListAppBar(state = appBarState)
546547
},
547548
selectionBottomSheet = {},
548549
)
@@ -578,7 +579,7 @@ private fun PreviewKeyMapsPaused() {
578579
)
579580
},
580581
appBarContent = {
581-
KeyMapAppBar(state = appBarState)
582+
KeyMapListAppBar(state = appBarState)
582583
},
583584
selectionBottomSheet = {},
584585
)
@@ -633,15 +634,15 @@ private fun PreviewKeyMapsWarnings() {
633634
)
634635
},
635636
appBarContent = {
636-
KeyMapAppBar(state = appBarState)
637+
KeyMapListAppBar(state = appBarState)
637638
},
638639
selectionBottomSheet = {},
639640
)
640641
}
641642
}
642643

643644
@OptIn(ExperimentalMaterial3Api::class)
644-
@Preview
645+
@Preview(device = Devices.PIXEL)
645646
@Composable
646647
private fun PreviewKeyMapsWarningsEmpty() {
647648
val warnings = listOf(
@@ -680,7 +681,7 @@ private fun PreviewKeyMapsWarningsEmpty() {
680681
)
681682
},
682683
appBarContent = {
683-
KeyMapAppBar(state = appBarState)
684+
KeyMapListAppBar(state = appBarState)
684685
},
685686
selectionBottomSheet = {},
686687
)

0 commit comments

Comments
 (0)