|
@@ -1,1966 +1,1991 @@
|
|
|
-package io.legado.app.ui.book.read
|
|
|
|
|
-
|
|
|
|
|
-//import io.legado.app.lib.theme.accentColor
|
|
|
|
|
-import android.annotation.SuppressLint
|
|
|
|
|
-import android.content.Intent
|
|
|
|
|
-import android.content.res.Configuration
|
|
|
|
|
-import android.graphics.Color
|
|
|
|
|
-import android.os.Bundle
|
|
|
|
|
-import android.view.Gravity
|
|
|
|
|
-import android.view.HapticFeedbackConstants
|
|
|
|
|
-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 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.size
|
|
|
|
|
-import androidx.lifecycle.lifecycleScope
|
|
|
|
|
-import com.google.android.material.transition.platform.MaterialContainerTransform
|
|
|
|
|
-import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback
|
|
|
|
|
-import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
|
|
|
|
|
-import com.script.rhino.runScriptWithContext
|
|
|
|
|
-import io.legado.app.BuildConfig
|
|
|
|
|
-import io.legado.app.R
|
|
|
|
|
-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.data.appDb
|
|
|
|
|
-import io.legado.app.data.entities.Book
|
|
|
|
|
-import io.legado.app.data.entities.BookChapter
|
|
|
|
|
-import io.legado.app.data.entities.BookProgress
|
|
|
|
|
-import io.legado.app.data.entities.BookSource
|
|
|
|
|
-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.update
|
|
|
|
|
-import io.legado.app.help.config.AppConfig
|
|
|
|
|
-import io.legado.app.help.config.ReadBookConfig
|
|
|
|
|
-import io.legado.app.help.config.ReadTipConfig
|
|
|
|
|
-import io.legado.app.help.coroutine.Coroutine
|
|
|
|
|
-import io.legado.app.help.source.getSourceType
|
|
|
|
|
-import io.legado.app.help.storage.Backup
|
|
|
|
|
-import io.legado.app.lib.dialogs.SelectItem
|
|
|
|
|
-import io.legado.app.lib.dialogs.alert
|
|
|
|
|
-import io.legado.app.lib.dialogs.selector
|
|
|
|
|
-import io.legado.app.model.ReadAloud
|
|
|
|
|
-import io.legado.app.model.ReadBook
|
|
|
|
|
-import io.legado.app.model.analyzeRule.AnalyzeRule
|
|
|
|
|
-import io.legado.app.model.analyzeRule.AnalyzeRule.Companion.setChapter
|
|
|
|
|
-import io.legado.app.model.analyzeRule.AnalyzeRule.Companion.setCoroutineContext
|
|
|
|
|
-import io.legado.app.model.analyzeRule.AnalyzeUrl.Companion.paramPattern
|
|
|
|
|
-import io.legado.app.model.localBook.EpubFile
|
|
|
|
|
-import io.legado.app.model.localBook.MobiFile
|
|
|
|
|
-import io.legado.app.receiver.NetworkChangedListener
|
|
|
|
|
-import io.legado.app.receiver.TimeBatteryReceiver
|
|
|
|
|
-import io.legado.app.service.BaseReadAloudService
|
|
|
|
|
-import io.legado.app.ui.about.AppLogDialog
|
|
|
|
|
-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.BgTextConfigDialog.Companion.BG_COLOR
|
|
|
|
|
-import io.legado.app.ui.book.read.config.FontConfigDialog
|
|
|
|
|
-import io.legado.app.ui.book.read.config.FontConfigDialog.Companion.S_COLOR
|
|
|
|
|
-import io.legado.app.ui.book.read.config.FontConfigDialog.Companion.TEXT_ACCENT_COLOR
|
|
|
|
|
-import io.legado.app.ui.book.read.config.FontConfigDialog.Companion.TEXT_COLOR
|
|
|
|
|
-import io.legado.app.ui.book.read.config.FontSelectDialog
|
|
|
|
|
-import io.legado.app.ui.book.read.config.ReadAloudDialog
|
|
|
|
|
-import io.legado.app.ui.book.read.config.ReadStyleDialog
|
|
|
|
|
-import io.legado.app.ui.book.read.config.TipConfigDialog.Companion.A_COLOR
|
|
|
|
|
-import io.legado.app.ui.book.read.config.TipConfigDialog.Companion.B_COLOR
|
|
|
|
|
-import io.legado.app.ui.book.read.config.TipConfigDialog.Companion.TIP_COLOR
|
|
|
|
|
-import io.legado.app.ui.book.read.config.TipConfigDialog.Companion.TIP_DIVIDER_COLOR
|
|
|
|
|
-import io.legado.app.ui.book.read.config.ToolButtonConfigDialog
|
|
|
|
|
-import io.legado.app.ui.book.read.config.UnderlineConfigDialog.Companion.U_COLOR
|
|
|
|
|
-import io.legado.app.ui.book.read.page.ContentTextView
|
|
|
|
|
-import io.legado.app.ui.book.read.page.ReadView
|
|
|
|
|
-import io.legado.app.ui.book.read.page.entities.PageDirection
|
|
|
|
|
-import io.legado.app.ui.book.read.page.entities.TextPage
|
|
|
|
|
-import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
|
|
|
|
-import io.legado.app.ui.book.read.page.provider.LayoutProgressListener
|
|
|
|
|
-import io.legado.app.ui.book.searchContent.SearchContentActivity
|
|
|
|
|
-import io.legado.app.ui.book.searchContent.SearchResult
|
|
|
|
|
-import io.legado.app.ui.book.source.edit.BookSourceEditActivity
|
|
|
|
|
-import io.legado.app.ui.book.toc.TocActivityResult
|
|
|
|
|
-import io.legado.app.ui.book.toc.rule.TxtTocRuleDialog
|
|
|
|
|
-import io.legado.app.ui.browser.WebViewActivity
|
|
|
|
|
-import io.legado.app.ui.dict.DictDialog
|
|
|
|
|
-import io.legado.app.ui.login.SourceLoginActivity
|
|
|
|
|
-import io.legado.app.ui.login.SourceLoginJsExtensions
|
|
|
|
|
-import io.legado.app.ui.replace.ReplaceEditRoute
|
|
|
|
|
-import io.legado.app.ui.replace.ReplaceRuleActivity
|
|
|
|
|
-import io.legado.app.ui.widget.PopupAction
|
|
|
|
|
-import io.legado.app.ui.widget.dialog.PhotoDialog
|
|
|
|
|
-import io.legado.app.utils.Debounce
|
|
|
|
|
-import io.legado.app.utils.GSON
|
|
|
|
|
-import io.legado.app.utils.LogUtils
|
|
|
|
|
-import io.legado.app.utils.NetworkUtils
|
|
|
|
|
-import io.legado.app.utils.StartActivityContract
|
|
|
|
|
-import io.legado.app.utils.applyOpenTint
|
|
|
|
|
-import io.legado.app.utils.buildMainHandler
|
|
|
|
|
-import io.legado.app.utils.dismissDialogFragment
|
|
|
|
|
-import io.legado.app.utils.fromJsonObject
|
|
|
|
|
-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.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.startActivityForBook
|
|
|
|
|
-import io.legado.app.utils.sysScreenOffTime
|
|
|
|
|
-import io.legado.app.utils.themeColor
|
|
|
|
|
-import io.legado.app.utils.throttle
|
|
|
|
|
-import io.legado.app.utils.toastOnUi
|
|
|
|
|
-import io.legado.app.utils.visible
|
|
|
|
|
-import kotlinx.coroutines.Dispatchers
|
|
|
|
|
-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
|
|
|
|
|
-
|
|
|
|
|
-/**
|
|
|
|
|
- * 阅读界面
|
|
|
|
|
- */
|
|
|
|
|
-class ReadBookActivity : BaseReadBookActivity(),
|
|
|
|
|
- View.OnTouchListener,
|
|
|
|
|
- ReadView.CallBack,
|
|
|
|
|
- TextActionMenu.CallBack,
|
|
|
|
|
- ContentTextView.CallBack,
|
|
|
|
|
- PopupMenu.OnMenuItemClickListener,
|
|
|
|
|
- ReadMenu.CallBack,
|
|
|
|
|
- SearchMenu.CallBack,
|
|
|
|
|
- ReadAloudDialog.CallBack,
|
|
|
|
|
- ChangeBookSourceDialog.CallBack,
|
|
|
|
|
- ChangeChapterSourceDialog.CallBack,
|
|
|
|
|
- ReadBook.CallBack,
|
|
|
|
|
- AutoReadDialog.CallBack,
|
|
|
|
|
- ToolButtonConfigDialog.CallBack,
|
|
|
|
|
- TxtTocRuleDialog.CallBack,
|
|
|
|
|
- ColorPickerDialogListener,
|
|
|
|
|
- FontConfigDialog.CallBack,
|
|
|
|
|
- FontSelectDialog.CallBack,
|
|
|
|
|
- LayoutProgressListener {
|
|
|
|
|
-
|
|
|
|
|
- private val tocActivity =
|
|
|
|
|
- registerForActivityResult(TocActivityResult()) {
|
|
|
|
|
- it?.let {
|
|
|
|
|
- viewModel.openChapter(it.first, it.second)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- private val sourceEditActivity =
|
|
|
|
|
- registerForActivityResult(StartActivityContract(BookSourceEditActivity::class.java)) {
|
|
|
|
|
- if (it.resultCode == RESULT_OK) {
|
|
|
|
|
- viewModel.upBookSource {
|
|
|
|
|
- upMenuView()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- private val replaceActivity =
|
|
|
|
|
- registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
|
|
|
|
- if (it.resultCode == RESULT_OK) {
|
|
|
|
|
- viewModel.replaceRuleChanged()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private val searchContentActivity =
|
|
|
|
|
- registerForActivityResult(StartActivityContract(SearchContentActivity::class.java)) {
|
|
|
|
|
- val data = it.data ?: return@registerForActivityResult
|
|
|
|
|
- val key = data.getLongExtra("key", System.currentTimeMillis())
|
|
|
|
|
- val index = data.getIntExtra("index", 0)
|
|
|
|
|
- val searchResult = IntentData.get<SearchResult>("searchResult$key")
|
|
|
|
|
- val searchResultList = IntentData.get<List<SearchResult>>("searchResultList$key")
|
|
|
|
|
- if (searchResult != null && searchResultList != null) {
|
|
|
|
|
- viewModel.searchContentQuery = searchResult.query
|
|
|
|
|
- binding.searchMenu.upSearchResultList(searchResultList)
|
|
|
|
|
- isShowingSearchResult = true
|
|
|
|
|
- viewModel.searchResultIndex = index
|
|
|
|
|
- binding.searchMenu.updateSearchResultIndex(index)
|
|
|
|
|
- binding.searchMenu.selectedSearchResult?.let { currentResult ->
|
|
|
|
|
- ReadBook.saveCurrentBookProgress() //退出全文搜索恢复此时进度
|
|
|
|
|
- skipToSearch(currentResult)
|
|
|
|
|
- showActionMenu()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- private val bookInfoActivity =
|
|
|
|
|
- registerForActivityResult(StartActivityContract(BookInfoActivity::class.java)) {
|
|
|
|
|
- if (it.resultCode == RESULT_OK) {
|
|
|
|
|
- setResult(RESULT_DELETED)
|
|
|
|
|
- finish()
|
|
|
|
|
- } else {
|
|
|
|
|
- ReadBook.loadOrUpContent()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- private var menu: Menu? = null
|
|
|
|
|
- private var backupJob: Job? = null
|
|
|
|
|
- private var tts: TTS? = null
|
|
|
|
|
- val textActionMenu: TextActionMenu by lazy {
|
|
|
|
|
- TextActionMenu(this, this)
|
|
|
|
|
- }
|
|
|
|
|
- private val popupAction: PopupAction by lazy {
|
|
|
|
|
- PopupAction(this)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 当前使用的字体路径(给 FontDialog 用)
|
|
|
|
|
- override val curFontPath: String
|
|
|
|
|
- get() = ReadBookConfig.textFont
|
|
|
|
|
-
|
|
|
|
|
- // 当选择了字体时被调用
|
|
|
|
|
- override fun selectFont(path: String) {
|
|
|
|
|
- // path 为空表示恢复系统默认字体
|
|
|
|
|
- ReadBookConfig.textFont = path
|
|
|
|
|
- // 通知阅读界面刷新字体
|
|
|
|
|
- postEvent(EventBus.UP_CONFIG, arrayListOf(8, 5, 2))
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override val isInitFinish: Boolean get() = viewModel.isInitFinish
|
|
|
|
|
- override val isScroll: Boolean get() = binding.readView.isScroll
|
|
|
|
|
- private val isAutoPage get() = binding.readView.isAutoPage
|
|
|
|
|
- override var isShowingSearchResult = false
|
|
|
|
|
- override var isSelectingSearchResult = false
|
|
|
|
|
- set(value) {
|
|
|
|
|
- field = value && isShowingSearchResult
|
|
|
|
|
- }
|
|
|
|
|
- private val timeBatteryReceiver = TimeBatteryReceiver()
|
|
|
|
|
- private var screenTimeOut: Long = 0
|
|
|
|
|
- private var loadStates: Boolean = false
|
|
|
|
|
- override val pageFactory get() = binding.readView.pageFactory
|
|
|
|
|
- override val pageDelegate get() = binding.readView.pageDelegate
|
|
|
|
|
- override val headerHeight: Int get() = binding.readView.curPage.headerHeight
|
|
|
|
|
- override val imgBgPaddingStart: Int get() = binding.readView.curPage.imgBgPaddingStart
|
|
|
|
|
- private val nextPageDebounce by lazy { Debounce { keyPage(PageDirection.NEXT) } }
|
|
|
|
|
- private val prevPageDebounce by lazy { Debounce { keyPage(PageDirection.PREV) } }
|
|
|
|
|
- private var bookChanged = false
|
|
|
|
|
- private var pageChanged = false
|
|
|
|
|
- private val handler by lazy { buildMainHandler() }
|
|
|
|
|
- private val screenOffRunnable by lazy { Runnable { keepScreenOn(false) } }
|
|
|
|
|
- private val executor = ReadBook.executor
|
|
|
|
|
- private val upSeekBarThrottle = throttle(200) {
|
|
|
|
|
- runOnUiThread {
|
|
|
|
|
- upSeekBarProgress()
|
|
|
|
|
- binding.readMenu.upSeekBar()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- //恢复跳转前进度对话框的交互结果
|
|
|
|
|
- private var confirmRestoreProcess: Boolean? = null
|
|
|
|
|
- private val networkChangedListener by lazy {
|
|
|
|
|
- NetworkChangedListener(this)
|
|
|
|
|
- }
|
|
|
|
|
- private var justInitData: Boolean = false
|
|
|
|
|
- private var syncDialog: AlertDialog? = null
|
|
|
|
|
-
|
|
|
|
|
- private val delayMillis: Long = 500
|
|
|
|
|
- private var hasLoadedBook = false
|
|
|
|
|
-
|
|
|
|
|
- private fun ensureLoadBook() {
|
|
|
|
|
- if (!hasLoadedBook) {
|
|
|
|
|
- hasLoadedBook = true
|
|
|
|
|
- loadBook()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- @SuppressLint("ClickableViewAccessibility")
|
|
|
|
|
- override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
|
- setEnterSharedElementCallback(MaterialContainerTransformSharedElementCallback())
|
|
|
|
|
- setExitSharedElementCallback(MaterialContainerTransformSharedElementCallback())
|
|
|
|
|
- val transform = MaterialContainerTransform().apply {
|
|
|
|
|
- addTarget(binding.root)
|
|
|
|
|
- scrimColor = Color.TRANSPARENT
|
|
|
|
|
- duration = delayMillis
|
|
|
|
|
- }
|
|
|
|
|
- window.sharedElementEnterTransition = transform
|
|
|
|
|
- window.sharedElementReturnTransition = transform
|
|
|
|
|
-
|
|
|
|
|
- super.onCreate(savedInstanceState)
|
|
|
|
|
-
|
|
|
|
|
- binding.rootView.transitionName = intent.getStringExtra("transitionName")
|
|
|
|
|
-
|
|
|
|
|
- upScreenTimeOut()
|
|
|
|
|
- ReadBook.register(this)
|
|
|
|
|
- binding.readMenu.colorSurfaceContainer =
|
|
|
|
|
- themeColor(com.google.android.material.R.attr.colorSurfaceContainer)
|
|
|
|
|
- binding.readMenu.colorSecondary =
|
|
|
|
|
- themeColor(androidx.appcompat.R.attr.colorPrimary)
|
|
|
|
|
- binding.readMenu.colorSecondaryContainer =
|
|
|
|
|
- themeColor(com.google.android.material.R.attr.colorSecondaryContainer)
|
|
|
|
|
- binding.cursorLeft.setOnTouchListener(this)
|
|
|
|
|
- binding.cursorRight.setOnTouchListener(this)
|
|
|
|
|
-
|
|
|
|
|
- onBackPressedDispatcher.addCallback(this) {
|
|
|
|
|
- if (isShowingSearchResult) {
|
|
|
|
|
- exitSearchMenu()
|
|
|
|
|
- restoreLastBookProcess()
|
|
|
|
|
- return@addCallback
|
|
|
|
|
- }
|
|
|
|
|
- //拦截返回供恢复阅读进度
|
|
|
|
|
- if (ReadBook.lastBookProgress != null && confirmRestoreProcess != false) {
|
|
|
|
|
- restoreLastBookProcess()
|
|
|
|
|
- return@addCallback
|
|
|
|
|
- }
|
|
|
|
|
- if (BaseReadAloudService.isPlay()) {
|
|
|
|
|
- ReadAloud.pause(this@ReadBookActivity)
|
|
|
|
|
- toastOnUi(R.string.read_aloud_pause)
|
|
|
|
|
- return@addCallback
|
|
|
|
|
- }
|
|
|
|
|
- if (isAutoPage) {
|
|
|
|
|
- autoPageStop()
|
|
|
|
|
- return@addCallback
|
|
|
|
|
- }
|
|
|
|
|
- if (getPrefBoolean("disableReturnKey") && !menuLayoutIsVisible) {
|
|
|
|
|
- return@addCallback
|
|
|
|
|
- }
|
|
|
|
|
- if (savedInstanceState != null || !ReadBook.inBookshelf) {
|
|
|
|
|
- ReadBook.commitReadSession()
|
|
|
|
|
- finish()
|
|
|
|
|
- } else {
|
|
|
|
|
- ReadBook.commitReadSession()
|
|
|
|
|
- supportFinishAfterTransition()
|
|
|
|
|
- }
|
|
|
|
|
- //TODO: 有关测量相关问题
|
|
|
|
|
- //孩子们,我的水平不够,只能这样修复测量问题了
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun onPostCreate(savedInstanceState: Bundle?) {
|
|
|
|
|
- super.onPostCreate(savedInstanceState)
|
|
|
|
|
- if (!AppConfig.sharedElementEnterTransitionEnable || !AppConfig.delayBookLoadEnable) {
|
|
|
|
|
- ensureLoadBook()
|
|
|
|
|
- }
|
|
|
|
|
- //虽然很扯但有用
|
|
|
|
|
- binding.rootView.postDelayed({
|
|
|
|
|
- ensureLoadBook()
|
|
|
|
|
- }, delayMillis)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun onNewIntent(intent: Intent) {
|
|
|
|
|
- super.onNewIntent(intent)
|
|
|
|
|
- viewModel.initData(intent)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun onWindowFocusChanged(hasFocus: Boolean) {
|
|
|
|
|
- super.onWindowFocusChanged(hasFocus)
|
|
|
|
|
- upSystemUiVisibility()
|
|
|
|
|
- if (hasFocus) {
|
|
|
|
|
- binding.readMenu.upBrightnessState()
|
|
|
|
|
- } else if (!menuLayoutIsVisible) {
|
|
|
|
|
- ReadBook.cancelPreDownloadTask()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun onConfigurationChanged(newConfig: Configuration) {
|
|
|
|
|
- super.onConfigurationChanged(newConfig)
|
|
|
|
|
- //upSystemUiVisibility()
|
|
|
|
|
- binding.readView.upStyle()
|
|
|
|
|
- recreate()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- override fun onTopResumedActivityChanged(isTopResumedActivity: Boolean) {
|
|
|
|
|
- if (!isTopResumedActivity) {
|
|
|
|
|
- ReadBook.cancelPreDownloadTask()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- @SuppressLint("UnspecifiedRegisterReceiverFlag")
|
|
|
|
|
- override fun onResume() {
|
|
|
|
|
- super.onResume()
|
|
|
|
|
- ReadBook.readStartTime = System.currentTimeMillis()
|
|
|
|
|
- ReadBook.initReadTime()
|
|
|
|
|
- ReadBook.startAutoSaveSession()
|
|
|
|
|
- if (bookChanged) {
|
|
|
|
|
- bookChanged = false
|
|
|
|
|
- ReadBook.callBack = this
|
|
|
|
|
- viewModel.initData(intent)
|
|
|
|
|
- justInitData = true
|
|
|
|
|
- } else {
|
|
|
|
|
- //web端阅读时,app处于阅读界面,本地记录会覆盖web保存的进度,在此处恢复
|
|
|
|
|
- ReadBook.webBookProgress?.let {
|
|
|
|
|
- ReadBook.setProgress(it)
|
|
|
|
|
- ReadBook.webBookProgress = null
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- upSystemUiVisibility()
|
|
|
|
|
- registerReceiver(timeBatteryReceiver, timeBatteryReceiver.filter)
|
|
|
|
|
- binding.readView.upTime()
|
|
|
|
|
- screenOffTimerStart()
|
|
|
|
|
- // 网络监听,当从无网切换到网络环境时同步进度(注意注册的同时就会收到监听,因此界面激活时无需重复执行同步操作)
|
|
|
|
|
- networkChangedListener.register()
|
|
|
|
|
- networkChangedListener.onNetworkChanged = {
|
|
|
|
|
- // 当网络是可用状态且无需初始化时同步进度(初始化中已有同步进度逻辑)
|
|
|
|
|
- if (AppConfig.syncBookProgressPlus && NetworkUtils.isAvailable() && !justInitData) {
|
|
|
|
|
- ReadBook.syncProgress({ progress -> sureNewProgress(progress) })
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun onPause() {
|
|
|
|
|
- super.onPause()
|
|
|
|
|
- autoPageStop()
|
|
|
|
|
- backupJob?.cancel()
|
|
|
|
|
- ReadBook.saveRead()
|
|
|
|
|
- ReadBook.stopAutoSaveSession()
|
|
|
|
|
- ReadBook.commitReadSession()
|
|
|
|
|
- ReadBook.cancelPreDownloadTask()
|
|
|
|
|
- unregisterReceiver(timeBatteryReceiver)
|
|
|
|
|
- upSystemUiVisibility()
|
|
|
|
|
- if (!BuildConfig.DEBUG) {
|
|
|
|
|
- if (AppConfig.syncBookProgressPlus) {
|
|
|
|
|
- ReadBook.syncProgress()
|
|
|
|
|
- } else {
|
|
|
|
|
- ReadBook.uploadProgress()
|
|
|
|
|
- }
|
|
|
|
|
- Backup.autoBack(this)
|
|
|
|
|
- }
|
|
|
|
|
- justInitData = false
|
|
|
|
|
- networkChangedListener.unRegister()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
|
|
|
|
|
- menuInflater.inflate(R.menu.book_read, menu)
|
|
|
|
|
- menu.iconItemOnLongClick(R.id.menu_change_source) {
|
|
|
|
|
- PopupMenu(this, it).apply {
|
|
|
|
|
- inflate(R.menu.book_read_change_source)
|
|
|
|
|
- this.menu.applyOpenTint(this@ReadBookActivity)
|
|
|
|
|
- setOnMenuItemClickListener(this@ReadBookActivity)
|
|
|
|
|
- }.show()
|
|
|
|
|
- }
|
|
|
|
|
- menu.iconItemOnLongClick(R.id.menu_refresh) {
|
|
|
|
|
- PopupMenu(this, it).apply {
|
|
|
|
|
- inflate(R.menu.book_read_refresh)
|
|
|
|
|
- this.menu.applyOpenTint(this@ReadBookActivity)
|
|
|
|
|
- setOnMenuItemClickListener(this@ReadBookActivity)
|
|
|
|
|
- }.show()
|
|
|
|
|
- }
|
|
|
|
|
- binding.readMenu.refreshMenuColorFilter()
|
|
|
|
|
- return super.onCompatCreateOptionsMenu(menu)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun onPrepareOptionsMenu(menu: Menu): Boolean {
|
|
|
|
|
- this.menu = menu
|
|
|
|
|
- upMenu()
|
|
|
|
|
- return super.onPrepareOptionsMenu(menu)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun onMenuOpened(featureId: Int, menu: Menu): Boolean {
|
|
|
|
|
- menu.findItem(R.id.menu_same_title_removed)?.isChecked =
|
|
|
|
|
- ReadBook.curTextChapter?.sameTitleRemoved == true
|
|
|
|
|
- return super.onMenuOpened(featureId, menu)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private fun loadBook() {
|
|
|
|
|
- lifecycleScope.launch(Dispatchers.Default) {
|
|
|
|
|
- viewModel.initReadBookConfig(intent)
|
|
|
|
|
- viewModel.initData(intent)
|
|
|
|
|
- withContext(Main) {
|
|
|
|
|
- justInitData = true
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 更新菜单
|
|
|
|
|
- */
|
|
|
|
|
- private fun upMenu() {
|
|
|
|
|
- val menu = menu ?: return
|
|
|
|
|
- val book = ReadBook.book ?: return
|
|
|
|
|
- val onLine = !book.isLocal
|
|
|
|
|
- for (i in 0 until menu.size) {
|
|
|
|
|
- val item = menu[i]
|
|
|
|
|
- when (item.groupId) {
|
|
|
|
|
- R.id.menu_group_on_line -> item.isVisible = onLine
|
|
|
|
|
- R.id.menu_group_local -> item.isVisible = !onLine
|
|
|
|
|
- R.id.menu_group_text -> item.isVisible = book.isLocalTxt
|
|
|
|
|
- R.id.menu_group_epub -> item.isVisible = book.isEpub
|
|
|
|
|
- else -> when (item.itemId) {
|
|
|
|
|
- R.id.menu_enable_replace -> item.isChecked = book.getUseReplaceRule()
|
|
|
|
|
- R.id.menu_re_segment -> item.isChecked = book.getReSegment()
|
|
|
|
|
-// R.id.menu_enable_review -> {
|
|
|
|
|
-// item.isVisible = BuildConfig.DEBUG
|
|
|
|
|
-// item.isChecked = AppConfig.enableReview
|
|
|
|
|
-// }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_reverse_content -> item.isVisible = onLine
|
|
|
|
|
- R.id.menu_del_ruby_tag -> item.isChecked = book.getDelTag(Book.rubyTag)
|
|
|
|
|
- R.id.menu_del_h_tag -> item.isChecked = book.getDelTag(Book.hTag)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- lifecycleScope.launch {
|
|
|
|
|
- menu.findItem(R.id.menu_get_progress)?.isVisible = withContext(IO) {
|
|
|
|
|
- AppWebDav.isOk
|
|
|
|
|
- }
|
|
|
|
|
- menu.findItem(R.id.menu_cover_progress)?.isVisible = withContext(IO) {
|
|
|
|
|
- AppWebDav.isOk
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private fun defaultChangeSource() {
|
|
|
|
|
- if (AppConfig.defaultSourceChangeAll)
|
|
|
|
|
- {
|
|
|
|
|
- binding.readMenu.runMenuOut()
|
|
|
|
|
- ReadBook.book?.let {
|
|
|
|
|
- showDialogFragment(ChangeBookSourceDialog(it.name, it.author))
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- lifecycleScope.launch {
|
|
|
|
|
- val book = ReadBook.book ?: return@launch
|
|
|
|
|
- val chapter =
|
|
|
|
|
- appDb.bookChapterDao.getChapter(book.bookUrl, ReadBook.durChapterIndex)
|
|
|
|
|
- ?: return@launch
|
|
|
|
|
- binding.readMenu.runMenuOut()
|
|
|
|
|
- showDialogFragment(
|
|
|
|
|
- ChangeChapterSourceDialog(book.name, book.author, chapter.index, chapter.title)
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 菜单
|
|
|
|
|
- */
|
|
|
|
|
- override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
|
|
|
|
|
- when (item.itemId) {
|
|
|
|
|
- R.id.menu_change_source -> { defaultChangeSource() }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_book_change_source -> {
|
|
|
|
|
- binding.readMenu.runMenuOut()
|
|
|
|
|
- ReadBook.book?.let {
|
|
|
|
|
- showDialogFragment(ChangeBookSourceDialog(it.name, it.author))
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_chapter_change_source -> lifecycleScope.launch {
|
|
|
|
|
- val book = ReadBook.book ?: return@launch
|
|
|
|
|
- val chapter =
|
|
|
|
|
- appDb.bookChapterDao.getChapter(book.bookUrl, ReadBook.durChapterIndex)
|
|
|
|
|
- ?: return@launch
|
|
|
|
|
- binding.readMenu.runMenuOut()
|
|
|
|
|
- showDialogFragment(
|
|
|
|
|
- ChangeChapterSourceDialog(book.name, book.author, chapter.index, chapter.title)
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_refresh,
|
|
|
|
|
- R.id.menu_refresh_dur -> {
|
|
|
|
|
- if (ReadBook.bookSource == null) {
|
|
|
|
|
- upContent()
|
|
|
|
|
- } else {
|
|
|
|
|
- ReadBook.book?.let {
|
|
|
|
|
- ReadBook.curTextChapter = null
|
|
|
|
|
- binding.readView.upContent()
|
|
|
|
|
- viewModel.refreshContentDur(it)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_refresh_after -> {
|
|
|
|
|
- if (ReadBook.bookSource == null) {
|
|
|
|
|
- upContent()
|
|
|
|
|
- } else {
|
|
|
|
|
- ReadBook.book?.let {
|
|
|
|
|
- ReadBook.clearTextChapter()
|
|
|
|
|
- binding.readView.upContent()
|
|
|
|
|
- viewModel.refreshContentAfter(it)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_refresh_all -> {
|
|
|
|
|
- if (ReadBook.bookSource == null) {
|
|
|
|
|
- upContent()
|
|
|
|
|
- } else {
|
|
|
|
|
- ReadBook.book?.let {
|
|
|
|
|
- refreshContentAll(it)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_setting_replace -> openReplaceRule()
|
|
|
|
|
- R.id.menu_download -> showDownloadDialog()
|
|
|
|
|
- R.id.menu_add_bookmark -> addBookmark()
|
|
|
|
|
- R.id.menu_simulated_reading -> showSimulatedReading()
|
|
|
|
|
- R.id.menu_edit_content -> {
|
|
|
|
|
- binding.readMenu.runMenuOut()
|
|
|
|
|
- showDialogFragment(ContentEditDialog())
|
|
|
|
|
- }
|
|
|
|
|
- R.id.menu_update_toc -> ReadBook.book?.let {
|
|
|
|
|
- if (it.isEpub) {
|
|
|
|
|
- BookHelp.clearCache(it)
|
|
|
|
|
- EpubFile.clear()
|
|
|
|
|
- }
|
|
|
|
|
- if (it.isMobi) {
|
|
|
|
|
- MobiFile.clear()
|
|
|
|
|
- }
|
|
|
|
|
- loadChapterList(it)
|
|
|
|
|
- }
|
|
|
|
|
- R.id.menu_enable_replace -> {
|
|
|
|
|
- changeReplaceRuleState()
|
|
|
|
|
- }
|
|
|
|
|
- R.id.menu_re_segment -> ReadBook.book?.let {
|
|
|
|
|
- it.setReSegment(!it.getReSegment())
|
|
|
|
|
- item.isChecked = it.getReSegment()
|
|
|
|
|
- ReadBook.loadContent(false)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_tool_button -> {
|
|
|
|
|
- binding.readMenu.runMenuOut()
|
|
|
|
|
- showDialogFragment(ToolButtonConfigDialog())
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-// R.id.menu_enable_review -> {
|
|
|
|
|
-// AppConfig.enableReview = !AppConfig.enableReview
|
|
|
|
|
-// item.isChecked = AppConfig.enableReview
|
|
|
|
|
-// ReadBook.loadContent(false)
|
|
|
|
|
-// }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_del_ruby_tag -> ReadBook.book?.let {
|
|
|
|
|
- item.isChecked = !item.isChecked
|
|
|
|
|
- if (item.isChecked) {
|
|
|
|
|
- it.addDelTag(Book.rubyTag)
|
|
|
|
|
- } else {
|
|
|
|
|
- it.removeDelTag(Book.rubyTag)
|
|
|
|
|
- }
|
|
|
|
|
- refreshContentAll(it)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_del_h_tag -> ReadBook.book?.let {
|
|
|
|
|
- item.isChecked = !item.isChecked
|
|
|
|
|
- if (item.isChecked) {
|
|
|
|
|
- it.addDelTag(Book.hTag)
|
|
|
|
|
- } else {
|
|
|
|
|
- it.removeDelTag(Book.hTag)
|
|
|
|
|
- }
|
|
|
|
|
- refreshContentAll(it)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_page_anim -> showPageAnimConfig {
|
|
|
|
|
- binding.readView.upPageAnim()
|
|
|
|
|
- ReadBook.loadContent(false)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_log -> showDialogFragment<AppLogDialog>()
|
|
|
|
|
- R.id.menu_toc_regex -> showDialogFragment(
|
|
|
|
|
- TxtTocRuleDialog(ReadBook.book?.tocUrl)
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_reverse_content -> ReadBook.book?.let {
|
|
|
|
|
- viewModel.reverseContent(it)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_set_charset -> showCharsetConfig()
|
|
|
|
|
- R.id.menu_image_style -> {
|
|
|
|
|
- val imgStyles =
|
|
|
|
|
- arrayListOf(
|
|
|
|
|
- Book.imgStyleDefault, Book.imgStyleFull, Book.imgStyleText,
|
|
|
|
|
- Book.imgStyleSingle
|
|
|
|
|
- )
|
|
|
|
|
- selector(
|
|
|
|
|
- R.string.image_style,
|
|
|
|
|
- imgStyles
|
|
|
|
|
- ) { _, index ->
|
|
|
|
|
- val imageStyle = imgStyles[index]
|
|
|
|
|
- ReadBook.book?.setImageStyle(imageStyle)
|
|
|
|
|
- if (imageStyle == Book.imgStyleSingle) {
|
|
|
|
|
- ReadBook.book?.setPageAnim(0) // 切换图片样式single后,自动切换为覆盖
|
|
|
|
|
- binding.readView.upPageAnim()
|
|
|
|
|
- }
|
|
|
|
|
- ReadBook.loadContent(false)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_get_progress -> ReadBook.book?.let {
|
|
|
|
|
- viewModel.syncBookProgress(it) { progress ->
|
|
|
|
|
- sureSyncProgress(progress)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_cover_progress -> ReadBook.book?.let {
|
|
|
|
|
- ReadBook.uploadProgress(true) { toastOnUi(R.string.upload_book_success) }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_same_title_removed -> {
|
|
|
|
|
- ReadBook.book?.let {
|
|
|
|
|
- val contentProcessor = ContentProcessor.get(it)
|
|
|
|
|
- val textChapter = ReadBook.curTextChapter
|
|
|
|
|
- if (textChapter != null
|
|
|
|
|
- && !textChapter.sameTitleRemoved
|
|
|
|
|
- && !contentProcessor.removeSameTitleCache.contains(
|
|
|
|
|
- textChapter.chapter.getFileName("nr")
|
|
|
|
|
- )
|
|
|
|
|
- ) {
|
|
|
|
|
- toastOnUi("未找到可移除的重复标题")
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- viewModel.reverseRemoveSameTitle()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_effective_replaces -> showDialogFragment<EffectiveReplacesDialog>()
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_help -> showHelp()
|
|
|
|
|
- }
|
|
|
|
|
- return super.onCompatOptionsItemSelected(item)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private fun refreshContentAll(book: Book) {
|
|
|
|
|
- ReadBook.clearTextChapter()
|
|
|
|
|
- binding.readView.upContent()
|
|
|
|
|
- viewModel.refreshContentAll(book)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun onMenuItemClick(item: MenuItem): Boolean {
|
|
|
|
|
- return onCompatOptionsItemSelected(item)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 按键拦截,显示菜单
|
|
|
|
|
- */
|
|
|
|
|
- override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
|
|
|
|
- val keyCode = event.keyCode
|
|
|
|
|
- val action = event.action
|
|
|
|
|
- val isDown = action == 0
|
|
|
|
|
-
|
|
|
|
|
- if (keyCode == KeyEvent.KEYCODE_MENU) {
|
|
|
|
|
- if (isDown && !binding.readMenu.canShowMenu) {
|
|
|
|
|
- binding.readMenu.runMenuIn()
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
- if (!isDown && !binding.readMenu.canShowMenu) {
|
|
|
|
|
- binding.readMenu.canShowMenu = true
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return super.dispatchKeyEvent(event)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 鼠标滚轮事件
|
|
|
|
|
- */
|
|
|
|
|
- override fun onGenericMotionEvent(event: MotionEvent): Boolean {
|
|
|
|
|
- if (0 != (event.source and InputDevice.SOURCE_CLASS_POINTER)) {
|
|
|
|
|
- if (event.action == MotionEvent.ACTION_SCROLL) {
|
|
|
|
|
- val axisValue = event.getAxisValue(MotionEvent.AXIS_VSCROLL)
|
|
|
|
|
- LogUtils.d("onGenericMotionEvent", "axisValue = $axisValue")
|
|
|
|
|
- // 获得垂直坐标上的滚动方向
|
|
|
|
|
- if (axisValue < 0.0f) { // 滚轮向下滚
|
|
|
|
|
- mouseWheelPage(PageDirection.NEXT)
|
|
|
|
|
- } else { // 滚轮向上滚
|
|
|
|
|
- mouseWheelPage(PageDirection.PREV)
|
|
|
|
|
- }
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return super.onGenericMotionEvent(event)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 按键事件
|
|
|
|
|
- */
|
|
|
|
|
- override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
|
|
|
|
- if (menuLayoutIsVisible) {
|
|
|
|
|
- return super.onKeyDown(keyCode, event)
|
|
|
|
|
- }
|
|
|
|
|
- val longPress = event.repeatCount > 0
|
|
|
|
|
- when {
|
|
|
|
|
- isPrevKey(keyCode) -> {
|
|
|
|
|
- handleKeyPage(PageDirection.PREV, longPress)
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- isNextKey(keyCode) -> {
|
|
|
|
|
- handleKeyPage(PageDirection.NEXT, longPress)
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- when (keyCode) {
|
|
|
|
|
- KeyEvent.KEYCODE_VOLUME_UP -> if (volumeKeyPage(PageDirection.PREV, longPress)) {
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- KeyEvent.KEYCODE_VOLUME_DOWN -> if (volumeKeyPage(PageDirection.NEXT, longPress)) {
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- KeyEvent.KEYCODE_PAGE_UP -> {
|
|
|
|
|
- handleKeyPage(PageDirection.PREV, longPress)
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- KeyEvent.KEYCODE_PAGE_DOWN -> {
|
|
|
|
|
- handleKeyPage(PageDirection.NEXT, longPress)
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- KeyEvent.KEYCODE_SPACE -> {
|
|
|
|
|
- handleKeyPage(PageDirection.NEXT, longPress)
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return super.onKeyDown(keyCode, event)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 松开按键事件
|
|
|
|
|
- */
|
|
|
|
|
- override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
|
|
|
|
|
- when (keyCode) {
|
|
|
|
|
- KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN -> {
|
|
|
|
|
- if (volumeKeyPage(PageDirection.NONE, false)) {
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- }
|
|
|
|
|
- return super.onKeyUp(keyCode, event)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * view触摸,文字选择
|
|
|
|
|
- */
|
|
|
|
|
- @SuppressLint("ClickableViewAccessibility")
|
|
|
|
|
- override fun onTouch(v: View, event: MotionEvent): Boolean = binding.run {
|
|
|
|
|
- if (!binding.readView.isTextSelected) {
|
|
|
|
|
- return false
|
|
|
|
|
- }
|
|
|
|
|
- when (event.action) {
|
|
|
|
|
- MotionEvent.ACTION_DOWN -> textActionMenu.dismiss()
|
|
|
|
|
- MotionEvent.ACTION_MOVE -> {
|
|
|
|
|
- when (v.id) {
|
|
|
|
|
- R.id.cursor_left -> if (!readView.curPage.getReverseStartCursor()) {
|
|
|
|
|
- readView.curPage.selectStartMove(
|
|
|
|
|
- event.rawX + cursorLeft.width,
|
|
|
|
|
- event.rawY - cursorLeft.height
|
|
|
|
|
- )
|
|
|
|
|
- } else {
|
|
|
|
|
- readView.curPage.selectEndMove(
|
|
|
|
|
- event.rawX - cursorRight.width,
|
|
|
|
|
- event.rawY - cursorRight.height
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- R.id.cursor_right -> if (readView.curPage.getReverseEndCursor()) {
|
|
|
|
|
- readView.curPage.selectStartMove(
|
|
|
|
|
- event.rawX + cursorLeft.width,
|
|
|
|
|
- event.rawY - cursorLeft.height
|
|
|
|
|
- )
|
|
|
|
|
- } else {
|
|
|
|
|
- readView.curPage.selectEndMove(
|
|
|
|
|
- event.rawX - cursorRight.width,
|
|
|
|
|
- event.rawY - cursorRight.height
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- MotionEvent.ACTION_UP -> {
|
|
|
|
|
- readView.curPage.resetReverseCursor()
|
|
|
|
|
- showTextActionMenu()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 更新文字选择开始位置
|
|
|
|
|
- */
|
|
|
|
|
- override fun upSelectedStart(x: Float, y: Float, top: Float): Unit = binding.run {
|
|
|
|
|
- cursorLeft.x = x - cursorLeft.width
|
|
|
|
|
- cursorLeft.y = y
|
|
|
|
|
- cursorLeft.visible(true)
|
|
|
|
|
- textMenuPosition.x = x
|
|
|
|
|
- textMenuPosition.y = top
|
|
|
|
|
-
|
|
|
|
|
- root.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 更新文字选择结束位置
|
|
|
|
|
- */
|
|
|
|
|
- override fun upSelectedEnd(x: Float, y: Float): Unit = binding.run {
|
|
|
|
|
- cursorRight.x = x
|
|
|
|
|
- cursorRight.y = y
|
|
|
|
|
- cursorRight.visible(true)
|
|
|
|
|
-
|
|
|
|
|
- root.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 取消文字选择
|
|
|
|
|
- */
|
|
|
|
|
- override fun onCancelSelect() = binding.run {
|
|
|
|
|
- cursorLeft.invisible()
|
|
|
|
|
- cursorRight.invisible()
|
|
|
|
|
- textActionMenu.dismiss()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun onLongScreenshotTouchEvent(event: MotionEvent): Boolean {
|
|
|
|
|
- return binding.readView.onTouchEvent(event)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 显示文本操作菜单
|
|
|
|
|
- */
|
|
|
|
|
- override fun showTextActionMenu() {
|
|
|
|
|
- val navigationBarHeight =
|
|
|
|
|
- if (!ReadBookConfig.hideNavigationBar && navigationBarGravity == Gravity.BOTTOM)
|
|
|
|
|
- binding.navigationBar.height else 0
|
|
|
|
|
- textActionMenu.show(
|
|
|
|
|
- binding.textMenuPosition,
|
|
|
|
|
- binding.root.height + navigationBarHeight,
|
|
|
|
|
- binding.textMenuPosition.x.toInt(),
|
|
|
|
|
- binding.textMenuPosition.y.toInt(),
|
|
|
|
|
- binding.cursorLeft.y.toInt() + binding.cursorLeft.height,
|
|
|
|
|
- binding.cursorRight.x.toInt(),
|
|
|
|
|
- binding.cursorRight.y.toInt() + binding.cursorRight.height
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 当前选择的文本
|
|
|
|
|
- */
|
|
|
|
|
- override val selectedText: String get() = binding.readView.getSelectText()
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 文本选择菜单操作
|
|
|
|
|
- */
|
|
|
|
|
- override fun onMenuItemSelected(itemId: Int): Boolean {
|
|
|
|
|
- when (itemId) {
|
|
|
|
|
- R.id.menu_aloud -> when (AppConfig.contentSelectSpeakMod) {
|
|
|
|
|
- 1 -> lifecycleScope.launch {
|
|
|
|
|
- binding.readView.aloudStartSelect()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- else -> speak(binding.readView.getSelectText())
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_bookmark -> binding.readView.curPage.let {
|
|
|
|
|
- val bookmark = it.createBookmark()
|
|
|
|
|
- if (bookmark == null) {
|
|
|
|
|
- toastOnUi(R.string.create_bookmark_error)
|
|
|
|
|
- } else {
|
|
|
|
|
- showDialogFragment(BookmarkDialog(bookmark))
|
|
|
|
|
- }
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_edit -> {
|
|
|
|
|
- val startPos = ReadBook.durChapterPos
|
|
|
|
|
- showDialogFragment<ContentEditDialog> {
|
|
|
|
|
- putInt("start_position", startPos)
|
|
|
|
|
- putString("selected_text", binding.readView.getSelectText())
|
|
|
|
|
- }
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_replace -> {
|
|
|
|
|
- val scopes = arrayListOf<String>()
|
|
|
|
|
- ReadBook.book?.name?.let { scopes.add(it) }
|
|
|
|
|
- ReadBook.bookSource?.bookSourceUrl?.let { scopes.add(it) }
|
|
|
|
|
-
|
|
|
|
|
- val text = selectedText.lineSequence().map { it.trim() }.joinToString("\n")
|
|
|
|
|
-
|
|
|
|
|
- val editRoute = ReplaceEditRoute(
|
|
|
|
|
- id = -1,
|
|
|
|
|
- pattern = text,
|
|
|
|
|
- scope = scopes.joinToString(";"),
|
|
|
|
|
- isScopeTitle = false,
|
|
|
|
|
- isScopeContent = true
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- replaceActivity.launch(
|
|
|
|
|
- ReplaceRuleActivity.startIntent(
|
|
|
|
|
- context = this,
|
|
|
|
|
- editRoute = editRoute
|
|
|
|
|
- )
|
|
|
|
|
- )
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_search_content -> {
|
|
|
|
|
- viewModel.searchContentQuery = selectedText
|
|
|
|
|
- openSearchActivity(selectedText)
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- R.id.menu_dict -> {
|
|
|
|
|
- showDialogFragment(DictDialog(selectedText))
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- return false
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private fun onEditTextAction(selectedText: String, startPos: Int) {
|
|
|
|
|
- val bundle = Bundle().apply {
|
|
|
|
|
- putString("selected_text", selectedText)
|
|
|
|
|
- putInt("start_position", startPos)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- val dialog = ContentEditDialog().apply {
|
|
|
|
|
- arguments = bundle
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- dialog.show(supportFragmentManager, "ContentEditDialog")
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 文本选择菜单操作完成
|
|
|
|
|
- */
|
|
|
|
|
- override fun onMenuActionFinally() = binding.run {
|
|
|
|
|
- textActionMenu.dismiss()
|
|
|
|
|
- readView.cancelSelect()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private fun speak(text: String) {
|
|
|
|
|
- if (tts == null) {
|
|
|
|
|
- tts = TTS()
|
|
|
|
|
- }
|
|
|
|
|
- tts?.speak(text)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 鼠标滚轮翻页
|
|
|
|
|
- */
|
|
|
|
|
- private fun mouseWheelPage(direction: PageDirection) {
|
|
|
|
|
- if (menuLayoutIsVisible || !AppConfig.mouseWheelPage) {
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- keyPageDebounce(direction, mouseWheel = true, longPress = false)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 音量键翻页
|
|
|
|
|
- */
|
|
|
|
|
- private fun volumeKeyPage(direction: PageDirection, longPress: Boolean): Boolean {
|
|
|
|
|
- if (!AppConfig.volumeKeyPage) {
|
|
|
|
|
- return false
|
|
|
|
|
- }
|
|
|
|
|
- if (!AppConfig.volumeKeyPageOnPlay && BaseReadAloudService.isPlay()) {
|
|
|
|
|
- return false
|
|
|
|
|
- }
|
|
|
|
|
- handleKeyPage(direction, longPress)
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private fun handleKeyPage(direction: PageDirection, longPress: Boolean) {
|
|
|
|
|
- if (AppConfig.keyPageOnLongPress || direction == PageDirection.NONE) {
|
|
|
|
|
- keyPage(direction)
|
|
|
|
|
- } else {
|
|
|
|
|
- keyPageDebounce(direction, longPress = longPress)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private fun keyPageDebounce(
|
|
|
|
|
- direction: PageDirection,
|
|
|
|
|
- mouseWheel: Boolean = false,
|
|
|
|
|
- longPress: Boolean
|
|
|
|
|
- ) {
|
|
|
|
|
- if (longPress) {
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- nextPageDebounce.apply {
|
|
|
|
|
- wait = if (mouseWheel) 200L else 600L
|
|
|
|
|
- leading = !mouseWheel
|
|
|
|
|
- trailing = mouseWheel
|
|
|
|
|
- }
|
|
|
|
|
- prevPageDebounce.apply {
|
|
|
|
|
- wait = if (mouseWheel) 200L else 600L
|
|
|
|
|
- leading = !mouseWheel
|
|
|
|
|
- trailing = mouseWheel
|
|
|
|
|
- }
|
|
|
|
|
- when (direction) {
|
|
|
|
|
- PageDirection.NEXT -> nextPageDebounce.invoke()
|
|
|
|
|
- PageDirection.PREV -> prevPageDebounce.invoke()
|
|
|
|
|
- else -> {}
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private fun keyPage(direction: PageDirection) {
|
|
|
|
|
- binding.readView.cancelSelect()
|
|
|
|
|
- binding.readView.pageDelegate?.isCancel = false
|
|
|
|
|
- binding.readView.pageDelegate?.keyTurnPage(direction)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun upMenuView() {
|
|
|
|
|
- handler.post {
|
|
|
|
|
- upMenu()
|
|
|
|
|
- binding.readMenu.upBookView()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun loadChapterList(book: Book) {
|
|
|
|
|
- ReadBook.upMsg(getString(R.string.toc_updateing))
|
|
|
|
|
- viewModel.loadChapterList(book)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 内容加载完成
|
|
|
|
|
- */
|
|
|
|
|
- override fun contentLoadFinish() {
|
|
|
|
|
- if (intent.getBooleanExtra("readAloud", false)) {
|
|
|
|
|
- intent.removeExtra("readAloud")
|
|
|
|
|
- ReadBook.readAloud()
|
|
|
|
|
- }
|
|
|
|
|
- loadStates = true
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 更新内容
|
|
|
|
|
- */
|
|
|
|
|
- override fun upContent(
|
|
|
|
|
- relativePosition: Int,
|
|
|
|
|
- resetPageOffset: Boolean,
|
|
|
|
|
- success: (() -> Unit)?
|
|
|
|
|
- ) {
|
|
|
|
|
- lifecycleScope.launch {
|
|
|
|
|
- binding.readView.upContent(relativePosition, resetPageOffset)
|
|
|
|
|
- if (relativePosition == 0) {
|
|
|
|
|
- upSeekBarProgress()
|
|
|
|
|
- }
|
|
|
|
|
- loadStates = false
|
|
|
|
|
- success?.invoke()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override suspend fun upContentAwait(
|
|
|
|
|
- relativePosition: Int,
|
|
|
|
|
- resetPageOffset: Boolean,
|
|
|
|
|
- success: (() -> Unit)?
|
|
|
|
|
- ) = withContext(Main.immediate) {
|
|
|
|
|
- binding.readView.upContent(relativePosition, resetPageOffset)
|
|
|
|
|
- if (relativePosition == 0) {
|
|
|
|
|
- upSeekBarProgress()
|
|
|
|
|
- }
|
|
|
|
|
- loadStates = false
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun upPageAnim(upRecorder: Boolean) {
|
|
|
|
|
- lifecycleScope.launch {
|
|
|
|
|
- binding.readView.upPageAnim(upRecorder)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun notifyBookChanged() {
|
|
|
|
|
- bookChanged = true
|
|
|
|
|
- if (!ReadBook.inBookshelf) {
|
|
|
|
|
- viewModel.removeFromBookshelf { super.finish() }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun cancelSelect() {
|
|
|
|
|
- runOnUiThread {
|
|
|
|
|
- binding.readView.cancelSelect()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 页面改变
|
|
|
|
|
- */
|
|
|
|
|
- override fun pageChanged() {
|
|
|
|
|
- pageChanged = true
|
|
|
|
|
- binding.readView.onPageChange()
|
|
|
|
|
- handler.post {
|
|
|
|
|
- upSeekBarProgress()
|
|
|
|
|
- }
|
|
|
|
|
- executor.execute {
|
|
|
|
|
- startBackupJob()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 更新进度条位置
|
|
|
|
|
- */
|
|
|
|
|
- private fun upSeekBarProgress() {
|
|
|
|
|
- val progress = when (AppConfig.progressBarBehavior) {
|
|
|
|
|
- "page" -> ReadBook.durPageIndex
|
|
|
|
|
- else /* chapter */ -> ReadBook.durChapterIndex
|
|
|
|
|
- }
|
|
|
|
|
- if (progress >= 0) {
|
|
|
|
|
- binding.readMenu.setSeekPage(progress)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 显示菜单
|
|
|
|
|
- */
|
|
|
|
|
- override fun showMenuBar() {
|
|
|
|
|
- binding.readMenu.runMenuIn()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override val oldBook: Book?
|
|
|
|
|
- get() = ReadBook.book
|
|
|
|
|
-
|
|
|
|
|
- override fun changeTo(source: BookSource, book: Book, toc: List<BookChapter>) {
|
|
|
|
|
- if (!book.isAudio) {
|
|
|
|
|
- viewModel.changeTo(book, toc)
|
|
|
|
|
- } else {
|
|
|
|
|
- ReadAloud.stop(this)
|
|
|
|
|
- lifecycleScope.launch {
|
|
|
|
|
- withContext(IO) {
|
|
|
|
|
- ReadBook.book?.migrateTo(book, toc)
|
|
|
|
|
- book.removeType(BookType.updateError)
|
|
|
|
|
- ReadBook.book?.delete()
|
|
|
|
|
- appDb.bookDao.insert(book)
|
|
|
|
|
- }
|
|
|
|
|
- startActivityForBook(book)
|
|
|
|
|
- finish()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun replaceContent(content: String) {
|
|
|
|
|
- ReadBook.book?.let {
|
|
|
|
|
- viewModel.saveContent(it, content)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun showActionMenu() {
|
|
|
|
|
- when {
|
|
|
|
|
- BaseReadAloudService.isRun -> showReadAloudDialog()
|
|
|
|
|
- isAutoPage -> showDialogFragment<AutoReadDialog>()
|
|
|
|
|
- isShowingSearchResult -> binding.searchMenu.runMenuIn()
|
|
|
|
|
- else -> binding.readMenu.runMenuIn()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 显示朗读菜单
|
|
|
|
|
- */
|
|
|
|
|
- override fun showReadAloudDialog() {
|
|
|
|
|
- showDialogFragment<ReadAloudDialog>()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 自动翻页
|
|
|
|
|
- */
|
|
|
|
|
- override fun autoPage() {
|
|
|
|
|
- ReadAloud.stop(this)
|
|
|
|
|
- if (isAutoPage) {
|
|
|
|
|
- autoPageStop()
|
|
|
|
|
- } else {
|
|
|
|
|
- binding.readView.autoPager.start()
|
|
|
|
|
- binding.readMenu.setAutoPage(true)
|
|
|
|
|
- screenTimeOut = -1L
|
|
|
|
|
- screenOffTimerStart()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun autoPageStop() {
|
|
|
|
|
- if (isAutoPage) {
|
|
|
|
|
- binding.readView.autoPager.stop()
|
|
|
|
|
- binding.readMenu.setAutoPage(false)
|
|
|
|
|
- dismissDialogFragment<AutoReadDialog>()
|
|
|
|
|
- upScreenTimeOut()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun openSourceEditActivity() {
|
|
|
|
|
- ReadBook.bookSource?.let {
|
|
|
|
|
- sourceEditActivity.launch {
|
|
|
|
|
- putExtra("sourceUrl", it.bookSourceUrl)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun openBookInfoActivity() {
|
|
|
|
|
- ReadBook.book?.let {
|
|
|
|
|
- bookInfoActivity.launch {
|
|
|
|
|
- putExtra("name", it.name)
|
|
|
|
|
- putExtra("author", it.author)
|
|
|
|
|
- putExtra("bookUrl", it.bookUrl)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 替换
|
|
|
|
|
- */
|
|
|
|
|
- override fun openReplaceRule() {
|
|
|
|
|
- replaceActivity.launch(Intent(this, ReplaceRuleActivity::class.java))
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 打开目录
|
|
|
|
|
- */
|
|
|
|
|
- override fun openChapterList() {
|
|
|
|
|
- ReadBook.book?.let {
|
|
|
|
|
- tocActivity.launch(it.bookUrl)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 打开搜索界面
|
|
|
|
|
- */
|
|
|
|
|
- override fun openSearchActivity(searchWord: String?) {
|
|
|
|
|
- val book = ReadBook.book ?: return
|
|
|
|
|
- searchContentActivity.launch {
|
|
|
|
|
- putExtra("bookUrl", book.bookUrl)
|
|
|
|
|
- putExtra("searchWord", searchWord)
|
|
|
|
|
- putExtra("searchResultIndex", viewModel.searchResultIndex)
|
|
|
|
|
- viewModel.searchResultList?.first()?.let {
|
|
|
|
|
- if (it.query == viewModel.searchContentQuery) {
|
|
|
|
|
- IntentData.put("searchResultList", viewModel.searchResultList)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 禁用书源
|
|
|
|
|
- */
|
|
|
|
|
- override fun disableSource() {
|
|
|
|
|
- viewModel.disableSource()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 显示阅读样式配置
|
|
|
|
|
- */
|
|
|
|
|
-// override fun showReadStyle() {
|
|
|
|
|
-// showDialogFragment<ReadStyleDialog>()
|
|
|
|
|
-// }
|
|
|
|
|
-//
|
|
|
|
|
-// /**
|
|
|
|
|
-// * 显示更多设置
|
|
|
|
|
-// */
|
|
|
|
|
-// override fun showMoreSetting() {
|
|
|
|
|
-// showDialogFragment<MoreConfigDialog>()
|
|
|
|
|
-// }
|
|
|
|
|
-//
|
|
|
|
|
- override fun showSearchSetting() {
|
|
|
|
|
- showDialogFragment<ReadStyleDialog>()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun showReadStyle() {
|
|
|
|
|
- showDialogFragment<ReadStyleDialog>()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 更新状态栏,导航栏
|
|
|
|
|
- */
|
|
|
|
|
- override fun upSystemUiVisibility() {
|
|
|
|
|
- upSystemUiVisibility(isInMultiWindow, !menuLayoutIsVisible)
|
|
|
|
|
- upNavigationBarColor()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 退出全文搜索
|
|
|
|
|
- override fun exitSearchMenu() {
|
|
|
|
|
- if (isShowingSearchResult) {
|
|
|
|
|
- isShowingSearchResult = false
|
|
|
|
|
- binding.searchMenu.invalidate()
|
|
|
|
|
- binding.searchMenu.invisible()
|
|
|
|
|
- ReadBook.clearSearchResult()
|
|
|
|
|
- binding.readView.cancelSelect(true)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /* 恢复到 全文搜索/进度条跳转前的位置 */
|
|
|
|
|
- private fun restoreLastBookProcess() {
|
|
|
|
|
- if (confirmRestoreProcess == true) {
|
|
|
|
|
- ReadBook.restoreLastBookProgress()
|
|
|
|
|
- } else if (confirmRestoreProcess == null) {
|
|
|
|
|
- alert(R.string.draw) {
|
|
|
|
|
- setMessage(R.string.restore_last_book_process)
|
|
|
|
|
- yesButton {
|
|
|
|
|
- confirmRestoreProcess = true
|
|
|
|
|
- ReadBook.restoreLastBookProgress() //恢复启动全文搜索前的进度
|
|
|
|
|
- }
|
|
|
|
|
- noButton {
|
|
|
|
|
- ReadBook.lastBookProgress = null
|
|
|
|
|
- confirmRestoreProcess = false
|
|
|
|
|
- }
|
|
|
|
|
- onCancelled {
|
|
|
|
|
- ReadBook.lastBookProgress = null
|
|
|
|
|
- confirmRestoreProcess = false
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun showLogin() {
|
|
|
|
|
- ReadBook.bookSource?.let {
|
|
|
|
|
- startActivity<SourceLoginActivity> {
|
|
|
|
|
- putExtra("bookType", BookType.text)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun payAction() {
|
|
|
|
|
- val book = ReadBook.book ?: return
|
|
|
|
|
- if (book.isLocal) return
|
|
|
|
|
- val chapter = appDb.bookChapterDao.getChapter(book.bookUrl, ReadBook.durChapterIndex)
|
|
|
|
|
- if (chapter == null) {
|
|
|
|
|
- toastOnUi("no chapter")
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- alert(R.string.chapter_pay) {
|
|
|
|
|
- setMessage(chapter.title)
|
|
|
|
|
- yesButton {
|
|
|
|
|
- Coroutine.async(lifecycleScope) {
|
|
|
|
|
- val source =
|
|
|
|
|
- ReadBook.bookSource ?: throw NoStackTraceException("no book source")
|
|
|
|
|
- val payAction = source.getContentRule().payAction
|
|
|
|
|
- if (payAction.isNullOrBlank()) {
|
|
|
|
|
- throw NoStackTraceException("no pay action")
|
|
|
|
|
- }
|
|
|
|
|
- val analyzeRule = AnalyzeRule(book, source)
|
|
|
|
|
- analyzeRule.setCoroutineContext(coroutineContext)
|
|
|
|
|
- analyzeRule.setBaseUrl(chapter.url)
|
|
|
|
|
- analyzeRule.setChapter(chapter)
|
|
|
|
|
- analyzeRule.evalJS(payAction).toString()
|
|
|
|
|
- }.onSuccess(IO) {
|
|
|
|
|
- if (it.isAbsUrl()) {
|
|
|
|
|
- startActivity<WebViewActivity> {
|
|
|
|
|
- val bookSource = ReadBook.bookSource
|
|
|
|
|
- putExtra("title", getString(R.string.chapter_pay))
|
|
|
|
|
- putExtra("url", it)
|
|
|
|
|
- putExtra("sourceOrigin", bookSource?.bookSourceUrl)
|
|
|
|
|
- putExtra("sourceName", bookSource?.bookSourceName)
|
|
|
|
|
- putExtra("sourceType", bookSource?.getSourceType())
|
|
|
|
|
- }
|
|
|
|
|
- } else if (it.isTrue()) {
|
|
|
|
|
- //购买成功后刷新目录
|
|
|
|
|
- ReadBook.book?.let {
|
|
|
|
|
- ReadBook.curTextChapter = null
|
|
|
|
|
- BookHelp.delContent(book, chapter)
|
|
|
|
|
- loadChapterList(book)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }.onError {
|
|
|
|
|
- AppLog.put("执行购买操作出错\n${it.localizedMessage}", it, true)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- noButton()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 朗读按钮
|
|
|
|
|
- */
|
|
|
|
|
- override fun onClickReadAloud() {
|
|
|
|
|
- autoPageStop()
|
|
|
|
|
- when {
|
|
|
|
|
- !BaseReadAloudService.isRun -> {
|
|
|
|
|
- ReadAloud.upReadAloudClass()
|
|
|
|
|
- val scrollPageAnim = ReadBook.pageAnim() == 3
|
|
|
|
|
- if (scrollPageAnim) {
|
|
|
|
|
- val pos = binding.readView.getReadAloudPos()
|
|
|
|
|
- if (pos != null) {
|
|
|
|
|
- val (index, line) = pos
|
|
|
|
|
- if (ReadBook.durChapterIndex != index) {
|
|
|
|
|
- ReadBook.openChapter(index, line.chapterPosition, false) {
|
|
|
|
|
- ReadBook.readAloud(startPos = line.pagePosition)
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- ReadBook.durChapterPos = line.chapterPosition
|
|
|
|
|
- ReadBook.readAloud(startPos = line.pagePosition)
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- ReadBook.readAloud()
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- ReadBook.readAloud()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- BaseReadAloudService.pause -> {
|
|
|
|
|
- val scrollPageAnim = ReadBook.pageAnim() == 3
|
|
|
|
|
- if (scrollPageAnim && pageChanged) {
|
|
|
|
|
- pageChanged = false
|
|
|
|
|
- val pos = binding.readView.getReadAloudPos()
|
|
|
|
|
- if (pos != null) {
|
|
|
|
|
- val (index, line) = pos
|
|
|
|
|
- if (ReadBook.durChapterIndex != index) {
|
|
|
|
|
- ReadBook.openChapter(index, line.chapterPosition, false) {
|
|
|
|
|
- ReadBook.readAloud(startPos = line.pagePosition)
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- ReadBook.durChapterPos = line.chapterPosition
|
|
|
|
|
- ReadBook.readAloud(startPos = line.pagePosition)
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- ReadBook.readAloud()
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- ReadAloud.resume(this)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- else -> ReadAloud.pause(this)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun showHelp() {
|
|
|
|
|
- showHelp("readMenuHelp")
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 点击图片
|
|
|
|
|
- */
|
|
|
|
|
- override fun oldClickImg(src: String): Boolean {
|
|
|
|
|
- val urlMatcher = paramPattern.matcher(src)
|
|
|
|
|
- if (urlMatcher.find()) {
|
|
|
|
|
- val urlOptionStr = src.substring(urlMatcher.end())
|
|
|
|
|
- val urlOptionMap = GSON.fromJsonObject<Map<String, String>>(urlOptionStr).getOrNull()
|
|
|
|
|
- val click = urlOptionMap?.get("click")
|
|
|
|
|
- if (click != null) {
|
|
|
|
|
- Coroutine.async(lifecycleScope, IO) {
|
|
|
|
|
- val source = ReadBook.bookSource ?: return@async
|
|
|
|
|
- val java = SourceLoginJsExtensions(this@ReadBookActivity, source, BookType.text)
|
|
|
|
|
- val book = ReadBook.book ?: return@async
|
|
|
|
|
- val chapter =
|
|
|
|
|
- appDb.bookChapterDao.getChapter(book.bookUrl, ReadBook.durChapterIndex)
|
|
|
|
|
- ?: throw Exception("no find chapter")
|
|
|
|
|
- runScriptWithContext {
|
|
|
|
|
- source.evalJS(click) {
|
|
|
|
|
- put("java", java)
|
|
|
|
|
- put("book", book)
|
|
|
|
|
- put("chapter", chapter)
|
|
|
|
|
- put("result", src)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }.onError {
|
|
|
|
|
- AppLog.put("执行图片链接click键值出错\n${it.localizedMessage}", it, true)
|
|
|
|
|
- }
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
- val jsStr = urlOptionMap?.get("js") ?: return false
|
|
|
|
|
- Coroutine.async(lifecycleScope, IO) {
|
|
|
|
|
- val source = ReadBook.bookSource ?: return@async
|
|
|
|
|
- val book = ReadBook.book ?: return@async
|
|
|
|
|
- val chapter =
|
|
|
|
|
- appDb.bookChapterDao.getChapter(book.bookUrl, ReadBook.durChapterIndex)
|
|
|
|
|
- ?: throw Exception("no find chapter")
|
|
|
|
|
- val urlNoOption = src.take(urlMatcher.start())
|
|
|
|
|
- AnalyzeRule(book, source).apply {
|
|
|
|
|
- setCoroutineContext(coroutineContext)
|
|
|
|
|
- setBaseUrl(chapter.url)
|
|
|
|
|
- setChapter(chapter)
|
|
|
|
|
- evalJS(jsStr, urlNoOption)
|
|
|
|
|
- }
|
|
|
|
|
- }.onError {
|
|
|
|
|
- AppLog.put("执行图片链接js键值出错\n${it.localizedMessage}", it, true)
|
|
|
|
|
- }
|
|
|
|
|
- return true
|
|
|
|
|
- }
|
|
|
|
|
- return false
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun clickImg(click: String, src: String) {
|
|
|
|
|
- Coroutine.async(lifecycleScope, IO) {
|
|
|
|
|
- val source = ReadBook.bookSource ?: return@async
|
|
|
|
|
- val java = SourceLoginJsExtensions(this@ReadBookActivity, source, BookType.text)
|
|
|
|
|
- val book = ReadBook.book ?: return@async
|
|
|
|
|
- val chapter = appDb.bookChapterDao.getChapter(book.bookUrl, ReadBook.durChapterIndex)
|
|
|
|
|
- ?: throw Exception("no find chapter")
|
|
|
|
|
- runScriptWithContext {
|
|
|
|
|
- source.evalJS(click) {
|
|
|
|
|
- put("java", java)
|
|
|
|
|
- put("book", book)
|
|
|
|
|
- put("chapter", chapter)
|
|
|
|
|
- put("result", src)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }.onError {
|
|
|
|
|
- AppLog.put("执行图片链接click键值出错\n${it.localizedMessage}", it, true)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 长按图片
|
|
|
|
|
- */
|
|
|
|
|
- @SuppressLint("RtlHardcoded")
|
|
|
|
|
- override fun onImageLongPress(x: Float, y: Float, src: String) {
|
|
|
|
|
- binding.root.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
|
|
|
|
- popupAction.setItems(
|
|
|
|
|
- listOf(
|
|
|
|
|
- SelectItem(getString(R.string.show), "show"),
|
|
|
|
|
- SelectItem(getString(R.string.refresh), "refresh"),
|
|
|
|
|
- SelectItem("保存到相册", "save"),
|
|
|
|
|
- SelectItem(getString(R.string.menu), "menu"),
|
|
|
|
|
- )
|
|
|
|
|
- )
|
|
|
|
|
- popupAction.onActionClick = {
|
|
|
|
|
- when (it) {
|
|
|
|
|
- "show" -> showDialogFragment(PhotoDialog(src))
|
|
|
|
|
- "refresh" -> viewModel.refreshImage(src)
|
|
|
|
|
- "save" -> {
|
|
|
|
|
- viewModel.saveImage(src)
|
|
|
|
|
- }
|
|
|
|
|
- "menu" -> showActionMenu()
|
|
|
|
|
- }
|
|
|
|
|
- popupAction.dismiss()
|
|
|
|
|
- }
|
|
|
|
|
- val navigationBarHeight =
|
|
|
|
|
- if (!ReadBookConfig.hideNavigationBar && navigationBarGravity == Gravity.BOTTOM)
|
|
|
|
|
- binding.navigationBar.height else 0
|
|
|
|
|
- popupAction.showAtLocation(
|
|
|
|
|
- binding.readView, Gravity.BOTTOM or Gravity.LEFT, x.toInt(),
|
|
|
|
|
- binding.root.height + navigationBarHeight - y.toInt()
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * colorSelectDialog
|
|
|
|
|
- */
|
|
|
|
|
- override fun onColorSelected(dialogId: Int, color: Int) = ReadBookConfig.durConfig.run {
|
|
|
|
|
- when (dialogId) {
|
|
|
|
|
- S_COLOR -> {
|
|
|
|
|
- setCurShadColor(color)
|
|
|
|
|
- postEvent(EventBus.UP_CONFIG, arrayListOf(2, 6, 9, 11))
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- TEXT_COLOR -> {
|
|
|
|
|
- setCurTextColor(color)
|
|
|
|
|
- postEvent(EventBus.UP_CONFIG, arrayListOf(2, 6, 9, 11))
|
|
|
|
|
- if (AppConfig.readBarStyleFollowPage) {
|
|
|
|
|
- postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- TEXT_ACCENT_COLOR -> {
|
|
|
|
|
- setCurTextAccentColor(color)
|
|
|
|
|
- postEvent(EventBus.UP_CONFIG, arrayListOf(2, 6, 9, 11))
|
|
|
|
|
- if (AppConfig.readBarStyleFollowPage) {
|
|
|
|
|
- postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- BG_COLOR -> {
|
|
|
|
|
- setCurBg(0, "#${color.hexString}")
|
|
|
|
|
- postEvent(EventBus.UP_CONFIG, arrayListOf(1))
|
|
|
|
|
- if (AppConfig.readBarStyleFollowPage) {
|
|
|
|
|
- postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- TIP_COLOR -> {
|
|
|
|
|
- ReadTipConfig.tipColor = color
|
|
|
|
|
- postEvent(EventBus.TIP_COLOR, "")
|
|
|
|
|
- postEvent(EventBus.UP_CONFIG, arrayListOf(2))
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- TIP_DIVIDER_COLOR -> {
|
|
|
|
|
- ReadTipConfig.tipDividerColor = color
|
|
|
|
|
- postEvent(EventBus.TIP_COLOR, "")
|
|
|
|
|
- postEvent(EventBus.UP_CONFIG, arrayListOf(2))
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- B_COLOR -> {
|
|
|
|
|
- setMenuCurBg(color)
|
|
|
|
|
- postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- A_COLOR -> {
|
|
|
|
|
- setMenuCurAc(color)
|
|
|
|
|
- postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- U_COLOR -> {
|
|
|
|
|
- setUnderlineColor(color)
|
|
|
|
|
- postEvent(EventBus.UP_CONFIG, arrayListOf(2))
|
|
|
|
|
- postEvent(EventBus.UP_CONFIG, arrayListOf(6, 9, 11))
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * colorSelectDialog
|
|
|
|
|
- */
|
|
|
|
|
- override fun onDialogDismissed(dialogId: Int) = Unit
|
|
|
|
|
-
|
|
|
|
|
- override fun onTocRegexDialogResult(tocRegex: String) {
|
|
|
|
|
- ReadBook.book?.let {
|
|
|
|
|
- it.tocUrl = tocRegex
|
|
|
|
|
- loadChapterList(it)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private fun sureSyncProgress(progress: BookProgress) {
|
|
|
|
|
- alert(R.string.get_book_progress) {
|
|
|
|
|
- setMessage(R.string.current_progress_exceeds_cloud)
|
|
|
|
|
- okButton {
|
|
|
|
|
- ReadBook.setProgress(progress)
|
|
|
|
|
- }
|
|
|
|
|
- noButton()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /* 进度条跳转到指定章节 */
|
|
|
|
|
- override fun skipToChapter(index: Int) {
|
|
|
|
|
- ReadBook.saveCurrentBookProgress() //退出章节跳转恢复此时进度
|
|
|
|
|
- viewModel.openChapter(index)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /* 全文搜索跳转 */
|
|
|
|
|
- override fun navigateToSearch(searchResult: SearchResult, index: Int) {
|
|
|
|
|
- viewModel.searchResultIndex = index
|
|
|
|
|
- skipToSearch(searchResult)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun onMenuShow() {
|
|
|
|
|
- binding.readView.autoPager.pause()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun onMenuHide() {
|
|
|
|
|
- binding.readView.autoPager.resume()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun refresh() {
|
|
|
|
|
- recreate()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun onLayoutPageCompleted(index: Int, page: TextPage) {
|
|
|
|
|
- upSeekBarThrottle.invoke()
|
|
|
|
|
- binding.readView.onLayoutPageCompleted(index, page)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /* 全文搜索跳转 */
|
|
|
|
|
- private fun skipToSearch(searchResult: SearchResult) {
|
|
|
|
|
- if (searchResult.chapterIndex != ReadBook.durChapterIndex) {
|
|
|
|
|
- viewModel.openChapter(searchResult.chapterIndex) {
|
|
|
|
|
- jumpToPosition(searchResult)
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- jumpToPosition(searchResult)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private fun jumpToPosition(searchResult: SearchResult) {
|
|
|
|
|
- val curTextChapter = ReadBook.curTextChapter ?: return
|
|
|
|
|
- binding.searchMenu.updateSearchInfo()
|
|
|
|
|
- val (pageIndex, lineIndex, charIndex, addLine, charIndex2) =
|
|
|
|
|
- viewModel.searchResultPositions(curTextChapter, searchResult)
|
|
|
|
|
- ReadBook.skipToPage(pageIndex) {
|
|
|
|
|
- isSelectingSearchResult = true
|
|
|
|
|
- binding.readView.curPage.selectStartMoveIndex(0, lineIndex, charIndex)
|
|
|
|
|
- when (addLine) {
|
|
|
|
|
- 0 -> binding.readView.curPage.selectEndMoveIndex(
|
|
|
|
|
- 0,
|
|
|
|
|
- lineIndex,
|
|
|
|
|
- charIndex + viewModel.searchContentQuery.length - 1
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- 1 -> binding.readView.curPage.selectEndMoveIndex(
|
|
|
|
|
- 0, lineIndex + 1, charIndex2
|
|
|
|
|
- )
|
|
|
|
|
- //consider change page, jump to scroll position
|
|
|
|
|
- -1 -> binding.readView.curPage.selectEndMoveIndex(1, 0, charIndex2)
|
|
|
|
|
- }
|
|
|
|
|
- binding.readView.isTextSelected = true
|
|
|
|
|
- isSelectingSearchResult = false
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun addBookmark() {
|
|
|
|
|
- val book = ReadBook.book
|
|
|
|
|
- val page = ReadBook.curTextChapter?.getPage(ReadBook.durPageIndex)
|
|
|
|
|
- if (book != null && page != null) {
|
|
|
|
|
- val bookmark = book.createBookMark().apply {
|
|
|
|
|
- chapterIndex = ReadBook.durChapterIndex
|
|
|
|
|
- chapterPos = ReadBook.durChapterPos
|
|
|
|
|
- chapterName = page.title
|
|
|
|
|
- bookText = page.text.replace(Regex("[袮꧁]"), "").trim()
|
|
|
|
|
- }
|
|
|
|
|
- showDialogFragment(BookmarkDialog(bookmark))
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun changeReplaceRuleState() {
|
|
|
|
|
- binding.readMenu.runMenuOut()
|
|
|
|
|
- ReadBook.book?.let {
|
|
|
|
|
- it.setUseReplaceRule(!it.getUseReplaceRule())
|
|
|
|
|
- ReadBook.saveRead()
|
|
|
|
|
- menu?.findItem(R.id.menu_enable_replace)?.isChecked = it.getUseReplaceRule()
|
|
|
|
|
- binding.readMenu.changeReplace(it.getUseReplaceRule())
|
|
|
|
|
- viewModel.replaceRuleChanged()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private fun startBackupJob() {
|
|
|
|
|
- backupJob?.cancel()
|
|
|
|
|
- backupJob = lifecycleScope.launch(IO) {
|
|
|
|
|
- delay(300000)
|
|
|
|
|
- ReadBook.book?.let {
|
|
|
|
|
- AppWebDav.uploadBookProgress(it)
|
|
|
|
|
- ensureActive()
|
|
|
|
|
- it.update()
|
|
|
|
|
- Backup.autoBack(this@ReadBookActivity)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- 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)
|
|
|
|
|
- }
|
|
|
|
|
- noButton()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun onDestroy() {
|
|
|
|
|
- super.onDestroy()
|
|
|
|
|
- tts?.clearTts()
|
|
|
|
|
- textActionMenu.dismiss()
|
|
|
|
|
- popupAction.dismiss()
|
|
|
|
|
- binding.readView.onDestroy()
|
|
|
|
|
- ReadBook.unregister(this)
|
|
|
|
|
- if (!ReadBook.inBookshelf && !isChangingConfigurations) {
|
|
|
|
|
- viewModel.removeFromBookshelf(null)
|
|
|
|
|
- }
|
|
|
|
|
- if (!BuildConfig.DEBUG) {
|
|
|
|
|
- Backup.autoBack(this)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun observeLiveBus() = binding.run {
|
|
|
|
|
- observeEvent<String>(EventBus.TIME_CHANGED) { readView.upTime() }
|
|
|
|
|
- observeEvent<Int>(EventBus.BATTERY_CHANGED) { readView.upBattery(it) }
|
|
|
|
|
- observeEvent<Boolean>(EventBus.MEDIA_BUTTON) {
|
|
|
|
|
- if (it) {
|
|
|
|
|
- onClickReadAloud()
|
|
|
|
|
- } else {
|
|
|
|
|
- ReadBook.readAloud(!BaseReadAloudService.pause)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- observeEvent<ArrayList<Int>>(EventBus.UP_CONFIG) {
|
|
|
|
|
- it.forEach { value ->
|
|
|
|
|
- when (value) {
|
|
|
|
|
- 0 -> upSystemUiVisibility()
|
|
|
|
|
- 1 -> readView.upBg()
|
|
|
|
|
- 2 -> {
|
|
|
|
|
- readView.upStyle()
|
|
|
|
|
- binding.readMenu.reset()
|
|
|
|
|
- }
|
|
|
|
|
- 3 -> readView.upBgAlpha()
|
|
|
|
|
- 4 -> readView.upPageSlopSquare()
|
|
|
|
|
- 5 -> if (isInitFinish) ReadBook.loadContent(resetPageOffset = false)
|
|
|
|
|
- 6 -> readView.upContent(resetPageOffset = false)
|
|
|
|
|
- 8 -> ChapterProvider.upStyle()
|
|
|
|
|
- 9 -> readView.invalidateTextPage()
|
|
|
|
|
- 10 -> ChapterProvider.upLayout()
|
|
|
|
|
- 11 -> readView.submitRenderTask()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- observeEvent<Int>(EventBus.ALOUD_STATE) {
|
|
|
|
|
- if (it == Status.STOP || it == Status.PAUSE) {
|
|
|
|
|
- ReadBook.curTextChapter?.let { textChapter ->
|
|
|
|
|
- val page = textChapter.getPageByReadPos(ReadBook.durChapterPos)
|
|
|
|
|
- if (page != null) {
|
|
|
|
|
- page.removePageAloudSpan()
|
|
|
|
|
- readView.upContent(resetPageOffset = false)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- observeEventSticky<Int>(EventBus.TTS_PROGRESS) { chapterStart ->
|
|
|
|
|
- lifecycleScope.launch(IO) {
|
|
|
|
|
- if (BaseReadAloudService.isPlay()) {
|
|
|
|
|
- ReadBook.curTextChapter?.let { textChapter ->
|
|
|
|
|
- val pageIndex = ReadBook.durPageIndex
|
|
|
|
|
- val aloudSpanStart = chapterStart - textChapter.getReadLength(pageIndex)
|
|
|
|
|
- textChapter.getPage(pageIndex)
|
|
|
|
|
- ?.upPageAloudSpan(aloudSpanStart)
|
|
|
|
|
- upContent()
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- observeEvent<Boolean>(PreferKey.keepLight) {
|
|
|
|
|
- upScreenTimeOut()
|
|
|
|
|
- }
|
|
|
|
|
- observeEvent<Boolean>(PreferKey.textSelectAble) {
|
|
|
|
|
- readView.curPage.upSelectAble(it)
|
|
|
|
|
- }
|
|
|
|
|
- observeEvent<String>(PreferKey.showBrightnessView) {
|
|
|
|
|
- readMenu.upBrightnessState()
|
|
|
|
|
- }
|
|
|
|
|
- observeEvent<List<SearchResult>>(EventBus.SEARCH_RESULT) {
|
|
|
|
|
- viewModel.searchResultList = it
|
|
|
|
|
- }
|
|
|
|
|
- observeEvent<Boolean>(EventBus.UPDATE_READ_ACTION_BAR) {
|
|
|
|
|
- readMenu.reset()
|
|
|
|
|
- }
|
|
|
|
|
- observeEvent<Boolean>(EventBus.UP_SEEK_BAR) {
|
|
|
|
|
- readMenu.upSeekBar()
|
|
|
|
|
- }
|
|
|
|
|
- observeEvent<Boolean>(EventBus.REFRESH_BOOK_CONTENT) {
|
|
|
|
|
- ReadBook.book?.let {
|
|
|
|
|
- ReadBook.curTextChapter = null
|
|
|
|
|
- binding.readView.upContent()
|
|
|
|
|
- viewModel.refreshContentDur(it)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- private fun upScreenTimeOut() {
|
|
|
|
|
- val keepLightPrefer = getPrefString(PreferKey.keepLight)?.toInt() ?: 0
|
|
|
|
|
- screenTimeOut = keepLightPrefer * 1000L
|
|
|
|
|
- screenOffTimerStart()
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 重置黑屏时间
|
|
|
|
|
- */
|
|
|
|
|
- override fun screenOffTimerStart() {
|
|
|
|
|
- handler.post {
|
|
|
|
|
- if (screenTimeOut < 0) {
|
|
|
|
|
- keepScreenOn(true)
|
|
|
|
|
- return@post
|
|
|
|
|
- }
|
|
|
|
|
- val t = screenTimeOut - sysScreenOffTime
|
|
|
|
|
- if (t > 0) {
|
|
|
|
|
- keepScreenOn(true)
|
|
|
|
|
- handler.removeCallbacks(screenOffRunnable)
|
|
|
|
|
- handler.postDelayed(screenOffRunnable, screenTimeOut)
|
|
|
|
|
- } else {
|
|
|
|
|
- keepScreenOn(false)
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- override fun addToBookshelf(book: Book, toc: List<BookChapter>) {
|
|
|
|
|
- viewModel.addToBookshelf(book, toc) {
|
|
|
|
|
- toastOnUi("已添加到书架")
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- companion object {
|
|
|
|
|
- const val RESULT_DELETED = 100
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
-}
|
|
|
|
|
|
|
+package io.legado.app.ui.book.read
|
|
|
|
|
+
|
|
|
|
|
+//import io.legado.app.lib.theme.accentColor
|
|
|
|
|
+import android.annotation.SuppressLint
|
|
|
|
|
+import android.content.Intent
|
|
|
|
|
+import android.content.res.Configuration
|
|
|
|
|
+import android.graphics.Color
|
|
|
|
|
+import android.os.Bundle
|
|
|
|
|
+import android.view.Gravity
|
|
|
|
|
+import android.view.HapticFeedbackConstants
|
|
|
|
|
+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 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.size
|
|
|
|
|
+import androidx.lifecycle.lifecycleScope
|
|
|
|
|
+import com.google.android.material.transition.platform.MaterialContainerTransform
|
|
|
|
|
+import com.google.android.material.transition.platform.MaterialContainerTransformSharedElementCallback
|
|
|
|
|
+import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
|
|
|
|
|
+import com.script.rhino.runScriptWithContext
|
|
|
|
|
+import io.legado.app.BuildConfig
|
|
|
|
|
+import io.legado.app.R
|
|
|
|
|
+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.data.appDb
|
|
|
|
|
+import io.legado.app.data.entities.Book
|
|
|
|
|
+import io.legado.app.data.entities.BookChapter
|
|
|
|
|
+import io.legado.app.data.entities.BookProgress
|
|
|
|
|
+import io.legado.app.data.entities.BookSource
|
|
|
|
|
+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.update
|
|
|
|
|
+import io.legado.app.help.config.AppConfig
|
|
|
|
|
+import io.legado.app.help.config.ReadBookConfig
|
|
|
|
|
+import io.legado.app.help.config.ReadTipConfig
|
|
|
|
|
+import io.legado.app.help.coroutine.Coroutine
|
|
|
|
|
+import io.legado.app.help.source.getSourceType
|
|
|
|
|
+import io.legado.app.help.storage.Backup
|
|
|
|
|
+import io.legado.app.lib.dialogs.SelectItem
|
|
|
|
|
+import io.legado.app.lib.dialogs.alert
|
|
|
|
|
+import io.legado.app.lib.dialogs.selector
|
|
|
|
|
+import io.legado.app.model.ReadAloud
|
|
|
|
|
+import io.legado.app.model.ReadBook
|
|
|
|
|
+import io.legado.app.model.analyzeRule.AnalyzeRule
|
|
|
|
|
+import io.legado.app.model.analyzeRule.AnalyzeRule.Companion.setChapter
|
|
|
|
|
+import io.legado.app.model.analyzeRule.AnalyzeRule.Companion.setCoroutineContext
|
|
|
|
|
+import io.legado.app.model.analyzeRule.AnalyzeUrl.Companion.paramPattern
|
|
|
|
|
+import io.legado.app.model.localBook.EpubFile
|
|
|
|
|
+import io.legado.app.model.localBook.MobiFile
|
|
|
|
|
+import io.legado.app.receiver.NetworkChangedListener
|
|
|
|
|
+import io.legado.app.receiver.TimeBatteryReceiver
|
|
|
|
|
+import io.legado.app.service.BaseReadAloudService
|
|
|
|
|
+import io.legado.app.ui.about.AppLogDialog
|
|
|
|
|
+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.BgTextConfigDialog.Companion.BG_COLOR
|
|
|
|
|
+import io.legado.app.ui.book.read.config.FontConfigDialog
|
|
|
|
|
+import io.legado.app.ui.book.read.config.FontConfigDialog.Companion.S_COLOR
|
|
|
|
|
+import io.legado.app.ui.book.read.config.FontConfigDialog.Companion.TEXT_ACCENT_COLOR
|
|
|
|
|
+import io.legado.app.ui.book.read.config.FontConfigDialog.Companion.TEXT_COLOR
|
|
|
|
|
+import io.legado.app.ui.book.read.config.FontSelectDialog
|
|
|
|
|
+import io.legado.app.ui.book.read.config.ReadAloudDialog
|
|
|
|
|
+import io.legado.app.ui.book.read.config.ReadStyleDialog
|
|
|
|
|
+import io.legado.app.ui.book.read.config.TipConfigDialog.Companion.A_COLOR
|
|
|
|
|
+import io.legado.app.ui.book.read.config.TipConfigDialog.Companion.B_COLOR
|
|
|
|
|
+import io.legado.app.ui.book.read.config.TipConfigDialog.Companion.TIP_COLOR
|
|
|
|
|
+import io.legado.app.ui.book.read.config.TipConfigDialog.Companion.TIP_DIVIDER_COLOR
|
|
|
|
|
+import io.legado.app.ui.book.read.config.ToolButtonConfigDialog
|
|
|
|
|
+import io.legado.app.ui.book.read.config.UnderlineConfigDialog.Companion.U_COLOR
|
|
|
|
|
+import io.legado.app.ui.book.read.page.ContentTextView
|
|
|
|
|
+import io.legado.app.ui.book.read.page.ReadView
|
|
|
|
|
+import io.legado.app.ui.book.read.page.entities.PageDirection
|
|
|
|
|
+import io.legado.app.ui.book.read.page.entities.TextPage
|
|
|
|
|
+import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
|
|
|
|
+import io.legado.app.ui.book.read.page.provider.LayoutProgressListener
|
|
|
|
|
+import io.legado.app.ui.book.searchContent.SearchContentActivity
|
|
|
|
|
+import io.legado.app.ui.book.searchContent.SearchResult
|
|
|
|
|
+import io.legado.app.ui.book.source.edit.BookSourceEditActivity
|
|
|
|
|
+import io.legado.app.ui.book.toc.TocActivityResult
|
|
|
|
|
+import io.legado.app.ui.book.toc.rule.TxtTocRuleDialog
|
|
|
|
|
+import io.legado.app.ui.browser.WebViewActivity
|
|
|
|
|
+import io.legado.app.ui.dict.DictDialog
|
|
|
|
|
+import io.legado.app.ui.login.SourceLoginActivity
|
|
|
|
|
+import io.legado.app.ui.login.SourceLoginJsExtensions
|
|
|
|
|
+import io.legado.app.ui.replace.ReplaceEditRoute
|
|
|
|
|
+import io.legado.app.ui.replace.ReplaceRuleActivity
|
|
|
|
|
+import io.legado.app.ui.widget.PopupAction
|
|
|
|
|
+import io.legado.app.ui.widget.dialog.PhotoDialog
|
|
|
|
|
+import io.legado.app.utils.Debounce
|
|
|
|
|
+import io.legado.app.utils.GSON
|
|
|
|
|
+import io.legado.app.utils.LogUtils
|
|
|
|
|
+import io.legado.app.utils.NetworkUtils
|
|
|
|
|
+import io.legado.app.utils.StartActivityContract
|
|
|
|
|
+import io.legado.app.utils.applyOpenTint
|
|
|
|
|
+import io.legado.app.utils.buildMainHandler
|
|
|
|
|
+import io.legado.app.utils.dismissDialogFragment
|
|
|
|
|
+import io.legado.app.utils.fromJsonObject
|
|
|
|
|
+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.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.startActivityForBook
|
|
|
|
|
+import io.legado.app.utils.sysScreenOffTime
|
|
|
|
|
+import io.legado.app.utils.themeColor
|
|
|
|
|
+import io.legado.app.utils.throttle
|
|
|
|
|
+import io.legado.app.utils.toastOnUi
|
|
|
|
|
+import io.legado.app.utils.visible
|
|
|
|
|
+import kotlinx.coroutines.Dispatchers
|
|
|
|
|
+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
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 阅读界面
|
|
|
|
|
+ */
|
|
|
|
|
+class ReadBookActivity : BaseReadBookActivity(),
|
|
|
|
|
+ View.OnTouchListener,
|
|
|
|
|
+ ReadView.CallBack,
|
|
|
|
|
+ TextActionMenu.CallBack,
|
|
|
|
|
+ ContentTextView.CallBack,
|
|
|
|
|
+ PopupMenu.OnMenuItemClickListener,
|
|
|
|
|
+ ReadMenu.CallBack,
|
|
|
|
|
+ SearchMenu.CallBack,
|
|
|
|
|
+ ReadAloudDialog.CallBack,
|
|
|
|
|
+ ChangeBookSourceDialog.CallBack,
|
|
|
|
|
+ ChangeChapterSourceDialog.CallBack,
|
|
|
|
|
+ ReadBook.CallBack,
|
|
|
|
|
+ AutoReadDialog.CallBack,
|
|
|
|
|
+ ToolButtonConfigDialog.CallBack,
|
|
|
|
|
+ TxtTocRuleDialog.CallBack,
|
|
|
|
|
+ ColorPickerDialogListener,
|
|
|
|
|
+ FontConfigDialog.CallBack,
|
|
|
|
|
+ FontSelectDialog.CallBack,
|
|
|
|
|
+ LayoutProgressListener {
|
|
|
|
|
+
|
|
|
|
|
+ private val tocActivity =
|
|
|
|
|
+ registerForActivityResult(TocActivityResult()) {
|
|
|
|
|
+ it?.let {
|
|
|
|
|
+ viewModel.openChapter(it.first, it.second)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ private val sourceEditActivity =
|
|
|
|
|
+ registerForActivityResult(StartActivityContract(BookSourceEditActivity::class.java)) {
|
|
|
|
|
+ if (it.resultCode == RESULT_OK) {
|
|
|
|
|
+ viewModel.upBookSource {
|
|
|
|
|
+ upMenuView()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ private val replaceActivity =
|
|
|
|
|
+ registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
|
|
|
|
+ if (it.resultCode == RESULT_OK) {
|
|
|
|
|
+ viewModel.replaceRuleChanged()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private val searchContentActivity =
|
|
|
|
|
+ registerForActivityResult(StartActivityContract(SearchContentActivity::class.java)) {
|
|
|
|
|
+ val data = it.data ?: return@registerForActivityResult
|
|
|
|
|
+ val key = data.getLongExtra("key", System.currentTimeMillis())
|
|
|
|
|
+ val index = data.getIntExtra("index", 0)
|
|
|
|
|
+ val searchResult = IntentData.get<SearchResult>("searchResult$key")
|
|
|
|
|
+ val searchResultList = IntentData.get<List<SearchResult>>("searchResultList$key")
|
|
|
|
|
+ if (searchResult != null && searchResultList != null) {
|
|
|
|
|
+ viewModel.searchContentQuery = searchResult.query
|
|
|
|
|
+ binding.searchMenu.upSearchResultList(searchResultList)
|
|
|
|
|
+ isShowingSearchResult = true
|
|
|
|
|
+ viewModel.searchResultIndex = index
|
|
|
|
|
+ binding.searchMenu.updateSearchResultIndex(index)
|
|
|
|
|
+ binding.searchMenu.selectedSearchResult?.let { currentResult ->
|
|
|
|
|
+ ReadBook.saveCurrentBookProgress() //退出全文搜索恢复此时进度
|
|
|
|
|
+ skipToSearch(currentResult)
|
|
|
|
|
+ showActionMenu()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ private val bookInfoActivity =
|
|
|
|
|
+ registerForActivityResult(StartActivityContract(BookInfoActivity::class.java)) {
|
|
|
|
|
+ if (it.resultCode == RESULT_OK) {
|
|
|
|
|
+ setResult(RESULT_DELETED)
|
|
|
|
|
+ finish()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ReadBook.loadOrUpContent()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ private var menu: Menu? = null
|
|
|
|
|
+ private var backupJob: Job? = null
|
|
|
|
|
+ private var tts: TTS? = null
|
|
|
|
|
+ val textActionMenu: TextActionMenu by lazy {
|
|
|
|
|
+ TextActionMenu(this, this)
|
|
|
|
|
+ }
|
|
|
|
|
+ private val popupAction: PopupAction by lazy {
|
|
|
|
|
+ PopupAction(this)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 当前使用的字体路径(给 FontDialog 用)
|
|
|
|
|
+ override val curFontPath: String
|
|
|
|
|
+ get() = ReadBookConfig.textFont
|
|
|
|
|
+
|
|
|
|
|
+ // 当选择了字体时被调用
|
|
|
|
|
+ override fun selectFont(path: String) {
|
|
|
|
|
+ // path 为空表示恢复系统默认字体
|
|
|
|
|
+ ReadBookConfig.textFont = path
|
|
|
|
|
+ // 通知阅读界面刷新字体
|
|
|
|
|
+ postEvent(EventBus.UP_CONFIG, arrayListOf(8, 5, 2))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override val isInitFinish: Boolean get() = viewModel.isInitFinish
|
|
|
|
|
+ override val isScroll: Boolean get() = binding.readView.isScroll
|
|
|
|
|
+ private val isAutoPage get() = binding.readView.isAutoPage
|
|
|
|
|
+ override var isShowingSearchResult = false
|
|
|
|
|
+ override var isSelectingSearchResult = false
|
|
|
|
|
+ set(value) {
|
|
|
|
|
+ field = value && isShowingSearchResult
|
|
|
|
|
+ }
|
|
|
|
|
+ private val timeBatteryReceiver = TimeBatteryReceiver()
|
|
|
|
|
+ private var screenTimeOut: Long = 0
|
|
|
|
|
+ private var loadStates: Boolean = false
|
|
|
|
|
+ override val pageFactory get() = binding.readView.pageFactory
|
|
|
|
|
+ override val pageDelegate get() = binding.readView.pageDelegate
|
|
|
|
|
+ override val headerHeight: Int get() = binding.readView.curPage.headerHeight
|
|
|
|
|
+ override val imgBgPaddingStart: Int get() = binding.readView.curPage.imgBgPaddingStart
|
|
|
|
|
+ private val nextPageDebounce by lazy { Debounce { keyPage(PageDirection.NEXT) } }
|
|
|
|
|
+ private val prevPageDebounce by lazy { Debounce { keyPage(PageDirection.PREV) } }
|
|
|
|
|
+ private var bookChanged = false
|
|
|
|
|
+ private var pageChanged = false
|
|
|
|
|
+ private val handler by lazy { buildMainHandler() }
|
|
|
|
|
+ private val screenOffRunnable by lazy { Runnable { keepScreenOn(false) } }
|
|
|
|
|
+ private val executor = ReadBook.executor
|
|
|
|
|
+ private val upSeekBarThrottle = throttle(200) {
|
|
|
|
|
+ runOnUiThread {
|
|
|
|
|
+ upSeekBarProgress()
|
|
|
|
|
+ binding.readMenu.upSeekBar()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ //恢复跳转前进度对话框的交互结果
|
|
|
|
|
+ private var confirmRestoreProcess: Boolean? = null
|
|
|
|
|
+ private val networkChangedListener by lazy {
|
|
|
|
|
+ NetworkChangedListener(this)
|
|
|
|
|
+ }
|
|
|
|
|
+ private var justInitData: Boolean = false
|
|
|
|
|
+ private var syncDialog: AlertDialog? = null
|
|
|
|
|
+
|
|
|
|
|
+ private val delayMillis: Long = 500
|
|
|
|
|
+ private var hasLoadedBook = false
|
|
|
|
|
+
|
|
|
|
|
+ private fun ensureLoadBook() {
|
|
|
|
|
+ if (!hasLoadedBook) {
|
|
|
|
|
+ hasLoadedBook = true
|
|
|
|
|
+ loadBook()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @SuppressLint("ClickableViewAccessibility")
|
|
|
|
|
+ override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
|
+ setEnterSharedElementCallback(MaterialContainerTransformSharedElementCallback())
|
|
|
|
|
+ setExitSharedElementCallback(MaterialContainerTransformSharedElementCallback())
|
|
|
|
|
+ val transform = MaterialContainerTransform().apply {
|
|
|
|
|
+ addTarget(binding.root)
|
|
|
|
|
+ scrimColor = Color.TRANSPARENT
|
|
|
|
|
+ duration = delayMillis
|
|
|
|
|
+ }
|
|
|
|
|
+ window.sharedElementEnterTransition = transform
|
|
|
|
|
+ window.sharedElementReturnTransition = transform
|
|
|
|
|
+
|
|
|
|
|
+ super.onCreate(savedInstanceState)
|
|
|
|
|
+
|
|
|
|
|
+ binding.rootView.transitionName = intent.getStringExtra("transitionName")
|
|
|
|
|
+
|
|
|
|
|
+ upScreenTimeOut()
|
|
|
|
|
+ ReadBook.register(this)
|
|
|
|
|
+ binding.readMenu.colorSurfaceContainer =
|
|
|
|
|
+ themeColor(com.google.android.material.R.attr.colorSurfaceContainer)
|
|
|
|
|
+ binding.readMenu.colorSecondary =
|
|
|
|
|
+ themeColor(androidx.appcompat.R.attr.colorPrimary)
|
|
|
|
|
+ binding.readMenu.colorSecondaryContainer =
|
|
|
|
|
+ themeColor(com.google.android.material.R.attr.colorSecondaryContainer)
|
|
|
|
|
+ binding.cursorLeft.setOnTouchListener(this)
|
|
|
|
|
+ binding.cursorRight.setOnTouchListener(this)
|
|
|
|
|
+
|
|
|
|
|
+ onBackPressedDispatcher.addCallback(this) {
|
|
|
|
|
+ if (isShowingSearchResult) {
|
|
|
|
|
+ exitSearchMenu()
|
|
|
|
|
+ restoreLastBookProcess()
|
|
|
|
|
+ return@addCallback
|
|
|
|
|
+ }
|
|
|
|
|
+ //拦截返回供恢复阅读进度
|
|
|
|
|
+ if (ReadBook.lastBookProgress != null && confirmRestoreProcess != false) {
|
|
|
|
|
+ restoreLastBookProcess()
|
|
|
|
|
+ return@addCallback
|
|
|
|
|
+ }
|
|
|
|
|
+ if (BaseReadAloudService.isPlay()) {
|
|
|
|
|
+ ReadAloud.pause(this@ReadBookActivity)
|
|
|
|
|
+ toastOnUi(R.string.read_aloud_pause)
|
|
|
|
|
+ return@addCallback
|
|
|
|
|
+ }
|
|
|
|
|
+ if (isAutoPage) {
|
|
|
|
|
+ autoPageStop()
|
|
|
|
|
+ return@addCallback
|
|
|
|
|
+ }
|
|
|
|
|
+ if (getPrefBoolean("disableReturnKey") && !menuLayoutIsVisible) {
|
|
|
|
|
+ return@addCallback
|
|
|
|
|
+ }
|
|
|
|
|
+ if (savedInstanceState != null || !ReadBook.inBookshelf) {
|
|
|
|
|
+ ReadBook.commitReadSession()
|
|
|
|
|
+ finish()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ReadBook.commitReadSession()
|
|
|
|
|
+ supportFinishAfterTransition()
|
|
|
|
|
+ }
|
|
|
|
|
+ //TODO: 有关测量相关问题
|
|
|
|
|
+ //孩子们,我的水平不够,只能这样修复测量问题了
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun onPostCreate(savedInstanceState: Bundle?) {
|
|
|
|
|
+ super.onPostCreate(savedInstanceState)
|
|
|
|
|
+ if (!AppConfig.sharedElementEnterTransitionEnable || !AppConfig.delayBookLoadEnable) {
|
|
|
|
|
+ ensureLoadBook()
|
|
|
|
|
+ }
|
|
|
|
|
+ //虽然很扯但有用
|
|
|
|
|
+ binding.rootView.postDelayed({
|
|
|
|
|
+ ensureLoadBook()
|
|
|
|
|
+ }, delayMillis)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun onNewIntent(intent: Intent) {
|
|
|
|
|
+ super.onNewIntent(intent)
|
|
|
|
|
+ viewModel.initData(intent)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun onWindowFocusChanged(hasFocus: Boolean) {
|
|
|
|
|
+ super.onWindowFocusChanged(hasFocus)
|
|
|
|
|
+ upSystemUiVisibility()
|
|
|
|
|
+ if (hasFocus) {
|
|
|
|
|
+ binding.readMenu.upBrightnessState()
|
|
|
|
|
+ } else if (!menuLayoutIsVisible) {
|
|
|
|
|
+ ReadBook.cancelPreDownloadTask()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun onConfigurationChanged(newConfig: Configuration) {
|
|
|
|
|
+ super.onConfigurationChanged(newConfig)
|
|
|
|
|
+ //upSystemUiVisibility()
|
|
|
|
|
+ binding.readView.upStyle()
|
|
|
|
|
+ recreate()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ override fun onTopResumedActivityChanged(isTopResumedActivity: Boolean) {
|
|
|
|
|
+ if (!isTopResumedActivity) {
|
|
|
|
|
+ ReadBook.cancelPreDownloadTask()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @SuppressLint("UnspecifiedRegisterReceiverFlag")
|
|
|
|
|
+ override fun onResume() {
|
|
|
|
|
+ super.onResume()
|
|
|
|
|
+ ReadBook.readStartTime = System.currentTimeMillis()
|
|
|
|
|
+ ReadBook.initReadTime()
|
|
|
|
|
+ ReadBook.startAutoSaveSession()
|
|
|
|
|
+ if (bookChanged) {
|
|
|
|
|
+ bookChanged = false
|
|
|
|
|
+ ReadBook.callBack = this
|
|
|
|
|
+ viewModel.initData(intent)
|
|
|
|
|
+ justInitData = true
|
|
|
|
|
+ } else {
|
|
|
|
|
+ //web端阅读时,app处于阅读界面,本地记录会覆盖web保存的进度,在此处恢复
|
|
|
|
|
+ ReadBook.webBookProgress?.let {
|
|
|
|
|
+ ReadBook.setProgress(it)
|
|
|
|
|
+ ReadBook.webBookProgress = null
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ upSystemUiVisibility()
|
|
|
|
|
+ registerReceiver(timeBatteryReceiver, timeBatteryReceiver.filter)
|
|
|
|
|
+ binding.readView.upTime()
|
|
|
|
|
+ screenOffTimerStart()
|
|
|
|
|
+ // 网络监听,当从无网切换到网络环境时同步进度(注意注册的同时就会收到监听,因此界面激活时无需重复执行同步操作)
|
|
|
|
|
+ networkChangedListener.register()
|
|
|
|
|
+ networkChangedListener.onNetworkChanged = {
|
|
|
|
|
+ // 当网络是可用状态且无需初始化时同步进度(初始化中已有同步进度逻辑)
|
|
|
|
|
+ if (AppConfig.syncBookProgressPlus && NetworkUtils.isAvailable() && !justInitData) {
|
|
|
|
|
+ ReadBook.syncProgress({ progress -> sureNewProgress(progress) })
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun onPause() {
|
|
|
|
|
+ super.onPause()
|
|
|
|
|
+ autoPageStop()
|
|
|
|
|
+ backupJob?.cancel()
|
|
|
|
|
+ ReadBook.saveRead()
|
|
|
|
|
+ ReadBook.stopAutoSaveSession()
|
|
|
|
|
+ ReadBook.commitReadSession()
|
|
|
|
|
+ ReadBook.cancelPreDownloadTask()
|
|
|
|
|
+ unregisterReceiver(timeBatteryReceiver)
|
|
|
|
|
+ upSystemUiVisibility()
|
|
|
|
|
+ if (!BuildConfig.DEBUG) {
|
|
|
|
|
+ if (AppConfig.syncBookProgressPlus) {
|
|
|
|
|
+ ReadBook.syncProgress()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ReadBook.uploadProgress()
|
|
|
|
|
+ }
|
|
|
|
|
+ Backup.autoBack(this)
|
|
|
|
|
+ }
|
|
|
|
|
+ justInitData = false
|
|
|
|
|
+ networkChangedListener.unRegister()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
|
|
|
|
|
+ menuInflater.inflate(R.menu.book_read, menu)
|
|
|
|
|
+ menu.iconItemOnLongClick(R.id.menu_change_source) {
|
|
|
|
|
+ PopupMenu(this, it).apply {
|
|
|
|
|
+ inflate(R.menu.book_read_change_source)
|
|
|
|
|
+ this.menu.applyOpenTint(this@ReadBookActivity)
|
|
|
|
|
+ setOnMenuItemClickListener(this@ReadBookActivity)
|
|
|
|
|
+ }.show()
|
|
|
|
|
+ }
|
|
|
|
|
+ menu.iconItemOnLongClick(R.id.menu_refresh) {
|
|
|
|
|
+ PopupMenu(this, it).apply {
|
|
|
|
|
+ inflate(R.menu.book_read_refresh)
|
|
|
|
|
+ this.menu.applyOpenTint(this@ReadBookActivity)
|
|
|
|
|
+ setOnMenuItemClickListener(this@ReadBookActivity)
|
|
|
|
|
+ }.show()
|
|
|
|
|
+ }
|
|
|
|
|
+ binding.readMenu.refreshMenuColorFilter()
|
|
|
|
|
+ return super.onCompatCreateOptionsMenu(menu)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun onPrepareOptionsMenu(menu: Menu): Boolean {
|
|
|
|
|
+ this.menu = menu
|
|
|
|
|
+ upMenu()
|
|
|
|
|
+ return super.onPrepareOptionsMenu(menu)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun onMenuOpened(featureId: Int, menu: Menu): Boolean {
|
|
|
|
|
+ menu.findItem(R.id.menu_same_title_removed)?.isChecked =
|
|
|
|
|
+ ReadBook.curTextChapter?.sameTitleRemoved == true
|
|
|
|
|
+ return super.onMenuOpened(featureId, menu)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private fun loadBook() {
|
|
|
|
|
+ lifecycleScope.launch(Dispatchers.Default) {
|
|
|
|
|
+ viewModel.initReadBookConfig(intent)
|
|
|
|
|
+ viewModel.initData(intent)
|
|
|
|
|
+ withContext(Main) {
|
|
|
|
|
+ justInitData = true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 更新菜单
|
|
|
|
|
+ */
|
|
|
|
|
+ private fun upMenu() {
|
|
|
|
|
+ val menu = menu ?: return
|
|
|
|
|
+ val book = ReadBook.book ?: return
|
|
|
|
|
+ val onLine = !book.isLocal
|
|
|
|
|
+ for (i in 0 until menu.size) {
|
|
|
|
|
+ val item = menu[i]
|
|
|
|
|
+ when (item.groupId) {
|
|
|
|
|
+ R.id.menu_group_on_line -> item.isVisible = onLine
|
|
|
|
|
+ R.id.menu_group_local -> item.isVisible = !onLine
|
|
|
|
|
+ R.id.menu_group_text -> item.isVisible = book.isLocalTxt
|
|
|
|
|
+ R.id.menu_group_epub -> item.isVisible = book.isEpub
|
|
|
|
|
+ else -> when (item.itemId) {
|
|
|
|
|
+ R.id.menu_enable_replace -> item.isChecked = book.getUseReplaceRule()
|
|
|
|
|
+ R.id.menu_re_segment -> item.isChecked = book.getReSegment()
|
|
|
|
|
+// R.id.menu_enable_review -> {
|
|
|
|
|
+// item.isVisible = BuildConfig.DEBUG
|
|
|
|
|
+// item.isChecked = AppConfig.enableReview
|
|
|
|
|
+// }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_reverse_content -> item.isVisible = onLine
|
|
|
|
|
+ R.id.menu_del_ruby_tag -> item.isChecked = book.getDelTag(Book.rubyTag)
|
|
|
|
|
+ R.id.menu_del_h_tag -> item.isChecked = book.getDelTag(Book.hTag)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ lifecycleScope.launch {
|
|
|
|
|
+ menu.findItem(R.id.menu_get_progress)?.isVisible = withContext(IO) {
|
|
|
|
|
+ AppWebDav.isOk
|
|
|
|
|
+ }
|
|
|
|
|
+ menu.findItem(R.id.menu_cover_progress)?.isVisible = withContext(IO) {
|
|
|
|
|
+ AppWebDav.isOk
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private fun defaultChangeSource() {
|
|
|
|
|
+ if (AppConfig.defaultSourceChangeAll)
|
|
|
|
|
+ {
|
|
|
|
|
+ binding.readMenu.runMenuOut()
|
|
|
|
|
+ ReadBook.book?.let {
|
|
|
|
|
+ showDialogFragment(ChangeBookSourceDialog(it.name, it.author))
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ lifecycleScope.launch {
|
|
|
|
|
+ val book = ReadBook.book ?: return@launch
|
|
|
|
|
+ val chapter =
|
|
|
|
|
+ appDb.bookChapterDao.getChapter(book.bookUrl, ReadBook.durChapterIndex)
|
|
|
|
|
+ ?: return@launch
|
|
|
|
|
+ binding.readMenu.runMenuOut()
|
|
|
|
|
+ showDialogFragment(
|
|
|
|
|
+ ChangeChapterSourceDialog(book.name, book.author, chapter.index, chapter.title)
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 菜单
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
|
|
|
|
|
+ when (item.itemId) {
|
|
|
|
|
+ R.id.menu_change_source -> { defaultChangeSource() }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_book_change_source -> {
|
|
|
|
|
+ binding.readMenu.runMenuOut()
|
|
|
|
|
+ ReadBook.book?.let {
|
|
|
|
|
+ showDialogFragment(ChangeBookSourceDialog(it.name, it.author))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_chapter_change_source -> lifecycleScope.launch {
|
|
|
|
|
+ val book = ReadBook.book ?: return@launch
|
|
|
|
|
+ val chapter =
|
|
|
|
|
+ appDb.bookChapterDao.getChapter(book.bookUrl, ReadBook.durChapterIndex)
|
|
|
|
|
+ ?: return@launch
|
|
|
|
|
+ binding.readMenu.runMenuOut()
|
|
|
|
|
+ showDialogFragment(
|
|
|
|
|
+ ChangeChapterSourceDialog(book.name, book.author, chapter.index, chapter.title)
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_refresh,
|
|
|
|
|
+ R.id.menu_refresh_dur -> {
|
|
|
|
|
+ if (ReadBook.bookSource == null) {
|
|
|
|
|
+ upContent()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ReadBook.book?.let {
|
|
|
|
|
+ ReadBook.curTextChapter = null
|
|
|
|
|
+ binding.readView.upContent()
|
|
|
|
|
+ viewModel.refreshContentDur(it)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_refresh_after -> {
|
|
|
|
|
+ if (ReadBook.bookSource == null) {
|
|
|
|
|
+ upContent()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ReadBook.book?.let {
|
|
|
|
|
+ ReadBook.clearTextChapter()
|
|
|
|
|
+ binding.readView.upContent()
|
|
|
|
|
+ viewModel.refreshContentAfter(it)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_refresh_all -> {
|
|
|
|
|
+ if (ReadBook.bookSource == null) {
|
|
|
|
|
+ upContent()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ReadBook.book?.let {
|
|
|
|
|
+ refreshContentAll(it)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_setting_replace -> openReplaceRule()
|
|
|
|
|
+ R.id.menu_download -> showDownloadDialog()
|
|
|
|
|
+ R.id.menu_add_bookmark -> addBookmark()
|
|
|
|
|
+ R.id.menu_simulated_reading -> showSimulatedReading()
|
|
|
|
|
+ R.id.menu_edit_content -> {
|
|
|
|
|
+ binding.readMenu.runMenuOut()
|
|
|
|
|
+ showDialogFragment(ContentEditDialog())
|
|
|
|
|
+ }
|
|
|
|
|
+ R.id.menu_update_toc -> ReadBook.book?.let {
|
|
|
|
|
+ if (it.isEpub) {
|
|
|
|
|
+ BookHelp.clearCache(it)
|
|
|
|
|
+ EpubFile.clear()
|
|
|
|
|
+ }
|
|
|
|
|
+ if (it.isMobi) {
|
|
|
|
|
+ MobiFile.clear()
|
|
|
|
|
+ }
|
|
|
|
|
+ loadChapterList(it)
|
|
|
|
|
+ }
|
|
|
|
|
+ R.id.menu_enable_replace -> {
|
|
|
|
|
+ changeReplaceRuleState()
|
|
|
|
|
+ }
|
|
|
|
|
+ R.id.menu_re_segment -> ReadBook.book?.let {
|
|
|
|
|
+ it.setReSegment(!it.getReSegment())
|
|
|
|
|
+ item.isChecked = it.getReSegment()
|
|
|
|
|
+ ReadBook.loadContent(false)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_tool_button -> {
|
|
|
|
|
+ binding.readMenu.runMenuOut()
|
|
|
|
|
+ showDialogFragment(ToolButtonConfigDialog())
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+// R.id.menu_enable_review -> {
|
|
|
|
|
+// AppConfig.enableReview = !AppConfig.enableReview
|
|
|
|
|
+// item.isChecked = AppConfig.enableReview
|
|
|
|
|
+// ReadBook.loadContent(false)
|
|
|
|
|
+// }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_del_ruby_tag -> ReadBook.book?.let {
|
|
|
|
|
+ item.isChecked = !item.isChecked
|
|
|
|
|
+ if (item.isChecked) {
|
|
|
|
|
+ it.addDelTag(Book.rubyTag)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ it.removeDelTag(Book.rubyTag)
|
|
|
|
|
+ }
|
|
|
|
|
+ refreshContentAll(it)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_del_h_tag -> ReadBook.book?.let {
|
|
|
|
|
+ item.isChecked = !item.isChecked
|
|
|
|
|
+ if (item.isChecked) {
|
|
|
|
|
+ it.addDelTag(Book.hTag)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ it.removeDelTag(Book.hTag)
|
|
|
|
|
+ }
|
|
|
|
|
+ refreshContentAll(it)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_page_anim -> showPageAnimConfig {
|
|
|
|
|
+ binding.readView.upPageAnim()
|
|
|
|
|
+ ReadBook.loadContent(false)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_log -> showDialogFragment<AppLogDialog>()
|
|
|
|
|
+ R.id.menu_toc_regex -> showDialogFragment(
|
|
|
|
|
+ TxtTocRuleDialog(ReadBook.book?.tocUrl)
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_reverse_content -> ReadBook.book?.let {
|
|
|
|
|
+ viewModel.reverseContent(it)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_set_charset -> showCharsetConfig()
|
|
|
|
|
+ R.id.menu_image_style -> {
|
|
|
|
|
+ val imgStyles =
|
|
|
|
|
+ arrayListOf(
|
|
|
|
|
+ Book.imgStyleDefault, Book.imgStyleFull, Book.imgStyleText,
|
|
|
|
|
+ Book.imgStyleSingle
|
|
|
|
|
+ )
|
|
|
|
|
+ selector(
|
|
|
|
|
+ R.string.image_style,
|
|
|
|
|
+ imgStyles
|
|
|
|
|
+ ) { _, index ->
|
|
|
|
|
+ val imageStyle = imgStyles[index]
|
|
|
|
|
+ ReadBook.book?.setImageStyle(imageStyle)
|
|
|
|
|
+ if (imageStyle == Book.imgStyleSingle) {
|
|
|
|
|
+ ReadBook.book?.setPageAnim(0) // 切换图片样式single后,自动切换为覆盖
|
|
|
|
|
+ binding.readView.upPageAnim()
|
|
|
|
|
+ }
|
|
|
|
|
+ ReadBook.loadContent(false)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_get_progress -> ReadBook.book?.let {
|
|
|
|
|
+ viewModel.syncBookProgress(it) { progress ->
|
|
|
|
|
+ sureSyncProgress(progress)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_cover_progress -> ReadBook.book?.let {
|
|
|
|
|
+ ReadBook.uploadProgress(true) { toastOnUi(R.string.upload_book_success) }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_same_title_removed -> {
|
|
|
|
|
+ ReadBook.book?.let {
|
|
|
|
|
+ val contentProcessor = ContentProcessor.get(it)
|
|
|
|
|
+ val textChapter = ReadBook.curTextChapter
|
|
|
|
|
+ if (textChapter != null
|
|
|
|
|
+ && !textChapter.sameTitleRemoved
|
|
|
|
|
+ && !contentProcessor.removeSameTitleCache.contains(
|
|
|
|
|
+ textChapter.chapter.getFileName("nr")
|
|
|
|
|
+ )
|
|
|
|
|
+ ) {
|
|
|
|
|
+ toastOnUi("未找到可移除的重复标题")
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ viewModel.reverseRemoveSameTitle()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_effective_replaces -> showDialogFragment<EffectiveReplacesDialog>()
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_help -> showHelp()
|
|
|
|
|
+ }
|
|
|
|
|
+ return super.onCompatOptionsItemSelected(item)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private fun refreshContentAll(book: Book) {
|
|
|
|
|
+ ReadBook.clearTextChapter()
|
|
|
|
|
+ binding.readView.upContent()
|
|
|
|
|
+ viewModel.refreshContentAll(book)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun onMenuItemClick(item: MenuItem): Boolean {
|
|
|
|
|
+ return onCompatOptionsItemSelected(item)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 按键拦截,显示菜单
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
|
|
|
|
+ val keyCode = event.keyCode
|
|
|
|
|
+ val action = event.action
|
|
|
|
|
+ val isDown = action == 0
|
|
|
|
|
+
|
|
|
|
|
+ if (keyCode == KeyEvent.KEYCODE_MENU) {
|
|
|
|
|
+ if (isDown && !binding.readMenu.canShowMenu) {
|
|
|
|
|
+ binding.readMenu.runMenuIn()
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!isDown && !binding.readMenu.canShowMenu) {
|
|
|
|
|
+ binding.readMenu.canShowMenu = true
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return super.dispatchKeyEvent(event)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 鼠标滚轮事件
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun onGenericMotionEvent(event: MotionEvent): Boolean {
|
|
|
|
|
+ if (0 != (event.source and InputDevice.SOURCE_CLASS_POINTER)) {
|
|
|
|
|
+ if (event.action == MotionEvent.ACTION_SCROLL) {
|
|
|
|
|
+ val axisValue = event.getAxisValue(MotionEvent.AXIS_VSCROLL)
|
|
|
|
|
+ LogUtils.d("onGenericMotionEvent", "axisValue = $axisValue")
|
|
|
|
|
+ // 获得垂直坐标上的滚动方向
|
|
|
|
|
+ if (axisValue < 0.0f) { // 滚轮向下滚
|
|
|
|
|
+ mouseWheelPage(PageDirection.NEXT)
|
|
|
|
|
+ } else { // 滚轮向上滚
|
|
|
|
|
+ mouseWheelPage(PageDirection.PREV)
|
|
|
|
|
+ }
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // 手柄摇杆控制翻页
|
|
|
|
|
+ if (0 != (event.source and InputDevice.SOURCE_CLASS_JOYSTICK)) {
|
|
|
|
|
+ if (event.action == MotionEvent.ACTION_MOVE) {
|
|
|
|
|
+ // 左摇杆上下移动控制翻页
|
|
|
|
|
+ val yAxis = event.getAxisValue(MotionEvent.AXIS_Y)
|
|
|
|
|
+ if (Math.abs(yAxis) > 0.5f) { // 死区设置
|
|
|
|
|
+ if (yAxis > 0) { // 摇杆向下
|
|
|
|
|
+ handleKeyPage(PageDirection.NEXT, false)
|
|
|
|
|
+ } else { // 摇杆向上
|
|
|
|
|
+ handleKeyPage(PageDirection.PREV, false)
|
|
|
|
|
+ }
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return super.onGenericMotionEvent(event)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 按键事件
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
|
|
|
|
+ if (menuLayoutIsVisible) {
|
|
|
|
|
+ return super.onKeyDown(keyCode, event)
|
|
|
|
|
+ }
|
|
|
|
|
+ val longPress = event.repeatCount > 0
|
|
|
|
|
+ when {
|
|
|
|
|
+ isPrevKey(keyCode) -> {
|
|
|
|
|
+ handleKeyPage(PageDirection.PREV, longPress)
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ isNextKey(keyCode) -> {
|
|
|
|
|
+ handleKeyPage(PageDirection.NEXT, longPress)
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ when (keyCode) {
|
|
|
|
|
+ KeyEvent.KEYCODE_VOLUME_UP -> if (volumeKeyPage(PageDirection.PREV, longPress)) {
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ KeyEvent.KEYCODE_VOLUME_DOWN -> if (volumeKeyPage(PageDirection.NEXT, longPress)) {
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ KeyEvent.KEYCODE_PAGE_UP -> {
|
|
|
|
|
+ handleKeyPage(PageDirection.PREV, longPress)
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ KeyEvent.KEYCODE_PAGE_DOWN -> {
|
|
|
|
|
+ handleKeyPage(PageDirection.NEXT, longPress)
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ KeyEvent.KEYCODE_SPACE -> {
|
|
|
|
|
+ handleKeyPage(PageDirection.NEXT, longPress)
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+ // 手柄方向键控制翻页
|
|
|
|
|
+ KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT -> {
|
|
|
|
|
+ handleKeyPage(PageDirection.PREV, longPress)
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT -> {
|
|
|
|
|
+ handleKeyPage(PageDirection.NEXT, longPress)
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return super.onKeyDown(keyCode, event)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 松开按键事件
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
|
|
|
|
|
+ when (keyCode) {
|
|
|
|
|
+ KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN -> {
|
|
|
|
|
+ if (volumeKeyPage(PageDirection.NONE, false)) {
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ return super.onKeyUp(keyCode, event)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * view触摸,文字选择
|
|
|
|
|
+ */
|
|
|
|
|
+ @SuppressLint("ClickableViewAccessibility")
|
|
|
|
|
+ override fun onTouch(v: View, event: MotionEvent): Boolean = binding.run {
|
|
|
|
|
+ if (!binding.readView.isTextSelected) {
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ when (event.action) {
|
|
|
|
|
+ MotionEvent.ACTION_DOWN -> textActionMenu.dismiss()
|
|
|
|
|
+ MotionEvent.ACTION_MOVE -> {
|
|
|
|
|
+ when (v.id) {
|
|
|
|
|
+ R.id.cursor_left -> if (!readView.curPage.getReverseStartCursor()) {
|
|
|
|
|
+ readView.curPage.selectStartMove(
|
|
|
|
|
+ event.rawX + cursorLeft.width,
|
|
|
|
|
+ event.rawY - cursorLeft.height
|
|
|
|
|
+ )
|
|
|
|
|
+ } else {
|
|
|
|
|
+ readView.curPage.selectEndMove(
|
|
|
|
|
+ event.rawX - cursorRight.width,
|
|
|
|
|
+ event.rawY - cursorRight.height
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.cursor_right -> if (readView.curPage.getReverseEndCursor()) {
|
|
|
|
|
+ readView.curPage.selectStartMove(
|
|
|
|
|
+ event.rawX + cursorLeft.width,
|
|
|
|
|
+ event.rawY - cursorLeft.height
|
|
|
|
|
+ )
|
|
|
|
|
+ } else {
|
|
|
|
|
+ readView.curPage.selectEndMove(
|
|
|
|
|
+ event.rawX - cursorRight.width,
|
|
|
|
|
+ event.rawY - cursorRight.height
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ MotionEvent.ACTION_UP -> {
|
|
|
|
|
+ readView.curPage.resetReverseCursor()
|
|
|
|
|
+ showTextActionMenu()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 更新文字选择开始位置
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun upSelectedStart(x: Float, y: Float, top: Float): Unit = binding.run {
|
|
|
|
|
+ cursorLeft.x = x - cursorLeft.width
|
|
|
|
|
+ cursorLeft.y = y
|
|
|
|
|
+ cursorLeft.visible(true)
|
|
|
|
|
+ textMenuPosition.x = x
|
|
|
|
|
+ textMenuPosition.y = top
|
|
|
|
|
+
|
|
|
|
|
+ root.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 更新文字选择结束位置
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun upSelectedEnd(x: Float, y: Float): Unit = binding.run {
|
|
|
|
|
+ cursorRight.x = x
|
|
|
|
|
+ cursorRight.y = y
|
|
|
|
|
+ cursorRight.visible(true)
|
|
|
|
|
+
|
|
|
|
|
+ root.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 取消文字选择
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun onCancelSelect() = binding.run {
|
|
|
|
|
+ cursorLeft.invisible()
|
|
|
|
|
+ cursorRight.invisible()
|
|
|
|
|
+ textActionMenu.dismiss()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun onLongScreenshotTouchEvent(event: MotionEvent): Boolean {
|
|
|
|
|
+ return binding.readView.onTouchEvent(event)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 显示文本操作菜单
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun showTextActionMenu() {
|
|
|
|
|
+ val navigationBarHeight =
|
|
|
|
|
+ if (!ReadBookConfig.hideNavigationBar && navigationBarGravity == Gravity.BOTTOM)
|
|
|
|
|
+ binding.navigationBar.height else 0
|
|
|
|
|
+ textActionMenu.show(
|
|
|
|
|
+ binding.textMenuPosition,
|
|
|
|
|
+ binding.root.height + navigationBarHeight,
|
|
|
|
|
+ binding.textMenuPosition.x.toInt(),
|
|
|
|
|
+ binding.textMenuPosition.y.toInt(),
|
|
|
|
|
+ binding.cursorLeft.y.toInt() + binding.cursorLeft.height,
|
|
|
|
|
+ binding.cursorRight.x.toInt(),
|
|
|
|
|
+ binding.cursorRight.y.toInt() + binding.cursorRight.height
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 当前选择的文本
|
|
|
|
|
+ */
|
|
|
|
|
+ override val selectedText: String get() = binding.readView.getSelectText()
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 文本选择菜单操作
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun onMenuItemSelected(itemId: Int): Boolean {
|
|
|
|
|
+ when (itemId) {
|
|
|
|
|
+ R.id.menu_aloud -> when (AppConfig.contentSelectSpeakMod) {
|
|
|
|
|
+ 1 -> lifecycleScope.launch {
|
|
|
|
|
+ binding.readView.aloudStartSelect()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ else -> speak(binding.readView.getSelectText())
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_bookmark -> binding.readView.curPage.let {
|
|
|
|
|
+ val bookmark = it.createBookmark()
|
|
|
|
|
+ if (bookmark == null) {
|
|
|
|
|
+ toastOnUi(R.string.create_bookmark_error)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ showDialogFragment(BookmarkDialog(bookmark))
|
|
|
|
|
+ }
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_edit -> {
|
|
|
|
|
+ val startPos = ReadBook.durChapterPos
|
|
|
|
|
+ showDialogFragment<ContentEditDialog> {
|
|
|
|
|
+ putInt("start_position", startPos)
|
|
|
|
|
+ putString("selected_text", binding.readView.getSelectText())
|
|
|
|
|
+ }
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_replace -> {
|
|
|
|
|
+ val scopes = arrayListOf<String>()
|
|
|
|
|
+ ReadBook.book?.name?.let { scopes.add(it) }
|
|
|
|
|
+ ReadBook.bookSource?.bookSourceUrl?.let { scopes.add(it) }
|
|
|
|
|
+
|
|
|
|
|
+ val text = selectedText.lineSequence().map { it.trim() }.joinToString("\n")
|
|
|
|
|
+
|
|
|
|
|
+ val editRoute = ReplaceEditRoute(
|
|
|
|
|
+ id = -1,
|
|
|
|
|
+ pattern = text,
|
|
|
|
|
+ scope = scopes.joinToString(";"),
|
|
|
|
|
+ isScopeTitle = false,
|
|
|
|
|
+ isScopeContent = true
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ replaceActivity.launch(
|
|
|
|
|
+ ReplaceRuleActivity.startIntent(
|
|
|
|
|
+ context = this,
|
|
|
|
|
+ editRoute = editRoute
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_search_content -> {
|
|
|
|
|
+ viewModel.searchContentQuery = selectedText
|
|
|
|
|
+ openSearchActivity(selectedText)
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ R.id.menu_dict -> {
|
|
|
|
|
+ showDialogFragment(DictDialog(selectedText))
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private fun onEditTextAction(selectedText: String, startPos: Int) {
|
|
|
|
|
+ val bundle = Bundle().apply {
|
|
|
|
|
+ putString("selected_text", selectedText)
|
|
|
|
|
+ putInt("start_position", startPos)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ val dialog = ContentEditDialog().apply {
|
|
|
|
|
+ arguments = bundle
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ dialog.show(supportFragmentManager, "ContentEditDialog")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 文本选择菜单操作完成
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun onMenuActionFinally() = binding.run {
|
|
|
|
|
+ textActionMenu.dismiss()
|
|
|
|
|
+ readView.cancelSelect()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private fun speak(text: String) {
|
|
|
|
|
+ if (tts == null) {
|
|
|
|
|
+ tts = TTS()
|
|
|
|
|
+ }
|
|
|
|
|
+ tts?.speak(text)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 鼠标滚轮翻页
|
|
|
|
|
+ */
|
|
|
|
|
+ private fun mouseWheelPage(direction: PageDirection) {
|
|
|
|
|
+ if (menuLayoutIsVisible || !AppConfig.mouseWheelPage) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ keyPageDebounce(direction, mouseWheel = true, longPress = false)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 音量键翻页
|
|
|
|
|
+ */
|
|
|
|
|
+ private fun volumeKeyPage(direction: PageDirection, longPress: Boolean): Boolean {
|
|
|
|
|
+ if (!AppConfig.volumeKeyPage) {
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!AppConfig.volumeKeyPageOnPlay && BaseReadAloudService.isPlay()) {
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ handleKeyPage(direction, longPress)
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private fun handleKeyPage(direction: PageDirection, longPress: Boolean) {
|
|
|
|
|
+ if (AppConfig.keyPageOnLongPress || direction == PageDirection.NONE) {
|
|
|
|
|
+ keyPage(direction)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ keyPageDebounce(direction, longPress = longPress)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private fun keyPageDebounce(
|
|
|
|
|
+ direction: PageDirection,
|
|
|
|
|
+ mouseWheel: Boolean = false,
|
|
|
|
|
+ longPress: Boolean
|
|
|
|
|
+ ) {
|
|
|
|
|
+ if (longPress) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ nextPageDebounce.apply {
|
|
|
|
|
+ wait = if (mouseWheel) 200L else 600L
|
|
|
|
|
+ leading = !mouseWheel
|
|
|
|
|
+ trailing = mouseWheel
|
|
|
|
|
+ }
|
|
|
|
|
+ prevPageDebounce.apply {
|
|
|
|
|
+ wait = if (mouseWheel) 200L else 600L
|
|
|
|
|
+ leading = !mouseWheel
|
|
|
|
|
+ trailing = mouseWheel
|
|
|
|
|
+ }
|
|
|
|
|
+ when (direction) {
|
|
|
|
|
+ PageDirection.NEXT -> nextPageDebounce.invoke()
|
|
|
|
|
+ PageDirection.PREV -> prevPageDebounce.invoke()
|
|
|
|
|
+ else -> {}
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private fun keyPage(direction: PageDirection) {
|
|
|
|
|
+ binding.readView.cancelSelect()
|
|
|
|
|
+ binding.readView.pageDelegate?.isCancel = false
|
|
|
|
|
+ binding.readView.pageDelegate?.keyTurnPage(direction)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun upMenuView() {
|
|
|
|
|
+ handler.post {
|
|
|
|
|
+ upMenu()
|
|
|
|
|
+ binding.readMenu.upBookView()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun loadChapterList(book: Book) {
|
|
|
|
|
+ ReadBook.upMsg(getString(R.string.toc_updateing))
|
|
|
|
|
+ viewModel.loadChapterList(book)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 内容加载完成
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun contentLoadFinish() {
|
|
|
|
|
+ if (intent.getBooleanExtra("readAloud", false)) {
|
|
|
|
|
+ intent.removeExtra("readAloud")
|
|
|
|
|
+ ReadBook.readAloud()
|
|
|
|
|
+ }
|
|
|
|
|
+ loadStates = true
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 更新内容
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun upContent(
|
|
|
|
|
+ relativePosition: Int,
|
|
|
|
|
+ resetPageOffset: Boolean,
|
|
|
|
|
+ success: (() -> Unit)?
|
|
|
|
|
+ ) {
|
|
|
|
|
+ lifecycleScope.launch {
|
|
|
|
|
+ binding.readView.upContent(relativePosition, resetPageOffset)
|
|
|
|
|
+ if (relativePosition == 0) {
|
|
|
|
|
+ upSeekBarProgress()
|
|
|
|
|
+ }
|
|
|
|
|
+ loadStates = false
|
|
|
|
|
+ success?.invoke()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override suspend fun upContentAwait(
|
|
|
|
|
+ relativePosition: Int,
|
|
|
|
|
+ resetPageOffset: Boolean,
|
|
|
|
|
+ success: (() -> Unit)?
|
|
|
|
|
+ ) = withContext(Main.immediate) {
|
|
|
|
|
+ binding.readView.upContent(relativePosition, resetPageOffset)
|
|
|
|
|
+ if (relativePosition == 0) {
|
|
|
|
|
+ upSeekBarProgress()
|
|
|
|
|
+ }
|
|
|
|
|
+ loadStates = false
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun upPageAnim(upRecorder: Boolean) {
|
|
|
|
|
+ lifecycleScope.launch {
|
|
|
|
|
+ binding.readView.upPageAnim(upRecorder)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun notifyBookChanged() {
|
|
|
|
|
+ bookChanged = true
|
|
|
|
|
+ if (!ReadBook.inBookshelf) {
|
|
|
|
|
+ viewModel.removeFromBookshelf { super.finish() }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun cancelSelect() {
|
|
|
|
|
+ runOnUiThread {
|
|
|
|
|
+ binding.readView.cancelSelect()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 页面改变
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun pageChanged() {
|
|
|
|
|
+ pageChanged = true
|
|
|
|
|
+ binding.readView.onPageChange()
|
|
|
|
|
+ handler.post {
|
|
|
|
|
+ upSeekBarProgress()
|
|
|
|
|
+ }
|
|
|
|
|
+ executor.execute {
|
|
|
|
|
+ startBackupJob()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 更新进度条位置
|
|
|
|
|
+ */
|
|
|
|
|
+ private fun upSeekBarProgress() {
|
|
|
|
|
+ val progress = when (AppConfig.progressBarBehavior) {
|
|
|
|
|
+ "page" -> ReadBook.durPageIndex
|
|
|
|
|
+ else /* chapter */ -> ReadBook.durChapterIndex
|
|
|
|
|
+ }
|
|
|
|
|
+ if (progress >= 0) {
|
|
|
|
|
+ binding.readMenu.setSeekPage(progress)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 显示菜单
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun showMenuBar() {
|
|
|
|
|
+ binding.readMenu.runMenuIn()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override val oldBook: Book?
|
|
|
|
|
+ get() = ReadBook.book
|
|
|
|
|
+
|
|
|
|
|
+ override fun changeTo(source: BookSource, book: Book, toc: List<BookChapter>) {
|
|
|
|
|
+ if (!book.isAudio) {
|
|
|
|
|
+ viewModel.changeTo(book, toc)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ReadAloud.stop(this)
|
|
|
|
|
+ lifecycleScope.launch {
|
|
|
|
|
+ withContext(IO) {
|
|
|
|
|
+ ReadBook.book?.migrateTo(book, toc)
|
|
|
|
|
+ book.removeType(BookType.updateError)
|
|
|
|
|
+ ReadBook.book?.delete()
|
|
|
|
|
+ appDb.bookDao.insert(book)
|
|
|
|
|
+ }
|
|
|
|
|
+ startActivityForBook(book)
|
|
|
|
|
+ finish()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun replaceContent(content: String) {
|
|
|
|
|
+ ReadBook.book?.let {
|
|
|
|
|
+ viewModel.saveContent(it, content)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun showActionMenu() {
|
|
|
|
|
+ when {
|
|
|
|
|
+ BaseReadAloudService.isRun -> showReadAloudDialog()
|
|
|
|
|
+ isAutoPage -> showDialogFragment<AutoReadDialog>()
|
|
|
|
|
+ isShowingSearchResult -> binding.searchMenu.runMenuIn()
|
|
|
|
|
+ else -> binding.readMenu.runMenuIn()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 显示朗读菜单
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun showReadAloudDialog() {
|
|
|
|
|
+ showDialogFragment<ReadAloudDialog>()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 自动翻页
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun autoPage() {
|
|
|
|
|
+ ReadAloud.stop(this)
|
|
|
|
|
+ if (isAutoPage) {
|
|
|
|
|
+ autoPageStop()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ binding.readView.autoPager.start()
|
|
|
|
|
+ binding.readMenu.setAutoPage(true)
|
|
|
|
|
+ screenTimeOut = -1L
|
|
|
|
|
+ screenOffTimerStart()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun autoPageStop() {
|
|
|
|
|
+ if (isAutoPage) {
|
|
|
|
|
+ binding.readView.autoPager.stop()
|
|
|
|
|
+ binding.readMenu.setAutoPage(false)
|
|
|
|
|
+ dismissDialogFragment<AutoReadDialog>()
|
|
|
|
|
+ upScreenTimeOut()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun openSourceEditActivity() {
|
|
|
|
|
+ ReadBook.bookSource?.let {
|
|
|
|
|
+ sourceEditActivity.launch {
|
|
|
|
|
+ putExtra("sourceUrl", it.bookSourceUrl)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun openBookInfoActivity() {
|
|
|
|
|
+ ReadBook.book?.let {
|
|
|
|
|
+ bookInfoActivity.launch {
|
|
|
|
|
+ putExtra("name", it.name)
|
|
|
|
|
+ putExtra("author", it.author)
|
|
|
|
|
+ putExtra("bookUrl", it.bookUrl)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 替换
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun openReplaceRule() {
|
|
|
|
|
+ replaceActivity.launch(Intent(this, ReplaceRuleActivity::class.java))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 打开目录
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun openChapterList() {
|
|
|
|
|
+ ReadBook.book?.let {
|
|
|
|
|
+ tocActivity.launch(it.bookUrl)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 打开搜索界面
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun openSearchActivity(searchWord: String?) {
|
|
|
|
|
+ val book = ReadBook.book ?: return
|
|
|
|
|
+ searchContentActivity.launch {
|
|
|
|
|
+ putExtra("bookUrl", book.bookUrl)
|
|
|
|
|
+ putExtra("searchWord", searchWord)
|
|
|
|
|
+ putExtra("searchResultIndex", viewModel.searchResultIndex)
|
|
|
|
|
+ viewModel.searchResultList?.first()?.let {
|
|
|
|
|
+ if (it.query == viewModel.searchContentQuery) {
|
|
|
|
|
+ IntentData.put("searchResultList", viewModel.searchResultList)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 禁用书源
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun disableSource() {
|
|
|
|
|
+ viewModel.disableSource()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 显示阅读样式配置
|
|
|
|
|
+ */
|
|
|
|
|
+// override fun showReadStyle() {
|
|
|
|
|
+// showDialogFragment<ReadStyleDialog>()
|
|
|
|
|
+// }
|
|
|
|
|
+//
|
|
|
|
|
+// /**
|
|
|
|
|
+// * 显示更多设置
|
|
|
|
|
+// */
|
|
|
|
|
+// override fun showMoreSetting() {
|
|
|
|
|
+// showDialogFragment<MoreConfigDialog>()
|
|
|
|
|
+// }
|
|
|
|
|
+//
|
|
|
|
|
+ override fun showSearchSetting() {
|
|
|
|
|
+ showDialogFragment<ReadStyleDialog>()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun showReadStyle() {
|
|
|
|
|
+ showDialogFragment<ReadStyleDialog>()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 更新状态栏,导航栏
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun upSystemUiVisibility() {
|
|
|
|
|
+ upSystemUiVisibility(isInMultiWindow, !menuLayoutIsVisible)
|
|
|
|
|
+ upNavigationBarColor()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 退出全文搜索
|
|
|
|
|
+ override fun exitSearchMenu() {
|
|
|
|
|
+ if (isShowingSearchResult) {
|
|
|
|
|
+ isShowingSearchResult = false
|
|
|
|
|
+ binding.searchMenu.invalidate()
|
|
|
|
|
+ binding.searchMenu.invisible()
|
|
|
|
|
+ ReadBook.clearSearchResult()
|
|
|
|
|
+ binding.readView.cancelSelect(true)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 恢复到 全文搜索/进度条跳转前的位置 */
|
|
|
|
|
+ private fun restoreLastBookProcess() {
|
|
|
|
|
+ if (confirmRestoreProcess == true) {
|
|
|
|
|
+ ReadBook.restoreLastBookProgress()
|
|
|
|
|
+ } else if (confirmRestoreProcess == null) {
|
|
|
|
|
+ alert(R.string.draw) {
|
|
|
|
|
+ setMessage(R.string.restore_last_book_process)
|
|
|
|
|
+ yesButton {
|
|
|
|
|
+ confirmRestoreProcess = true
|
|
|
|
|
+ ReadBook.restoreLastBookProgress() //恢复启动全文搜索前的进度
|
|
|
|
|
+ }
|
|
|
|
|
+ noButton {
|
|
|
|
|
+ ReadBook.lastBookProgress = null
|
|
|
|
|
+ confirmRestoreProcess = false
|
|
|
|
|
+ }
|
|
|
|
|
+ onCancelled {
|
|
|
|
|
+ ReadBook.lastBookProgress = null
|
|
|
|
|
+ confirmRestoreProcess = false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun showLogin() {
|
|
|
|
|
+ ReadBook.bookSource?.let {
|
|
|
|
|
+ startActivity<SourceLoginActivity> {
|
|
|
|
|
+ putExtra("bookType", BookType.text)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun payAction() {
|
|
|
|
|
+ val book = ReadBook.book ?: return
|
|
|
|
|
+ if (book.isLocal) return
|
|
|
|
|
+ val chapter = appDb.bookChapterDao.getChapter(book.bookUrl, ReadBook.durChapterIndex)
|
|
|
|
|
+ if (chapter == null) {
|
|
|
|
|
+ toastOnUi("no chapter")
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ alert(R.string.chapter_pay) {
|
|
|
|
|
+ setMessage(chapter.title)
|
|
|
|
|
+ yesButton {
|
|
|
|
|
+ Coroutine.async(lifecycleScope) {
|
|
|
|
|
+ val source =
|
|
|
|
|
+ ReadBook.bookSource ?: throw NoStackTraceException("no book source")
|
|
|
|
|
+ val payAction = source.getContentRule().payAction
|
|
|
|
|
+ if (payAction.isNullOrBlank()) {
|
|
|
|
|
+ throw NoStackTraceException("no pay action")
|
|
|
|
|
+ }
|
|
|
|
|
+ val analyzeRule = AnalyzeRule(book, source)
|
|
|
|
|
+ analyzeRule.setCoroutineContext(coroutineContext)
|
|
|
|
|
+ analyzeRule.setBaseUrl(chapter.url)
|
|
|
|
|
+ analyzeRule.setChapter(chapter)
|
|
|
|
|
+ analyzeRule.evalJS(payAction).toString()
|
|
|
|
|
+ }.onSuccess(IO) {
|
|
|
|
|
+ if (it.isAbsUrl()) {
|
|
|
|
|
+ startActivity<WebViewActivity> {
|
|
|
|
|
+ val bookSource = ReadBook.bookSource
|
|
|
|
|
+ putExtra("title", getString(R.string.chapter_pay))
|
|
|
|
|
+ putExtra("url", it)
|
|
|
|
|
+ putExtra("sourceOrigin", bookSource?.bookSourceUrl)
|
|
|
|
|
+ putExtra("sourceName", bookSource?.bookSourceName)
|
|
|
|
|
+ putExtra("sourceType", bookSource?.getSourceType())
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (it.isTrue()) {
|
|
|
|
|
+ //购买成功后刷新目录
|
|
|
|
|
+ ReadBook.book?.let {
|
|
|
|
|
+ ReadBook.curTextChapter = null
|
|
|
|
|
+ BookHelp.delContent(book, chapter)
|
|
|
|
|
+ loadChapterList(book)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }.onError {
|
|
|
|
|
+ AppLog.put("执行购买操作出错\n${it.localizedMessage}", it, true)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ noButton()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 朗读按钮
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun onClickReadAloud() {
|
|
|
|
|
+ autoPageStop()
|
|
|
|
|
+ when {
|
|
|
|
|
+ !BaseReadAloudService.isRun -> {
|
|
|
|
|
+ ReadAloud.upReadAloudClass()
|
|
|
|
|
+ val scrollPageAnim = ReadBook.pageAnim() == 3
|
|
|
|
|
+ if (scrollPageAnim) {
|
|
|
|
|
+ val pos = binding.readView.getReadAloudPos()
|
|
|
|
|
+ if (pos != null) {
|
|
|
|
|
+ val (index, line) = pos
|
|
|
|
|
+ if (ReadBook.durChapterIndex != index) {
|
|
|
|
|
+ ReadBook.openChapter(index, line.chapterPosition, false) {
|
|
|
|
|
+ ReadBook.readAloud(startPos = line.pagePosition)
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ReadBook.durChapterPos = line.chapterPosition
|
|
|
|
|
+ ReadBook.readAloud(startPos = line.pagePosition)
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ReadBook.readAloud()
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ReadBook.readAloud()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ BaseReadAloudService.pause -> {
|
|
|
|
|
+ val scrollPageAnim = ReadBook.pageAnim() == 3
|
|
|
|
|
+ if (scrollPageAnim && pageChanged) {
|
|
|
|
|
+ pageChanged = false
|
|
|
|
|
+ val pos = binding.readView.getReadAloudPos()
|
|
|
|
|
+ if (pos != null) {
|
|
|
|
|
+ val (index, line) = pos
|
|
|
|
|
+ if (ReadBook.durChapterIndex != index) {
|
|
|
|
|
+ ReadBook.openChapter(index, line.chapterPosition, false) {
|
|
|
|
|
+ ReadBook.readAloud(startPos = line.pagePosition)
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ReadBook.durChapterPos = line.chapterPosition
|
|
|
|
|
+ ReadBook.readAloud(startPos = line.pagePosition)
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ReadBook.readAloud()
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ReadAloud.resume(this)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ else -> ReadAloud.pause(this)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun showHelp() {
|
|
|
|
|
+ showHelp("readMenuHelp")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 点击图片
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun oldClickImg(src: String): Boolean {
|
|
|
|
|
+ val urlMatcher = paramPattern.matcher(src)
|
|
|
|
|
+ if (urlMatcher.find()) {
|
|
|
|
|
+ val urlOptionStr = src.substring(urlMatcher.end())
|
|
|
|
|
+ val urlOptionMap = GSON.fromJsonObject<Map<String, String>>(urlOptionStr).getOrNull()
|
|
|
|
|
+ val click = urlOptionMap?.get("click")
|
|
|
|
|
+ if (click != null) {
|
|
|
|
|
+ Coroutine.async(lifecycleScope, IO) {
|
|
|
|
|
+ val source = ReadBook.bookSource ?: return@async
|
|
|
|
|
+ val java = SourceLoginJsExtensions(this@ReadBookActivity, source, BookType.text)
|
|
|
|
|
+ val book = ReadBook.book ?: return@async
|
|
|
|
|
+ val chapter =
|
|
|
|
|
+ appDb.bookChapterDao.getChapter(book.bookUrl, ReadBook.durChapterIndex)
|
|
|
|
|
+ ?: throw Exception("no find chapter")
|
|
|
|
|
+ runScriptWithContext {
|
|
|
|
|
+ source.evalJS(click) {
|
|
|
|
|
+ put("java", java)
|
|
|
|
|
+ put("book", book)
|
|
|
|
|
+ put("chapter", chapter)
|
|
|
|
|
+ put("result", src)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }.onError {
|
|
|
|
|
+ AppLog.put("执行图片链接click键值出错\n${it.localizedMessage}", it, true)
|
|
|
|
|
+ }
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+ val jsStr = urlOptionMap?.get("js") ?: return false
|
|
|
|
|
+ Coroutine.async(lifecycleScope, IO) {
|
|
|
|
|
+ val source = ReadBook.bookSource ?: return@async
|
|
|
|
|
+ val book = ReadBook.book ?: return@async
|
|
|
|
|
+ val chapter =
|
|
|
|
|
+ appDb.bookChapterDao.getChapter(book.bookUrl, ReadBook.durChapterIndex)
|
|
|
|
|
+ ?: throw Exception("no find chapter")
|
|
|
|
|
+ val urlNoOption = src.take(urlMatcher.start())
|
|
|
|
|
+ AnalyzeRule(book, source).apply {
|
|
|
|
|
+ setCoroutineContext(coroutineContext)
|
|
|
|
|
+ setBaseUrl(chapter.url)
|
|
|
|
|
+ setChapter(chapter)
|
|
|
|
|
+ evalJS(jsStr, urlNoOption)
|
|
|
|
|
+ }
|
|
|
|
|
+ }.onError {
|
|
|
|
|
+ AppLog.put("执行图片链接js键值出错\n${it.localizedMessage}", it, true)
|
|
|
|
|
+ }
|
|
|
|
|
+ return true
|
|
|
|
|
+ }
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun clickImg(click: String, src: String) {
|
|
|
|
|
+ Coroutine.async(lifecycleScope, IO) {
|
|
|
|
|
+ val source = ReadBook.bookSource ?: return@async
|
|
|
|
|
+ val java = SourceLoginJsExtensions(this@ReadBookActivity, source, BookType.text)
|
|
|
|
|
+ val book = ReadBook.book ?: return@async
|
|
|
|
|
+ val chapter = appDb.bookChapterDao.getChapter(book.bookUrl, ReadBook.durChapterIndex)
|
|
|
|
|
+ ?: throw Exception("no find chapter")
|
|
|
|
|
+ runScriptWithContext {
|
|
|
|
|
+ source.evalJS(click) {
|
|
|
|
|
+ put("java", java)
|
|
|
|
|
+ put("book", book)
|
|
|
|
|
+ put("chapter", chapter)
|
|
|
|
|
+ put("result", src)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }.onError {
|
|
|
|
|
+ AppLog.put("执行图片链接click键值出错\n${it.localizedMessage}", it, true)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 长按图片
|
|
|
|
|
+ */
|
|
|
|
|
+ @SuppressLint("RtlHardcoded")
|
|
|
|
|
+ override fun onImageLongPress(x: Float, y: Float, src: String) {
|
|
|
|
|
+ binding.root.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
|
|
|
|
+ popupAction.setItems(
|
|
|
|
|
+ listOf(
|
|
|
|
|
+ SelectItem(getString(R.string.show), "show"),
|
|
|
|
|
+ SelectItem(getString(R.string.refresh), "refresh"),
|
|
|
|
|
+ SelectItem("保存到相册", "save"),
|
|
|
|
|
+ SelectItem(getString(R.string.menu), "menu"),
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ popupAction.onActionClick = {
|
|
|
|
|
+ when (it) {
|
|
|
|
|
+ "show" -> showDialogFragment(PhotoDialog(src))
|
|
|
|
|
+ "refresh" -> viewModel.refreshImage(src)
|
|
|
|
|
+ "save" -> {
|
|
|
|
|
+ viewModel.saveImage(src)
|
|
|
|
|
+ }
|
|
|
|
|
+ "menu" -> showActionMenu()
|
|
|
|
|
+ }
|
|
|
|
|
+ popupAction.dismiss()
|
|
|
|
|
+ }
|
|
|
|
|
+ val navigationBarHeight =
|
|
|
|
|
+ if (!ReadBookConfig.hideNavigationBar && navigationBarGravity == Gravity.BOTTOM)
|
|
|
|
|
+ binding.navigationBar.height else 0
|
|
|
|
|
+ popupAction.showAtLocation(
|
|
|
|
|
+ binding.readView, Gravity.BOTTOM or Gravity.LEFT, x.toInt(),
|
|
|
|
|
+ binding.root.height + navigationBarHeight - y.toInt()
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * colorSelectDialog
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun onColorSelected(dialogId: Int, color: Int) = ReadBookConfig.durConfig.run {
|
|
|
|
|
+ when (dialogId) {
|
|
|
|
|
+ S_COLOR -> {
|
|
|
|
|
+ setCurShadColor(color)
|
|
|
|
|
+ postEvent(EventBus.UP_CONFIG, arrayListOf(2, 6, 9, 11))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ TEXT_COLOR -> {
|
|
|
|
|
+ setCurTextColor(color)
|
|
|
|
|
+ postEvent(EventBus.UP_CONFIG, arrayListOf(2, 6, 9, 11))
|
|
|
|
|
+ if (AppConfig.readBarStyleFollowPage) {
|
|
|
|
|
+ postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ TEXT_ACCENT_COLOR -> {
|
|
|
|
|
+ setCurTextAccentColor(color)
|
|
|
|
|
+ postEvent(EventBus.UP_CONFIG, arrayListOf(2, 6, 9, 11))
|
|
|
|
|
+ if (AppConfig.readBarStyleFollowPage) {
|
|
|
|
|
+ postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ BG_COLOR -> {
|
|
|
|
|
+ setCurBg(0, "#${color.hexString}")
|
|
|
|
|
+ postEvent(EventBus.UP_CONFIG, arrayListOf(1))
|
|
|
|
|
+ if (AppConfig.readBarStyleFollowPage) {
|
|
|
|
|
+ postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ TIP_COLOR -> {
|
|
|
|
|
+ ReadTipConfig.tipColor = color
|
|
|
|
|
+ postEvent(EventBus.TIP_COLOR, "")
|
|
|
|
|
+ postEvent(EventBus.UP_CONFIG, arrayListOf(2))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ TIP_DIVIDER_COLOR -> {
|
|
|
|
|
+ ReadTipConfig.tipDividerColor = color
|
|
|
|
|
+ postEvent(EventBus.TIP_COLOR, "")
|
|
|
|
|
+ postEvent(EventBus.UP_CONFIG, arrayListOf(2))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ B_COLOR -> {
|
|
|
|
|
+ setMenuCurBg(color)
|
|
|
|
|
+ postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ A_COLOR -> {
|
|
|
|
|
+ setMenuCurAc(color)
|
|
|
|
|
+ postEvent(EventBus.UPDATE_READ_ACTION_BAR, true)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ U_COLOR -> {
|
|
|
|
|
+ setUnderlineColor(color)
|
|
|
|
|
+ postEvent(EventBus.UP_CONFIG, arrayListOf(2))
|
|
|
|
|
+ postEvent(EventBus.UP_CONFIG, arrayListOf(6, 9, 11))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * colorSelectDialog
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun onDialogDismissed(dialogId: Int) = Unit
|
|
|
|
|
+
|
|
|
|
|
+ override fun onTocRegexDialogResult(tocRegex: String) {
|
|
|
|
|
+ ReadBook.book?.let {
|
|
|
|
|
+ it.tocUrl = tocRegex
|
|
|
|
|
+ loadChapterList(it)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private fun sureSyncProgress(progress: BookProgress) {
|
|
|
|
|
+ alert(R.string.get_book_progress) {
|
|
|
|
|
+ setMessage(R.string.current_progress_exceeds_cloud)
|
|
|
|
|
+ okButton {
|
|
|
|
|
+ ReadBook.setProgress(progress)
|
|
|
|
|
+ }
|
|
|
|
|
+ noButton()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 进度条跳转到指定章节 */
|
|
|
|
|
+ override fun skipToChapter(index: Int) {
|
|
|
|
|
+ ReadBook.saveCurrentBookProgress() //退出章节跳转恢复此时进度
|
|
|
|
|
+ viewModel.openChapter(index)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 全文搜索跳转 */
|
|
|
|
|
+ override fun navigateToSearch(searchResult: SearchResult, index: Int) {
|
|
|
|
|
+ viewModel.searchResultIndex = index
|
|
|
|
|
+ skipToSearch(searchResult)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun onMenuShow() {
|
|
|
|
|
+ binding.readView.autoPager.pause()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun onMenuHide() {
|
|
|
|
|
+ binding.readView.autoPager.resume()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun refresh() {
|
|
|
|
|
+ recreate()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun onLayoutPageCompleted(index: Int, page: TextPage) {
|
|
|
|
|
+ upSeekBarThrottle.invoke()
|
|
|
|
|
+ binding.readView.onLayoutPageCompleted(index, page)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /* 全文搜索跳转 */
|
|
|
|
|
+ private fun skipToSearch(searchResult: SearchResult) {
|
|
|
|
|
+ if (searchResult.chapterIndex != ReadBook.durChapterIndex) {
|
|
|
|
|
+ viewModel.openChapter(searchResult.chapterIndex) {
|
|
|
|
|
+ jumpToPosition(searchResult)
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ jumpToPosition(searchResult)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private fun jumpToPosition(searchResult: SearchResult) {
|
|
|
|
|
+ val curTextChapter = ReadBook.curTextChapter ?: return
|
|
|
|
|
+ binding.searchMenu.updateSearchInfo()
|
|
|
|
|
+ val (pageIndex, lineIndex, charIndex, addLine, charIndex2) =
|
|
|
|
|
+ viewModel.searchResultPositions(curTextChapter, searchResult)
|
|
|
|
|
+ ReadBook.skipToPage(pageIndex) {
|
|
|
|
|
+ isSelectingSearchResult = true
|
|
|
|
|
+ binding.readView.curPage.selectStartMoveIndex(0, lineIndex, charIndex)
|
|
|
|
|
+ when (addLine) {
|
|
|
|
|
+ 0 -> binding.readView.curPage.selectEndMoveIndex(
|
|
|
|
|
+ 0,
|
|
|
|
|
+ lineIndex,
|
|
|
|
|
+ charIndex + viewModel.searchContentQuery.length - 1
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ 1 -> binding.readView.curPage.selectEndMoveIndex(
|
|
|
|
|
+ 0, lineIndex + 1, charIndex2
|
|
|
|
|
+ )
|
|
|
|
|
+ //consider change page, jump to scroll position
|
|
|
|
|
+ -1 -> binding.readView.curPage.selectEndMoveIndex(1, 0, charIndex2)
|
|
|
|
|
+ }
|
|
|
|
|
+ binding.readView.isTextSelected = true
|
|
|
|
|
+ isSelectingSearchResult = false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun addBookmark() {
|
|
|
|
|
+ val book = ReadBook.book
|
|
|
|
|
+ val page = ReadBook.curTextChapter?.getPage(ReadBook.durPageIndex)
|
|
|
|
|
+ if (book != null && page != null) {
|
|
|
|
|
+ val bookmark = book.createBookMark().apply {
|
|
|
|
|
+ chapterIndex = ReadBook.durChapterIndex
|
|
|
|
|
+ chapterPos = ReadBook.durChapterPos
|
|
|
|
|
+ chapterName = page.title
|
|
|
|
|
+ bookText = page.text.replace(Regex("[袮꧁]"), "").trim()
|
|
|
|
|
+ }
|
|
|
|
|
+ showDialogFragment(BookmarkDialog(bookmark))
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun changeReplaceRuleState() {
|
|
|
|
|
+ binding.readMenu.runMenuOut()
|
|
|
|
|
+ ReadBook.book?.let {
|
|
|
|
|
+ it.setUseReplaceRule(!it.getUseReplaceRule())
|
|
|
|
|
+ ReadBook.saveRead()
|
|
|
|
|
+ menu?.findItem(R.id.menu_enable_replace)?.isChecked = it.getUseReplaceRule()
|
|
|
|
|
+ binding.readMenu.changeReplace(it.getUseReplaceRule())
|
|
|
|
|
+ viewModel.replaceRuleChanged()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private fun startBackupJob() {
|
|
|
|
|
+ backupJob?.cancel()
|
|
|
|
|
+ backupJob = lifecycleScope.launch(IO) {
|
|
|
|
|
+ delay(300000)
|
|
|
|
|
+ ReadBook.book?.let {
|
|
|
|
|
+ AppWebDav.uploadBookProgress(it)
|
|
|
|
|
+ ensureActive()
|
|
|
|
|
+ it.update()
|
|
|
|
|
+ Backup.autoBack(this@ReadBookActivity)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 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)
|
|
|
|
|
+ }
|
|
|
|
|
+ noButton()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun onDestroy() {
|
|
|
|
|
+ super.onDestroy()
|
|
|
|
|
+ tts?.clearTts()
|
|
|
|
|
+ textActionMenu.dismiss()
|
|
|
|
|
+ popupAction.dismiss()
|
|
|
|
|
+ binding.readView.onDestroy()
|
|
|
|
|
+ ReadBook.unregister(this)
|
|
|
|
|
+ if (!ReadBook.inBookshelf && !isChangingConfigurations) {
|
|
|
|
|
+ viewModel.removeFromBookshelf(null)
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!BuildConfig.DEBUG) {
|
|
|
|
|
+ Backup.autoBack(this)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun observeLiveBus() = binding.run {
|
|
|
|
|
+ observeEvent<String>(EventBus.TIME_CHANGED) { readView.upTime() }
|
|
|
|
|
+ observeEvent<Int>(EventBus.BATTERY_CHANGED) { readView.upBattery(it) }
|
|
|
|
|
+ observeEvent<Boolean>(EventBus.MEDIA_BUTTON) {
|
|
|
|
|
+ if (it) {
|
|
|
|
|
+ onClickReadAloud()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ReadBook.readAloud(!BaseReadAloudService.pause)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ observeEvent<ArrayList<Int>>(EventBus.UP_CONFIG) {
|
|
|
|
|
+ it.forEach { value ->
|
|
|
|
|
+ when (value) {
|
|
|
|
|
+ 0 -> upSystemUiVisibility()
|
|
|
|
|
+ 1 -> readView.upBg()
|
|
|
|
|
+ 2 -> {
|
|
|
|
|
+ readView.upStyle()
|
|
|
|
|
+ binding.readMenu.reset()
|
|
|
|
|
+ }
|
|
|
|
|
+ 3 -> readView.upBgAlpha()
|
|
|
|
|
+ 4 -> readView.upPageSlopSquare()
|
|
|
|
|
+ 5 -> if (isInitFinish) ReadBook.loadContent(resetPageOffset = false)
|
|
|
|
|
+ 6 -> readView.upContent(resetPageOffset = false)
|
|
|
|
|
+ 8 -> ChapterProvider.upStyle()
|
|
|
|
|
+ 9 -> readView.invalidateTextPage()
|
|
|
|
|
+ 10 -> ChapterProvider.upLayout()
|
|
|
|
|
+ 11 -> readView.submitRenderTask()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ observeEvent<Int>(EventBus.ALOUD_STATE) {
|
|
|
|
|
+ if (it == Status.STOP || it == Status.PAUSE) {
|
|
|
|
|
+ ReadBook.curTextChapter?.let { textChapter ->
|
|
|
|
|
+ val page = textChapter.getPageByReadPos(ReadBook.durChapterPos)
|
|
|
|
|
+ if (page != null) {
|
|
|
|
|
+ page.removePageAloudSpan()
|
|
|
|
|
+ readView.upContent(resetPageOffset = false)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ observeEventSticky<Int>(EventBus.TTS_PROGRESS) { chapterStart ->
|
|
|
|
|
+ lifecycleScope.launch(IO) {
|
|
|
|
|
+ if (BaseReadAloudService.isPlay()) {
|
|
|
|
|
+ ReadBook.curTextChapter?.let { textChapter ->
|
|
|
|
|
+ val pageIndex = ReadBook.durPageIndex
|
|
|
|
|
+ val aloudSpanStart = chapterStart - textChapter.getReadLength(pageIndex)
|
|
|
|
|
+ textChapter.getPage(pageIndex)
|
|
|
|
|
+ ?.upPageAloudSpan(aloudSpanStart)
|
|
|
|
|
+ upContent()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ observeEvent<Boolean>(PreferKey.keepLight) {
|
|
|
|
|
+ upScreenTimeOut()
|
|
|
|
|
+ }
|
|
|
|
|
+ observeEvent<Boolean>(PreferKey.textSelectAble) {
|
|
|
|
|
+ readView.curPage.upSelectAble(it)
|
|
|
|
|
+ }
|
|
|
|
|
+ observeEvent<String>(PreferKey.showBrightnessView) {
|
|
|
|
|
+ readMenu.upBrightnessState()
|
|
|
|
|
+ }
|
|
|
|
|
+ observeEvent<List<SearchResult>>(EventBus.SEARCH_RESULT) {
|
|
|
|
|
+ viewModel.searchResultList = it
|
|
|
|
|
+ }
|
|
|
|
|
+ observeEvent<Boolean>(EventBus.UPDATE_READ_ACTION_BAR) {
|
|
|
|
|
+ readMenu.reset()
|
|
|
|
|
+ }
|
|
|
|
|
+ observeEvent<Boolean>(EventBus.UP_SEEK_BAR) {
|
|
|
|
|
+ readMenu.upSeekBar()
|
|
|
|
|
+ }
|
|
|
|
|
+ observeEvent<Boolean>(EventBus.REFRESH_BOOK_CONTENT) {
|
|
|
|
|
+ ReadBook.book?.let {
|
|
|
|
|
+ ReadBook.curTextChapter = null
|
|
|
|
|
+ binding.readView.upContent()
|
|
|
|
|
+ viewModel.refreshContentDur(it)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private fun upScreenTimeOut() {
|
|
|
|
|
+ val keepLightPrefer = getPrefString(PreferKey.keepLight)?.toInt() ?: 0
|
|
|
|
|
+ screenTimeOut = keepLightPrefer * 1000L
|
|
|
|
|
+ screenOffTimerStart()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 重置黑屏时间
|
|
|
|
|
+ */
|
|
|
|
|
+ override fun screenOffTimerStart() {
|
|
|
|
|
+ handler.post {
|
|
|
|
|
+ if (screenTimeOut < 0) {
|
|
|
|
|
+ keepScreenOn(true)
|
|
|
|
|
+ return@post
|
|
|
|
|
+ }
|
|
|
|
|
+ val t = screenTimeOut - sysScreenOffTime
|
|
|
|
|
+ if (t > 0) {
|
|
|
|
|
+ keepScreenOn(true)
|
|
|
|
|
+ handler.removeCallbacks(screenOffRunnable)
|
|
|
|
|
+ handler.postDelayed(screenOffRunnable, screenTimeOut)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ keepScreenOn(false)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ override fun addToBookshelf(book: Book, toc: List<BookChapter>) {
|
|
|
|
|
+ viewModel.addToBookshelf(book, toc) {
|
|
|
|
|
+ toastOnUi("已添加到书架")
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ companion object {
|
|
|
|
|
+ const val RESULT_DELETED = 100
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+}
|