# 縦書き 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 a paragraph to focus its text color. Tap the focused paragraph again or tap empty space 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 `
` 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. ```sh ./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: ```text 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.java` alongside 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: ```sh 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. 1. Tap empty space (or the focused paragraph) to show controls. Tap **傾き: OFF** to enable it. 2. Hold the phone at your comfortable reading angle; tap **Set neutral**. 3. Tap **読書に戻る**. Tilt sideways to accelerate, tilt the other way to reverse, or return to neutral to brake. Opening controls stops scrolling immediately. 4. 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](https://developer.android.com/develop/sensors-and-location/sensors/sensors_overview). 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. ## Version 1.2.0 — paragraph focus Tap any paragraph, including its ruby text, to apply the focused text color. Only one paragraph is focused at a time. Enter or Space on a keyboard-focused paragraph also selects it. Links, swipes and long-press text selection keep their existing behavior. Tap the focused paragraph again or tap empty space to toggle controls. While accelerometer scrolling moves the text outside the braking range, focus tracks the visible paragraph nearest the screen center, in either direction and during calibration preview. Inside the braking range, the current focus stays unchanged, even while residual velocity brakes. Manual scrolling does not automatically change focus. **Normal text** and **Focus text** color controls save your choices locally. Custom colors apply across themes; **Reset text colors** restores the current theme's defaults. Focus changes only text color and does not shift the layout.