同步进度功能补充 (#4352)
* 进度同步补充:
在初始阅读界面时拉取云端进度 --> 初始阅读界面保留原有拉取逻辑,除此之外的阅读界面处于已恢复状态时执行同步云端进度,若云端进度快于本地则弹窗提醒是否同步
无 --> 监听到网络切换到有网环境时执行同步云端进度,若云端进度快于本地则弹窗提醒是否同步
页面进入已暂停状态时上传阅读进度 --> 页面进入已暂停状态时执行同步云端进度
以上新增的同步云端进度逻辑为:如果本地进度快于服务器进度或者没有进度则进行上传,如果慢与服务器进度则返回云端进度按需执行
* 点击区域增加同步阅读进度功能
* 阅读界面初始化同步逻辑修改
* merge
* merge
* merge
* 增加覆盖云端进度
* merge
* merge
* 多次弹窗问题修改
* 补充的同步功能增加配置开关
* Revert "优化"
This reverts commit 8d4fba2e2f.
* merge
---------
Co-authored-by: mozhu <lcl_em@163.com>
This commit is contained in:
committed by
GitHub
co-authored by
mozhu
parent
afa9ac2e97
commit
fdd88a8510
@@ -88,6 +88,7 @@ object PreferKey {
|
||||
const val importKeepGroup = "importKeepGroup"
|
||||
const val screenOrientation = "screenOrientation"
|
||||
const val syncBookProgress = "syncBookProgress"
|
||||
const val syncBookProgressPlus = "syncBookProgressPlus"
|
||||
const val cronet = "Cronet"
|
||||
const val antiAlias = "antiAlias"
|
||||
const val bitmapCacheSize = "bitmapCacheSize"
|
||||
|
||||
@@ -451,6 +451,8 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
val syncBookProgress get() = appCtx.getPrefBoolean(PreferKey.syncBookProgress, true)
|
||||
|
||||
val syncBookProgressPlus get() = appCtx.getPrefBoolean(PreferKey.syncBookProgressPlus, false)
|
||||
|
||||
val mediaButtonOnExit get() = appCtx.getPrefBoolean("mediaButtonOnExit", true)
|
||||
|
||||
val readAloudByMediaButton
|
||||
|
||||
@@ -199,12 +199,47 @@ object ReadBook : CoroutineScope by MainScope() {
|
||||
nextTextChapter?.clearSearchResult()
|
||||
}
|
||||
|
||||
fun uploadProgress() {
|
||||
fun uploadProgress(successAction: (() -> Unit)? = null) {
|
||||
book?.let {
|
||||
launch(IO) {
|
||||
AppWebDav.uploadBookProgress(it)
|
||||
ensureActive()
|
||||
it.update()
|
||||
successAction?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步阅读进度
|
||||
* 如果当前进度快于服务器进度或者没有进度进行上传,如果慢与服务器进度则执行传入动作
|
||||
*/
|
||||
fun syncProgress(
|
||||
newProgressAction: ((progress: BookProgress) -> Unit)? = null,
|
||||
uploadSuccessAction: (() -> Unit)? = null,
|
||||
syncSuccessAction: (() -> Unit)? = null) {
|
||||
if (!AppConfig.syncBookProgress) return
|
||||
book?.let {
|
||||
Coroutine.async {
|
||||
AppWebDav.getBookProgress(it)
|
||||
}.onError {
|
||||
AppLog.put("拉取阅读进度失败", it)
|
||||
}.onSuccess { progress ->
|
||||
if (progress == null || progress.durChapterIndex < it.durChapterIndex ||
|
||||
(progress.durChapterIndex == it.durChapterIndex
|
||||
&& progress.durChapterPos < it.durChapterPos)) {
|
||||
// 服务器没有进度或者进度比服务器快,上传现有进度
|
||||
Coroutine.async {
|
||||
AppWebDav.uploadBookProgress(BookProgress(it), uploadSuccessAction)
|
||||
it.save()
|
||||
}
|
||||
} else if (progress.durChapterIndex > it.durChapterIndex ||
|
||||
progress.durChapterPos > it.durChapterPos) {
|
||||
// 进度比服务器慢,执行传入动作
|
||||
newProgressAction?.invoke(progress)
|
||||
} else {
|
||||
syncSuccessAction?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -893,6 +928,8 @@ object ReadBook : CoroutineScope by MainScope() {
|
||||
|
||||
fun notifyBookChanged()
|
||||
|
||||
fun sureNewProgress(progress: BookProgress)
|
||||
|
||||
fun cancelSelect()
|
||||
}
|
||||
|
||||
|
||||
@@ -7,15 +7,10 @@ import android.content.res.Configuration
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.Looper
|
||||
import android.view.Gravity
|
||||
import android.view.InputDevice
|
||||
import android.view.KeyEvent
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.*
|
||||
import androidx.activity.addCallback
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.view.get
|
||||
import androidx.core.view.isVisible
|
||||
@@ -24,12 +19,7 @@ import androidx.lifecycle.lifecycleScope
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
|
||||
import io.legado.app.BuildConfig
|
||||
import io.legado.app.R
|
||||
import io.legado.app.constant.AppConst
|
||||
import io.legado.app.constant.AppLog
|
||||
import io.legado.app.constant.BookType
|
||||
import io.legado.app.constant.EventBus
|
||||
import io.legado.app.constant.PreferKey
|
||||
import io.legado.app.constant.Status
|
||||
import io.legado.app.constant.*
|
||||
import io.legado.app.data.appDb
|
||||
import io.legado.app.data.entities.Book
|
||||
import io.legado.app.data.entities.BookChapter
|
||||
@@ -39,14 +29,7 @@ import io.legado.app.exception.NoStackTraceException
|
||||
import io.legado.app.help.AppWebDav
|
||||
import io.legado.app.help.IntentData
|
||||
import io.legado.app.help.TTS
|
||||
import io.legado.app.help.book.BookHelp
|
||||
import io.legado.app.help.book.ContentProcessor
|
||||
import io.legado.app.help.book.isAudio
|
||||
import io.legado.app.help.book.isEpub
|
||||
import io.legado.app.help.book.isLocal
|
||||
import io.legado.app.help.book.isLocalTxt
|
||||
import io.legado.app.help.book.isMobi
|
||||
import io.legado.app.help.book.removeType
|
||||
import io.legado.app.help.book.*
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.help.config.ReadBookConfig
|
||||
import io.legado.app.help.config.ReadTipConfig
|
||||
@@ -59,6 +42,7 @@ import io.legado.app.lib.theme.accentColor
|
||||
import io.legado.app.model.ReadAloud
|
||||
import io.legado.app.model.ReadBook
|
||||
import io.legado.app.model.analyzeRule.AnalyzeRule
|
||||
import io.legado.app.receiver.NetworkChangedListener
|
||||
import io.legado.app.model.localBook.EpubFile
|
||||
import io.legado.app.model.localBook.MobiFile
|
||||
import io.legado.app.receiver.TimeBatteryReceiver
|
||||
@@ -69,7 +53,7 @@ import io.legado.app.ui.book.bookmark.BookmarkDialog
|
||||
import io.legado.app.ui.book.changesource.ChangeBookSourceDialog
|
||||
import io.legado.app.ui.book.changesource.ChangeChapterSourceDialog
|
||||
import io.legado.app.ui.book.info.BookInfoActivity
|
||||
import io.legado.app.ui.book.read.config.AutoReadDialog
|
||||
import io.legado.app.ui.book.read.config.*
|
||||
import io.legado.app.ui.book.read.config.BgTextConfigDialog.Companion.BG_COLOR
|
||||
import io.legado.app.ui.book.read.config.BgTextConfigDialog.Companion.TEXT_COLOR
|
||||
import io.legado.app.ui.book.read.config.MoreConfigDialog
|
||||
@@ -96,38 +80,10 @@ import io.legado.app.ui.replace.ReplaceRuleActivity
|
||||
import io.legado.app.ui.replace.edit.ReplaceEditActivity
|
||||
import io.legado.app.ui.widget.PopupAction
|
||||
import io.legado.app.ui.widget.dialog.PhotoDialog
|
||||
import io.legado.app.utils.ACache
|
||||
import io.legado.app.utils.Debounce
|
||||
import io.legado.app.utils.LogUtils
|
||||
import io.legado.app.utils.StartActivityContract
|
||||
import io.legado.app.utils.applyOpenTint
|
||||
import io.legado.app.utils.buildMainHandler
|
||||
import io.legado.app.utils.getPrefBoolean
|
||||
import io.legado.app.utils.getPrefString
|
||||
import io.legado.app.utils.hexString
|
||||
import io.legado.app.utils.iconItemOnLongClick
|
||||
import io.legado.app.utils.invisible
|
||||
import io.legado.app.utils.isAbsUrl
|
||||
import io.legado.app.utils.isTrue
|
||||
import io.legado.app.utils.launch
|
||||
import io.legado.app.utils.navigationBarGravity
|
||||
import io.legado.app.utils.observeEvent
|
||||
import io.legado.app.utils.observeEventSticky
|
||||
import io.legado.app.utils.postEvent
|
||||
import io.legado.app.utils.showDialogFragment
|
||||
import io.legado.app.utils.showHelp
|
||||
import io.legado.app.utils.startActivity
|
||||
import io.legado.app.utils.sysScreenOffTime
|
||||
import io.legado.app.utils.throttle
|
||||
import io.legado.app.utils.toastOnUi
|
||||
import io.legado.app.utils.visible
|
||||
import io.legado.app.utils.*
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Dispatchers.Main
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
/**
|
||||
* 阅读界面
|
||||
@@ -245,6 +201,11 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
|
||||
//恢复跳转前进度对话框的交互结果
|
||||
private var confirmRestoreProcess: Boolean? = null
|
||||
private val networkChangedListener by lazy {
|
||||
NetworkChangedListener(this)
|
||||
}
|
||||
private var justInitData: Boolean = false
|
||||
private var syncDialog: AlertDialog? = null
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
@@ -289,6 +250,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
viewModel.initData(intent)
|
||||
false
|
||||
}
|
||||
justInitData = true
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent) {
|
||||
@@ -318,6 +280,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
bookChanged = false
|
||||
ReadBook.callBack = this
|
||||
viewModel.initData(intent)
|
||||
justInitData = true
|
||||
} else {
|
||||
//web端阅读时,app处于阅读界面,本地记录会覆盖web保存的进度,在此处恢复
|
||||
ReadBook.webBookProgress?.let {
|
||||
@@ -329,6 +292,14 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
registerReceiver(timeBatteryReceiver, timeBatteryReceiver.filter)
|
||||
binding.readView.upTime()
|
||||
screenOffTimerStart()
|
||||
// 网络监听,当从无网切换到网络环境时同步进度(注意注册的同时就会收到监听,因此界面激活时无需重复执行同步操作)
|
||||
networkChangedListener.register()
|
||||
networkChangedListener.onNetworkChanged = {
|
||||
// 当网络是可用状态且无需初始化时同步进度(初始化中已有同步进度逻辑)
|
||||
if (AppConfig.syncBookProgressPlus && NetworkUtils.isAvailable() && !justInitData) {
|
||||
ReadBook.syncProgress({progress -> sureNewProgress(progress)}, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
@@ -339,9 +310,15 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
unregisterReceiver(timeBatteryReceiver)
|
||||
upSystemUiVisibility()
|
||||
if (!BuildConfig.DEBUG) {
|
||||
ReadBook.uploadProgress()
|
||||
if (AppConfig.syncBookProgressPlus) {
|
||||
ReadBook.syncProgress()
|
||||
} else {
|
||||
ReadBook.uploadProgress()
|
||||
}
|
||||
Backup.autoBack(this)
|
||||
}
|
||||
justInitData = false
|
||||
networkChangedListener.unRegister()
|
||||
}
|
||||
|
||||
override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
|
||||
@@ -408,6 +385,9 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
menu.findItem(R.id.menu_get_progress)?.isVisible = withContext(IO) {
|
||||
AppWebDav.isOk
|
||||
}
|
||||
menu.findItem(R.id.menu_cover_progress)?.isVisible = withContext(IO) {
|
||||
AppWebDav.isOk
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -551,6 +531,10 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
}
|
||||
}
|
||||
|
||||
R.id.menu_cover_progress -> ReadBook.book?.let {
|
||||
ReadBook.uploadProgress({ toastOnUi(R.string.upload_book_success) })
|
||||
}
|
||||
|
||||
R.id.menu_same_title_removed -> {
|
||||
ReadBook.book?.let {
|
||||
val contentProcessor = ContentProcessor.get(it)
|
||||
@@ -1499,6 +1483,18 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
}
|
||||
}
|
||||
|
||||
override fun sureNewProgress(progress: BookProgress) {
|
||||
syncDialog?.dismiss()
|
||||
syncDialog = alert(R.string.get_book_progress) {
|
||||
setMessage(R.string.cloud_progress_exceeds_current)
|
||||
okButton {
|
||||
ReadBook.setProgress(progress)
|
||||
ReadBook.saveRead()
|
||||
}
|
||||
noButton()
|
||||
}
|
||||
}
|
||||
|
||||
override fun finish() {
|
||||
val book = ReadBook.book ?: return super.finish()
|
||||
|
||||
|
||||
@@ -125,7 +125,11 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
|
||||
// 有章节跳转不同步阅读进度
|
||||
ReadBook.chapterChanged = false
|
||||
} else if (!isSameBook || !BaseReadAloudService.isRun) {
|
||||
syncBookProgress(book)
|
||||
if (AppConfig.syncBookProgressPlus) {
|
||||
ReadBook.syncProgress({ progress -> ReadBook.callBack?.sureNewProgress(progress) }, null)
|
||||
} else {
|
||||
syncBookProgress(book)
|
||||
}
|
||||
}
|
||||
if (!book.isLocal && ReadBook.bookSource == null) {
|
||||
autoChangeSource(book.name, book.author)
|
||||
|
||||
@@ -35,7 +35,8 @@ class ClickActionConfigDialog : BaseDialogFragment(R.layout.dialog_click_action_
|
||||
Pair(8, getString(R.string.edit_content)),
|
||||
Pair(9, getString(R.string.replace_state_change)),
|
||||
Pair(10, getString(R.string.chapter_list)),
|
||||
Pair(11, getString(R.string.search_content))
|
||||
Pair(11, getString(R.string.search_content)),
|
||||
Pair(12, getString(R.string.sync_book_progress_t))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,9 @@ import android.view.MotionEvent
|
||||
import android.view.ViewConfiguration
|
||||
import android.view.WindowInsets
|
||||
import android.widget.FrameLayout
|
||||
import io.legado.app.R
|
||||
import io.legado.app.constant.PageAnim
|
||||
import io.legado.app.data.entities.BookProgress
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.help.config.ReadBookConfig
|
||||
import io.legado.app.model.ReadAloud
|
||||
@@ -34,6 +36,7 @@ import io.legado.app.ui.book.read.page.provider.TextPageFactory
|
||||
import io.legado.app.utils.activity
|
||||
import io.legado.app.utils.canvasrecorder.pools.BitmapPool
|
||||
import io.legado.app.utils.invisible
|
||||
import io.legado.app.utils.longToastOnUi
|
||||
import io.legado.app.utils.showDialogFragment
|
||||
import io.legado.app.utils.throttle
|
||||
import java.text.BreakIterator
|
||||
@@ -434,6 +437,9 @@ class ReadView(context: Context, attrs: AttributeSet) :
|
||||
9 -> callBack.changeReplaceRuleState()
|
||||
10 -> callBack.openChapterList()
|
||||
11 -> callBack.openSearchActivity(null)
|
||||
12 -> ReadBook.syncProgress({progress -> callBack.sureNewProgress(progress)},
|
||||
{ context.longToastOnUi(context.getString(R.string.upload_book_success)) },
|
||||
{ context.longToastOnUi(context.getString(R.string.sync_book_progress_success)) })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -726,5 +732,6 @@ class ReadView(context: Context, attrs: AttributeSet) :
|
||||
fun changeReplaceRuleState()
|
||||
fun openSearchActivity(searchWord: String?)
|
||||
fun upSystemUiVisibility()
|
||||
fun sureNewProgress(progress: BookProgress)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user