feat(learn,todo,meditate): dictate through the phone's voice input app (ADR 0038, D18)

On Android the microphone now opens the app handling
RECOGNIZE_SPEECH (FUTO Voice Input, Google's), the default the person
chose in Android, and puts the text it returns where the cursor stood,
as Dictation does; cancelled, nothing. Only where none is installed, or
it can't be opened, does speech_to_text listen in line as before.

halkyon_app: VoiceInputApp (one listen, an answer once closed) on the
channel app.halkyon/voice_input, which DeviceSpeechInput asks first at
every listen, answering SpeechStart.finished so that Dictation doesn't
listen again; speech_to_text isn't touched on that route, so our app
asks for no microphone. SpeechInput.listen and Dictation.start take the
prompt the voice input app shows (the field's hint).

Each app: VoiceInput.kt (queryIntentActivities, startActivityForResult,
the first of EXTRA_RESULTS) attached in MainActivity, which hands it
activity results, and RECOGNIZE_SPEECH among the manifest's queries.
The privacy pages of Learn and Meditate say so.

Our own Whisper and Meditate's dream recordings are dropped (D18).

Whats-New: The microphone now dictates through your phone's voice input app, such as FUTO Voice Input.
Whats-New-De: Das Mikrofon diktiert jetzt über die Spracheingabe-App deines Telefons, etwa FUTO Voice Input.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-11-07 23:45:20 +01:00
commit c36bd83c6a
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -204,6 +204,39 @@ names and pictures are its own). Everything below is Halkyon's own.
- The platforms declare it as Learn's do: `RECORD_AUDIO` and the recognition service's
query on Android, the microphone's and speech recognition's usage texts on iOS and
macOS, and macOS's `audio-input` entitlement.
- **D18 Dictation through the phone's voice input app** (7 Nov 2026, the developer's
decision, amending D15 for Learn's, Todo's and Meditate's dictation alike): on Android
the microphone opens the app handling `RecognizerIntent.ACTION_RECOGNIZE_SPEECH` (FUTO
Voice Input, or Google's), whichever the person made the default in Android itself, and
puts the text it returns where the cursor stood, as D15's dictation does (halkyon_app's
`DictationInsertion`); cancelled, nothing. Only where none is installed, or it can't be
opened, does D15's in-line recogniser (`speech_to_text`) listen; where neither exists the
microphone stays hidden. Text only: no sound is kept, and our app asks for no
microphone on this route, as the voice input app records.
- **Why**: FUTO Voice Input recognises on the device with Whisper, but offers no
`SpeechRecognizer` (`RecognitionService`) API, registering only a placeholder
service, so `speech_to_text` can't reach it; it does handle the intent, in a floating
window, and returns text. Our own Whisper (whisper.cpp in a package of ours, the
`whisper-spike` branch) is not pursued: a model to download and a native build per
CPU, for what an installed app already does. FUTO's code is under the FUTO Source
First License, which doesn't go with our AGPL, so it is used as a separate app only,
never linked or copied. And the dream's recordings (audio kept beside the text,
synced end to end encrypted in a table of their own) are dropped: the transcript is
enough, and the intent returns text, not audio.
- **How**: `VoiceInputApp` in halkyon_app (one listen, an answer once closed: its own
shape, as `SpeechInput` streams words and `Dictation` listens again after each pause),
on each app's `VoiceInput.kt` over the channel `app.halkyon/voice_input` (`installed`
by `queryIntentActivities`, `listen` by `startActivityForResult`, the first of
`EXTRA_RESULTS`), with `EXTRA_LANGUAGE_MODEL` free form, `EXTRA_LANGUAGE` the app's
language and `EXTRA_PROMPT` the field's hint (Meditate: *What did you dream?*); each
manifest declares the intent among its `<queries>` (Android 11 on). `DeviceSpeechInput`
asks it first at every tap, answering `SpeechStart.finished`, after which `Dictation`
doesn't listen again. While it is open the button shows Stop and nothing else listens;
Stop drops what it returns later.
- **Consequences**: on a phone with Google's app, which handles the intent too, its
dialog listens now rather than the in-line recogniser, one utterance a tap, and D15's
recognition on the device (`preferOnDevice`) applies only where no app handles the
intent; which recognises on the device is then that app's choice.
## The operator's library (3 Nov 2026, the developer's request)