新增漫画阅读模式[条漫、日漫等]
新增条漫侧边留白功能 更新滤镜功能,现在支持亮度跟随系统 其他界面更新
This commit is contained in:
1 parent
c35177715c
commit
1701285d33
24 files changed
+585
-277
No files matched your search
@@ -190,4 +190,6 @@ object PreferKey {
|
||||
const val themePref = "themePref"
|
||||
const val bookshelfSortOrder = "bookshelfSortOrder"
|
||||
const val showBottomView = "showBottomView"
|
||||
const val mangaScrollMode = "mangaScrollMode"
|
||||
const val webtoonSidePaddingDp = "webtoonSidePaddingDp"
|
||||
}
|
||||
@@ -403,6 +403,8 @@ data class Book(
|
||||
var startDate: LocalDate? = null,
|
||||
var startChapter: Int? = null, // 用户设置的起始章节
|
||||
var dailyChapters: Int = 3 // 用户设置的每日更新章节数
|
||||
|
||||
|
||||
) : Parcelable
|
||||
|
||||
class Converters {
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.os.Build
|
||||
import io.legato.kazusa.BuildConfig
|
||||
import io.legato.kazusa.constant.PreferKey
|
||||
import io.legato.kazusa.data.appDb
|
||||
import io.legato.kazusa.ui.book.manga.config.MangaScrollMode
|
||||
import io.legato.kazusa.utils.canvasrecorder.CanvasRecorderFactory
|
||||
import io.legato.kazusa.utils.getPrefBoolean
|
||||
import io.legato.kazusa.utils.getPrefInt
|
||||
@@ -664,6 +665,14 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
appCtx.putPrefBoolean(PreferKey.enableMangaHorizontalScroll, value)
|
||||
}
|
||||
|
||||
//漫画滚动方式
|
||||
var mangaScrollMode: Int
|
||||
get() = appCtx.getPrefInt(PreferKey.mangaScrollMode, MangaScrollMode.PAGE_RIGHT_TO_LEFT)
|
||||
set(value) {
|
||||
appCtx.putPrefInt(PreferKey.mangaScrollMode, value)
|
||||
}
|
||||
|
||||
//漫画滤镜
|
||||
var mangaColorFilter
|
||||
get() = appCtx.getPrefString(PreferKey.mangaColorFilter, "")
|
||||
set(value) {
|
||||
@@ -701,5 +710,12 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
set(value) {
|
||||
appCtx.putPrefBoolean(PreferKey.enableMangaGray, value)
|
||||
}
|
||||
|
||||
var webtoonSidePaddingDp: Int
|
||||
get() = appCtx.getPrefInt(PreferKey.webtoonSidePaddingDp, 0)
|
||||
set(value) {
|
||||
appCtx.putPrefInt(PreferKey.webtoonSidePaddingDp, value)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -607,7 +607,7 @@ object ReadManga : CoroutineScope by MainScope() {
|
||||
if (imageCount == 0 && chapter.isVolume) {
|
||||
pages.add(ReaderLoading(chapter.index, -1, chapter.title, true))
|
||||
} else {
|
||||
pages.add(ReaderLoading(chapter.index, -1, "阅读 ${chapter.title}"))
|
||||
pages.add(ReaderLoading(chapter.index, -1, "下一章 ${chapter.title}"))
|
||||
pages.addAll(list)
|
||||
pages.add(ReaderLoading(chapter.index, imageCount, "已读完 ${chapter.title}"))
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import android.view.WindowManager
|
||||
import android.view.animation.LinearInterpolator
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.view.doOnLayout
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
@@ -45,6 +46,8 @@ import io.legato.kazusa.ui.book.manga.config.MangaColorFilterConfig
|
||||
import io.legato.kazusa.ui.book.manga.config.MangaColorFilterDialog
|
||||
import io.legato.kazusa.ui.book.manga.config.MangaFooterConfig
|
||||
import io.legato.kazusa.ui.book.manga.config.MangaFooterSettingDialog
|
||||
import io.legato.kazusa.ui.book.manga.config.MangaScrollMode
|
||||
import io.legato.kazusa.ui.book.manga.config.MangaScrollModeDialog
|
||||
import io.legato.kazusa.ui.book.manga.entities.BaseMangaPage
|
||||
import io.legato.kazusa.ui.book.manga.entities.MangaPage
|
||||
import io.legato.kazusa.ui.book.manga.recyclerview.MangaAdapter
|
||||
@@ -77,11 +80,13 @@ import kotlin.math.ceil
|
||||
|
||||
class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewModel>(),
|
||||
ReadManga.Callback, ChangeBookSourceDialog.CallBack, MangaMenu.CallBack,
|
||||
MangaColorFilterDialog.Callback, ScrollTimer.ScrollCallback, MangaFooterSettingDialog.Callback {
|
||||
MangaColorFilterDialog.Callback, ScrollTimer.ScrollCallback, MangaFooterSettingDialog.Callback,
|
||||
MangaScrollModeDialog.Callback {
|
||||
|
||||
private val mLayoutManager by lazy {
|
||||
MangaLayoutManager(this)
|
||||
}
|
||||
|
||||
private val mAdapter: MangaAdapter by lazy {
|
||||
MangaAdapter(this)
|
||||
}
|
||||
@@ -99,6 +104,8 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
|
||||
|
||||
private var mMenu: Menu? = null
|
||||
|
||||
private var scrollMode: Int = MangaScrollMode.PAGE_LEFT_TO_RIGHT
|
||||
|
||||
private var mRecyclerViewPreloader: RecyclerViewPreloader<Any>? = null
|
||||
|
||||
private val networkChangedListener by lazy {
|
||||
@@ -192,7 +199,8 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
|
||||
enableMangaEInk(AppConfig.enableMangaEInk, AppConfig.mangaEInkThreshold)
|
||||
enableGray(AppConfig.enableMangaGray)
|
||||
}
|
||||
setHorizontalScroll(AppConfig.enableMangaHorizontalScroll)
|
||||
//setHorizontalScroll(AppConfig.enableMangaHorizontalScroll)
|
||||
setScrollMode(AppConfig.mangaScrollMode)
|
||||
binding.recyclerView.run {
|
||||
adapter = mAdapter
|
||||
itemAnimator = null
|
||||
@@ -201,6 +209,7 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
|
||||
setDisableClickScroll(AppConfig.disableClickScroll)
|
||||
setDisableMangaScale(AppConfig.disableMangaScale)
|
||||
setRecyclerViewPreloader(AppConfig.mangaPreDownloadNum)
|
||||
updateWebtoonSidePadding(AppConfig.webtoonSidePaddingDp)
|
||||
setPreScrollListener { _, _, _, position ->
|
||||
if (mAdapter.isNotEmpty()) {
|
||||
val item = mAdapter.getItem(position)
|
||||
@@ -431,10 +440,26 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
|
||||
binding.recyclerView.smoothScrollBy(10000, 10000, mLinearInterpolator, time)
|
||||
}
|
||||
|
||||
override fun scrollPage() {
|
||||
scrollToNext()
|
||||
override fun scrollPage(direction: Int) {
|
||||
if (scrollMode == MangaScrollMode.WEBTOON || scrollMode == MangaScrollMode.WEBTOON_WITH_GAP) {
|
||||
// 条漫平滑滚动距离
|
||||
val dy =
|
||||
(binding.recyclerView.height - binding.recyclerView.paddingTop - binding.recyclerView.paddingBottom) * direction
|
||||
binding.recyclerView.smoothScrollBy(0, dy)
|
||||
} else {
|
||||
// 分页按 position 翻页
|
||||
val layoutManager = binding.recyclerView.layoutManager as? LinearLayoutManager ?: return
|
||||
val currentPosition = layoutManager.findFirstVisibleItemPosition()
|
||||
val nextPosition = currentPosition + direction
|
||||
val itemCount = binding.recyclerView.adapter?.itemCount ?: return
|
||||
|
||||
if (nextPosition in 0 until itemCount) {
|
||||
binding.recyclerView.smoothScrollToPosition(nextPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override val oldBook: Book?
|
||||
get() = ReadManga.book
|
||||
|
||||
@@ -495,46 +520,11 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
|
||||
}
|
||||
}
|
||||
|
||||
R.id.menu_disable_manga_scale -> {
|
||||
item.isChecked = !item.isChecked
|
||||
AppConfig.disableMangaScale = item.isChecked
|
||||
setDisableMangaScale(item.isChecked)
|
||||
}
|
||||
|
||||
R.id.menu_disable_click_scroll -> {
|
||||
item.isChecked = !item.isChecked
|
||||
AppConfig.disableClickScroll = item.isChecked
|
||||
setDisableClickScroll(item.isChecked)
|
||||
}
|
||||
|
||||
R.id.menu_enable_horizontal_scroll -> {
|
||||
item.isChecked = !item.isChecked
|
||||
AppConfig.enableMangaHorizontalScroll = item.isChecked
|
||||
mMenu?.findItem(R.id.menu_disable_horizontal_animation)?.isVisible = item.isChecked
|
||||
setHorizontalScroll(item.isChecked)
|
||||
mAdapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
R.id.menu_manga_color_filter -> {
|
||||
binding.mangaMenu.runMenuOut()
|
||||
showDialogFragment(MangaColorFilterDialog())
|
||||
}
|
||||
|
||||
R.id.menu_enable_auto_scroll -> {
|
||||
item.isChecked = !item.isChecked
|
||||
mScrollTimer.isEnabled = item.isChecked
|
||||
|
||||
enableAutoScroll = item.isChecked
|
||||
enableAutoScrollPage = false
|
||||
mScrollTimer.isEnabledPage = false
|
||||
|
||||
if (enableAutoScroll) {
|
||||
mPagerSnapHelper.attachToRecyclerView(null)
|
||||
} else if (AppConfig.enableMangaHorizontalScroll) {
|
||||
mPagerSnapHelper.attachToRecyclerView(binding.recyclerView)
|
||||
}
|
||||
}
|
||||
|
||||
R.id.menu_hide_manga_title -> {
|
||||
item.isChecked = !item.isChecked
|
||||
AppConfig.hideMangaTitle = item.isChecked
|
||||
@@ -577,6 +567,40 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
|
||||
showDialogFragment(MangaColorFilterDialog())
|
||||
}
|
||||
|
||||
override fun showScrollModeDialog() {
|
||||
showDialogFragment(MangaScrollModeDialog().apply {
|
||||
initialScrollMode = AppConfig.mangaScrollMode
|
||||
callback = this@ReadMangaActivity
|
||||
})
|
||||
}
|
||||
|
||||
//漫画模式
|
||||
override fun onScrollModeChanged(mode: Int) {
|
||||
AppConfig.mangaScrollMode = mode
|
||||
setScrollMode(mode)
|
||||
updateWebtoonSidePadding(AppConfig.webtoonSidePaddingDp)
|
||||
setAutoReadEnabled(false)
|
||||
}
|
||||
|
||||
//点击滑动
|
||||
override fun onClickScrollDisabledChanged(enabled: Boolean) {
|
||||
AppConfig.disableClickScroll = enabled
|
||||
setDisableClickScroll(enabled)
|
||||
}
|
||||
|
||||
//双击缩放
|
||||
override fun onMangaScaleDisabledChanged(enabled: Boolean) {
|
||||
AppConfig.disableMangaScale = enabled
|
||||
setDisableMangaScale(enabled)
|
||||
}
|
||||
|
||||
//侧边留白
|
||||
override fun upSidePadding(paddingDp: Int) {
|
||||
AppConfig.webtoonSidePaddingDp = paddingDp
|
||||
updateWebtoonSidePadding(paddingDp)
|
||||
}
|
||||
|
||||
//墨水屏
|
||||
override fun updateEpaperMode(enabled: Boolean, threshold: Int) {
|
||||
AppConfig.enableMangaEInk = enabled
|
||||
AppConfig.enableMangaGray = false
|
||||
@@ -584,12 +608,28 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
|
||||
mAdapter.enableMangaEInk(enabled, threshold)
|
||||
}
|
||||
|
||||
//灰度
|
||||
override fun updateGrayMode(enabled: Boolean) {
|
||||
AppConfig.enableMangaGray = enabled
|
||||
AppConfig.enableMangaEInk = false
|
||||
mAdapter.enableGray(enabled)
|
||||
}
|
||||
|
||||
//自动翻页
|
||||
override fun onAutoPageToggle(enabled: Boolean) {
|
||||
setAutoReadEnabled(enabled)
|
||||
}
|
||||
|
||||
//自动翻页速度
|
||||
override fun onAutoPageSpeedChanged(speed: Int) {
|
||||
setAutoReadEnabled(false)
|
||||
AppConfig.mangaAutoPageSpeed = speed
|
||||
mScrollTimer.setSpeed(speed)
|
||||
setAutoReadEnabled(true)
|
||||
// if (enableAutoScrollPage) {
|
||||
// mScrollTimer.isEnabledPage = true
|
||||
// }
|
||||
}
|
||||
|
||||
override fun openBookInfoActivity() {
|
||||
ReadManga.book?.let {
|
||||
@@ -600,22 +640,27 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAutoPageToggle(enabled: Boolean) {
|
||||
mScrollTimer.isEnabledPage = enabled
|
||||
mScrollTimer.isEnabled = false
|
||||
|
||||
enableAutoScrollPage = enabled
|
||||
enableAutoScroll = false
|
||||
|
||||
mMenu?.findItem(R.id.menu_enable_auto_scroll)?.isChecked = false
|
||||
}
|
||||
|
||||
override fun onAutoPageSpeedChanged(speed: Int) {
|
||||
AppConfig.mangaAutoPageSpeed = speed
|
||||
mScrollTimer.setSpeed(speed)
|
||||
|
||||
if (enableAutoScrollPage) {
|
||||
mScrollTimer.isEnabledPage = true
|
||||
//1 自动翻页 2 自动滚动 0 关闭
|
||||
fun setAutoReadEnabled(enabled: Boolean) {
|
||||
val isWebtoonMode =
|
||||
scrollMode == MangaScrollMode.WEBTOON || scrollMode == MangaScrollMode.WEBTOON_WITH_GAP
|
||||
if (enabled) {
|
||||
if (isWebtoonMode) {
|
||||
mScrollTimer.isEnabled = true
|
||||
enableAutoScroll = true
|
||||
enableAutoScrollPage = false
|
||||
mScrollTimer.isEnabledPage = false
|
||||
} else {
|
||||
mScrollTimer.isEnabledPage = true
|
||||
enableAutoScrollPage = true
|
||||
enableAutoScroll = false
|
||||
mScrollTimer.isEnabled = false
|
||||
}
|
||||
} else {
|
||||
enableAutoScroll = false
|
||||
enableAutoScrollPage = false
|
||||
mScrollTimer.isEnabled = false
|
||||
mScrollTimer.isEnabledPage = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -648,6 +693,70 @@ class ReadMangaActivity : VMBaseActivity<ActivityMangaBinding, ReadMangaViewMode
|
||||
return super.dispatchKeyEvent(event)
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun setScrollMode(mode: Int) {
|
||||
scrollMode = mode
|
||||
|
||||
val isHorizontalPage =
|
||||
mode == MangaScrollMode.PAGE_LEFT_TO_RIGHT || mode == MangaScrollMode.PAGE_RIGHT_TO_LEFT
|
||||
|
||||
val isWebtoon = mode == MangaScrollMode.WEBTOON || mode == MangaScrollMode.WEBTOON_WITH_GAP
|
||||
|
||||
when (mode) {
|
||||
MangaScrollMode.PAGE_LEFT_TO_RIGHT -> {
|
||||
mLayoutManager.orientation = LinearLayoutManager.HORIZONTAL
|
||||
mLayoutManager.reverseLayout = false
|
||||
}
|
||||
|
||||
MangaScrollMode.PAGE_RIGHT_TO_LEFT -> {
|
||||
mLayoutManager.orientation = LinearLayoutManager.HORIZONTAL
|
||||
mLayoutManager.reverseLayout = true
|
||||
}
|
||||
|
||||
MangaScrollMode.PAGE_TOP_TO_BOTTOM,
|
||||
MangaScrollMode.WEBTOON,
|
||||
MangaScrollMode.WEBTOON_WITH_GAP -> {
|
||||
mLayoutManager.orientation = LinearLayoutManager.VERTICAL
|
||||
mLayoutManager.reverseLayout = false
|
||||
}
|
||||
}
|
||||
|
||||
mAdapter.isHorizontal = isHorizontalPage || mode == MangaScrollMode.PAGE_TOP_TO_BOTTOM
|
||||
|
||||
if (!isWebtoon) {
|
||||
mPagerSnapHelper.attachToRecyclerView(binding.recyclerView)
|
||||
} else {
|
||||
mPagerSnapHelper.attachToRecyclerView(null)
|
||||
}
|
||||
|
||||
mAdapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
/**
|
||||
* 调整条漫侧边留白设置
|
||||
* @param paddingDp 侧边留白距离,单位dp,默认16dp
|
||||
*/
|
||||
private fun updateWebtoonSidePadding(paddingDp: Int) {
|
||||
if (scrollMode != MangaScrollMode.WEBTOON && scrollMode != MangaScrollMode.WEBTOON_WITH_GAP) {
|
||||
// 非条漫模式,取消留白
|
||||
binding.recyclerView.setPadding(0, 0, 0, 0)
|
||||
binding.recyclerView.clipToPadding = true
|
||||
return
|
||||
}
|
||||
|
||||
binding.recyclerView.doOnLayout { recyclerView ->
|
||||
val width = recyclerView.width
|
||||
if (paddingDp == 0) {
|
||||
recyclerView.setPadding(0, 0, 0, 0)
|
||||
} else if (width > 0) {
|
||||
val paddingPx = (width * paddingDp / 100).toInt()
|
||||
recyclerView.setPadding(paddingPx, 0, paddingPx, 0)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private fun setRecyclerViewPreloader(maxPreload: Int) {
|
||||
if (mRecyclerViewPreloader != null) {
|
||||
binding.recyclerView.removeOnScrollListener(mRecyclerViewPreloader!!)
|
||||