HandyReader Blog

How's Handy Reader Coming Along in the First Half of 2026?

Looked through the git log — since the New Year, 260+ commits, 13 releases, roughly one every couple of weeks. Half of 2026 already gone. Time to pause and take stock of what’s been done these past six months.

Major Feature Milestones

March · Rendering Engine Refactor

The old page-turning logic was scattered across several classes, getting more coupled by the day — fixing one bug often broke something elsewhere. I spent about two weeks restructuring the layout, rendering, and page-turn animation components with a Facade pattern, combined with a versioned LRU cache, to finally nail the long-standing cache inconsistency issue. Also fixed up the color picker, added tap zone mode and left-handed mode — the first major overhaul of the reading interaction. Late in the month, reading background images went live. Users can pick system textures or choose their own from the gallery. Small feature on the surface, but I had to split the DataStore theme config into a standalone theme_prefs — took quite a while to sort out.

April · TTS Upgrade

I was using the built-in Android TTS before — it worked, barely. This time I switched to the Sherpa ONNX offline AI engine, supporting Chinese, English, Japanese, and more. With speed/pitch controls and a sleep timer, it’s a whole tier above the system TTS. It’s a nice differentiator against competitors too — worth digging deeper into. Same month, I built the user feedback system — a Go backend for the feedback API with email notifications. Users can submit feedback right from the app, and I get notified instantly.

May · Fonts, Dictionary, Translation, Multi-language

Font system — supports 30+ Chinese and English fonts from CDN, plus local imports. Variant auto-detection (Regular/Bold/Italic, etc.), freely switchable inside the reader. Dictionary + translation — in-reader word lookup launched, integrating Free Dictionary API, Wiktionary, Chinese dictionaries, and more. Selected text can be translated directly. I also put together a dedicated Cloudflare Workers dictionary service at dict.handyreader.top, covering 10+ languages. Also rolled out Traditional Chinese support.

June · Vertical Scrolling + Homepage Redesign

Continuous vertical scrolling — started on this back in early March, took about three months of on-and-off work to stabilize. In this mode, every paged-mode feature — highlights, underlines, notes, text selection, navigation, TTS tracking, text search — had to be re-adapted for continuous scrolling. Lots of pitfalls: cache invalidation, rendering offsets, chapter loading race conditions. Finally shipped it by mid-June. Complete homepage redesign — used to be just a grid of books. The new version adds a home feed, recent reads, and a reading heatmap. It’s no longer just a bookshelf — more of a reading dashboard. Also that month:

  • Open book files directly from other apps
  • In-book search
  • Global UI unification (Slider replacement, cover image standards, color alignment)
  • Reading page style themes
  • OPDS online book catalogs

July · Sync and PDF

July brought backup and restore — cover image sync, cross-device progress restoration, SHA-256 based book deduplication, and orphan book promotion. Also per-book theme config — each book can have its own color scheme and layout, no more one-size-fits-all. The rest of July is focused on PDF parsing, layout, and rendering. This one’s a big deal — I need to crack it.

Supporting Web Ecosystem

Beyond the app, I’ve also built a supporting web ecosystem over these six months:

  • handyreader.top (https://handyreader.top) — multilingual site in 15 languages, with blog, FAQ, feedback form, privacy policy. Static HTML, multilingual content generated with Python + data-i18n.
  • Go backend API — feedback handling, translation quota, Sherpa model data, reading backgrounds
  • Dictionary API — 10+ language dictionary lookup on Cloudflare Workers
  • ASO Dashboard — Next.js keyword rank tracker that scrapes Google Play rankings daily, with AI daily reports powered by DeepSeek
  • AppShotDeck — browser-based store screenshot generator for Google Play listing images (this tool is pretty interesting too — I’ll cover it separately)
  • OPDS book source directory — curated multiple OPDS sources available in Chinese

Lessons Learned

A few technical decisions that stand out:

  1. Separate DataStore for theme config — earlier, theme and general settings were mixed together, and their caches interfered with each other. After splitting into a dedicated theme_prefs DataStore, the problem was completely resolved.
  2. contentHash deduplication — the old approach used filename + CRC for dedup, which was unstable across devices. Switched to SHA-256 as the primary match key, unifying the matching on both import and restore sides.
  3. SAF skip trap — InputStream.skip() doesn’t guarantee skipping the specified number of bytes, and it’s especially unreliable under SAF. Since I didn’t go the full-file-permission route like most ebook readers, I took quite a few detours in file access.

Takeaways

For an ebook reader, parsing, layout, and rendering are the three core pillars. Building all three in-house wasn’t easy, but the upside is I know the internals inside out — when something breaks, I know exactly where to look. The downside is the slow start; you have to get the foundation right first, or everything downstream is a mess. Looking back, it was the right call — slow at first, but what I’ve built is mine to own.

What’s Next

WebDAV sync is still in progress — halfway through. Better PDF rendering and display is the immediate battle. Further out, I’m considering migrating to Kotlin Multiplatform for cross-platform support. Better make the most of the time we’ve got. Onwards through the second half — let’s fill in these gaps. A huge thanks to all our users for the support and feedback — every issue you file is what keeps me fixing bugs late into the night! 😄