willowtreeapps / willowtreeapps/vocable-android
Feature: Voice Selection — Hybrid (Device Default + In-App Override)
@rhyslutsky is already working on this.
Since Aug 3, 2026.
- Dominant language
- Kotlin
- Stars
- 127
- Forks
- 21
- Avg merge
- 16m
- Merged PRs (30d)
- 4
Description
Why
Vocable's TTS pipeline has no defined behavior for which voice it speaks with — it relies on whatever android.speech.tts.TextToSpeech happens to default to, never reading the device's actual configured voice. Some users also want Vocable to speak in a voice different from the device-wide default (similar to how Google Maps lets you pick a separate navigation voice). This epic tracks bringing Android to a "hybrid" model: live-check the device's default voice when nothing's explicitly chosen, but let an explicit in-app pick persist and stick across restarts.
Scope
Epic tracking issue — the actual work is broken into the child issues below, each independently scoped and PR'd against feature/voice-selection.
Child Issues
- #630 — Remove Language Selection/FuelIX scaffolding (unrelated work tangled into the same branch)
- #632 — TTS live device-voice fallback + explicit-pick persistence + stale-voice silent revert
- #642 — Fix stale-voice detection to check download status, not just name/locale match (follow-up to #632)
- #634 — Spike: on-device TTS voice-engine validation (2+ real engines)
- #618 — Hide undownloaded/unavailable voices from the picker
- #622 — Design: Figma deliverables for the three UI screens below
- #635 — Settings root Voice row + Settings → Voice screen
- #636 — Change Voice screen visual rework (title, selected-state checkmark, Default-row removal, pagination, empty state)
- #643 — Change Voice: play/preview button per row
- #644 — Change Voice: responsive 2-column grid matching iOS size classes
- #637 — Remove the old Voice entry point from Selection Mode
- #620 — Final regression + QA pass, once everything above lands
Closed / Superseded
These originally tracked pieces of this epic's scope, now closed with their work folded into the list above — kept here so the history isn't lost:
- #617 ("Day 2 — capture and persist the device's voice") — closed; its TTS-logic scope shipped via #632, its UI scope split into #635/#636/#637
- #619 ("Day 4 — silently revert stale voice") — closed; shipped via #632/#633 + #642/#645 (a real gap #619 didn't originally anticipate — a voice can be uninstalled without disappearing from
getVoices()); its multi-engine verification AC folded into #634 - #615/#616/#624 (Day 1 spikes) — closed, consolidated into #634
Out of Scope
- Language Selection / machine-translation pipeline — permanently out of scope for this feature (see #630)
- Play Store release-pipeline validation — general CI health work, not feature-specific; tracked separately in #626
- Reset App Settings' voice-fallback behavior — depends on this feature shipping first; tracked in #627
Links
- Branch:
feature/voice-selection(all child-issue PRs target this, notmain, until every child issue above is done) - FIGMA: https://www.figma.com/design/mNrwUygVhTmuWnKNHHkenR/Vocable---Current?node-id=6539-3337&m=dev
FINAL ACCEPTANCE CRITERIA
- When no voice is explicitly selected, Vocable uses the device's current system voice, checked live whenever it's needed — this is never persisted.
- Once the user explicitly picks a voice in Change Voice, that choice persists across app restarts, even if the device's system voice changes afterward.
- The user can explicitly change the voice via Change Voice, which lists only downloaded/available device voices and never modifies the device-wide system setting.
- If a persisted (explicit) voice becomes stale or unavailable, Vocable clears it and silently falls back to the live device-voice check — with no notification, toast, or alert shown to the user.
- Existing speak functionality and gaze/dwell timing work as expected.
- Design matches figma including responsive design across tablets and handhelds.
- The Play Store release pipeline is validated end-to-end (Day 5)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.