diff --git a/CHANGELOG.md b/CHANGELOG.md index d9a3ec583b..50cc80581a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ - #2045 add action to input on-screen keyboard enter/send button. - #2106 disable the keyboard auto-switching setting when manually switching the keyboard in the Key Mapper homescreen menu. +## Fixed + +- #2091 show an error on the "open device assistant" action when no device assistant is installed. + ## [4.0.5](https://github.com/sds100/KeyMapper/releases/tag/v4.0.5) #### 26 February 2026 diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionErrorSnapshot.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionErrorSnapshot.kt index a79591be43..9cf7a00748 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionErrorSnapshot.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionErrorSnapshot.kt @@ -8,6 +8,7 @@ import io.github.sds100.keymapper.common.BuildConfigProvider import io.github.sds100.keymapper.common.models.ShellExecutionMode import io.github.sds100.keymapper.common.utils.KMError import io.github.sds100.keymapper.common.utils.firstBlocking +import io.github.sds100.keymapper.common.utils.isSuccess import io.github.sds100.keymapper.common.utils.onFailure import io.github.sds100.keymapper.common.utils.onSuccess import io.github.sds100.keymapper.common.utils.valueOrNull @@ -53,6 +54,9 @@ class LazyActionErrorSnapshot( private val isCompatibleImeEnabled by lazy { keyMapperImeHelper.isCompatibleImeEnabled() } private val isCompatibleImeChosen by lazy { keyMapperImeHelper.isCompatibleImeChosen() } private val isVoiceAssistantInstalled by lazy { packageManager.isVoiceAssistantInstalled() } + private val isDeviceAssistantInstalled by lazy { + packageManager.getDeviceAssistantPackage().isSuccess + } private val grantedPermissions: MutableMap = mutableMapOf() private val flashLenses by lazy { buildSet { @@ -190,6 +194,12 @@ class LazyActionErrorSnapshot( } } + is ActionData.DeviceAssistant -> { + if (!isDeviceAssistantInstalled) { + return KMError.NoDeviceAssistant + } + } + is ActionData.Flashlight -> if (!flashLenses.contains(action.lens)) { return when (action.lens) { diff --git a/base/src/test/java/io/github/sds100/keymapper/base/actions/GetActionErrorUseCaseTest.kt b/base/src/test/java/io/github/sds100/keymapper/base/actions/GetActionErrorUseCaseTest.kt index f3f233625b..f1a978b026 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/actions/GetActionErrorUseCaseTest.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/actions/GetActionErrorUseCaseTest.kt @@ -5,10 +5,12 @@ import io.github.sds100.keymapper.base.repositories.FakePreferenceRepository import io.github.sds100.keymapper.base.system.inputmethod.FakeInputMethodAdapter import io.github.sds100.keymapper.base.utils.TestBuildConfigProvider import io.github.sds100.keymapper.common.utils.KMError +import io.github.sds100.keymapper.common.utils.Success import io.github.sds100.keymapper.data.Keys import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState import io.github.sds100.keymapper.sysbridge.utils.SystemBridgeError +import io.github.sds100.keymapper.system.apps.PackageManagerAdapter import io.github.sds100.keymapper.system.inputmethod.ImeInfo import io.github.sds100.keymapper.system.permissions.Permission import io.github.sds100.keymapper.system.permissions.PermissionAdapter @@ -59,6 +61,7 @@ class GetActionErrorUseCaseTest { private lateinit var mockPermissionAdapter: PermissionAdapter private lateinit var fakePreferenceRepository: FakePreferenceRepository private lateinit var mockSystemBridgeConnectionManager: SystemBridgeConnectionManager + private lateinit var mockPackageManagerAdapter: PackageManagerAdapter @Before fun init() { @@ -66,9 +69,10 @@ class GetActionErrorUseCaseTest { mockPermissionAdapter = mock() fakePreferenceRepository = FakePreferenceRepository() mockSystemBridgeConnectionManager = mock() + mockPackageManagerAdapter = mock() useCase = GetActionErrorUseCaseImpl( - packageManagerAdapter = mock(), + packageManagerAdapter = mockPackageManagerAdapter, inputMethodAdapter = fakeInputMethodAdapter, switchImeInterface = mock(), permissionAdapter = mockPermissionAdapter, @@ -300,4 +304,32 @@ class GetActionErrorUseCaseTest { assertThat(errors[0], `is`(KMError.KeyEventActionError(SystemBridgeError.Disconnected))) } + + @Test + fun `show an error for device assistant action when no device assistant is installed`() = + testScope.runTest { + whenever( + mockPackageManagerAdapter.getDeviceAssistantPackage(), + ).thenReturn(KMError.NoDeviceAssistant) + + val action = ActionData.DeviceAssistant + + val errors = useCase.actionErrorSnapshot.first().getErrors(listOf(action)) + + assertThat(errors[action], `is`(KMError.NoDeviceAssistant)) + } + + @Test + fun `do not show an error for device assistant action when a device assistant is installed`() = + testScope.runTest { + whenever( + mockPackageManagerAdapter.getDeviceAssistantPackage(), + ).thenReturn(Success("com.google.android.googlequicksearchbox")) + + val action = ActionData.DeviceAssistant + + val errors = useCase.actionErrorSnapshot.first().getErrors(listOf(action)) + + assertThat(errors[action], nullValue()) + } }