合并上游更改

感谢:@821938089、@zhongerxll、@youke2580c、@Leroy-X、@Dark-Tide、@lhjgege、@3harmony、@mgz0227
This commit is contained in:
HapeLee committed 2025-09-01 00:57:17 +08:00
1 parent dccb72adab
commit 8eb4881767
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()
)
}
}
}
@@ -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