×

Android-приложение с голосовым ядром: пошаговый конструктор

В своей практике замечал, что инклюзивный интерфейс начинается с аудиоканала. Голос убирает барьер между пользователем и устройством, поэтому начну с описания минимального окружения.

голосовое управление

Android Studio Hedgehog, Gradle8+, compileSdk 34, min Sdk 21. Kotlin ускоряет чтение кода, Java держит совместимость с библиотеками наследия.

Выбор стека

Для локального распознавания годятся Vosk или PocketSphinx, для облачных сервисов — Google Speech-to-Text. При ограничении трафика часто выбирают Vosk: вес библиотеки 40 МБ, скорость 1×RT на Cortex-A73.

Manifest содержит android.permission.RECORD_AUDIO и android.permission.FOREGROUND_SERVICE_MICROPHONE. Сервис-предок запрашивает Runtime-доступ через register For Activity Result(ActivityResult Contracts.RequestPermission()). Отказ блокирует кнопку активации и выводит SnackBar с подсказкой.

Класс SpeechManager инкапсулирует SpeechRecognizer:

private val recognizer = SpeechRecognizer.createSpeechRecognizer(context).apply {

override fun on Results(bundle: Bundle) {

val text = bundle.get String ArrayList(

SpeechRecognizer.RESULTS_RECOGNITION

)?.firstOrNull() ?: return

_ _stateFlow.value = Voice Event.Final(text)

}

override fun on Error(error: Int) {

_ _stateFlow.value = Voice Event.Error(error)

}

})

}

RecognizerIntent.EXTRA_LANGUAGE_MODEL,

RecognizerIntent.LANGUAGE_MODEL_FREE_FORM

)

}

}

Аудиопоток классефицируется как STREAM_VOICE_CALL, тогда система снижает громкость музыки. AudioManager.requestAudioFocus(

AudioFocus Request.Builder(

AudioManager.STREAM_MUSIC,

AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK

).build()

) решает задачу.

Слушатель событий

Корутина собирает _stateFlow и передаёт intents в слой Presentation. Activity освобождается от громоздких listen-логик, жизненный цикл упорядочен.

Команды описываются в файле commands.html:

[greeting]

Парсер Html.kt считывает карту utterance=action, маршрутизатор вызывает метод-приемника через Class.callSuspend.

Обратная связь

Микроанимация ripples визуализирует активное прослушивание. Алгоритм Кёниг-Дети вычисляет радиальную амплитуду: amplitude = ln(1 + rms) = scale.

Использую термин «иллокут» — минимальная единица речевого намерения. Когда иллокут классифицирован как intentshare, приложение открывает системный Share sheet.

Beam Search ×8 ускоряет поиск последовательности фонем в PocketSphinx. Русская bigram-модель на 250 K токенов умещается в 27 МБ и подгружается лениво.

UI-тест сочетает Espresso и Robolectric Mic Test Runner. wav-паттерн «открой профиль» подаётся в AudioRecord shadow, AssetManager подтверждает запуск ActivityProfile.

SpeechRecognizer в резервном режиме тратит ≈ 18 мА. После 800 мс тишины debounce отправляет recognizer.stopListening() и освобождает аудио-фокус.

Голоса содержат биометрические признаки. Перед отправкой в облако применяю волновое маскирование BackStage: в сигнал внедряется псевдошум, который затрудняет реконструкцию тембра, но нее снижает точность распознавания.

Голосовое ядро готово. Остаётся релиз, сквозное шифрование Proguard и манифест с queries для микрофона. Пакет помещается в Google Play без дополнительных запросов благодаря декларации Data Safety.