From 33b12d072426cacc8d87c65d0750aa5abc3a59fe Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 22 Jul 2022 23:24:56 +1000 Subject: [PATCH] Use backend to update sync status; trigger media sync on auto full sync + Update sync status after normal media sync completes Closes #11902 --- .../main/java/com/ichi2/anki/DeckPicker.kt | 14 +++++--- .../src/main/java/com/ichi2/anki/Sync.kt | 26 +++++++++----- .../com/ichi2/libanki/sync/BackendSync.kt | 5 +++ .../main/java/com/ichi2/utils/SyncStatus.kt | 34 +++++++++---------- 4 files changed, 49 insertions(+), 30 deletions(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt index 47ef63fdead4..1df809255746 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt @@ -663,15 +663,20 @@ open class DeckPicker : @VisibleForTesting protected open suspend fun displaySyncBadge(menu: Menu) { - val syncStatus = withOpenColOrNull { SyncStatus.getSyncStatus(this) } + val auth = syncAuth() + val syncStatus = withOpenColOrNull { + SyncStatus.getSyncStatus(this, auth) + } if (syncStatus == null) { return } val syncMenu = menu.findItem(R.id.action_sync) when (syncStatus) { SyncStatus.BADGE_DISABLED, SyncStatus.NO_CHANGES, SyncStatus.INCONCLUSIVE -> { - BadgeDrawableBuilder.removeBadge(syncMenu) - syncMenu.setTitle(R.string.button_sync) + syncMenu?.let { + BadgeDrawableBuilder.removeBadge(it) + it.setTitle(R.string.button_sync) + } } SyncStatus.HAS_CHANGES -> { // Light orange icon @@ -1519,14 +1524,13 @@ open class DeckPicker : override fun sync(conflict: ConflictResolution?) { val preferences = AnkiDroidApp.getSharedPrefs(baseContext) val hkey = preferences.getString("hkey", "") - val hostNum = HostNumFactory.getInstance(baseContext).getHostNum() if (hkey!!.isEmpty()) { Timber.w("User not logged in") mPullToSyncWrapper.isRefreshing = false showSyncErrorDialog(SyncErrorDialog.DIALOG_USER_NOT_LOGGED_IN_SYNC) } else { if (!BackendFactory.defaultLegacySchema) { - handleNewSync(hkey, hostNum ?: 0, conflict) + handleNewSync(conflict) } else { Connection.sync( mSyncListener, diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/Sync.kt b/AnkiDroid/src/main/java/com/ichi2/anki/Sync.kt index a868630fdb90..272989a4ecc0 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/Sync.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/Sync.kt @@ -42,15 +42,22 @@ import net.ankiweb.rsdroid.Backend import net.ankiweb.rsdroid.exceptions.BackendSyncException import timber.log.Timber +fun DeckPicker.syncAuth(): SyncAuth? { + val preferences = AnkiDroidApp.getSharedPrefs(this) + val hkey = preferences.getString("hkey", null) + val hostNum = HostNumFactory.getInstance(baseContext).getHostNum() + return hkey?.let { + syncAuth { + this.hkey = hkey + this.hostNumber = hostNum ?: 0 + } + } +} + fun DeckPicker.handleNewSync( - hkey: String, - hostNum: Int, conflict: Connection.ConflictResolution? ) { - val auth = syncAuth { - this.hkey = hkey - this.hostNumber = hostNum - } + val auth = this.syncAuth() ?: return val deckPicker = this launchCatchingTask { try { @@ -121,6 +128,7 @@ private suspend fun handleNormalSync( SyncCollectionResponse.ChangesRequired.NO_CHANGES -> { // a successful sync returns this value deckPicker.showSyncLogMessage(R.string.sync_database_acknowledge, output.serverMessage) + deckPicker.refreshState() // kick off media sync - future implementations may want to run this in the // background instead handleMediaSync(deckPicker, auth) @@ -128,12 +136,10 @@ private suspend fun handleNormalSync( SyncCollectionResponse.ChangesRequired.FULL_DOWNLOAD -> { handleDownload(deckPicker, auth) - handleMediaSync(deckPicker, auth) } SyncCollectionResponse.ChangesRequired.FULL_UPLOAD -> { handleUpload(deckPicker, auth) - handleMediaSync(deckPicker, auth) } SyncCollectionResponse.ChangesRequired.FULL_SYNC -> { @@ -178,6 +184,8 @@ private suspend fun handleDownload( reopen(afterFullSync = true) } } + deckPicker.refreshState() + handleMediaSync(deckPicker, auth) } Timber.i("Full Download Completed") @@ -200,6 +208,8 @@ private suspend fun handleUpload( reopen(afterFullSync = true) } } + deckPicker.refreshState() + handleMediaSync(deckPicker, auth) } Timber.i("Full Upload Completed") deckPicker.showSyncLogMessage(R.string.sync_log_uploading_message, "") diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/sync/BackendSync.kt b/AnkiDroid/src/main/java/com/ichi2/libanki/sync/BackendSync.kt index 02cd6b5ccc0e..1f6ace09b6c4 100644 --- a/AnkiDroid/src/main/java/com/ichi2/libanki/sync/BackendSync.kt +++ b/AnkiDroid/src/main/java/com/ichi2/libanki/sync/BackendSync.kt @@ -18,6 +18,7 @@ package com.ichi2.libanki.sync import anki.sync.SyncAuth import anki.sync.SyncCollectionResponse +import anki.sync.SyncStatusResponse import com.ichi2.libanki.CollectionV16 fun CollectionV16.syncLogin(username: String, password: String): SyncAuth { @@ -39,3 +40,7 @@ fun CollectionV16.fullDownload(auth: SyncAuth) { fun CollectionV16.syncMedia(auth: SyncAuth) { return backend.syncMedia(input = auth) } + +fun CollectionV16.syncStatus(auth: SyncAuth): SyncStatusResponse { + return backend.syncStatus(input = auth) +} diff --git a/AnkiDroid/src/main/java/com/ichi2/utils/SyncStatus.kt b/AnkiDroid/src/main/java/com/ichi2/utils/SyncStatus.kt index eac41f2acbf0..bac7844847a5 100644 --- a/AnkiDroid/src/main/java/com/ichi2/utils/SyncStatus.kt +++ b/AnkiDroid/src/main/java/com/ichi2/utils/SyncStatus.kt @@ -16,10 +16,11 @@ package com.ichi2.utils +import anki.sync.SyncAuth +import anki.sync.SyncStatusResponse import com.ichi2.anki.AnkiDroidApp import com.ichi2.libanki.Collection -import timber.log.Timber -import java.util.function.Supplier +import net.ankiweb.rsdroid.BackendFactory enum class SyncStatus { INCONCLUSIVE, NO_ACCOUNT, NO_CHANGES, HAS_CHANGES, FULL_SYNC, BADGE_DISABLED; @@ -29,26 +30,16 @@ enum class SyncStatus { private var sMarkedInMemory = false @JvmStatic - fun getSyncStatus(getCol: Supplier): SyncStatus { - return try { - val col = getCol.get() - // may fail when the collection is closed for a full sync, - // as col.db is null - getSyncStatus(col) - } catch (e: Exception) { - Timber.w(e) - return INCONCLUSIVE - } - } - - @JvmStatic - fun getSyncStatus(col: Collection): SyncStatus { + fun getSyncStatus(col: Collection, auth: SyncAuth?): SyncStatus { if (isDisabled) { return BADGE_DISABLED } - if (!isLoggedIn) { + if (auth == null) { return NO_ACCOUNT } + if (!BackendFactory.defaultLegacySchema) { + return syncStatusFromRequired(col.newBackend.backend.syncStatus(auth).required) + } if (col.schemaChanged()) { return FULL_SYNC } @@ -59,6 +50,15 @@ enum class SyncStatus { } } + private fun syncStatusFromRequired(required: SyncStatusResponse.Required?): SyncStatus { + return when (required) { + SyncStatusResponse.Required.NO_CHANGES -> NO_CHANGES + SyncStatusResponse.Required.NORMAL_SYNC -> HAS_CHANGES + SyncStatusResponse.Required.FULL_SYNC -> FULL_SYNC + SyncStatusResponse.Required.UNRECOGNIZED, null -> TODO("unexpected required response") + } + } + private val isDisabled: Boolean get() { val preferences = AnkiDroidApp.getSharedPrefs(AnkiDroidApp.getInstance())