contacts: инкрементальная синхронизация по CTag (Этап 2, DAV)

CardDavClient.collectionSignature() — дешёвый PROPFIND depth:1 за CTag книг
(расширение CalendarServer, Nextcloud; фолбэк sync-token), без address-data.
ContactsRepository.syncContacts: перед полной закачкой всех vCard сверяет
подпись коллекций; не менялось → отдаём кэш (Room), не качаем. Ускоряет
pull-to-refresh (раньше — полный PROPFIND ~500 vCard каждый раз).
+4 unit-теста на parseCollectionSignature (regex-парсер, без Android XmlPull).
This commit is contained in:
b-dev-mobile
2026-07-09 06:44:49 +00:00
parent 2709dba03f
commit 00daa980a8
3 changed files with 120 additions and 2 deletions
@@ -74,6 +74,18 @@ class ContactsRepository(context: Context) {
) {
return dao.getAll(key).map { it.toItem() }
}
// Дешёвая проверка CTag: если адресные книги не менялись (и бэкфиллы сделаны) —
// полный PROPFIND всех vCard не нужен, отдаём кэш. Это ускоряет pull-to-refresh.
val needsBackfill = needsPhotoBackfill || needsDetailsBackfill
val client = authedClient(session)
val userId = session.davUserId ?: OcsUserResolver.resolveDavUserId(session)
val currentSig = CardDavClient.collectionSignature(client, session.serverUrl, userId)
if (!needsBackfill && currentSig != null &&
currentSig == prefs.getString(ctagKey(key), null)
) {
prefs.edit().putLong(lastSyncKey(key), System.currentTimeMillis()).apply()
return dao.getAll(key).map { it.toItem() }
}
val remote = fetchRemoteContacts(session)
val entities = remote.map { contact ->
ContactEntity(
@@ -95,11 +107,12 @@ class ContactsRepository(context: Context) {
)
}
dao.replaceAll(key, entities)
prefs.edit()
val editor = prefs.edit()
.putLong(lastSyncKey(key), System.currentTimeMillis())
.putBoolean(photoSyncDoneKey(key), true)
.putBoolean(detailsSyncDoneKey(key), true)
.apply()
if (currentSig != null) editor.putString(ctagKey(key), currentSig) else editor.remove(ctagKey(key))
editor.apply()
return entities.map { it.toItem() }
}
@@ -190,6 +203,8 @@ class ContactsRepository(context: Context) {
private fun lastSyncKey(accountKey: String) = "last_sync_$accountKey"
private fun ctagKey(accountKey: String) = "ctag_$accountKey"
private fun photoSyncDoneKey(accountKey: String) = "photo_sync_done_$accountKey"
private fun detailsSyncDoneKey(accountKey: String) = "details_sync_done_$accountKey"