fix: robustez de sync comprovada no emulador (duplicatas eliminadas)

- ApiClient: IllegalArgumentException de URL vira Network (banner, sem crash);
  DTOs com listas nullable (servidor podia enviar categories/tags: null)
- SyncEngine: mutex global serializa runs (logs BEGIN/END); resolveConflict
  idempotente (não duplica cópia de conflito); registra rekey:<local>→<server>
  em meta para o editor aberto sobreviver à troca de id do create
- ViewModel: resolve(id) em save/delete; createLocal enfileira create único
- Outbox: índice único (noteId, op); DB v2 com fallbackToDestructiveMigration
- proguard: regras corretas de kotlinx.serialization (Companion.serializer)
- README: seção 'Testado em' com os cenários de emulador executados
This commit is contained in:
lgcosta committed 2026-11-04 19:28:02 -03:00
1 parent 2f63c7a2f5
commit 9e0bcc3e9b
9 files changed
+133 -44

No files matched your search

+12 -2
View File
@@ -121,9 +121,19 @@ adb install -r app/build/outputs/apk/debug/app-debug.apk
## Testado em
- `assembleDebug` limpo com JDK 21 / AGP 8.7.3 / SDK 35 (Linux, sem device — verificação de
compilação + lint básico).
- **Build**: `assembleRelease` limpo (JDK 21 / AGP 8.7.3 / Kotlin 2.0.21 / SDK 35), APK assinado
com R8 ≈ **1,6 MB** (debug ≈ 17 MB).
- **Emulador Android 15 (x86_64, Pixel 6)** contra o servidor notas-online real, cenários:
- configuração de servidor com *Testar conexão* → OK;
- criar nota offline → autosave por ociosidade (10 s) → `POST` único (sem duplicatas);
- editar offline → fila resiste à queda de rede → push automático ao restabelecer (`ConnectivityManager`);
- nota criada na Web aparece no device via pull;
- **conflito real**: device e Web editam a mesma nota → HTTP 409 (`base_hash`) → nota principal
adota o servidor e o texto do Android vira exatamente **uma** cópia "… (conflito)"
(idempotente; sincronizações serializadas por mutex + índices únicos na outbox).
- Integração com a API: mesmos contratos do web client (`base_hash`/409, categorias, revisões).
O servidor normaliza `categories`/`tags` vazios como `[]` (null quebrava clientes com
tipos não-nuláveis, ex. kotlinx.serialization).
## Roadmap honesto (não implementado aqui)