Skip to main content
Use Vosk for offline, fixed-phrase commands that mirror the Rokid touchpad actions: select, back, next, previous.

Build inputs

Keep the Vosk and JNA dependencies as inline strings: Gradle version catalogs lose the @aar qualifier and can pull duplicate JNA classes. Add android.permission.RECORD_AUDIO to the manifest and request it at runtime before opening AudioRecord. Bundle a model at app/src/main/assets/model-en-us/. Recommended default: vosk-model-small-en-us-0.15.
Load the bundled model through Vosk’s Android storage helper:
Check context.assets.list("model-en-us") before unpacking so missing models report a useful runtime error.

Recognizer

Normalize configured commands and recognized text with trim().lowercase(Locale.US). Keep [unk] in the grammar so out-of-grammar speech does not force a command. The endpoint delays above bias command recognition toward short utterances: tolerate startup silence, finalize quickly after trailing silence, and cap utterances at three seconds.

Audio loop

Feed the recognizer 16 kHz mono PCM16 from a worker thread. Use sample counts, not byte counts, when passing a ShortArray to acceptWaveForm.
Parse Vosk JSON with JSONObject: final results use "text" and partial results use "partial".
Callbacks from the recognition thread must hop to the main thread before touching Android views.

Lifecycle

  • Start only after the model is unpacked and RECORD_AUDIO is granted.
  • On stop, set a stop flag, stop AudioRecord, interrupt/join the worker briefly, release AudioRecord, clear partial UI state, and reset any audio meter to zero.
  • On destroy, close Recognizer and Model.
  • Call recognizer.reset() before each new listening session.
  • Suppress duplicate final commands in a short window, around 400ms, because endpointing can produce repeated finals.
  • Surface actionable errors for missing model, unpack failure, missing permission, invalid buffer size, recorder init/start failure, negative reads, and runtime exceptions.