9 Commits

Author SHA1 Message Date
umbra audio
22c702c42c Add Apache-2.0 LICENSE, NOTICE, and SPDX headers 2026-10-18 19:01:01 -07:00
fbf372916d Add hamburger menu to the shelf that opens the About screen 2026-10-17 06:17:54 -07:00
45b3069b96 Fix shelf legibility in dark mode; open books from shelf; offer to open on duplicate create 2026-10-15 12:38:24 -07:00
5b64d268f0 Add UmbraTools-branded app icon
PageScribe joins the UmbraTools suite, so give it an icon in the shared
"umbra" identity: an open bronze book bound along its gutter by a glowing
cyan weave, held inside a broken reticle ring on a midnight field.

- Full adaptive icon: background + foreground + themed monochrome layer,
  with the mark sized to the 66dp safe circle so circle, squircle and
  rounded-square launcher masks all crop it cleanly.
- Legacy ic_launcher / ic_launcher_round bitmaps at all five densities.
- 1024 master lockup, Play Store 512 icon and the rounded-square mark in
  brand/, next to the generator and design notes.
- brand/generate_icons.py rebuilds every asset deterministically (SVG via
  cairosvg for the gradients and glow, Montserrat for the wordmark);
  brand/README.md documents the palette, what each element means, and how
  to regenerate.

The old flat vector drawable/ic_launcher.xml is replaced; the manifest now
points at @mipmap/ic_launcher with a roundIcon.
2026-10-15 12:16:25 -07:00
0f72c94f36 Play a shutter click when a photo is taken
A camera you are not looking at has to say it heard the tap. A synthesised
shutter_click.wav (110 ms, two filtered noise transients for the shutter
open and close) is bundled and played through SoundPool on each accepted
shot.

The click sits behind the capture guards, so a tap the camera refuses -
still busy, or not ready - stays silent and the click always means
"taken". SoundPool loads asynchronously, so a tap that beats the sample
into memory plays a short system tone instead of nothing. The sample is
released in onDestroy.

Verified on device: with system touch sounds disabled, dumpsys audio shows
player piid:1263 (package:com.bm.pagescribe, type:android.media.SoundPool)
emitting event:started on the tap, while AudioFlinger's mixer leaves
standby at the same moment.
2026-10-15 11:34:42 -07:00
684c65b025 Track page coverage from folios and flag pages to re-shoot
The printed page number is read anyway for ordering, so use it to keep
count of what has actually been captured.

PageCoverage turns the folios in pages/ into a span [low, high] with one
state per page: captured, needs re-capture (a page file that exists but is
blank - the folio was read, the body was not), or missing (no page file at
all). The shelf, book and capture screens surface it, and the book list
interleaves the gaps in folio order, so a page that was skipped or came out
blank sits exactly where the reader would look for it. A photo holding no
text at all has no folio to attribute; it stays in failed/ and is listed
separately as unreadable.

A one- or two-line half is not a text block, so PageNumberDetector's
top/bottom band test could never recognise a folio on the very page this
feature cares about - a body that failed to OCR, leaving only its page
number. Sparse halves now accept an isolated bare number sitting in the
outer margin.

98 unit tests, 0 failures (was 83). Verified end to end on the Pixel 7:
"Pages 42-51 - 7 of 10 captured - 3 to shoot", with page 47 flagged for
re-capture and 48/49 reported missing.
2026-10-15 11:34:39 -07:00
fc486d87e9 UI: fix status-bar/action-bar overlap under enforced edge-to-edge
targetSdk 36 enforces edge-to-edge and the built-in AppCompat action bar
is no longer inset correctly: the dump showed the action bar at
[0,136..283] while android:id/content started at y=0, so the first list
row sat under the bar and taps on it never reached the row.

Replace the theme action bar with a MaterialToolbar in an AppBarLayout on
each screen (NoActionBar theme) and apply the system-bar insets explicitly
via util/EdgeToEdge: the status-bar inset pads the app bar (whose
background then paints the strip behind the bar) and the navigation-bar
inset pads the content below it. enableEdgeToEdge with a dark status-bar
style keeps the icons light against the always-dark toolbar.

Verified on a Pixel 7: content now starts at y=283, the first book row is
tappable and opens the camera, the capture screen binds CameraX and gets
its first frame, and BookActivity lays out with a working up arrow.

Add README.
2026-10-15 10:20:38 -07:00
051022bb4b OCR: fix paragraph duplication, caption and folio edge cases
- ParagraphReflow.toText re-appended the previous line on a plain line
  break (the line was already in the output), duplicating text across the
  spread. Only the join is now written, and a glued hyphen removes the
  hyphen that had already been emitted. Drops the now-unused join().
- CaptionFilter: the gapBelow branches were Int/Double and inferred
  Comparable<*>; widen to Double.
- PageNumberDetector: tighten folio geometry and margin isolation.

Add the 83-test JVM/Robolectric suite that locks these down.
2026-10-15 10:20:29 -07:00
3dd15ffa9b PageScribe 0.1: camera capture, background OCR queue, page-numbered text assembly, image archiver 2026-10-15 09:35:13 -07:00