合并上游更改
感谢:@821938089、@zhongerxll、@youke2580c、@Leroy-X、@Dark-Tide、@lhjgege、@3harmony、@mgz0227
This commit is contained in:
87 files changed
+800
-365
No files matched your search
@@ -56,7 +56,7 @@ class OnLineImportViewModel(app: Application) : BaseAssociationViewModel(app) {
|
||||
|
||||
fun importReadConfig(bytes: ByteArray, finally: (title: String, msg: String) -> Unit) {
|
||||
execute {
|
||||
val config = ReadBookConfig.import(bytes).getOrThrow()
|
||||
val config = ReadBookConfig.import(bytes)
|
||||
ReadBookConfig.configList.forEachIndexed { index, c ->
|
||||
if (c.name == config.name) {
|
||||
ReadBookConfig.configList[index] = config
|
||||
|
||||
@@ -64,6 +64,7 @@ class AudioPlayActivity :
|
||||
override val viewModel by viewModels<AudioPlayViewModel>()
|
||||
private val timerSliderPopup by lazy { TimerSliderPopup(this) }
|
||||
private var adjustProgress = false
|
||||
private var playMode = AudioPlay.PlayMode.LIST_END_STOP
|
||||
|
||||
private val progressTimeFormat by lazy {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
@@ -140,6 +141,14 @@ class AudioPlayActivity :
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
binding.ivPlayMode.setOnClickListener {
|
||||
AudioPlay.changePlayMode()
|
||||
}
|
||||
|
||||
observeEventSticky<AudioPlay.PlayMode>(EventBus.PLAY_MODE_CHANGED) {
|
||||
playMode = it
|
||||
updatePlayModeIcon()
|
||||
}
|
||||
binding.fabPlayStop.setOnClickListener {
|
||||
playButton()
|
||||
}
|
||||
@@ -207,6 +216,10 @@ class AudioPlayActivity :
|
||||
binding.llPlayMenu.applyNavigationBarPadding()
|
||||
}
|
||||
|
||||
private fun updatePlayModeIcon() {
|
||||
binding.ivPlayMode.setImageResource(playMode.iconRes)
|
||||
}
|
||||
|
||||
private fun upCover(path: String?) {
|
||||
BookCover.load(this, path, sourceOrigin = AudioPlay.bookSource?.bookSourceUrl) {
|
||||
BookCover.loadBlur(this, path, sourceOrigin = AudioPlay.bookSource?.bookSourceUrl)
|
||||
|
||||
@@ -26,7 +26,7 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application)
|
||||
|
||||
fun initData(intent: Intent) = AudioPlay.apply {
|
||||
execute {
|
||||
val bookUrl = intent.getStringExtra("bookUrl") ?: return@execute
|
||||
val bookUrl = intent.getStringExtra("bookUrl") ?: book?.bookUrl ?: return@execute
|
||||
val book = appDb.bookDao.getBook(bookUrl) ?: return@execute
|
||||
inBookshelf = intent.getBooleanExtra("inBookshelf", true)
|
||||
initBook(book)
|
||||
@@ -71,7 +71,7 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application)
|
||||
if (oldBook.bookUrl == book.bookUrl) {
|
||||
appDb.bookDao.update(book)
|
||||
} else {
|
||||
appDb.bookDao.insert(book)
|
||||
appDb.bookDao.replace(oldBook, book)
|
||||
}
|
||||
appDb.bookChapterDao.delByBook(book.bookUrl)
|
||||
appDb.bookChapterDao.insert(*cList.toTypedArray())
|
||||
|
||||
@@ -739,7 +739,7 @@ class BookInfoActivity :
|
||||
else -> readBookResult.launch(
|
||||
Intent(
|
||||
this,
|
||||
if (book.isImage && AppConfig.showMangaUi) ReadMangaActivity::class.java
|
||||
if (!book.isLocal && book.isImage && AppConfig.showMangaUi) ReadMangaActivity::class.java
|
||||
else ReadBookActivity::class.java
|
||||
)
|
||||
.putExtra("bookUrl", book.bookUrl)
|
||||
|
||||
@@ -235,7 +235,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
|
||||
WebBook.getChapterList(scope, bookSource, book, runPreUpdateJs)
|
||||
.onSuccess(IO) {
|
||||
if (inBookshelf) {
|
||||
book.save()
|
||||
appDb.bookDao.replace(oldBook, book)
|
||||
/**
|
||||
* runPreUpdateJs 有可能会修改 book 的 bookUrl
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package io.legato.kazusa.ui.book.info.edit
|
||||
|
||||
import android.app.Application
|
||||
import android.database.sqlite.SQLiteConstraintException
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import io.legato.kazusa.base.BaseViewModel
|
||||
import io.legato.kazusa.constant.AppLog
|
||||
import io.legato.kazusa.data.appDb
|
||||
import io.legato.kazusa.data.entities.Book
|
||||
import io.legato.kazusa.model.ReadBook
|
||||
@@ -28,6 +30,12 @@ class BookInfoEditViewModel(application: Application) : BaseViewModel(applicatio
|
||||
appDb.bookDao.update(book)
|
||||
}.onSuccess {
|
||||
success?.invoke()
|
||||
}.onError {
|
||||
if (it is SQLiteConstraintException) {
|
||||
AppLog.put("书籍信息保存失败,存在相同书名作者书籍\n$it", it, true)
|
||||
} else {
|
||||
AppLog.put("书籍信息保存失败\n$it", it, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,7 @@ class ReadMangaViewModel(application: Application) : BaseViewModel(application)
|
||||
if (oldBook.bookUrl == book.bookUrl) {
|
||||
appDb.bookDao.update(book)
|
||||
} else {
|
||||
appDb.bookDao.insert(book)
|
||||
appDb.bookDao.replace(oldBook, book)
|
||||
BookHelp.updateCacheFolder(oldBook, book)
|
||||
}
|
||||
appDb.bookChapterDao.delByBook(oldBook.bookUrl)
|
||||
|
||||
@@ -5,7 +5,6 @@ import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.Gravity
|
||||
import android.view.HapticFeedbackConstants
|
||||
@@ -19,6 +18,7 @@ import androidx.activity.addCallback
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.view.doOnPreDraw
|
||||
import androidx.core.view.get
|
||||
import androidx.core.view.size
|
||||
@@ -906,10 +906,11 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
ReadBook.bookSource?.bookSourceUrl?.let {
|
||||
scopes.add(it)
|
||||
}
|
||||
val text = selectedText.lineSequence().map { it.trim() }.joinToString("\n")
|
||||
replaceActivity.launch(
|
||||
ReplaceEditActivity.startIntent(
|
||||
this,
|
||||
pattern = selectedText,
|
||||
pattern = text,
|
||||
scope = scopes.joinToString(";")
|
||||
)
|
||||
)
|
||||
@@ -1421,7 +1422,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
value = src
|
||||
}
|
||||
} else {
|
||||
viewModel.saveImage(src, Uri.parse(path))
|
||||
viewModel.saveImage(src, path.toUri())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -214,7 +214,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
|
||||
if (oldBook.bookUrl == book.bookUrl) {
|
||||
appDb.bookDao.update(book)
|
||||
} else {
|
||||
appDb.bookDao.insert(book)
|
||||
appDb.bookDao.replace(oldBook, book)
|
||||
BookHelp.updateCacheFolder(oldBook, book)
|
||||
}
|
||||
appDb.bookChapterDao.delByBook(oldBook.bookUrl)
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
package io.legato.kazusa.ui.book.read.config
|
||||
|
||||
//import io.legado.app.lib.theme.bottomBackground
|
||||
//import io.legado.app.lib.theme.getPrimaryTextColor
|
||||
//import io.legado.app.lib.theme.getSecondaryTextColor
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.DialogInterface
|
||||
import android.graphics.Color
|
||||
import android.graphics.PorterDuff
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.TooltipCompat
|
||||
import androidx.core.graphics.toColorInt
|
||||
import androidx.core.view.isGone
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import com.google.android.material.slider.Slider
|
||||
import com.jaredrummler.android.colorpicker.ColorPickerDialog
|
||||
@@ -22,12 +20,14 @@ import io.legato.kazusa.databinding.DialogEditTextBinding
|
||||
import io.legato.kazusa.databinding.DialogReadBgTextBinding
|
||||
import io.legato.kazusa.databinding.ItemBgImageBinding
|
||||
import io.legato.kazusa.help.DefaultData
|
||||
import io.legato.kazusa.help.book.isImage
|
||||
import io.legato.kazusa.help.config.ReadBookConfig
|
||||
import io.legato.kazusa.help.http.newCallResponseBody
|
||||
import io.legato.kazusa.help.http.okHttpClient
|
||||
import io.legato.kazusa.lib.dialogs.SelectItem
|
||||
import io.legato.kazusa.lib.dialogs.alert
|
||||
import io.legato.kazusa.lib.dialogs.selector
|
||||
import io.legato.kazusa.model.ReadBook
|
||||
import io.legato.kazusa.ui.book.read.ReadBookActivity
|
||||
import io.legato.kazusa.ui.file.HandleFileContract
|
||||
import io.legato.kazusa.utils.FileUtils
|
||||
@@ -119,6 +119,7 @@ class BgTextConfigDialog : BaseBottomSheetDialogFragment(R.layout.dialog_read_bg
|
||||
tvName.text = getString(R.string.select_image)
|
||||
ivBg.setImageResource(R.drawable.ic_image)
|
||||
ivBg.setColorFilter(primaryTextColor, PorterDuff.Mode.SRC_IN)
|
||||
swUnderline.isGone = ReadBook.book?.isImage == true
|
||||
root.setOnClickListener {
|
||||
selectBgImage.launch()
|
||||
}
|
||||
@@ -184,8 +185,8 @@ class BgTextConfigDialog : BaseBottomSheetDialogFragment(R.layout.dialog_read_bg
|
||||
}
|
||||
binding.tvBgColor.setOnClickListener {
|
||||
val bgColor =
|
||||
if (curBgType() == 0) Color.parseColor(curBgStr())
|
||||
else Color.parseColor("#015A86")
|
||||
if (curBgType() == 0) curBgStr().toColorInt()
|
||||
else "#015A86".toColorInt()
|
||||
ColorPickerDialog.newBuilder()
|
||||
.setColor(bgColor)
|
||||
.setShowAlphaSlider(false)
|
||||
@@ -355,7 +356,7 @@ class BgTextConfigDialog : BaseBottomSheetDialogFragment(R.layout.dialog_read_bg
|
||||
|
||||
private fun importConfig(byteArray: ByteArray) {
|
||||
execute {
|
||||
ReadBookConfig.import(byteArray).getOrThrow()
|
||||
ReadBookConfig.import(byteArray)
|
||||
}.onSuccess {
|
||||
ReadBookConfig.durConfig = it
|
||||
postEvent(EventBus.UP_CONFIG, arrayListOf(1, 2, 5))
|
||||
|
||||
@@ -36,7 +36,8 @@ class ClickActionConfigDialog : BaseDialogFragment(R.layout.dialog_click_action_
|
||||
Pair(9, getString(R.string.replace_state_change)),
|
||||
Pair(10, getString(R.string.chapter_list)),
|
||||
Pair(11, getString(R.string.search_content)),
|
||||
Pair(12, getString(R.string.sync_book_progress_t))
|
||||
Pair(12, getString(R.string.sync_book_progress_t)),
|
||||
Pair(13, getString(R.string.read_aloud_pause_resume))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -130,6 +130,10 @@ class MoreConfigDialog : BaseBottomSheetDialogFragment(R.layout.dialog_more_conf
|
||||
ReadBook.callBack?.upPageAnim(true)
|
||||
ReadBook.loadContent(false)
|
||||
}
|
||||
|
||||
PreferKey.paddingDisplayCutouts -> {
|
||||
postEvent(EventBus.UP_CONFIG, arrayListOf(2))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,12 +14,12 @@ import io.legato.kazusa.utils.themeColor
|
||||
/**
|
||||
* 自动翻页
|
||||
*/
|
||||
class AutoPager(
|
||||
private val readView: ReadView) {
|
||||
class AutoPager(private val readView: ReadView) : Runnable {
|
||||
private var progress = 0
|
||||
var isRunning = false
|
||||
private set
|
||||
private var isPausing = false
|
||||
private var isEInkMode = false
|
||||
private var scrollOffsetRemain = 0.0
|
||||
private var scrollOffset = 0
|
||||
private var lastTimeMillis = 0L
|
||||
@@ -29,10 +29,15 @@ class AutoPager(
|
||||
|
||||
fun start() {
|
||||
isRunning = true
|
||||
paint.color = readView.context.themeColor(androidx.appcompat.R.attr.colorPrimary)
|
||||
lastTimeMillis = SystemClock.uptimeMillis()
|
||||
isEInkMode = AppConfig.isEInkMode
|
||||
readView.curPage.upSelectAble(false)
|
||||
readView.invalidate()
|
||||
if (isEInkMode) {
|
||||
readView.postDelayed(this, ReadBookConfig.autoReadSpeed * 1000L)
|
||||
} else {
|
||||
paint.color = readView.context.themeColor(androidx.appcompat.R.attr.colorPrimary)
|
||||
lastTimeMillis = SystemClock.uptimeMillis()
|
||||
readView.invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
@@ -41,6 +46,8 @@ class AutoPager(
|
||||
}
|
||||
isRunning = false
|
||||
isPausing = false
|
||||
isEInkMode = false
|
||||
readView.removeCallbacks(this)
|
||||
readView.curPage.upSelectAble(AppConfig.textSelectAble)
|
||||
readView.invalidate()
|
||||
reset()
|
||||
@@ -52,6 +59,7 @@ class AutoPager(
|
||||
return
|
||||
}
|
||||
isPausing = true
|
||||
readView.removeCallbacks(this)
|
||||
}
|
||||
|
||||
fun resume() {
|
||||
@@ -59,15 +67,24 @@ class AutoPager(
|
||||
return
|
||||
}
|
||||
isPausing = false
|
||||
lastTimeMillis = SystemClock.uptimeMillis()
|
||||
readView.invalidate()
|
||||
if (isEInkMode) {
|
||||
readView.postDelayed(this, ReadBookConfig.autoReadSpeed * 1000L)
|
||||
} else {
|
||||
lastTimeMillis = SystemClock.uptimeMillis()
|
||||
readView.invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
progress = 0
|
||||
scrollOffsetRemain = 0.0
|
||||
scrollOffset = 0
|
||||
canvasRecorder.invalidate()
|
||||
if (isEInkMode) {
|
||||
readView.removeCallbacks(this)
|
||||
readView.postDelayed(this, ReadBookConfig.autoReadSpeed * 1000L)
|
||||
} else {
|
||||
progress = 0
|
||||
scrollOffsetRemain = 0.0
|
||||
scrollOffset = 0
|
||||
canvasRecorder.invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
fun upRecorder() {
|
||||
@@ -76,12 +93,15 @@ class AutoPager(
|
||||
}
|
||||
|
||||
fun onDraw(canvas: Canvas) {
|
||||
if (!isRunning) {
|
||||
if (!isRunning || isEInkMode) {
|
||||
return
|
||||
}
|
||||
|
||||
if (readView.isScroll) {
|
||||
if (!isPausing) readView.curPage.scroll(-scrollOffset)
|
||||
if (!isPausing) {
|
||||
readView.curPage.scroll(-scrollOffset)
|
||||
scrollOffset = 0
|
||||
}
|
||||
} else {
|
||||
val bottom = progress
|
||||
val width = readView.width
|
||||
@@ -104,7 +124,7 @@ class AutoPager(
|
||||
}
|
||||
|
||||
fun computeOffset() {
|
||||
if (!isRunning || isPausing) {
|
||||
if (!isRunning || isPausing || isEInkMode) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -132,4 +152,15 @@ class AutoPager(
|
||||
}
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
if (!isRunning || isPausing) {
|
||||
return
|
||||
}
|
||||
if (!readView.fillPage(PageDirection.NEXT)) {
|
||||
stop()
|
||||
} else {
|
||||
readView.postDelayed(this, ReadBookConfig.autoReadSpeed * 1000L)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,15 +2,20 @@ package io.legato.kazusa.ui.book.read.page
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isInvisible
|
||||
import io.legato.kazusa.R
|
||||
import io.legato.kazusa.constant.AppConst.timeFormat
|
||||
import io.legato.kazusa.data.entities.Bookmark
|
||||
import io.legato.kazusa.databinding.ViewBookPageBinding
|
||||
import io.legato.kazusa.help.config.AppConfig
|
||||
import io.legato.kazusa.help.config.ReadBookConfig
|
||||
import io.legato.kazusa.help.config.ReadTipConfig
|
||||
import io.legato.kazusa.model.ReadBook
|
||||
@@ -25,6 +30,7 @@ import io.legato.kazusa.utils.applyNavigationBarPadding
|
||||
import io.legato.kazusa.utils.applyStatusBarPadding
|
||||
import io.legato.kazusa.utils.dpToPx
|
||||
import io.legato.kazusa.utils.gone
|
||||
import io.legato.kazusa.utils.setOnApplyWindowInsetsListenerCompat
|
||||
import io.legato.kazusa.utils.setTextIfNotEqual
|
||||
import io.legato.kazusa.utils.statusBarHeight
|
||||
import splitties.views.backgroundColor
|
||||
@@ -56,7 +62,7 @@ class PageView(context: Context) : FrameLayout(context) {
|
||||
get() {
|
||||
val h1 = if (binding.vwStatusBar.isGone) 0 else binding.vwStatusBar.height
|
||||
val h2 = if (binding.llHeader.isGone) 0 else binding.llHeader.height
|
||||
return h1 + h2
|
||||
return h1 + h2 + binding.vwRoot.paddingTop
|
||||
}
|
||||
|
||||
init {
|
||||
@@ -94,6 +100,7 @@ class PageView(context: Context) : FrameLayout(context) {
|
||||
vwBottomDivider.backgroundColor = tipDividerColor
|
||||
upStatusBar()
|
||||
upNavigationBar()
|
||||
upPaddingDisplayCutouts()
|
||||
llHeader.setPadding(
|
||||
it.headerPaddingLeft.dpToPx(),
|
||||
it.headerPaddingTop.dpToPx(),
|
||||
@@ -125,6 +132,24 @@ class PageView(context: Context) : FrameLayout(context) {
|
||||
binding.vwNavigationBar.isGone = ReadBookConfig.hideNavigationBar
|
||||
}
|
||||
|
||||
fun upPaddingDisplayCutouts() {
|
||||
if (AppConfig.paddingDisplayCutouts) {
|
||||
binding.vwRoot.setOnApplyWindowInsetsListenerCompat { _, windowInsets ->
|
||||
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||
binding.vwRoot.setPadding(
|
||||
insets.left,
|
||||
if (binding.vwStatusBar.isGone) insets.top else 0,
|
||||
insets.right,
|
||||
insets.bottom
|
||||
)
|
||||
windowInsets
|
||||
}
|
||||
} else {
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.vwRoot, null)
|
||||
binding.vwRoot.setPadding(0, 0, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新阅读信息
|
||||
*/
|
||||
@@ -239,8 +264,12 @@ class PageView(context: Context) : FrameLayout(context) {
|
||||
* 更新背景
|
||||
*/
|
||||
fun upBg() {
|
||||
binding.vwRoot.backgroundColor = ReadBookConfig.bgMeanColor
|
||||
binding.vwBg.background = ReadBookConfig.bg
|
||||
binding.vwRoot.background = LayerDrawable(
|
||||
arrayOf(
|
||||
ReadBookConfig.bgMeanColor.toDrawable(),
|
||||
ReadBookConfig.bg
|
||||
)
|
||||
)
|
||||
upBgAlpha()
|
||||
}
|
||||
|
||||
@@ -248,7 +277,8 @@ class PageView(context: Context) : FrameLayout(context) {
|
||||
* 更新背景透明度
|
||||
*/
|
||||
fun upBgAlpha() {
|
||||
binding.vwBg.alpha = ReadBookConfig.bgAlpha / 100f
|
||||
ReadBookConfig.bg?.alpha = (ReadBookConfig.bgAlpha / 100f * 255).toInt()
|
||||
binding.vwRoot.invalidate()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,7 @@ import io.legato.kazusa.help.config.AppConfig
|
||||
import io.legato.kazusa.help.config.ReadBookConfig
|
||||
import io.legato.kazusa.model.ReadAloud
|
||||
import io.legato.kazusa.model.ReadBook
|
||||
import io.legato.kazusa.service.BaseReadAloudService
|
||||
import io.legato.kazusa.ui.book.read.ContentEditDialog
|
||||
import io.legato.kazusa.ui.book.read.page.api.DataSource
|
||||
import io.legato.kazusa.ui.book.read.page.delegate.CoverPageDelegate
|
||||
@@ -456,6 +457,13 @@ class ReadView(context: Context, attrs: AttributeSet) :
|
||||
{ progress -> callBack.sureNewProgress(progress) },
|
||||
{ context.longToastOnUi(context.getString(R.string.upload_book_success)) },
|
||||
{ context.longToastOnUi(context.getString(R.string.sync_book_progress_success)) })
|
||||
13 -> {
|
||||
if (BaseReadAloudService.isPlay()) {
|
||||
ReadAloud.pause(context)
|
||||
} else {
|
||||
ReadAloud.resume(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -160,6 +160,15 @@ data class TextLine(
|
||||
columns[i].draw(view, canvas)
|
||||
}
|
||||
}
|
||||
// 墨水屏模式下的朗读和搜索下划线
|
||||
if (AppConfig.isEInkMode && (isReadAloud || searchResultColumnCount > 0)) {
|
||||
val underlinePaint = PaintPool.obtain()
|
||||
underlinePaint.set(ChapterProvider.contentPaint)
|
||||
underlinePaint.strokeWidth = 1.dpToPx().toFloat()
|
||||
val lineY = height - 1.dpToPx()
|
||||
canvas.drawLine(lineStart + indentWidth, lineY, lineEnd, lineY, underlinePaint)
|
||||
PaintPool.recycle(underlinePaint)
|
||||
}
|
||||
if (ReadBookConfig.underline && !isImage && ReadBook.book?.isImage != true) {
|
||||
drawUnderline(canvas)
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import io.legato.kazusa.utils.postEvent
|
||||
import io.legato.kazusa.utils.spToPx
|
||||
import io.legato.kazusa.utils.splitNotBlank
|
||||
import io.legato.kazusa.utils.textHeight
|
||||
import io.legato.kazusa.utils.toastOnUi
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import splitties.init.appCtx
|
||||
import java.util.LinkedList
|
||||
@@ -1001,20 +1002,8 @@ object ChapterProvider {
|
||||
}
|
||||
}
|
||||
|
||||
if (viewWidth > 0 && viewHeight > 0) {
|
||||
paddingLeft = ReadBookConfig.paddingLeft.dpToPx()
|
||||
paddingTop = ReadBookConfig.paddingTop.dpToPx()
|
||||
paddingRight = ReadBookConfig.paddingRight.dpToPx()
|
||||
paddingBottom = ReadBookConfig.paddingBottom.dpToPx()
|
||||
visibleWidth = if (doublePage) {
|
||||
viewWidth / 2 - paddingLeft - paddingRight
|
||||
} else {
|
||||
viewWidth - paddingLeft - paddingRight
|
||||
}
|
||||
//留1dp画最后一行下划线
|
||||
visibleHeight = viewHeight - paddingTop - paddingBottom
|
||||
visibleRight = viewWidth - paddingRight
|
||||
visibleBottom = paddingTop + visibleHeight
|
||||
if (viewWidth <= 0 || viewHeight <= 0) {
|
||||
return
|
||||
}
|
||||
|
||||
visibleRect.set(
|
||||
@@ -1024,6 +1013,37 @@ object ChapterProvider {
|
||||
visibleBottom.toFloat()
|
||||
)
|
||||
|
||||
paddingLeft = ReadBookConfig.paddingLeft.dpToPx()
|
||||
paddingTop = ReadBookConfig.paddingTop.dpToPx()
|
||||
paddingRight = ReadBookConfig.paddingRight.dpToPx()
|
||||
paddingBottom = ReadBookConfig.paddingBottom.dpToPx()
|
||||
visibleWidth = if (doublePage) {
|
||||
viewWidth / 2 - paddingLeft - paddingRight
|
||||
} else {
|
||||
viewWidth - paddingLeft - paddingRight
|
||||
}
|
||||
//留1dp画最后一行下划线
|
||||
visibleHeight = viewHeight - paddingTop - paddingBottom
|
||||
visibleRight = viewWidth - paddingRight
|
||||
visibleBottom = paddingTop + visibleHeight
|
||||
|
||||
if (paddingLeft >= visibleRight || paddingTop >= visibleBottom) {
|
||||
appCtx.toastOnUi("边距设置过大,请重新设置")
|
||||
visibleRect.set(
|
||||
0f,
|
||||
0f,
|
||||
viewWidth.toFloat(),
|
||||
viewHeight.toFloat()
|
||||
)
|
||||
} else {
|
||||
visibleRect.set(
|
||||
paddingLeft.toFloat(),
|
||||
paddingTop.toFloat(),
|
||||
visibleRight.toFloat(),
|
||||
visibleBottom.toFloat()
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+10
-11
@@ -20,7 +20,6 @@ import io.legato.kazusa.ui.book.read.page.entities.TextChapter
|
||||
import io.legato.kazusa.ui.book.read.page.entities.TextLine
|
||||
import io.legato.kazusa.ui.book.read.page.entities.TextPage
|
||||
import io.legato.kazusa.ui.book.read.page.entities.column.ImageColumn
|
||||
import io.legato.kazusa.ui.book.read.page.entities.column.ReviewColumn
|
||||
import io.legato.kazusa.ui.book.read.page.entities.column.TextColumn
|
||||
import io.legato.kazusa.utils.dpToPx
|
||||
import io.legato.kazusa.utils.fastSum
|
||||
@@ -214,13 +213,13 @@ class TextChapterLayout(
|
||||
emptyContent = contents.isEmpty(),
|
||||
isVolumeTitle = bookChapter.isVolume
|
||||
)
|
||||
pendingTextPage.lines.last().isParagraphEnd = true
|
||||
stringBuilder.append("\n")
|
||||
}
|
||||
pendingTextPage.lines.last().isParagraphEnd = true
|
||||
stringBuilder.append("\n")
|
||||
durY += titleBottomSpacing
|
||||
|
||||
// 如果是单图模式且当前页有内容,强制分页
|
||||
if (isSingleImageStyle && pendingTextPage.lines.isNotEmpty()) {
|
||||
if (isSingleImageStyle && pendingTextPage.lines.isNotEmpty() && contents.isNotEmpty()) {
|
||||
prepareNextPageIfNeed()
|
||||
}
|
||||
}
|
||||
@@ -708,13 +707,13 @@ class TextChapterLayout(
|
||||
)
|
||||
}
|
||||
|
||||
isLineEnd && char == ChapterProvider.reviewChar -> {
|
||||
ReviewColumn(
|
||||
start = absStartX + xStart,
|
||||
end = absStartX + xEnd,
|
||||
count = 100
|
||||
)
|
||||
}
|
||||
// isLineEnd && char == ChapterProvider.reviewChar -> {
|
||||
// ReviewColumn(
|
||||
// start = absStartX + xStart,
|
||||
// end = absStartX + xEnd,
|
||||
// count = 100
|
||||
// )
|
||||
// }
|
||||
|
||||
else -> {
|
||||
TextColumn(
|
||||
|
||||
@@ -12,12 +12,13 @@ import androidx.activity.viewModels
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.flexbox.FlexboxLayoutManager
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import com.google.android.material.search.SearchBar
|
||||
import com.google.android.material.search.SearchView
|
||||
import io.legato.kazusa.R
|
||||
@@ -45,6 +46,7 @@ import io.legato.kazusa.utils.viewbindingdelegate.viewBinding
|
||||
import io.legato.kazusa.utils.visible
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.awaitCancellation
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
@@ -183,21 +185,19 @@ class SearchActivity : VMBaseActivity<ActivityBookSearchBinding, SearchViewModel
|
||||
|
||||
binding.appBar.post {
|
||||
val maxPaddingTop = binding.appBar.paddingTop
|
||||
binding.appBar.addOnOffsetChangedListener(
|
||||
AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
|
||||
val totalScrollRange = appBarLayout.totalScrollRange
|
||||
val scrollRatio =
|
||||
(1f - (abs(verticalOffset).toFloat() / totalScrollRange)).coerceIn(0f, 1f)
|
||||
binding.appBar.addOnOffsetChangedListener { appBarLayout, verticalOffset ->
|
||||
val totalScrollRange = appBarLayout.totalScrollRange
|
||||
val scrollRatio =
|
||||
(1f - (abs(verticalOffset).toFloat() / totalScrollRange)).coerceIn(0f, 1f)
|
||||
|
||||
appBarLayout.alpha = scrollRatio
|
||||
appBarLayout.setPadding(
|
||||
appBarLayout.paddingLeft,
|
||||
(maxPaddingTop * scrollRatio).toInt(),
|
||||
appBarLayout.paddingRight,
|
||||
appBarLayout.paddingBottom
|
||||
)
|
||||
}
|
||||
)
|
||||
appBarLayout.alpha = scrollRatio
|
||||
appBarLayout.setPadding(
|
||||
appBarLayout.paddingLeft,
|
||||
(maxPaddingTop * scrollRatio).toInt(),
|
||||
appBarLayout.paddingRight,
|
||||
appBarLayout.paddingBottom
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
searchBar.setOnMenuItemClickListener { item ->
|
||||
@@ -343,6 +343,16 @@ class SearchActivity : VMBaseActivity<ActivityBookSearchBinding, SearchViewModel
|
||||
groups = it
|
||||
}
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||
viewModel.resume()
|
||||
try {
|
||||
awaitCancellation()
|
||||
} finally {
|
||||
viewModel.pause()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -123,6 +123,14 @@ class SearchViewModel(application: Application) : BaseViewModel(application) {
|
||||
searchModel.cancelSearch()
|
||||
}
|
||||
|
||||
fun pause() {
|
||||
searchModel.pause()
|
||||
}
|
||||
|
||||
fun resume() {
|
||||
searchModel.resume()
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存搜索关键字
|
||||
*/
|
||||
|
||||
@@ -2,6 +2,7 @@ package io.legato.kazusa.ui.book.searchContent
|
||||
|
||||
import android.text.Spanned
|
||||
import androidx.core.text.HtmlCompat
|
||||
import io.legato.kazusa.help.config.AppConfig
|
||||
|
||||
data class SearchResult(
|
||||
val resultCount: Int = 0,
|
||||
@@ -22,19 +23,34 @@ data class SearchResult(
|
||||
val leftString = resultText.substring(0, queryIndexInSurrounding)
|
||||
val rightString =
|
||||
resultText.substring(queryIndexInSurrounding + query.length, resultText.length)
|
||||
val html = buildString {
|
||||
append(chapterTitle.colorTextForHtml(accentColor))
|
||||
append("<br>")
|
||||
append(leftString.colorTextForHtml(textColor))
|
||||
append(query.colorTextForHtml(accentColor))
|
||||
append(rightString.colorTextForHtml(textColor))
|
||||
// 检查是否为墨水屏模式
|
||||
val html = if (AppConfig.isEInkMode) {
|
||||
// 墨水屏模式:使用下划线
|
||||
buildString {
|
||||
append("<u>${chapterTitle}</u>")
|
||||
append("<br>")
|
||||
append(leftString)
|
||||
append("<u>${query}</u>")
|
||||
append(rightString)
|
||||
}
|
||||
} else {
|
||||
// 普通模式:使用颜色
|
||||
buildString {
|
||||
append(chapterTitle.colorTextForHtml(accentColor))
|
||||
append("<br>")
|
||||
append(leftString.colorTextForHtml(textColor))
|
||||
append(query.colorTextForHtml(accentColor))
|
||||
append(rightString.colorTextForHtml(textColor))
|
||||
}
|
||||
}
|
||||
HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||
} else {
|
||||
HtmlCompat.fromHtml(
|
||||
resultText.colorTextForHtml(textColor),
|
||||
HtmlCompat.FROM_HTML_MODE_LEGACY
|
||||
)
|
||||
val html = if (AppConfig.isEInkMode) {
|
||||
resultText
|
||||
} else {
|
||||
resultText.colorTextForHtml(textColor)
|
||||
}
|
||||
HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,8 +65,19 @@ class DictDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_dict) {
|
||||
tag = it
|
||||
})
|
||||
}
|
||||
setupTabLayoutMode(it.size)
|
||||
}
|
||||
}
|
||||
|
||||
//根据已启用词典数动态选取布局
|
||||
private fun setupTabLayoutMode(dictCount: Int) {
|
||||
if (dictCount <= 4) {
|
||||
binding.tabLayout.tabMode = TabLayout.MODE_FIXED
|
||||
binding.tabLayout.tabGravity = TabLayout.GRAVITY_FILL
|
||||
} else {
|
||||
binding.tabLayout.tabMode = TabLayout.MODE_SCROLLABLE
|
||||
binding.tabLayout.tabGravity = TabLayout.GRAVITY_CENTER
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,9 +17,11 @@ class SourceLoginActivity : VMBaseActivity<ActivitySourceLoginBinding, SourceLog
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
viewModel.initData(intent) { source ->
|
||||
viewModel.initData(intent, success = { source ->
|
||||
initView(source)
|
||||
}
|
||||
}, error = {
|
||||
finish()
|
||||
})
|
||||
}
|
||||
|
||||
private fun initView(source: BaseSource) {
|
||||
|
||||
@@ -2,11 +2,12 @@ package io.legato.kazusa.ui.login
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
import com.script.rhino.runScriptWithContext
|
||||
import io.legato.kazusa.base.BaseViewModel
|
||||
import io.legato.kazusa.constant.AppLog
|
||||
import io.legato.kazusa.data.appDb
|
||||
import io.legato.kazusa.data.entities.BaseSource
|
||||
import io.legato.kazusa.exception.NoStackTraceException
|
||||
import com.script.rhino.runScriptWithContext
|
||||
import io.legato.kazusa.utils.toastOnUi
|
||||
|
||||
class SourceLoginViewModel(application: Application) : BaseViewModel(application) {
|
||||
@@ -14,7 +15,7 @@ class SourceLoginViewModel(application: Application) : BaseViewModel(application
|
||||
var source: BaseSource? = null
|
||||
var headerMap: Map<String, String> = emptyMap()
|
||||
|
||||
fun initData(intent: Intent, success: (bookSource: BaseSource) -> Unit) {
|
||||
fun initData(intent: Intent, success: (bookSource: BaseSource) -> Unit, error: () -> Unit) {
|
||||
execute {
|
||||
val sourceKey = intent.getStringExtra("key")
|
||||
?: throw NoStackTraceException("没有参数")
|
||||
@@ -33,6 +34,9 @@ class SourceLoginViewModel(application: Application) : BaseViewModel(application
|
||||
} else {
|
||||
context.toastOnUi("未找到书源")
|
||||
}
|
||||
}.onError {
|
||||
error.invoke()
|
||||
AppLog.put("登录 UI 初始化失败\n$it", it, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ class MainViewModel(application: Application) : BaseViewModel(application) {
|
||||
if (book.bookUrl == bookUrl) {
|
||||
appDb.bookDao.update(book)
|
||||
} else {
|
||||
appDb.bookDao.insert(book)
|
||||
appDb.bookDao.replace(oldBook, book)
|
||||
BookHelp.updateCacheFolder(oldBook, book)
|
||||
}
|
||||
appDb.bookChapterDao.delByBook(bookUrl)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.legato.kazusa.ui.replace
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
@@ -25,8 +24,6 @@ import io.legato.kazusa.help.DirectLinkUpload
|
||||
import io.legato.kazusa.help.book.ContentProcessor
|
||||
import io.legato.kazusa.help.coroutine.Coroutine
|
||||
import io.legato.kazusa.lib.dialogs.alert
|
||||
//import io.legado.app.lib.theme.primaryColor
|
||||
//import io.legado.app.lib.theme.primaryTextColor
|
||||
import io.legato.kazusa.ui.association.ImportReplaceRuleDialog
|
||||
import io.legato.kazusa.ui.file.HandleFileContract
|
||||
import io.legato.kazusa.ui.qrcode.QrCodeResult
|
||||
@@ -72,7 +69,7 @@ class ReplaceRuleActivity : VMBaseActivity<ActivityReplaceRuleBinding, ReplaceRu
|
||||
private val searchView: SearchView by lazy {
|
||||
binding.titleBar.findViewById(R.id.search_view)
|
||||
}
|
||||
private var groups = hashSetOf<String>()
|
||||
private var groups = arrayListOf<String>()
|
||||
private var groupMenu: SubMenu? = null
|
||||
private var replaceRuleFlowJob: Job? = null
|
||||
private var dataInit = false
|
||||
@@ -224,7 +221,7 @@ class ReplaceRuleActivity : VMBaseActivity<ActivityReplaceRuleBinding, ReplaceRu
|
||||
AppLog.put("替换规则管理界面更新数据出错", it)
|
||||
}.flowOn(IO).conflate().collect {
|
||||
if (dataInit) {
|
||||
setResult(Activity.RESULT_OK)
|
||||
setResult(RESULT_OK)
|
||||
}
|
||||
adapter.setItems(it, adapter.diffItemCallBack)
|
||||
dataInit = true
|
||||
|
||||
@@ -20,8 +20,6 @@ import io.legato.kazusa.databinding.ActivityRssSourceBinding
|
||||
import io.legato.kazusa.databinding.DialogEditTextBinding
|
||||
import io.legato.kazusa.help.DirectLinkUpload
|
||||
import io.legato.kazusa.lib.dialogs.alert
|
||||
//import io.legado.app.lib.theme.primaryColor
|
||||
//import io.legado.app.lib.theme.primaryTextColor
|
||||
import io.legato.kazusa.ui.association.ImportRssSourceDialog
|
||||
import io.legato.kazusa.ui.file.HandleFileContract
|
||||
import io.legato.kazusa.ui.qrcode.QrCodeResult
|
||||
@@ -70,7 +68,7 @@ class RssSourceActivity : VMBaseActivity<ActivityRssSourceBinding, RssSourceView
|
||||
binding.titleBar.findViewById(R.id.search_view)
|
||||
}
|
||||
private var sourceFlowJob: Job? = null
|
||||
private var groups = hashSetOf<String>()
|
||||
private var groups = arrayListOf<String>()
|
||||
private var groupMenu: SubMenu? = null
|
||||
private val qrCodeResult = registerForActivityResult(QrCodeResult()) {
|
||||
it ?: return@registerForActivityResult
|
||||
|
||||
Reference in new issue
Block a user