This commit is contained in:
Horis 2023-12-24 23:15:14 +08:00
parent 5315f6bcef
commit d96de11b12
3 changed files with 163 additions and 33 deletions

View File

@ -6,7 +6,6 @@ import android.content.Intent
import android.content.res.Configuration
import android.net.Uri
import android.os.Bundle
import android.os.SystemClock
import android.view.Gravity
import android.view.InputDevice
import android.view.KeyEvent
@ -93,11 +92,11 @@ import io.legado.app.ui.widget.PopupAction
import io.legado.app.ui.widget.dialog.PhotoDialog
import io.legado.app.ui.widget.dialog.TextDialog
import io.legado.app.utils.ACache
import io.legado.app.utils.Debounce
import io.legado.app.utils.LogUtils
import io.legado.app.utils.StartActivityContract
import io.legado.app.utils.SyncedRenderer
import io.legado.app.utils.applyOpenTint
import io.legado.app.utils.buildMainHandler
import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.getPrefString
import io.legado.app.utils.hexString
@ -118,7 +117,6 @@ import io.legado.app.utils.toastOnUi
import io.legado.app.utils.visible
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Job
import kotlinx.coroutines.Runnable
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@ -217,15 +215,13 @@ class ReadBookActivity : BaseReadBookActivity(),
override val pageFactory: TextPageFactory get() = binding.readView.pageFactory
override val headerHeight: Int get() = binding.readView.curPage.headerHeight
private val menuLayoutIsVisible get() = bottomDialog > 0 || binding.readMenu.isVisible
private val nextPageRunnable by lazy { Runnable { keyPage(PageDirection.NEXT) } }
private val prevPageRunnable by lazy { Runnable { keyPage(PageDirection.PREV) } }
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 var reloadContent = false
private val autoPageRenderer by lazy { SyncedRenderer { doAutoPage(it) } }
private var autoPageScrollOffset = 0.0
private val handler by lazy { buildMainHandler() }
private var lastKeyPageTime = 0L
//恢复跳转前进度对话框的交互结果
private var confirmRestoreProcess: Boolean? = null
@ -624,38 +620,39 @@ class ReadBookActivity : BaseReadBookActivity(),
if (menuLayoutIsVisible) {
return super.onKeyDown(keyCode, event)
}
val longPress = event.repeatCount > 0
when {
isPrevKey(keyCode) -> {
handleKeyPage(PageDirection.PREV)
handleKeyPage(PageDirection.PREV, longPress)
return true
}
isNextKey(keyCode) -> {
handleKeyPage(PageDirection.NEXT)
handleKeyPage(PageDirection.NEXT, longPress)
return true
}
}
when (keyCode) {
KeyEvent.KEYCODE_VOLUME_UP -> if (volumeKeyPage(PageDirection.PREV)) {
KeyEvent.KEYCODE_VOLUME_UP -> if (volumeKeyPage(PageDirection.PREV, longPress)) {
return true
}
KeyEvent.KEYCODE_VOLUME_DOWN -> if (volumeKeyPage(PageDirection.NEXT)) {
KeyEvent.KEYCODE_VOLUME_DOWN -> if (volumeKeyPage(PageDirection.NEXT, longPress)) {
return true
}
KeyEvent.KEYCODE_PAGE_UP -> {
handleKeyPage(PageDirection.PREV)
handleKeyPage(PageDirection.PREV, longPress)
return true
}
KeyEvent.KEYCODE_PAGE_DOWN -> {
handleKeyPage(PageDirection.NEXT)
handleKeyPage(PageDirection.NEXT, longPress)
return true
}
KeyEvent.KEYCODE_SPACE -> {
handleKeyPage(PageDirection.NEXT)
handleKeyPage(PageDirection.NEXT, longPress)
return true
}
}
@ -669,7 +666,7 @@ class ReadBookActivity : BaseReadBookActivity(),
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
when (keyCode) {
KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN -> {
if (volumeKeyPage(PageDirection.NONE)) {
if (volumeKeyPage(PageDirection.NONE, false)) {
return true
}
}
@ -852,49 +849,52 @@ class ReadBookActivity : BaseReadBookActivity(),
if (menuLayoutIsVisible || !AppConfig.mouseWheelPage) {
return
}
keyPageDebounce(direction, 200L, true)
keyPageDebounce(direction, mouseWheel = true, longPress = false)
}
/**
* 音量键翻页
*/
private fun volumeKeyPage(direction: PageDirection): Boolean {
private fun volumeKeyPage(direction: PageDirection, longPress: Boolean): Boolean {
if (!AppConfig.volumeKeyPage) {
return false
}
if (!AppConfig.volumeKeyPageOnPlay && BaseReadAloudService.isPlay()) {
return false
}
handleKeyPage(direction)
handleKeyPage(direction, longPress)
return true
}
private fun handleKeyPage(direction: PageDirection) {
private fun handleKeyPage(direction: PageDirection, longPress: Boolean) {
if (AppConfig.keyPageOnLongPress || direction == PageDirection.NONE) {
keyPage(direction)
} else {
keyPageDebounce(direction)
keyPageDebounce(direction, longPress = longPress)
}
}
private fun keyPageDebounce(
direction: PageDirection,
delay: Long = 100L,
mouseWheel: Boolean = false
mouseWheel: Boolean = false,
longPress: Boolean
) {
handler.removeCallbacks(nextPageRunnable)
handler.removeCallbacks(prevPageRunnable)
if (!mouseWheel) {
val keyPageTime = SystemClock.uptimeMillis()
val elapsed = keyPageTime - lastKeyPageTime
lastKeyPageTime = keyPageTime
if (elapsed < 100) {
return
}
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 -> handler.postDelayed(nextPageRunnable, delay)
PageDirection.PREV -> handler.postDelayed(prevPageRunnable, delay)
PageDirection.NEXT -> nextPageDebounce.invoke()
PageDirection.PREV -> prevPageDebounce.invoke()
else -> {}
}
}

View File

@ -0,0 +1,121 @@
package io.legado.app.utils
import android.os.SystemClock
import kotlin.math.max
@Suppress("MemberVisibilityCanBePrivate")
open class Debounce<T>(
var wait: Long = 0L,
var maxWait: Long = -1L,
var leading: Boolean = false,
var trailing: Boolean = true,
private val func: () -> T
) {
companion object {
private val handler by lazy { buildMainHandler() }
}
private var lastCallTime = -1L
private var lastInvokeTime = 0L
private val maxing get() = maxWait != -1L
private var result: T? = null
private var hasTimer = false
private val timerExpiredRunnable = Runnable {
timerExpired()
}
init {
maxWait = if (maxing) max(maxWait, wait) else maxWait
}
private fun invokeFunc(time: Long): T {
lastInvokeTime = time
return func.invoke().also { result = it }
}
private fun startTimer(wait: Long) {
hasTimer = true
handler.postDelayed(timerExpiredRunnable, wait)
}
private fun cancelTimer() {
handler.removeCallbacks(timerExpiredRunnable)
}
private fun leadingEdge(time: Long): T? {
lastInvokeTime = time
startTimer(wait)
return if (leading) invokeFunc(time) else result
}
private fun trailingEdge(time: Long): T? {
hasTimer = false
return if (trailing) invokeFunc(time) else result
}
private fun remainingWait(time: Long): Long {
val timeSinceLastCall = time - lastCallTime
val timeSinceLastInvoke = time - lastInvokeTime
val timeWaiting = wait - timeSinceLastCall
return if (maxing) timeWaiting.coerceAtMost(maxWait - timeSinceLastInvoke) else timeWaiting
}
private fun shouldInvoke(time: Long): Boolean {
val timeSinceLastCall = time - lastCallTime
val timeSinceLastInvoke = time - lastInvokeTime
return lastCallTime == -1L
|| timeSinceLastCall >= wait
|| timeSinceLastCall < 0
|| maxing && timeSinceLastInvoke >= maxWait
}
private fun timerExpired() {
val time = SystemClock.uptimeMillis()
if (shouldInvoke(time)) {
trailingEdge(time)
} else {
startTimer(remainingWait(time))
}
}
fun cancel() {
if (hasTimer) {
cancelTimer()
}
lastInvokeTime = 0
lastCallTime = -1L
hasTimer = false
}
fun flush(): T? {
return if (hasTimer) trailingEdge(SystemClock.uptimeMillis()) else result
}
fun pending(): Boolean = hasTimer
operator fun invoke(): T? {
val time = SystemClock.uptimeMillis()
val isInvoking = shouldInvoke(time)
lastCallTime = time
if (isInvoking) {
if (!hasTimer) {
return leadingEdge(lastCallTime)
}
if (maxing) {
startTimer(wait)
return invokeFunc(lastCallTime)
}
}
if (!hasTimer) {
startTimer(wait)
}
return result
}
}

View File

@ -0,0 +1,9 @@
package io.legado.app.utils
@Suppress("unused")
class Throttle<T>(
wait: Long = 0L,
leading: Boolean = true,
trailing: Boolean = true,
func: () -> T
) : Debounce<T>(wait, wait, leading, trailing, func)