Taken from upstream with some tweaks: *Removed always show at start option *"Left" and "Right" in reader overlay now say "Next" or "Previous" in LRNav *Changing Inversion also shows navigation overlay *Show new user the nav overlay the first time for both paged and webview (since j2k has auto webtoon) Also unrelated, changed "page transitions" to "animate page transitions" Co-Authored-By: arkon <4098258+arkon@users.noreply.github.com>pull/7308/head
parent
380b609847
commit
999247d7dd
@ -0,0 +1,130 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.reader
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewPropertyAnimator
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
class ReaderNavigationOverlayView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) {
|
||||||
|
|
||||||
|
private var viewPropertyAnimator: ViewPropertyAnimator? = null
|
||||||
|
|
||||||
|
private var navigation: ViewerNavigation? = null
|
||||||
|
|
||||||
|
var isLTR = true
|
||||||
|
|
||||||
|
fun setNavigation(navigation: ViewerNavigation, showOnStart: Boolean) {
|
||||||
|
if (!showOnStart && (this.navigation == null || this.navigation === navigation) && !forceShowOverlay) {
|
||||||
|
if (this.navigation == null) {
|
||||||
|
this.navigation = navigation
|
||||||
|
isVisible = false
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
forceShowOverlay = false
|
||||||
|
this.navigation = navigation
|
||||||
|
invalidate()
|
||||||
|
|
||||||
|
if (isVisible) return
|
||||||
|
|
||||||
|
viewPropertyAnimator = animate()
|
||||||
|
.alpha(1f)
|
||||||
|
.setDuration(FADE_DURATION)
|
||||||
|
.withStartAction {
|
||||||
|
isVisible = true
|
||||||
|
}
|
||||||
|
.withEndAction {
|
||||||
|
viewPropertyAnimator = null
|
||||||
|
}
|
||||||
|
viewPropertyAnimator?.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val regionPaint = Paint()
|
||||||
|
|
||||||
|
private val textPaint = Paint().apply {
|
||||||
|
textAlign = Paint.Align.CENTER
|
||||||
|
color = Color.WHITE
|
||||||
|
textSize = 64f
|
||||||
|
}
|
||||||
|
|
||||||
|
private val textBorderPaint = Paint().apply {
|
||||||
|
textAlign = Paint.Align.CENTER
|
||||||
|
color = Color.BLACK
|
||||||
|
textSize = 64f
|
||||||
|
style = Paint.Style.STROKE
|
||||||
|
strokeWidth = 8f
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas?) {
|
||||||
|
if (navigation == null) return
|
||||||
|
|
||||||
|
navigation?.regions?.forEach {
|
||||||
|
val region = it.invert(navigation!!.invertMode)
|
||||||
|
val rect = region.rectF
|
||||||
|
|
||||||
|
canvas?.save()
|
||||||
|
|
||||||
|
// Scale rect from 1f,1f to screen width and height
|
||||||
|
canvas?.scale(width.toFloat(), height.toFloat())
|
||||||
|
val directionalRegion = region.type.directionalRegion(isLTR)
|
||||||
|
regionPaint.color = ContextCompat.getColor(context, directionalRegion.colorRes)
|
||||||
|
canvas?.drawRect(rect, regionPaint)
|
||||||
|
|
||||||
|
canvas?.restore()
|
||||||
|
// Don't want scale anymore because it messes with drawText
|
||||||
|
canvas?.save()
|
||||||
|
|
||||||
|
// Translate origin to rect start (left, top)
|
||||||
|
canvas?.translate((width * rect.left), (height * rect.top))
|
||||||
|
|
||||||
|
// Calculate center of rect width on screen
|
||||||
|
val x = width * (abs(rect.left - rect.right) / 2)
|
||||||
|
|
||||||
|
// Calculate center of rect height on screen
|
||||||
|
val y = height * (abs(rect.top - rect.bottom) / 2)
|
||||||
|
|
||||||
|
canvas?.drawText(context.getString(directionalRegion.nameRes), x, y, textBorderPaint)
|
||||||
|
canvas?.drawText(context.getString(directionalRegion.nameRes), x, y, textPaint)
|
||||||
|
|
||||||
|
canvas?.restore()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun performClick(): Boolean {
|
||||||
|
super.performClick()
|
||||||
|
|
||||||
|
if (viewPropertyAnimator == null && isVisible) {
|
||||||
|
viewPropertyAnimator = animate()
|
||||||
|
.alpha(0f)
|
||||||
|
.setDuration(FADE_DURATION)
|
||||||
|
.withEndAction {
|
||||||
|
isVisible = false
|
||||||
|
viewPropertyAnimator = null
|
||||||
|
}
|
||||||
|
viewPropertyAnimator?.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTouchEvent(event: MotionEvent?): Boolean {
|
||||||
|
// Hide overlay if user start tapping or swiping
|
||||||
|
performClick()
|
||||||
|
return super.onTouchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var forceShowOverlay = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val FADE_DURATION = 1000L
|
@ -0,0 +1,72 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.reader.viewer
|
||||||
|
|
||||||
|
import android.graphics.PointF
|
||||||
|
import android.graphics.RectF
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
|
||||||
|
abstract class ViewerNavigation {
|
||||||
|
|
||||||
|
sealed class NavigationRegion(@StringRes val nameRes: Int, val colorRes: Int) {
|
||||||
|
object MENU : NavigationRegion(R.string.menu, R.color.navigation_menu)
|
||||||
|
object PREV : NavigationRegion(R.string.previous, R.color.navigation_prev)
|
||||||
|
object NEXT : NavigationRegion(R.string.next, R.color.navigation_next)
|
||||||
|
object LEFT : NavigationRegion(R.string.left, R.color.navigation_left)
|
||||||
|
object RIGHT : NavigationRegion(R.string.right, R.color.navigation_right)
|
||||||
|
|
||||||
|
fun directionalRegion(LTR: Boolean): NavigationRegion {
|
||||||
|
return if (this === LEFT || this === RIGHT) {
|
||||||
|
if (if (LTR) this === LEFT else this === RIGHT) NEXT else PREV
|
||||||
|
}
|
||||||
|
else this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class Region(
|
||||||
|
val rectF: RectF,
|
||||||
|
val type: NavigationRegion
|
||||||
|
) {
|
||||||
|
fun invert(invertMode: TappingInvertMode): Region {
|
||||||
|
if (invertMode == TappingInvertMode.NONE) return this
|
||||||
|
return this.copy(
|
||||||
|
rectF = this.rectF.invert(invertMode)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var constantMenuRegion: RectF = RectF(0f, 0f, 1f, 0.05f)
|
||||||
|
|
||||||
|
abstract var regions: List<Region>
|
||||||
|
|
||||||
|
var invertMode: TappingInvertMode = TappingInvertMode.NONE
|
||||||
|
|
||||||
|
fun getAction(pos: PointF): NavigationRegion {
|
||||||
|
val x = pos.x
|
||||||
|
val y = pos.y
|
||||||
|
val region = regions.map { it.invert(invertMode) }
|
||||||
|
.find { it.rectF.contains(x, y) }
|
||||||
|
return when {
|
||||||
|
region != null -> region.type
|
||||||
|
constantMenuRegion.contains(x, y) -> NavigationRegion.MENU
|
||||||
|
else -> NavigationRegion.MENU
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class TappingInvertMode(val shouldInvertHorizontal: Boolean = false, val shouldInvertVertical: Boolean = false) {
|
||||||
|
NONE,
|
||||||
|
HORIZONTAL(shouldInvertHorizontal = true),
|
||||||
|
VERTICAL(shouldInvertVertical = true),
|
||||||
|
BOTH(shouldInvertHorizontal = true, shouldInvertVertical = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RectF.invert(invertMode: ViewerNavigation.TappingInvertMode): RectF {
|
||||||
|
val horizontal = invertMode.shouldInvertHorizontal
|
||||||
|
val vertical = invertMode.shouldInvertVertical
|
||||||
|
return when {
|
||||||
|
horizontal && vertical -> RectF(1f - this.right, 1f - this.bottom, 1f - this.left, 1f - this.top)
|
||||||
|
vertical -> RectF(this.left, 1f - this.bottom, this.right, 1f - this.top)
|
||||||
|
horizontal -> RectF(1f - this.right, this.top, 1f - this.left, this.bottom)
|
||||||
|
else -> this
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.reader.viewer.navigation
|
||||||
|
|
||||||
|
import android.graphics.RectF
|
||||||
|
import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visualization of default state without any inversion
|
||||||
|
* +---+---+---+
|
||||||
|
* | N | N | N | P: Previous
|
||||||
|
* +---+---+---+
|
||||||
|
* | N | M | N | M: Menu
|
||||||
|
* +---+---+---+
|
||||||
|
* | N | P | N | N: Next
|
||||||
|
* +---+---+---+
|
||||||
|
*/
|
||||||
|
class EdgeNavigation : ViewerNavigation() {
|
||||||
|
|
||||||
|
override var regions: List<Region> = listOf(
|
||||||
|
Region(
|
||||||
|
rectF = RectF(0f, 0f, 0.33f, 1f),
|
||||||
|
type = NavigationRegion.NEXT
|
||||||
|
),
|
||||||
|
Region(
|
||||||
|
rectF = RectF(0.33f, 0.66f, 0.66f, 1f),
|
||||||
|
type = NavigationRegion.PREV
|
||||||
|
),
|
||||||
|
Region(
|
||||||
|
rectF = RectF(0.66f, 0f, 1f, 1f),
|
||||||
|
type = NavigationRegion.NEXT
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.reader.viewer.navigation
|
||||||
|
|
||||||
|
import android.graphics.RectF
|
||||||
|
import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visualization of default state without any inversion
|
||||||
|
* +---+---+---+
|
||||||
|
* | M | M | M | P: Previous
|
||||||
|
* +---+---+---+
|
||||||
|
* | P | N | N | M: Menu
|
||||||
|
* +---+---+---+
|
||||||
|
* | P | N | N | N: Next
|
||||||
|
* +---+---+---+
|
||||||
|
*/
|
||||||
|
class KindlishNavigation : ViewerNavigation() {
|
||||||
|
|
||||||
|
override var regions: List<Region> = listOf(
|
||||||
|
Region(
|
||||||
|
rectF = RectF(0.33f, 0.33f, 1f, 1f),
|
||||||
|
type = NavigationRegion.NEXT
|
||||||
|
),
|
||||||
|
Region(
|
||||||
|
rectF = RectF(0f, 0.33f, 0.33f, 1f),
|
||||||
|
type = NavigationRegion.PREV
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.reader.viewer.navigation
|
||||||
|
|
||||||
|
import android.graphics.RectF
|
||||||
|
import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visualization of default state without any inversion
|
||||||
|
* +---+---+---+
|
||||||
|
* | P | P | P | P: Previous
|
||||||
|
* +---+---+---+
|
||||||
|
* | P | M | N | M: Menu
|
||||||
|
* +---+---+---+
|
||||||
|
* | N | N | N | N: Next
|
||||||
|
* +---+---+---+
|
||||||
|
*/
|
||||||
|
open class LNavigation : ViewerNavigation() {
|
||||||
|
|
||||||
|
override var regions: List<Region> = listOf(
|
||||||
|
Region(
|
||||||
|
rectF = RectF(0f, 0.33f, 0.33f, 0.66f),
|
||||||
|
type = NavigationRegion.PREV
|
||||||
|
),
|
||||||
|
Region(
|
||||||
|
rectF = RectF(0f, 0f, 1f, 0.33f),
|
||||||
|
type = NavigationRegion.PREV
|
||||||
|
),
|
||||||
|
Region(
|
||||||
|
rectF = RectF(0.66f, 0.33f, 1f, 0.66f),
|
||||||
|
type = NavigationRegion.NEXT
|
||||||
|
),
|
||||||
|
Region(
|
||||||
|
rectF = RectF(0f, 0.66f, 1f, 1f),
|
||||||
|
type = NavigationRegion.NEXT
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.reader.viewer.navigation
|
||||||
|
|
||||||
|
import android.graphics.RectF
|
||||||
|
import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visualization of default state without any inversion
|
||||||
|
* +---+---+---+
|
||||||
|
* | N | M | P | P: Move Right
|
||||||
|
* +---+---+---+
|
||||||
|
* | N | M | P | M: Menu
|
||||||
|
* +---+---+---+
|
||||||
|
* | N | M | P | N: Move Left
|
||||||
|
* +---+---+---+
|
||||||
|
*/
|
||||||
|
class RightAndLeftNavigation : ViewerNavigation() {
|
||||||
|
|
||||||
|
override var regions: List<Region> = listOf(
|
||||||
|
Region(
|
||||||
|
rectF = RectF(0f, 0f, 0.33f, 1f),
|
||||||
|
type = NavigationRegion.LEFT
|
||||||
|
),
|
||||||
|
Region(
|
||||||
|
rectF = RectF(0.66f, 0f, 1f, 1f),
|
||||||
|
type = NavigationRegion.RIGHT
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in new issue