Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
## [3.0 Beta 4](https://github.com/sds100/KeyMapper/releases/tag/v3.0.0-beta.4)

#### 2 April 2025

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

## Added

- #1620 enable Key Mapper Basic Input Method without user interaction on Android 13+.
- #1619 Automatically select the non key mapper keyboard when the device is locked and wanting to type.

## Changed

- *Finally* renamed the theme settings after many years. @jambl3r.

## Bug fixes

- #1618, #1532, #1590 The Key Mapper keyboard is no longer required for Text actions.
- Flashlight action works again on devices that do not support variable brightness

## [3.0 Beta 3](https://github.com/sds100/KeyMapper/releases/tag/v3.0.0-beta.3)

_See the changes from previous 3.0 Beta releases as well._
Expand Down
2 changes: 0 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ dependencies {
def room_version = "2.6.1"
def coroutinesVersion = "1.9.0"
def nav_version = '2.8.9'
def work_version = "2.10.0"
def epoxy_version = "4.6.2"
def splitties_version = "3.0.0"
def multidex_version = "2.0.1"
Expand Down Expand Up @@ -211,7 +210,6 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.8.7"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.8.7"
implementation "androidx.room:room-ktx:$room_version"
implementation "androidx.work:work-runtime-ktx:$work_version"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
implementation "androidx.multidex:multidex:$multidex_version"
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:directBootAware="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme.NoActionBar"
Expand Down Expand Up @@ -174,6 +175,7 @@
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
</intent-filter>
</receiver>

Expand Down Expand Up @@ -231,8 +233,10 @@
android:resource="@xml/config_accessibility_service" />
</service>

<!-- IMPORTANT! Must be direct boot aware so it can automatically choose a proper keyboard when the user wants to unlock their phone. -->
<service
android:name=".system.inputmethod.KeyMapperImeService"
android:directBootAware="true"
android:exported="true"
android:label="@string/ime_service_label"
android:permission="android.permission.BIND_INPUT_METHOD">
Expand Down
39 changes: 37 additions & 2 deletions app/src/main/java/io/github/sds100/keymapper/KeyMapperApp.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package io.github.sds100.keymapper

import android.annotation.SuppressLint
import android.content.Intent
import android.os.Build
import android.os.UserManager
import android.util.Log
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.getSystemService
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
Expand Down Expand Up @@ -64,7 +68,10 @@ import java.util.Calendar
/**
* Created by sds100 on 19/05/2020.
*/
@SuppressLint("LogNotTimber")
class KeyMapperApp : MultiDexApplication() {
private val tag = KeyMapperApp::class.simpleName

val appCoroutineScope = MainScope()

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

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

private val userManager: UserManager? by lazy { getSystemService<UserManager>() }

private val initLock: Any = Any()
private var initialized = false

override fun onCreate() {
val priorExceptionHandler = Thread.getDefaultUncaughtExceptionHandler()

Log.i(tag, "KeyMapperApp: OnCreate")

Thread.setDefaultUncaughtExceptionHandler { thread, exception ->
// log in a blocking manner and always log regardless of whether the setting is turned on
val entry = LogEntryEntity(
Expand All @@ -188,9 +202,30 @@ class KeyMapperApp : MultiDexApplication() {

super.onCreate()

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// DynamicColors.applyToActivitiesIfAvailable(this)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && userManager?.isUserUnlocked == false) {
Log.i(tag, "KeyMapperApp: Delay init because locked.")
// If the device is still encrypted and locked do not initialize anything that
// may potentially need the encrypted app storage like databases.
return
}

synchronized(initLock) {
init()
initialized = true
}
}

fun onBootUnlocked() {
synchronized(initLock) {
if (!initialized) {
init()
}
initialized = true
}
}

private fun init() {
Log.i(tag, "KeyMapperApp: Init")

ServiceLocator.settingsRepository(this).get(Keys.darkTheme)
.map { it?.toIntOrNull() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -777,7 +777,8 @@ fun ActionData.canBeHeldDown(): Boolean = when (this) {

fun ActionData.canUseImeToPerform(): Boolean = when (this) {
is ActionData.InputKeyEvent -> !useShell
is ActionData.Text -> true
// Android 13+ can use the accessibility service to input text.
is ActionData.Text -> Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU
is ActionData.MoveCursorToEnd -> true
else -> false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,11 @@ class PerformActionsUseCaseImpl(
}

is ActionData.Text -> {
imeInputEventInjector.inputText(action.text)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
accessibilityService.inputText(action.text)
} else {
imeInputEventInjector.inputText(action.text)
}
result = Success(Unit)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,8 @@ class BackupManagerImpl(
modifiedGroup = RepositoryUtils.saveUniqueName(
modifiedGroup,
saveBlock = { renamedGroup ->
if (siblings.any { sibling -> sibling.name == renamedGroup.name }) {
// Do not rename the group with a (1) if it is the same UID. Just overwrite the name.
if (siblings.any { sibling -> sibling.uid != renamedGroup.uid && sibling.name == renamedGroup.name }) {
throw IllegalStateException("Non unique group name")
}
},
Expand Down
57 changes: 31 additions & 26 deletions app/src/main/java/io/github/sds100/keymapper/groups/GroupRow.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import io.github.sds100.keymapper.util.ui.compose.ComposeIconInfo
fun GroupRow(
modifier: Modifier = Modifier,
groups: List<GroupListItemModel>,
showNewGroup: Boolean = true,
onNewGroupClick: () -> Unit = {},
onGroupClick: (String) -> Unit = {},
enabled: Boolean = true,
Expand Down Expand Up @@ -80,19 +81,21 @@ fun GroupRow(
// Show new group button in the expand indicator if the new group button
// in the flow row has overflowed.
Row {
NewGroupButton(
onClick = onNewGroupClick,
text = if (isSubgroups) {
stringResource(R.string.home_new_subgroup_button)
} else {
stringResource(R.string.home_new_group_button)
},
icon = {
Icon(imageVector = Icons.Rounded.Add, null)
},
showText = groups.isEmpty(),
enabled = enabled,
)
if (showNewGroup) {
NewGroupButton(
onClick = onNewGroupClick,
text = if (isSubgroups) {
stringResource(R.string.home_new_subgroup_button)
} else {
stringResource(R.string.home_new_group_button)
},
icon = {
Icon(imageVector = Icons.Rounded.Add, null)
},
showText = groups.isEmpty(),
enabled = enabled,
)
}

Spacer(Modifier.width(8.dp))

Expand Down Expand Up @@ -160,19 +163,21 @@ fun GroupRow(
)
}

NewGroupButton(
onClick = onNewGroupClick,
text = if (isSubgroups) {
stringResource(R.string.home_new_subgroup_button)
} else {
stringResource(R.string.home_new_group_button)
},
icon = {
Icon(imageVector = Icons.Rounded.Add, null)
},
showText = groups.isEmpty(),
enabled = enabled,
)
if (showNewGroup) {
NewGroupButton(
onClick = onNewGroupClick,
text = if (isSubgroups) {
stringResource(R.string.home_new_subgroup_button)
} else {
stringResource(R.string.home_new_group_button)
},
icon = {
Icon(imageVector = Icons.Rounded.Add, null)
},
showText = groups.isEmpty(),
enabled = enabled,
)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
Expand Down Expand Up @@ -176,7 +177,7 @@ fun HomeKeyMapListScreen(
)
},
appBarContent = {
KeyMapAppBar(
KeyMapListAppBar(
state = state.appBarState,
scrollBehavior = scrollBehavior,
onSettingsClick = onSettingsClick,
Expand Down Expand Up @@ -499,7 +500,7 @@ private fun PreviewSelectingKeyMaps() {
)
},
appBarContent = {
KeyMapAppBar(state = appBarState)
KeyMapListAppBar(state = appBarState)
},
selectionBottomSheet = {
SelectionBottomSheet(
Expand Down Expand Up @@ -542,7 +543,7 @@ private fun PreviewKeyMapsRunning() {
)
},
appBarContent = {
KeyMapAppBar(state = appBarState)
KeyMapListAppBar(state = appBarState)
},
selectionBottomSheet = {},
)
Expand Down Expand Up @@ -578,7 +579,7 @@ private fun PreviewKeyMapsPaused() {
)
},
appBarContent = {
KeyMapAppBar(state = appBarState)
KeyMapListAppBar(state = appBarState)
},
selectionBottomSheet = {},
)
Expand Down Expand Up @@ -633,15 +634,15 @@ private fun PreviewKeyMapsWarnings() {
)
},
appBarContent = {
KeyMapAppBar(state = appBarState)
KeyMapListAppBar(state = appBarState)
},
selectionBottomSheet = {},
)
}
}

@OptIn(ExperimentalMaterial3Api::class)
@Preview
@Preview(device = Devices.PIXEL)
@Composable
private fun PreviewKeyMapsWarningsEmpty() {
val warnings = listOf(
Expand Down Expand Up @@ -680,7 +681,7 @@ private fun PreviewKeyMapsWarningsEmpty() {
)
},
appBarContent = {
KeyMapAppBar(state = appBarState)
KeyMapListAppBar(state = appBarState)
},
selectionBottomSheet = {},
)
Expand Down
Loading