willowtreeapps / willowtreeapps/vocable-android

Feature: Voice Selection — Hybrid (Device Default + In-App Override)

Open
#613 1 comment 0 reactions 1 assignee View on GitHub

@rhyslutsky is already working on this.

Since Aug 3, 2026.

enhancement
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


FINAL ACCEPTANCE CRITERIA

  1. When no voice is explicitly selected, Vocable uses the device's current system voice, checked live whenever it's needed — this is never persisted.
  2. 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.
  3. 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.
  4. 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.
  5. Existing speak functionality and gaze/dwell timing work as expected.
  6. Design matches figma including responsive design across tablets and handhelds.
  7. The Play Store release pipeline is validated end-to-end (Day 5)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.