縦書き Reader for Android
A standalone, immersive Narou reader powered by Mozilla GeckoView. The vertical reading extension is bundled inside the APK; Firefox and Mozilla add-on signing are not needed.
Install
Use narou-reader-1.1.0-arm64.apk on a 64-bit ARM Android phone such as a Pixel.
Requires Android 8.0 (API 26) or newer. Download the APK on the phone, open it,
and allow that browser/file manager to install apps when Android asks.
This is a development-signed build for personal use, not a Play Store release.
The download is about 99 MiB because it includes the Firefox engine. Keep this
source archive, including signing/debug.keystore, for compatible future updates.
Read
The first launch opens the chapter supplied in the original request: https://ncode.syosetu.com/n0552ep/75/
- Swipe right to advance through the columns; swipe left to go back.
- Tap the text to show or hide controls. Long-press still selects text.
- 文字: font size; 行間: column spacing; 段落: paragraph spacing.
- 配色: paper, white or dark theme.
- 自動: open the reader automatically on chapter pages.
- 読書に戻る: hide controls without changing the text layout.
- 元のページ: show the original website; 縦書き reopens the reader.
- 前へ / 目次 / 次へ: chapter navigation.
- ☰: open another Narou URL, reload, share, open in a browser, or view help.
- Android Back goes back through browsing history, then closes the Activity.
- Swipe from the system edge to temporarily reveal Android's system bars.
The screen is reserved for the text while reading. Controls overlay the page, so opening them does not change the text layout. Camera cutouts are respected.
Paragraphs containing only <br> elements or whitespace are removed from the
reading copy. Empty span wrappers around breaks are handled too. Paragraphs
containing illustrations remain. Paragraph spacing is independent of column
spacing, with a 0–2em control and a default of 0.3em.
The app remembers the last Narou URL and saves normalized horizontal progress for up to 200 chapter URLs. Position, font, column/paragraph spacing, theme and auto-opening preference are local to this app. Progress is approximate when the layout or chapter text changes. This version reads online; it does not download whole books for offline use.
You can share a Narou chapter URL from another app to 縦書き Reader. Opening Narou links directly in the app may require enabling supported links in Android App Info, because this personal app does not own or verify the site's domain.
Build on macOS / Linux / Windows
Install a current Android Studio, or OpenJDK 27 (JDK 17 also works) and the Android command-line tools.
Install SDK Platform 37.2 and Build Tools 37.0.0 through SDK Manager. In Android
Studio open this directory, or set ANDROID_HOME to your SDK location.
The project uses Gradle 9.8.1, Android Gradle Plugin 9.4.1, and GeckoView 158.0.20261007201254. The Gradle wrapper is included. No machine-specific SDK path is included in the source archive.
./build.sh # Build only; no adb/device required
./build.sh --install # Build, then install on one connected device
./build.sh --serial DEVICE # Build, then install on the selected device
./gradlew :app:lintDebug # Optional Android lint check
build.sh uses your existing JAVA_HOME or Java from PATH, including OpenJDK 27.
It stops immediately on a failed build. Installation is opt-in; it checks for an
authorized device and selects ARM64 or x86_64 automatically. The Android bytecode
target remains Java 17, independently of the JDK used to run Gradle.
On Windows use gradlew.bat. Output:
app/build/outputs/apk/debug/app-arm64-v8a-debug.apk
app/build/outputs/apk/debug/app-x86_64-debug.apk
The ARM64 build is for current phones; x86_64 is for emulator testing. Native
libraries are compressed in the APK to reduce download size. The included
development key is deliberately usable by the project; see signing/README.md.
Generate and protect a separate release key before any public distribution.
Where to edit
app/src/main/java/net/astropenguin/naroureader/MainActivity.java: app UI, immersive mode, URL navigation, back handling, sharing and native menus.ReaderApplication.javaalongside it: process-wide Gecko runtime and extension installation.app/src/main/assets/reader/content.js: reading layout, text cleanup, controls, native touch scrolling and saved chapter positions.app/src/main/assets/reader/manifest.json: bundled extension registration.
The browser engine is bundled, so updating Firefox on the phone does not update this app. Update the pinned GeckoView dependency and rebuild to ship engine fixes.
Validation and limits
- Both ARM64 and x86_64 debug APKs compiled successfully.
- Android lint completed with no errors. Warnings concern the intentionally conservative target API (35), unverified website links, and Android backup configuration.
- ARM64 APK signature, ZIP alignment, native architecture, minimum SDK, and bundled reader asset identity verified.
- Reader behavior tested with Firefox 153 against the supplied chapter and a separate synthetic fixture. Tests cover spacer removal, separate paragraph spacing, native-message requests, tapping controls without text reflow, full-height layout, persisted settings/progress, reload, rotation and toggling back to the original page. WebExtension storage and native messaging are simulated in these browser tests.
- A headless Android emulator was attempted but failed to boot reliably in the build environment. The APK has not been run on a physical phone or a working emulator. Device behavior, including gesture navigation and Android-native dialogs, still needs an on-device check.
To run the bundled reader regression test, install Node.js and Playwright in a temporary development environment, then run:
npm install --no-save playwright
npx playwright install firefox
node tests/reader.test.cjs
The test uses synthetic Japanese text; no copied novel is included. Japanese fonts are needed for meaningful visual screenshots on Linux.
Privacy and licenses
No app analytics or telemetry is added. Network access loads the original website
and its resources; its normal cookies and requests still apply. This app has its
own browser profile and does not share Firefox's cookies or logged-in accounts.
The reader is injected only on https://ncode.syosetu.com/. Other Syosetu pages
can be browsed normally; clicked external HTTPS links open in another browser.
App code is MIT-licensed. GeckoView is MPL 2.0 with third-party notices; see
THIRD_PARTY_NOTICES.md and app menu → About / help → Engine licenses.
JDK 27 build update
Gradle 9.6 cannot run on JDK 27 and fails with Unsupported class file major version 71. This project now pins Gradle 9.8.1, including its distribution
SHA-256 and regenerated wrapper files. No JDK downgrade is required.
For an existing checkout, copy the updated gradle/wrapper/ directory, gradlew,
gradlew.bat, and build.sh from this archive. Preserve your application code
and signing key. If you maintain your own script, ensure it exits after a failed
Gradle command instead of continuing to adb.
Verified with a clean ARM64/x86_64 build and Android lint using Amazon Corretto OpenJDK 27+35-FR and Gradle 9.8.1 on Linux. App class files remain Java 17 (major version 61). Build-script checks cover compilation failure, build-only mode, missing devices, and ABI selection for installation.
Version 1.0.1 — camera cutout theme
The native Android window and camera-cutout safe area now follow the selected paper, white or dark reader theme. The native theme is restored at startup, and system-bar icon contrast follows the theme when the bars are temporarily shown. The bundled extension version is also bumped so installed apps load the update.
Install this APK over the existing app to keep settings and reading positions.
It uses the same development signing key. When updating an existing source build,
run ./gradlew :app:clean once before ./build.sh --install to remove stale APK
packaging state. The updated reader regression verifies theme messages on first
load, theme changes and saved-theme restoration.
Version 1.1.0 — accelerometer scrolling
Ported from your Windows AccelerScroll.cs and ReaderContent.xaml.cs. This uses sideways accelerometer tilt (including gravity), not gyroscope angular velocity. The acceleration → capped velocity → neutral-zone braking behavior is retained. Values now use CSS pixels/second and pixels/second², rather than pixels/timer tick. The neutral zone is symmetric around the calibrated position, correcting the old offset-dependent range. The old auto-anchor option is replaced by the reader's existing saved position, checkpointed every two seconds while motion scrolling.
- Tap text to show controls. Tap 傾き: OFF to enable it.
- Hold the phone at your comfortable reading angle; tap Set neutral.
- Tap 読書に戻る. Tilt sideways to accelerate, tilt the other way to reverse, or return to neutral to brake. Tap the text to stop immediately.
- Expand Tilt scrolling to adjust acceleration, maximum speed, neutral-zone width, braking strength, or reverse direction. Controls initially pause scrolling.
Version 1.1.1 — live calibration
Calibration shows a live tilt marker on a −1 to +1 g scale, a shaded braking range, its center, and exact trigger thresholds with reading direction labels. The stop-range offset slider moves the center; Set neutral captures the current tilt. The offset remains session-only and resets on rotation, as before. The marker keeps updating while paused, and the status shows actual scroll velocity.
Use Resume preview to scroll the chapter while calibration stays open; Pause preview stops immediately. Changes to the sliders take effect during preview. Collapsing calibration, toggling controls, disabling tilt or leaving the app cancels preview. Ordinary reading resumes when controls are hidden.
Tuning preferences and the enabled switch persist (default OFF). Opening a chapter, enabling motion, or rotating captures a fresh neutral angle; calibration is intentionally session-only so an old grip does not unexpectedly start scrolling. Touch/selection pauses motion. Closing the reader, losing app focus, backgrounding, missing sensor samples or navigation stops it. The Android listener has a one-second watchdog and unregisters when unused. Screen-on is requested only while moving. No new Android permission is required. Devices without an accelerometer show an unavailable message; ordinary swipe reading still works.
Implementation: TiltSensor.java collects filtered, display-relative samples;
motion.js holds the acceleration/braking model; content.js owns calibration,
controls, animation, and native polling. Android sensor coordinates follow the
Android sensor documentation.
Run node tests/motion.test.cjs for physics checks and node tests/reader.test.cjs
for Firefox integration checks with a simulated native accelerometer. The latter
covers movement, pausing, reversing, inactive samples, rotation and settings.
Actual sensor feel and device lifecycle still require a phone check.