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