AVAXs3 — synchronizace složek: vylepšení appky + PC stažení¶
Status: NÁVRH (2026-06-08). Navazuje na
s3-data-exchange.md§3 (device folder sync) a §3.4 (_options.json). Řeší: (a) UX přepracování AVAXs3, (b) reálný upload souborů (zatím chybí), (c) stažení sdílených složek na PC z launcheru.
0. Aktuální stav (ověřeno 2026-06-08)¶
Na S3 pod users/{uid}/sync/ jsou jen 2 metadata soubory, žádné fotky:
sync/_options.json 1944 B ← nabídka kategorií (backend)
sync/_selections.json 1068 B ← výběr uživatele (telefon)
_selections.json mají všechny folder_uri = null → SAF výběr složky se
nedokončil/neuložil. A hlavně: appka zatím soubory vůbec nenahrává — uloží jen
výběr. Proto na serveru nejsou žádné fotky.
Závěr: chybí (1) spolehlivý výběr složky + vizuální potvrzení, (2) reálný upload obsahu, (3) PC strana pro stažení. Tento spec to dořeší.
1. AVAXs3 — UX přepracování¶
1.1 Kamera za tlačítkem (ne při startu)¶
Dnes se skener spustí hned po otevření appky. Změna: - Domovská obrazovka = stav (spárováno? kolik složek, poslední záloha) + akce. - Kamera/QR jen po tapu „Spárovat mobil" v Nastavení (a po spárování skrytá). - Sekce: Domů · Synchronizace · Nastavení (spodní navigace / taby).
1.2 Seznam synchronizací¶
Záložka Synchronizace = seznam položek (kategorie z _options.json +
vlastní složky uživatele):
- řádek: ikona dle kind · název · cílový s3_subdir · stav (●Čeká/⟳Nahrávám/✓Hotovo/⚠Chyba) · velikost/počet.
- toggle zapnout/vypnout · tap → detail složky (§1.4).
1.3 Přidat vlastní složku¶
Tlačítko „+ Přidat složku" → SAF OpenDocumentTree → uloží se jako nová sync
položka kind:"custom", s3_subdir = "custom/<slug-názvu>". Vlastní složky se
ukládají lokálně (DataStore) — nezávisí na _options.json.
1.4 Detail složky — obsah + postup zálohy¶
Po tapu na položku:
- Obsah složky: seznam souborů (název, velikost, datum) + souhrn („128 souborů · 412 MB").
Čte se přes SAF DocumentFile.listFiles() z vybraného tree URI.
- Postup zálohy: per soubor stav (čeká/nahrává %/hotovo/chyba) + celkový progress
bar + „24/128 · 78 MB / 412 MB". Tlačítka Zálohovat teď / Pozastavit.
Tím se vyřeší dnešní problém „nevím jestli se něco nahrálo" — uživatel vidí obsah i postup.
2. Reálný upload (F1) — manifest protokol¶
Per s3-data-exchange.md §3.1–3.2. Pro každou zapnutou složku:
Cíl v S3 (company bucket, user prefix):
users/{uid}/sync/<s3_subdir>/
├── manifest.json ← { device_id, updated_at, files:[{path, size, sha256, mtime}] }
└── files/<relativní cesta>
Cyklus:
1. Projdi tree URI (DocumentFile), spočítej sha256 změněných (cache mtime/size → přeskoč nezměněné).
2. Stáhni vzdálený manifest.json (S3Client, existuje).
3. Diff: lokální nový/změněný → upload files/<path> (multipart pro velké) → progress callback.
4. manifest.json zapsat POSLEDNÍ = commit marker (konzument nikdy nečte half-upload).
5. Foreground service + notifikace s progresem; WiFi-only toggle, retry/backoff.
Smazání: soubor zmizí lokálně → varianta A: nech na S3 (jen přibývá), varianta B: tombstone v manifestu + GC. Default A (bezpečnější), GC později.
3. PC strana — stažení sdílených složek z mobilu (launcher2)¶
Odpověď na „jak je načtu v PC + předvyplněný vzor, já jen vyberu kam ukládat".
Nová sekce v launcheru (vedle Zálohování): „📱 Sdílené z mobilu". Launcher má
S3 přístup (SyncAgent/boto3) → čte tytéž users/{uid}/sync/*/.
3.1 Předvyplněný vzor (mapování)¶
Launcher zná výchozí strukturu — uživatel vybere jeden základní PC adresář
(<base>), zbytek se rozloží automaticky:
Mobilní s3_subdir |
Výchozí PC cíl |
|---|---|
photos |
<base>/Fotky/ |
service-settings |
<base>/Nastaveni/ |
apps/<slug> |
<base>/Aplikace/<slug>/ |
custom/<name> |
<base>/<name>/ |
Per-položka lze cíl přepsat. Mapování se uloží do launcher configu.
3.2 Stažení / zrcadlení¶
- Launcher vylistuje
users/{uid}/sync/(prefixy = sdílené složky) + jejichmanifest.json. - Per složka: Stáhnout → mirror
files/*do namapovaného PC adresáře (diff dle sha256, jen nové/změněné). Progress + „naposledy staženo". - Volitelně auto-pull (SyncAgent tick) — periodicky tahá nové soubory z mobilu.
3.3 Tok celkem¶
Telefon (AVAXs3) ──upload──▶ S3 users/{uid}/sync/<subdir>/ ──pull──▶ PC launcher <base>/…
manifest + files manifest + files
4. Datový model na S3 (manifest/v1)¶
// users/{uid}/sync/<subdir>/manifest.json
{
"schema": "sync-manifest/v1",
"device_id": "<android-id>",
"subdir": "photos",
"updated_at": "2026-06-08T…Z",
"files": [
{ "path": "IMG_2026.jpg", "size": 3145728, "sha256": "…", "mtime": 1733… }
]
}
5. Bezpečnost / poznámky¶
- Telefon i launcher = company creds (test) / STS prefix-scope
users/{uid}/sync/(produkce). sha256integrita na obou koncích.- SAF persistovaná oprávnění (
takePersistableUriPermission) přežijí restart. - Foreground service = Android notifikace povinná (jinak systém zabije upload).
- Velké soubory: multipart upload + resumable (uchovat upload stav).
6. Moje změny + doporučení¶
| # | Komponenta | Změna |
|---|---|---|
| 1 | AVAXs3 nav | Domů/Synchronizace/Nastavení taby; kamera za tlačítkem (ne autostart) |
| 2 | AVAXs3 | „+ Přidat složku" (SAF) → vlastní sync položka |
| 3 | AVAXs3 | Detail složky: obsah (DocumentFile.listFiles) + progress UI |
| 4 | AVAXs3 | Reálný upload (manifest + foreground service + progress) — SyncEngine.kt |
| 5 | launcher2 | Sekce „Sdílené z mobilu" + předvyplněný vzor + výběr <base> + mirror pull |
| 6 | backend | (volitelně) manifest/v1 helper; jinak čistě klient↔S3 |
Doporučení:
1. Upload prioritně (§2) — bez něj je vše ostatní prázdné; teď na S3 nejsou žádné fotky.
2. Opravit SAF výběr — dnes folder_uri=null; přidat vizuální potvrzení („Vybráno: …, N souborů").
3. WiFi-only + foreground service od začátku (jinak Android upload zabije / sežere data).
4. Manifest jako jediný kontrakt obou stran → launcher i telefon nezávisle.
5. Pořadí: §1.1 nav + §1.4 detail → §2 upload → §3 PC pull.
7. Fáze¶
- A ✅ AVAXs3 nav rework (Domů/Synchronizace/Nastavení, kamera za tlačítkem) +
detail složky (obsah
DocumentFile.listFiles+ progress) + „➕ Přidat složku". →MainActivity.kt(AppRoot/HomeScreen/SettingsTab/PairingScreen),SettingsScreen.kt(SyncScreen + FolderDetailScreen). APK 0.3.0. - B ✅ Reálný upload —
SyncEngine.kt(walk + diff dle path/size/mtime + sha256 - manifest poslední),
SyncService.kt(foreground service + notifikace progress),SyncState.kt,SyncManifest.kt. S3Client putFile/listUnderPrefix. Manifestusers/{uid}/sync/<subdir>/manifest.json+files/. - C ✅ Launcher
screens/mobile_shared.py— „📱 Sdílené z mobilu" (nav v _dd_storage), předvyplněný vzor_local_subpath, výběr<base>(filedialog, persist~/.avax/mobile_sync.json), download/mirror dle velikosti. - D ⏳ Auto-pull (SyncAgent tick) + smazání/GC + WiFi-only + multipart resumable.
8. v2 Sync engine — škálování (40 GB+), baterie, jen nové soubory¶
Problém (2026-06-08): přidání adresáře se 40 GB fotek appku zaseklo (freeze / ANR). v1 engine neškáluje na desítky tisíc souborů. v2 to přepisuje.
8.1 Tři příčiny záseku (v1)¶
- Enumerace přes
DocumentFile(SyncEngine.enumeraterekurze +listFiles()) — každý soubor = víc IPC dotazů na DocumentsProvider; ~40k souborů = 100k+ IPC + velkýList<DocumentFile>v paměti → minuty / OOM / ANR. Hlavní příčina. - Enumerate-then-process —
SyncServicenapřed projde VŠE +sumOftotals, teprve pak uploaduje → dlouho „appka nic nedělá". - Diff přes remote
manifest.json+ double-read (copy→sha, pak put) každý běh.
8.2 v2 princip¶
| Vrstva | v1 | v2 |
|---|---|---|
| Walk | DocumentFile rekurze, List všeho |
streaming DocumentsContract cursor (1 dotaz/adresář, žádný DocumentFile objekt) |
| „Co je hotové" | remote manifest fetch | lokální index (Room/SQLite) = source of truth |
| Zpracování | enumerate → process | stream: walk → diff → enqueue → upload inkrementálně |
| Běh | foreground service (vše naráz) | WorkManager (resumable, constraint-aware) |
| Velké soubory | 1× PUT (93 MB video) | S3 multipart (resume per part) |
| Integrita | copy+sha (2× čtení) | 1× čtení (stream + DigestInputStream) |
| Concurrency | sériově | bounded 2–3 paralelně |
8.3 Lokální index = jádro „jen nové soubory"¶
Room synced_file(folder_id, doc_id, rel_path, size, mtime, etag, uploaded_at).
- Re-sync: cursor walk → per soubor lookup index[doc_id]; size+mtime sedí →
SKIP (žádné čtení, síť ani sha). Jen chybějící/změněné → upload + upsert.
- Re-sync nezměněného 40 GB adresáře = jen cursor walk (sekundy), nula uploadu,
minimum baterie. (Přesně požadavek „už neřeš synchronizované, jen nové".)
- Self-healing: ztracený index (clear app data) → reconcile ze S3 listingu
(list files/ + size → seed index) → nepřeuploaduje, co na S3 už je.
- Remote manifest.json pro diff netřeba — launcher PC-pull diffuje přes S3 listing
(mobile_shared.py). Manifest volitelný/legacy.
8.4 Baterie & data¶
- WorkManager constraints: velký initial backup default WiFi (unmetered) + nabíjení (nebo battery-not-low) → 40 GB přes noc na nabíječce, baterie se neřeší. Inkrementál (pár fotek) i na baterii.
- Žádné re-čtení/re-sha synced (index) → žádné mrhání CPU/I/O.
- 1 čtení na soubor; WorkManager backoff + zastaví při odpojení a sám pokračuje.
8.5 40 GB initial backup¶
Resumable: WorkManager přežije kill/reboot; index = hotové → po přerušení pokračuje (skip indexovaných). Multipart resume velkých videí. Uživatel zapojí nabíječku na WiFi → projede 40 GB, pak už se těch souborů nikdy nedotkne.
8.6 UI (žádný freeze)¶
- Detail složky NEenumeruje SAF strom — souhrn z indexu (synced X / pending Y / GB)
- WorkInfo progress + malý recent vzorek.
- Přidání složky = jen registrace; walk až v jobu na pozadí.
8.7 Fáze¶
- E1 (fix záseku + jádro): streaming cursor walk (nahradí
DocumentFile) + Room index (jen nové) + stream-process + 1-read upload. ⇒ odstraní freeze + „jen nové" + velká část baterie. - E2 (baterie/robustnost): WorkManager + constraints (WiFi+nabíjení) + bounded concurrency + backoff.
- E3 (velké soubory): S3 multipart resumable (>8 MB).
9. Související¶
docs/spec/s3-data-exchange.md— §3 device sync, §3.4_options.json, QR pairing.docs/spec/android-app-distribution.md— AVAXs3 store/updater (sesterský feature).desktop/launcher2/screens/backup.py— vzor S3 sync UI (SyncAgent).backend/app/services/devices.py—DeviceSyncDirmodel.