readest/apps/readest-app
Huang Xin ab628fe258
fix(android): background TTS media controls + lock-screen scrubber/seek + Edge click fix (#4994)
* fix(android): keep background TTS media controls alive when backgrounded

The media-session update commands pushed metadata and playback state to
MediaPlaybackService via Context.startService(), which Android 8+ rejects
with "app is in background" once the app leaves the foreground. Every
per-sentence update then failed, so the lock-screen control went stale and
the foreground service lost the notification refresh that keeps it alive,
dropping background playback.

Deliver updates to the running service in-process (the pattern the existing
requestDeactivation() already uses) so they can never trigger a service
start. Also request POST_NOTIFICATIONS whenever the session activates, not
only when alwaysInForeground is set, so the foreground-service media
notification (the lock-screen control) is not suppressed on Android 13+.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(android): show section duration and enable seek on the TTS media session

The Edge/WebAudio TTS engine already reports an estimated section timeline
(position and duration) to the media session on every sentence, but the
Android service never surfaced it: the lock screen had no scrubber and no
seek. Set METADATA_KEY_DURATION so the scrubber shows its length, add
ACTION_SEEK_TO, and handle onSeekTo by handing the target back to the JS
controller (seekToTime) through the existing media-session-seek event.

Playback-state updates that only flip play/pause omit position and duration,
so the service now preserves the last known values instead of resetting the
scrubber to 0. Native TextToSpeech has no timeline (duration stays 0), so the
scrubber simply does not appear there.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(android): harden TTS foreground-service promotion + add diagnostics

On MIUI the media service was reclaimed on idle ("Stopping service due to app
idle"), which means startForeground never promoted it to a real foreground
service. Promote with ServiceCompat and an explicit
FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK type (robust on targetSdk 34+) and log
any failure instead of swallowing it.

Decouple the POST_NOTIFICATIONS request from the service start in setActive: a
throwing or hung permission request no longer aborts set_media_session_active,
so the foreground service always starts.

Add trace logging (set_media_session_active, activateSession, startForeground)
to pinpoint the promotion path on-device.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(android): drop required keepAppInForeground so the TTS service starts

The set_media_session_active payload required keep_app_in_foreground, but the
media bridge activates with just { active: true }. Tauri rejected the invoke
at the serde layer ("missing field keepAppInForeground") before the command
ran, so the foreground service never started: no media notification, and
Android 15 audio hardening then muted background playback.

The flag was dead everywhere: no platform read it (the foreground service
always starts and the POST_NOTIFICATIONS request is now unconditional). Remove
it from the Rust, Kotlin, iOS, and TS payloads. Effective behavior is
always-foreground.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(tts): fade sentence-buffer edges to stop Edge TTS clicks

The silence trim (findSpeechBounds) cuts at an amplitude threshold, not a zero
crossing, so each Edge TTS sentence buffer begins and ends on a non-zero
sample. An AudioBufferSourceNode steps straight from/to silence at its edges,
and that discontinuity clicks/pops between sentences (WSOLA is not involved:
it is a no-op at rate 1.0 and cross-fades its internal splices).

Apply a ~3ms linear fade to each buffer's own copy after WSOLA so the edges
ramp to zero. The trim and the controlled inter-sentence gap are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(android): remove the dead "Background Read Aloud" setting

alwaysInForeground only drove the removed keepAppInForeground flag; nothing
reads it now (the foreground service always starts, and POST_NOTIFICATIONS is
requested at play time). Remove the setting, its default, the Android
library-menu toggle (which just requested the notification permission), and
prune the now-unused i18n key from all locales.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 13:35:53 +02:00
..
.agents agent: support cross-agent workspace, now it should work with claude and codex (#4318) 2026-05-27 06:28:39 +02:00
.claude feat(sync): S3-compatible cloud sync provider (#4990) 2026-07-07 09:34:28 +02:00
bench fix(db): forward DatabaseOpts to tauri-plugin-turso (#4292) 2026-05-25 18:51:49 +02:00
data/wordlens feat(wordlens): en-en glosses, styling, derivation lemmas, display-time cap (#4744) 2026-06-23 11:15:34 +02:00
docs refactor(wordlens): rename "Word Wise" to "Word Lens" (#4633) 2026-06-17 19:09:29 +02:00
e2e test(e2e): add a Playwright web e2e lane (reading & annotation flows) (#4214) 2026-05-18 08:22:17 +02:00
extensions chore(send-to-readest): add Chrome Web Store screenshot generator (#4664) 2026-06-19 12:58:24 +02:00
public fix(android): background TTS media controls + lock-screen scrubber/seek + Edge click fix (#4994) 2026-07-07 13:35:53 +02:00
scripts fix(auth): surface OAuth callback errors on desktop deeplink (#4881) (#4894) 2026-07-02 17:03:56 +02:00
src fix(android): background TTS media controls + lock-screen scrubber/seek + Edge click fix (#4994) 2026-07-07 13:35:53 +02:00
src-tauri fix(android): background TTS media controls + lock-screen scrubber/seek + Edge click fix (#4994) 2026-07-07 13:35:53 +02:00
workers feat(payment): observability for store subscription webhooks (#4704) 2026-06-21 18:02:02 +02:00
.env fix: resolve various tracked exceptions in ph (#3584) 2026-03-22 08:16:38 +01:00
.env.local.example feat(discord): support show reading status with Discord Rich Presence, closes #1538 (#2998) 2026-01-19 17:42:19 +01:00
.env.tauri fix(flathub): use custom dbus id for single instance on Linux (#2971) 2026-01-16 05:59:25 +01:00
.env.tauri.example feat(ai): AI reading assistant phase 2 (#3023) 2026-01-24 11:38:48 +01:00
.env.web
.env.web.example feat(ai): AI reading assistant phase 2 (#3023) 2026-01-24 11:38:48 +01:00
.gitignore chore(agent): update agent memories (#4833) 2026-06-28 05:11:46 +02:00
AGENTS.md agent: update implementation scope (#4319) 2026-05-27 07:50:31 +02:00
CLAUDE.md chore(agents): add AGENTS.md for readest-app (#3415) 2026-02-28 15:30:31 +01:00
components.json refactor: rename components/ui to components/primitives (#3064) 2026-01-25 06:41:18 +01:00
DESIGN.md fix(sync): cloud-sync settings polish + temporary premium ungate (#4828) 2026-06-27 19:11:17 +02:00
i18n-langs.json feat(i18n): add Uzbek and Brazilian Portuguese translations (#4061) 2026-05-05 09:20:44 +02:00
i18next-scanner.config.cjs fix(cbz): ComicInfo metadata + CBZ page count + WebDAV i18n (#4282) 2026-05-23 15:59:29 +02:00
next.config.mjs build(web): standalone Docker image + drop Turbopack build cache (#4619) 2026-06-16 21:21:24 +02:00
open-next.config.ts chore(config): add disableIncrementalCache to skip populating remote R2 incremental cache (#4623) 2026-06-17 03:51:17 +02:00
package.json release: version 0.11.17 (hotfix for an Android crash) (#4852) 2026-06-29 04:21:28 +02:00
playwright.config.ts test(e2e): add a Playwright web e2e lane (reading & annotation flows) (#4214) 2026-05-18 08:22:17 +02:00
postcss.config.mjs
raw-loader.d.ts
release-notes.json release: version 0.11.17 (hotfix for an Android crash) (#4852) 2026-06-29 04:21:28 +02:00
skills-lock.json ui/ux: codify design system and migrate settings to shared primitives (#4116) 2026-05-10 17:46:25 +02:00
tailwind.config.ts chore: bump next.js to the latest version (#3106) 2026-01-29 12:29:17 +01:00
TODOS.md feat(sync): route library sync exclusively to the selected cloud provider (#4380) (#4975) 2026-07-06 18:05:54 +02:00
tsconfig.json fix(db): forward DatabaseOpts to tauri-plugin-turso (#4292) 2026-05-25 18:51:49 +02:00
vite.config.ts chore: experimental vinext build (#3486) 2026-03-06 18:04:28 +01:00
vitest.android.config.mts fix(reader): Android text selection breaks on the first word of hyphenated paragraphs (#4545) 2026-06-12 12:30:56 +08:00
vitest.browser.config.mts chore: bump turso to the latest version (#4086) 2026-05-07 20:11:47 +02:00
vitest.config.mts fix(reader): Android text selection breaks on the first word of hyphenated paragraphs (#4545) 2026-06-12 12:30:56 +08:00
vitest.env.mts feat(node): refactor appService into focused service modules and add app service for Node runtime (#3530) 2026-03-13 17:51:35 +01:00
vitest.setup.ts feat(tts): reuse the speaking session across paragraph & RSVP modes (#4657) 2026-06-19 07:47:29 +02:00
vitest.tauri.config.mts perf(import+open): native Rust EPUB/MOBI parser, OPF prefetch, parallel TOC enrichment (#4369) 2026-06-10 16:58:25 +02:00
vitest.tauri.setup.ts feat(node): refactor appService into focused service modules and add app service for Node runtime (#3530) 2026-03-13 17:51:35 +01:00
wdio.conf.ts chore(testing): add Tauri integration tests and E2E test infrastructure (#3483) 2026-03-05 19:14:01 +01:00
wrangler.toml feat(payment): observability for store subscription webhooks (#4704) 2026-06-21 18:02:02 +02:00