What happens
The Open device assistant and Open voice assistant actions do nothing when the device is locked. On a locked or screen-off device the key map fires, but no assistant ever appears.
The key map itself is fine. I confirmed this by pointing the same trigger at a toggle flashlight action, which fires reliably with the screen off. So detection and action dispatch both work in that state; it is specifically the assistant actions that produce nothing.
Why
launchDeviceAssistant() and launchVoiceAssistant() start an activity with no keyguard handling at all:
https://github.com/keymapperorg/KeyMapper/blob/master/system/src/main/java/io/github/sds100/keymapper/system/apps/AndroidPackageManagerAdapter.kt#L163-L174
override fun launchDeviceAssistant(): KMResult<*> {
try {
Intent(Intent.ACTION_ASSIST).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
ctx.startActivity(this)
}
...
launchVoiceAssistant() is the same shape with ACTION_VOICE_COMMAND.
Compare openApp() about ninety lines further down in the same file, which handles exactly this case:
https://github.com/keymapperorg/KeyMapper/blob/master/system/src/main/java/io/github/sds100/keymapper/system/apps/AndroidPackageManagerAdapter.kt#L259-L281
// Use a trampoline activity that will dismiss the keyguard when it is locked.
val intent = if (keyguardManager.isKeyguardLocked) {
Intent(ctx, TrampolineActivity::class.java).apply {
putExtra(TrampolineActivity.EXTRA_INTENT, packageIntent)
}
} else {
packageIntent
}
val pendingIntent = PendingIntent.getActivity(ctx, 0, intent, PendingIntent.FLAG_IMMUTABLE)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
val bundle = ActivityOptions.makeBasic()
.setPendingIntentBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED,
)
.toBundle()
pendingIntent.send(bundle)
} else {
pendingIntent.send()
}
That machinery came from #1788 ("dismiss lockscreen when launching app action from lockscreen") and was never extended to the assistant actions. With the keyguard up, the assist activity starts behind the lock screen and nothing is visible. With the screen off there is the additional problem that a plain activity start does not wake the display.
Suggested fix
Route the assistant actions through the same TrampolineActivity plus PendingIntent path that openApp() uses. That should be a small change and would reuse code that already exists in the file.
One thing worth considering beyond the trampoline: for users who have Expert Mode connected, injecting the key event may be a better implementation than starting an activity. The system bridge runs as the shell user and holds INJECT_EVENTS, and AOSP's KEYCODE_ASSIST handling in PhoneWindowManager.interceptKeyBeforeQueueing has no keyguard check at all, which is why long-pressing the power button shows the assistant on a locked phone. Injection reaches the assistant where startActivity cannot.
Workaround for anyone who finds this
A Shell command action with Execution Mode ADB (so it runs through the system bridge):
input keyevent --longpress 26
26 is KEYCODE_POWER and --longpress makes the system treat it as a held button, so it takes the same path a physical long press does and carries INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS with it. This requires Settings > System > Gestures > Press and hold power button to be set to Digital assistant, since the command simulates that gesture.
This launches the assistant over the lock screen from a fully dark screen, and importantly it opens listening. Two alternatives that fall short, for comparison:
| Command |
Result |
input keyevent --longpress 26 |
Works. Launches over the lock screen and cues audio input |
input keyevent 219 (KEYCODE_ASSIST) |
Launches over the lock screen, but does not cue audio input |
input keyevent 231 (KEYCODE_VOICE_ASSIST) |
Does not work through the lock screen |
The difference between the first two looks like the invocation type: the power long press path passes INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS, which appears to be what makes the assistant open listening rather than merely open.
Setup
- Google Pixel 5a
- Key Mapper 4.5.0 from Google Play
- Expert Mode connected
- Trigger is a Bluetooth HID remote sending Volume Up, scoped to that device
- Assistant is Gemini, with "Use Gemini without unlocking" enabled
What happens
The Open device assistant and Open voice assistant actions do nothing when the device is locked. On a locked or screen-off device the key map fires, but no assistant ever appears.
The key map itself is fine. I confirmed this by pointing the same trigger at a toggle flashlight action, which fires reliably with the screen off. So detection and action dispatch both work in that state; it is specifically the assistant actions that produce nothing.
Why
launchDeviceAssistant()andlaunchVoiceAssistant()start an activity with no keyguard handling at all:https://github.com/keymapperorg/KeyMapper/blob/master/system/src/main/java/io/github/sds100/keymapper/system/apps/AndroidPackageManagerAdapter.kt#L163-L174
launchVoiceAssistant()is the same shape withACTION_VOICE_COMMAND.Compare
openApp()about ninety lines further down in the same file, which handles exactly this case:https://github.com/keymapperorg/KeyMapper/blob/master/system/src/main/java/io/github/sds100/keymapper/system/apps/AndroidPackageManagerAdapter.kt#L259-L281
That machinery came from #1788 ("dismiss lockscreen when launching app action from lockscreen") and was never extended to the assistant actions. With the keyguard up, the assist activity starts behind the lock screen and nothing is visible. With the screen off there is the additional problem that a plain activity start does not wake the display.
Suggested fix
Route the assistant actions through the same
TrampolineActivityplusPendingIntentpath thatopenApp()uses. That should be a small change and would reuse code that already exists in the file.One thing worth considering beyond the trampoline: for users who have Expert Mode connected, injecting the key event may be a better implementation than starting an activity. The system bridge runs as the shell user and holds
INJECT_EVENTS, and AOSP'sKEYCODE_ASSISThandling inPhoneWindowManager.interceptKeyBeforeQueueinghas no keyguard check at all, which is why long-pressing the power button shows the assistant on a locked phone. Injection reaches the assistant wherestartActivitycannot.Workaround for anyone who finds this
A Shell command action with Execution Mode ADB (so it runs through the system bridge):
26isKEYCODE_POWERand--longpressmakes the system treat it as a held button, so it takes the same path a physical long press does and carriesINVOCATION_TYPE_POWER_BUTTON_LONG_PRESSwith it. This requires Settings > System > Gestures > Press and hold power button to be set to Digital assistant, since the command simulates that gesture.This launches the assistant over the lock screen from a fully dark screen, and importantly it opens listening. Two alternatives that fall short, for comparison:
input keyevent --longpress 26input keyevent 219(KEYCODE_ASSIST)input keyevent 231(KEYCODE_VOICE_ASSIST)The difference between the first two looks like the invocation type: the power long press path passes
INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS, which appears to be what makes the assistant open listening rather than merely open.Setup