commit a4f50eb15068384a7ee0df89da5a38e11e1279cd truex <truex@equwal.com> 2026-09-21 03:55:29 -0700 Ask for a grant at the moment the user needs it GuidedSetupActivity.askIfMissing takes any context and says whether it opened the allow step. The menu editor calls it after a save. The Navigation screen calls it for the first Power binding that needs something. A Power press that arrives while button remapping is off opens the allow step and no longer shows a toast, which the reader hides. Voice typing with no microphone grant opens the Voice typing screen.
CHANGELOG.md | 5 ++++ .../dev/equwal/assistkey/channel/ChannelEntry.kt | 7 ++---- .../dev/equwal/assistkey/menu/MenuEditActivity.kt | 4 ++++ .../equwal/assistkey/setup/GuidedSetupActivity.kt | 27 ++++++++++++++-------- .../equwal/assistkey/ui/ActionPickerActivity.kt | 6 ++++- .../dev/equwal/assistkey/ui/NavigationActivity.kt | 4 ++++ .../java/dev/equwal/assistkey/voice/Dictation.kt | 10 +++++++- 7 files changed, 47 insertions(+), 16 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f4e300..3831071 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ - **Change a button from the Done step.** "Change what it does" and "Change how you press it" go back to those steps with the button kept. Back on the Done step goes to the actions too. Tests: `Route.back`. +- **Ask when the user tries it.** The first run asks for every grant once. + After that the app asks at the moment something is missing: after a menu is + saved, after the Power switch on the Navigation screen, when a Power press + arrives while button remapping is off, and when voice typing starts with no + microphone grant. Before, those places showed a short message or nothing. - **Fix: system Back always closed the button setup.** From Android 13 the system does not call `onBackPressed` for an app with this target version. The setup now registers the Back callback, and Back goes one step back. diff --git a/app/src/main/java/dev/equwal/assistkey/channel/ChannelEntry.kt b/app/src/main/java/dev/equwal/assistkey/channel/ChannelEntry.kt index 6f9f824..3d4ec09 100644 --- a/app/src/main/java/dev/equwal/assistkey/channel/ChannelEntry.kt +++ b/app/src/main/java/dev/equwal/assistkey/channel/ChannelEntry.kt @@ -61,11 +61,8 @@ object ChannelEntry { if (ActionRouter.requiresAccessibility(spec)) { if (!ServiceHolder.isRunning) { - Toast.makeText( - activity, - "Turn on button remapping first", - Toast.LENGTH_SHORT - ).show() + // The user tried the button, and something is not allowed yet. Ask now. + dev.equwal.assistkey.setup.GuidedSetupActivity.askIfMissing(activity.applicationContext, trigger) activity.finishAndVanish() return } diff --git a/app/src/main/java/dev/equwal/assistkey/menu/MenuEditActivity.kt b/app/src/main/java/dev/equwal/assistkey/menu/MenuEditActivity.kt index 5200e6f..27083f4 100644 --- a/app/src/main/java/dev/equwal/assistkey/menu/MenuEditActivity.kt +++ b/app/src/main/java/dev/equwal/assistkey/menu/MenuEditActivity.kt @@ -59,6 +59,10 @@ class MenuEditActivity : Activity() { col.primaryButton(if (items.isEmpty()) "Remove the menu" else "Save the menu") { Store.bind(this, trigger, if (items.isEmpty()) ActionSpec.PASS else Menu.spec(items)) + // Ask now for what the menu needs. The guided setup asks by itself. + if (items.isNotEmpty() && !intent.getBooleanExtra(ActionPickerActivity.EXTRA_NO_ASK, false)) { + dev.equwal.assistkey.setup.GuidedSetupActivity.askIfMissing(this, trigger) + } setResult(RESULT_OK) finish() } diff --git a/app/src/main/java/dev/equwal/assistkey/setup/GuidedSetupActivity.kt b/app/src/main/java/dev/equwal/assistkey/setup/GuidedSetupActivity.kt index 7ade463..6c5523d 100644 --- a/app/src/main/java/dev/equwal/assistkey/setup/GuidedSetupActivity.kt +++ b/app/src/main/java/dev/equwal/assistkey/setup/GuidedSetupActivity.kt @@ -247,7 +247,7 @@ class GuidedSetupActivity : Activity() { } col.row("A menu of actions", "One press, many choices") { leftForPicker = true - startActivity(MenuEditActivity.intent(this, t)) + startActivity(MenuEditActivity.intent(this, t).putExtra(ActionPickerActivity.EXTRA_NO_ASK, true)) } col.row("Advanced", "Every action") { leftForPicker = true @@ -356,16 +356,25 @@ class GuidedSetupActivity : Activity() { private const val EXTRA_ASK = "ask" /** - * For a screen that has just made a binding for [t]. Opens the allow - * step when the binding needs something the user has not allowed yet. + * For any code that has just made a binding for [t], or has just tried + * to run it. Opens the allow step when the binding needs something the + * user has not allowed yet. True when it opened the step. */ - fun askIfMissing(a: Activity, t: Trigger) { - val spec = Store.bindings(a)[t] ?: return + fun askIfMissing(c: android.content.Context, t: Trigger): Boolean { + if (!missing(c, t)) return false + val ask = Intent(c, GuidedSetupActivity::class.java).putExtra(EXTRA_ASK, t.id) + // A service or a channel entry is not a screen. It needs a task of its own. + if (c !is Activity) ask.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + return runCatching { c.startActivity(ask) }.isSuccess + } + + /** True when the binding of [t] needs something that the user has not allowed yet. */ + fun missing(c: android.content.Context, t: Trigger): Boolean { + val spec = Store.bindings(c)[t] ?: return false val needsFilter = ActionRouter.requiresAccessibility(spec) || spec.kind == ActionKind.MENU - val plan = Route.plan(t, needsFilter, env(a)) - val microphone = spec.kind == ActionKind.VOICE && !Dictation.hasMicrophone(a) - if (plan.possible && plan.needs.all { satisfied(a, it) } && !microphone) return - a.startActivity(Intent(a, GuidedSetupActivity::class.java).putExtra(EXTRA_ASK, t.id)) + val plan = Route.plan(t, needsFilter, env(c)) + val microphone = spec.kind == ActionKind.VOICE && !Dictation.hasMicrophone(c) + return !(plan.possible && plan.needs.all { satisfied(c, it) } && !microphone) } private fun env(c: android.content.Context): Route.Env = diff --git a/app/src/main/java/dev/equwal/assistkey/ui/ActionPickerActivity.kt b/app/src/main/java/dev/equwal/assistkey/ui/ActionPickerActivity.kt index 552dff7..a404c0f 100644 --- a/app/src/main/java/dev/equwal/assistkey/ui/ActionPickerActivity.kt +++ b/app/src/main/java/dev/equwal/assistkey/ui/ActionPickerActivity.kt @@ -48,7 +48,11 @@ class ActionPickerActivity : Activity() { col.row("Now", Store.bindings(this).raw(trigger).describe(), enabled = false) col.header("More than one") col.row("Build a menu", "As many actions as you want") { - startActivityForResult(dev.equwal.assistkey.menu.MenuEditActivity.intent(this, trigger), MENU) + startActivityForResult( + dev.equwal.assistkey.menu.MenuEditActivity.intent(this, trigger) + .putExtra(EXTRA_NO_ASK, intent.getBooleanExtra(EXTRA_NO_ASK, false)), + MENU + ) } dev.equwal.assistkey.menu.Menu.presets(this).forEach { (name, items) -> col.row(name + " menu", items.joinToString(", ") { it.describe() }) { diff --git a/app/src/main/java/dev/equwal/assistkey/ui/NavigationActivity.kt b/app/src/main/java/dev/equwal/assistkey/ui/NavigationActivity.kt index 61778a9..0d320df 100644 --- a/app/src/main/java/dev/equwal/assistkey/ui/NavigationActivity.kt +++ b/app/src/main/java/dev/equwal/assistkey/ui/NavigationActivity.kt @@ -16,6 +16,7 @@ import dev.equwal.assistkey.model.HwKey import dev.equwal.assistkey.model.Trigger import dev.equwal.assistkey.native.NavNative import dev.equwal.assistkey.route.ServiceHolder +import dev.equwal.assistkey.setup.GuidedSetupActivity import dev.equwal.assistkey.shell.PowerControl import dev.equwal.assistkey.shell.Shell import dev.equwal.assistkey.store.Store @@ -117,6 +118,9 @@ class NavigationActivity : Activity() { Channels.setEnabled(this, Channel.ASSISTANT, true) } (ServiceHolder.service as? KeyFilterService)?.syncPower() + // One question at a time: the first of these bindings that needs something. + if (on) defaults.map { it.first }.firstOrNull { GuidedSetupActivity.missing(this, it) } + ?.let { GuidedSetupActivity.askIfMissing(this, it) } } // ---- the bar and the gestures ---------------------------------------------------------- diff --git a/app/src/main/java/dev/equwal/assistkey/voice/Dictation.kt b/app/src/main/java/dev/equwal/assistkey/voice/Dictation.kt index a984251..b70829b 100644 --- a/app/src/main/java/dev/equwal/assistkey/voice/Dictation.kt +++ b/app/src/main/java/dev/equwal/assistkey/voice/Dictation.kt @@ -110,7 +110,15 @@ object Dictation { it() return true } - if (!hasMicrophone(svc)) return fail(svc, "Voice typing needs the microphone") + if (!hasMicrophone(svc)) { + // The user tried voice typing. Open the screen that asks for the microphone. + runCatching { + svc.startActivity( + Intent(svc, dev.equwal.assistkey.ui.VoiceActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + ) + } + return false + } if (!available(svc)) return fail(svc, "No speech app on this device") return runCatching { svc.startActivity(