Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ import com.ichi2.anki.snackbar.showSnackbar
import com.ichi2.anki.study.StudyAvailability
import com.ichi2.anki.study.StudyCompletionUi
import com.ichi2.anki.study.currentDeckStudyState
import com.ichi2.anki.sync.MediaSyncBottomSheet
import com.ichi2.anki.sync.MeteredSyncPolicy
import com.ichi2.anki.sync.launchCatchingRequiringOneWaySyncDiscardUndo
import com.ichi2.anki.sync.syncStatus
Expand Down Expand Up @@ -1296,6 +1297,11 @@ open class DeckPicker :
val progressIndicator =
syncItem.actionView
?.findViewById<LinearProgressIndicator>(R.id.progress_indicator)
(MenuItemCompat.getActionProvider(syncItem) as? SyncActionProvider)
?.setLongClickAction(getString(CommonString.media_sync_status_title)) {
Timber.i("DeckPicker:: Sync button long pressed")
showMediaSyncStatus()
}

val workManager = WorkManager.getInstance(this)
val flow = workManager.getWorkInfosForUniqueWorkFlow(UniqueWorkNames.SYNC_MEDIA)
Expand All @@ -1315,6 +1321,11 @@ open class DeckPicker :
}
}

private fun showMediaSyncStatus() {
if (supportFragmentManager.findFragmentByTag(MediaSyncBottomSheet.TAG) != null) return
MediaSyncBottomSheet().show(supportFragmentManager, MediaSyncBottomSheet.TAG)
}

private fun setupSearchIcon(menuItem: MenuItem) {
menuItem.setOnActionExpandListener(
object : MenuItem.OnActionExpandListener {
Expand Down Expand Up @@ -1479,9 +1490,7 @@ open class DeckPicker :
toolbarSearchItem?.collapseActionView()
val actionProvider = MenuItemCompat.getActionProvider(item) as? SyncActionProvider
if (actionProvider?.isProgressShown == true) {
launchCatchingTask {
monitorMediaSync(this@DeckPicker)
}
showMediaSyncStatus()
} else {
sync()
}
Expand Down
59 changes: 0 additions & 59 deletions AnkiDroid/src/main/java/com/ichi2/anki/Sync.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@
package com.ichi2.anki

import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import androidx.work.NetworkType
import anki.collection.Progress
import anki.sync.SyncCollectionResponse
import anki.sync.syncAuth
import com.google.android.material.snackbar.Snackbar
import com.ichi2.anki.CollectionManager.TR
import com.ichi2.anki.CollectionManager.withCol
import com.ichi2.anki.CollectionManager.withColExclusive
Expand All @@ -25,23 +23,12 @@ import com.ichi2.anki.sync.MeteredSyncPolicy
import com.ichi2.anki.sync.SyncAuth
import com.ichi2.anki.sync.fullUploadOrDownload
import com.ichi2.anki.sync.syncCollection
import com.ichi2.anki.ui.internationalization.sentenceCase
import com.ichi2.anki.worker.SyncMediaWorker
import com.ichi2.preferences.VersatileTextWithASwitchPreference
import com.ichi2.utils.NetworkUtils
import com.ichi2.utils.dismissSafely
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.ankiweb.rsdroid.Backend
import net.ankiweb.rsdroid.exceptions.BackendInterruptedException
import net.ankiweb.rsdroid.exceptions.BackendSyncException
import timber.log.Timber
import kotlin.time.Duration.Companion.milliseconds

object SyncPreferences {
const val PREF_CURRENT_SYNC_URI = "currentSyncUri"
Expand Down Expand Up @@ -359,52 +346,6 @@ fun shouldFetchMedia(): Boolean {
(shouldFetchMedia == ShouldFetchMedia.ONLY_UNMETERED && !NetworkUtils.isActiveNetworkMetered())
}

suspend fun monitorMediaSync(deckPicker: DeckPicker) {
val backend = CollectionManager.getBackend()
val scope = CoroutineScope(Dispatchers.IO)
var isAborted = false

val dialog =
withContext(Dispatchers.Main) {
AlertDialog
.Builder(deckPicker)
.setTitle(with(deckPicker) { TR.sentenceCase.mediaSyncLog })
.setMessage("")
.setPositiveButton(CommonString.dialog_continue) { _, _ ->
scope.cancel()
}.setNegativeButton(TR.syncAbortButton()) { _, _ ->
isAborted = true
cancelMediaSync(backend)
}.show()
}

fun showMessage(msg: String) = deckPicker.showSnackbar(msg, Snackbar.LENGTH_SHORT)

scope.launch {
try {
while (true) {
// this will throw if the sync exited with an error
val resp = backend.mediaSyncStatus()
if (!resp.active) {
break
}
val text = resp.progress.run { "$added\n$removed\n$checked" }
dialog.setMessage(text)
delay(100.milliseconds)
}
showMessage(if (isAborted) TR.syncMediaAborted() else TR.syncMediaComplete())
} catch (_: BackendInterruptedException) {
showMessage(TR.syncMediaAborted())
} catch (_: CancellationException) {
// do nothing
} catch (_: Exception) {
showMessage(TR.syncMediaFailed())
} finally {
dialog.dismissSafely()
}
}
}

/**
* Show a simple snackbar message or notification if the activity is not in foreground
* @param messageResource String resource for message
Expand Down
27 changes: 26 additions & 1 deletion AnkiDroid/src/main/java/com/ichi2/anki/SyncActionProvider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import androidx.appcompat.widget.AppCompatImageButton
import androidx.core.view.ViewCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
import androidx.core.view.isVisible
import com.google.android.material.progressindicator.LinearProgressIndicator
import com.ichi2.anki.compat.setTooltipTextCompat
Expand All @@ -21,6 +23,8 @@ class SyncActionProvider(

private var progressIndicator: LinearProgressIndicator? = null
private var syncButton: AppCompatImageButton? = null
private var longClickAction: (() -> Unit)? = null
private var longClickListener: View.OnLongClickListener? = null

val isProgressShown: Boolean
get() = progressIndicator?.isVisible == true
Expand All @@ -36,6 +40,15 @@ class SyncActionProvider(
val view = inflater.inflate(R.layout.view_sync_progress_layout, null)

progressIndicator = view.findViewById(R.id.progress_indicator)
longClickListener =
View.OnLongClickListener {
val action = longClickAction
if (!forItem.isEnabled || action == null) {
return@OnLongClickListener false
}
action()
true
}
syncButton =
view.findViewById<AppCompatImageButton>(R.id.button).apply {
setOnClickListener {
Expand All @@ -44,12 +57,24 @@ class SyncActionProvider(
}
activity.onOptionsItemSelected(forItem)
}
setOnLongClickListener(longClickListener)
}

return view
}

fun setTooltipText(value: CharSequence) {
syncButton?.setTooltipTextCompat(value)
syncButton?.run {
setTooltipTextCompat(value)
setOnLongClickListener(longClickListener)
}
}

fun setLongClickAction(
label: CharSequence,
action: () -> Unit,
) {
longClickAction = action
syncButton?.let { ViewCompat.replaceAccessibilityAction(it, AccessibilityActionCompat.ACTION_LONG_CLICK, label, null) }
}
}
14 changes: 14 additions & 0 deletions AnkiDroid/src/main/java/com/ichi2/anki/settings/Prefs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import com.ichi2.anki.common.preferences.NotificationPreferences
import com.ichi2.anki.common.preferences.sharedPrefs
import com.ichi2.anki.common.utils.isRunningAsUnitTest
import com.ichi2.anki.libanki.DeckId
import com.ichi2.anki.libanki.EpochMilliseconds
import com.ichi2.anki.preferences.SharedPreferencesProvider
import com.ichi2.anki.settings.enums.AppTheme
import com.ichi2.anki.settings.enums.DayTheme
Expand Down Expand Up @@ -270,6 +271,19 @@ open class PrefsRepository protected constructor(
val shouldFetchMedia: ShouldFetchMedia
get() = getEnum(R.string.sync_fetch_media_key, ShouldFetchMedia.ALWAYS)

val lastMediaSyncTime: EpochMilliseconds by longPref(CommonPreferenceKey.last_media_sync_time_key, defaultValue = 0L)
val lastMediaSyncFailed by booleanPref(CommonPreferenceKey.last_media_sync_failed_key, defaultValue = false)

fun recordMediaSync(
time: EpochMilliseconds,
failed: Boolean,
) {
sharedPrefs.edit {
putLong(key(CommonPreferenceKey.last_media_sync_time_key), time)
putBoolean(key(CommonPreferenceKey.last_media_sync_failed_key), failed)
}
}

var networkTimeoutSecs by intPref(R.string.sync_io_timeout_secs_key, defaultValue = 60)

//region Custom sync server
Expand Down
Loading
Loading