Skip to content

Commit e178bbf

Browse files
authored
Merge pull request #2117 from keymapperorg/claude/issue-2091-device-assistant-error
Show action error for device assistant action when no assistant is installed
2 parents 597bcde + 498a559 commit e178bbf

3 files changed

Lines changed: 47 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
- #2045 add action to input on-screen keyboard enter/send button.
88
- #2106 disable the keyboard auto-switching setting when manually switching the keyboard in the Key Mapper homescreen menu.
99

10+
## Fixed
11+
12+
- #2091 show an error on the "open device assistant" action when no device assistant is installed.
13+
1014
## [4.0.5](https://github.com/sds100/KeyMapper/releases/tag/v4.0.5)
1115

1216
#### 26 February 2026

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import io.github.sds100.keymapper.common.BuildConfigProvider
88
import io.github.sds100.keymapper.common.models.ShellExecutionMode
99
import io.github.sds100.keymapper.common.utils.KMError
1010
import io.github.sds100.keymapper.common.utils.firstBlocking
11+
import io.github.sds100.keymapper.common.utils.isSuccess
1112
import io.github.sds100.keymapper.common.utils.onFailure
1213
import io.github.sds100.keymapper.common.utils.onSuccess
1314
import io.github.sds100.keymapper.common.utils.valueOrNull
@@ -53,6 +54,9 @@ class LazyActionErrorSnapshot(
5354
private val isCompatibleImeEnabled by lazy { keyMapperImeHelper.isCompatibleImeEnabled() }
5455
private val isCompatibleImeChosen by lazy { keyMapperImeHelper.isCompatibleImeChosen() }
5556
private val isVoiceAssistantInstalled by lazy { packageManager.isVoiceAssistantInstalled() }
57+
private val isDeviceAssistantInstalled by lazy {
58+
packageManager.getDeviceAssistantPackage().isSuccess
59+
}
5660
private val grantedPermissions: MutableMap<Permission, Boolean> = mutableMapOf()
5761
private val flashLenses by lazy {
5862
buildSet {
@@ -190,6 +194,12 @@ class LazyActionErrorSnapshot(
190194
}
191195
}
192196

197+
is ActionData.DeviceAssistant -> {
198+
if (!isDeviceAssistantInstalled) {
199+
return KMError.NoDeviceAssistant
200+
}
201+
}
202+
193203
is ActionData.Flashlight ->
194204
if (!flashLenses.contains(action.lens)) {
195205
return when (action.lens) {

base/src/test/java/io/github/sds100/keymapper/base/actions/GetActionErrorUseCaseTest.kt

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import io.github.sds100.keymapper.base.repositories.FakePreferenceRepository
55
import io.github.sds100.keymapper.base.system.inputmethod.FakeInputMethodAdapter
66
import io.github.sds100.keymapper.base.utils.TestBuildConfigProvider
77
import io.github.sds100.keymapper.common.utils.KMError
8+
import io.github.sds100.keymapper.common.utils.Success
89
import io.github.sds100.keymapper.data.Keys
910
import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager
1011
import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState
1112
import io.github.sds100.keymapper.sysbridge.utils.SystemBridgeError
13+
import io.github.sds100.keymapper.system.apps.PackageManagerAdapter
1214
import io.github.sds100.keymapper.system.inputmethod.ImeInfo
1315
import io.github.sds100.keymapper.system.permissions.Permission
1416
import io.github.sds100.keymapper.system.permissions.PermissionAdapter
@@ -59,16 +61,18 @@ class GetActionErrorUseCaseTest {
5961
private lateinit var mockPermissionAdapter: PermissionAdapter
6062
private lateinit var fakePreferenceRepository: FakePreferenceRepository
6163
private lateinit var mockSystemBridgeConnectionManager: SystemBridgeConnectionManager
64+
private lateinit var mockPackageManagerAdapter: PackageManagerAdapter
6265

6366
@Before
6467
fun init() {
6568
fakeInputMethodAdapter = FakeInputMethodAdapter()
6669
mockPermissionAdapter = mock()
6770
fakePreferenceRepository = FakePreferenceRepository()
6871
mockSystemBridgeConnectionManager = mock()
72+
mockPackageManagerAdapter = mock()
6973

7074
useCase = GetActionErrorUseCaseImpl(
71-
packageManagerAdapter = mock(),
75+
packageManagerAdapter = mockPackageManagerAdapter,
7276
inputMethodAdapter = fakeInputMethodAdapter,
7377
switchImeInterface = mock(),
7478
permissionAdapter = mockPermissionAdapter,
@@ -300,4 +304,32 @@ class GetActionErrorUseCaseTest {
300304

301305
assertThat(errors[0], `is`(KMError.KeyEventActionError(SystemBridgeError.Disconnected)))
302306
}
307+
308+
@Test
309+
fun `show an error for device assistant action when no device assistant is installed`() =
310+
testScope.runTest {
311+
whenever(
312+
mockPackageManagerAdapter.getDeviceAssistantPackage(),
313+
).thenReturn(KMError.NoDeviceAssistant)
314+
315+
val action = ActionData.DeviceAssistant
316+
317+
val errors = useCase.actionErrorSnapshot.first().getErrors(listOf(action))
318+
319+
assertThat(errors[action], `is`(KMError.NoDeviceAssistant))
320+
}
321+
322+
@Test
323+
fun `do not show an error for device assistant action when a device assistant is installed`() =
324+
testScope.runTest {
325+
whenever(
326+
mockPackageManagerAdapter.getDeviceAssistantPackage(),
327+
).thenReturn(Success("com.google.android.googlequicksearchbox"))
328+
329+
val action = ActionData.DeviceAssistant
330+
331+
val errors = useCase.actionErrorSnapshot.first().getErrors(listOf(action))
332+
333+
assertThat(errors[action], nullValue())
334+
}
303335
}

0 commit comments

Comments
 (0)