feat(android): sync device albums with a server album
Add phone album sync to the Android client: MediaStore source (device albums), a Room index with upload provenance, on-device previews, and a mixed server + device album list where a linked pair collapses into a single device card with a link indicator and mode label. Three modes over one pipeline: A (off), B (upload copies, originals stay local), C (upload originals and offload them locally to previews). The sync engine tracks device changes (add/remove/rename/move/content), matches assets to server photos by sha256 content hash, deletes only this client's uploads, and reflects server changes without touching local originals. Offload is automatic; the zoomed viewer pulls the server original. Backend: expose the original content checksum in photo data (Postgres and SQLite), additive with no migration. Also anchor the `.gitignore` `data/` rule to `/data/` so the Android `com.galo.android.data` package is tracked, and add the halo launcher icon.
This commit is contained in:
@@ -100,6 +100,48 @@ While an album screen is active the client holds
|
||||
`GET /api/sse/albums/:id?token=` and backfills missed changes via
|
||||
`GET /api/albums/:id/photos?since=` after a reconnect.
|
||||
|
||||
## Device albums and synchronization
|
||||
|
||||
The album list mixes the server's albums with the device's own albums (MediaStore
|
||||
buckets). Each card is tagged with its source (`Сервер` / `Телефон`), and a linked
|
||||
pair shows the connection both ways: the device album shows `→ <server album>`, the
|
||||
server album shows `источник: <device albums>`.
|
||||
|
||||
- **Access.** Enumerating device albums needs media read access: `READ_MEDIA_IMAGES`
|
||||
and `READ_MEDIA_VIDEO` on Android 13+, or `READ_EXTERNAL_STORAGE` on Android 12
|
||||
and lower. The app requests it at runtime; without (or with partial) access the
|
||||
device albums are simply not shown, and server albums keep working.
|
||||
- **Synchronization modes** are chosen per device album from the album screen
|
||||
(swipe right, or the settings icon):
|
||||
- **Выключена (A)** — local only. Previews are generated on the device and cached
|
||||
in the app's private cache; the album makes **no server requests**.
|
||||
- **Копии оригиналов на сервере (B)** — originals stay on the device, copies are
|
||||
uploaded to a chosen server album.
|
||||
- **Перенос оригиналов на сервер (C)** — originals move to the server; the device
|
||||
keeps previews. Freeing space replaces the original in MediaStore only after the
|
||||
user confirms the OS write request, in batches.
|
||||
- Previews are capped at a 1600px long side (JPEG q80) and generated on the device,
|
||||
including a poster frame for videos.
|
||||
- **Deduplication.** A device asset is matched to a server photo by the sha256 of the
|
||||
bytes that leave the device (the server exposes each original's checksum). Content
|
||||
already present is linked instead of re-uploaded; an edited asset is re-uploaded and
|
||||
its previous upload removed; a pure rename is propagated via `PATCH /api/photos/:id`.
|
||||
- **Deletions.** The server photo is deleted only when this client uploaded it, its
|
||||
content is gone from the device and no other linked album still holds it. Photos the
|
||||
client did not upload are never touched, and a photo already gone from the server is
|
||||
only reflected in the local index.
|
||||
- **Background.** Synchronization runs on app start and while the app is open (a
|
||||
MediaStore content observer), and periodically in the background via WorkManager.
|
||||
The periodic pass honors the **Только Wi-Fi** setting. Freeing space in mode C is
|
||||
never automatic — it always waits for the user's confirmation.
|
||||
- **Provenance.** Mode C writes a marker into the EXIF of the preview it leaves
|
||||
behind (server album/photo, original hash, mode). After a reinstall the app reads
|
||||
these markers to restore the album link and which uploads are its own; unmarked
|
||||
media is left alone.
|
||||
- **Reversibility.** Leaving mode C warns first and offers to fetch the originals
|
||||
back from the server (space permitting); declining keeps previews only. Unlinking
|
||||
an album drops the local link and never deletes photos on the server.
|
||||
|
||||
## Testing
|
||||
|
||||
```sh
|
||||
|
||||
Reference in New Issue
Block a user