Files
photog/android
puttaalu 2143b95657 Fix Android manage thumbnails and multi-select picker
Thumbnails were blank on the manage screen because the app fetched them from
the static /thumbs/ path, which a front-door botwall on `location /` blocks for
a non-browser client even though /api/ (proxied to the app) works. Add an authed
GET /api/thumb/{slug} that serves the thumbnail JPEG from public/thumbs/, and
have the app fetch from there. Also show the thumbnail in the edit dialog, not
just the list rows.

Construct the photo picker with an explicit maxItems so it reliably opens in
multi-select mode; the no-arg PickMultipleVisualMedia default has been seen to
present single-select on some devices.
2026-10-19 15:33:25 +02:00
..
2026-08-13 22:01:20 +02:00
2026-08-13 22:01:20 +02:00
2026-08-13 22:01:20 +02:00
2026-08-13 22:01:20 +02:00
2026-08-13 22:01:20 +02:00
2026-08-13 22:01:20 +02:00
2026-08-13 22:01:20 +02:00

Gallery Upload — Android client

Native uploader and manager for the photogallery server. Pick or share one or many photos, add caption/date/tags, and post. A Manage screen lists the published posts and edits their caption/date/tags or deletes them (to the server's trash) over the same /api/* endpoints the web /manage uses. Credentials are entered once and sealed with a key held in the Android Keystore.

The server is not modified by this app. Everything it needs, it does client side; the management calls send no browser headers, so they pass the server's same-origin (CSRF) check as a non-browser client.

Build

cd android
nix develop          # Android SDK 36, build-tools 36.0.0, JDK 17, gradle
gradle wrapper       # first time only, generates ./gradlew
./gradlew assembleDebug
adb install -r app/build/outputs/apk/debug/app-debug.apk

The dev shell exports ANDROID_HOME, JAVA_HOME and a GRADLE_OPTS that points AGP at the nix-provided aapt2. Without that last one AGP downloads a generic-linux aapt2 from Maven which will not run on NixOS.

Release signing

app/build.gradle.kts wires up a release signing config only when android/keystore.properties exists. Both that file and *.jks are gitignored — keep the keystore outside the repo and back it up somewhere you will still have in five years, because losing it means you can never upgrade an installed build in place.

mkdir -p ~/keys
keytool -genkeypair -v \
  -keystore ~/keys/photogallery-release.jks \
  -alias photogallery \
  -keyalg RSA -keysize 4096 -validity 10000

Then write android/keystore.properties:

storeFile=/home/you/keys/photogallery-release.jks
storePassword=…
keyAlias=photogallery
keyPassword=…
./gradlew assembleRelease

Without keystore.properties the release build still runs, it just comes out unsigned.

How it maps onto the server

Server behaviour What the app does about it
Slug derived from the uploaded filename Sends yyyy-MM-dd-HHmmss.jpg, generated once at enqueue
Same filename overwrites the same post Reused across retries, which makes retries idempotent
Uploaded file published verbatim as "full size" Strips EXIF, including GPS, before upload
Extension check rejects HEIC Converts HEIC to JPEG; JPEG and PNG pass through losslessly
nginx client_max_body_size 20M Steps quality, then resolution, if the result exceeds 18 MiB
Thumbnailer ignores EXIF orientation Bakes rotation into the pixels rather than relying on the tag
nginx only proxies /upload, so /health 404s Tests credentials with GET /upload instead
Replies uploaded <slug>.jpg in plain text Parses that into <base>/photo/<slug>/ for the "Open" link