mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-29 19:53:17 -05:00
Add navigation layout overlay (#4683)
* Add navigation layout overlay * Minor clean up Destroy animator when done not on start Move and change pref title Add summary
This commit is contained in:
parent
d912a42249
commit
5a7f2684b3
12 changed files with 195 additions and 0 deletions
|
@ -79,6 +79,10 @@ object PreferenceKeys {
|
||||||
|
|
||||||
const val navigationModeWebtoon = "reader_navigation_mode_webtoon"
|
const val navigationModeWebtoon = "reader_navigation_mode_webtoon"
|
||||||
|
|
||||||
|
const val showNavigationOverlayNewUser = "reader_navigation_overlay_new_user"
|
||||||
|
|
||||||
|
const val showNavigationOverlayOnStart = "reader_navigation_overlay_on_start"
|
||||||
|
|
||||||
const val webtoonSidePadding = "webtoon_side_padding"
|
const val webtoonSidePadding = "webtoon_side_padding"
|
||||||
|
|
||||||
const val portraitColumns = "pref_library_columns_portrait_key"
|
const val portraitColumns = "pref_library_columns_portrait_key"
|
||||||
|
|
|
@ -149,6 +149,10 @@ class PreferencesHelper(val context: Context) {
|
||||||
|
|
||||||
fun navigationModeWebtoon() = flowPrefs.getInt(Keys.navigationModeWebtoon, 0)
|
fun navigationModeWebtoon() = flowPrefs.getInt(Keys.navigationModeWebtoon, 0)
|
||||||
|
|
||||||
|
fun showNavigationOverlayNewUser() = flowPrefs.getBoolean(Keys.showNavigationOverlayNewUser, true)
|
||||||
|
|
||||||
|
fun showNavigationOverlayOnStart() = flowPrefs.getBoolean(Keys.showNavigationOverlayOnStart, false)
|
||||||
|
|
||||||
fun portraitColumns() = flowPrefs.getInt(Keys.portraitColumns, 0)
|
fun portraitColumns() = flowPrefs.getInt(Keys.portraitColumns, 0)
|
||||||
|
|
||||||
fun landscapeColumns() = flowPrefs.getInt(Keys.landscapeColumns, 0)
|
fun landscapeColumns() = flowPrefs.getInt(Keys.landscapeColumns, 0)
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
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.R
|
||||||
|
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
|
||||||
|
|
||||||
|
fun setNavigation(navigation: ViewerNavigation, showOnStart: Boolean) {
|
||||||
|
if (!showOnStart && this.navigation == null) {
|
||||||
|
this.navigation = navigation
|
||||||
|
isVisible = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.navigation = navigation
|
||||||
|
invalidate()
|
||||||
|
|
||||||
|
if (isVisible) return
|
||||||
|
|
||||||
|
viewPropertyAnimator = animate()
|
||||||
|
.alpha(1f)
|
||||||
|
.setDuration(1000L)
|
||||||
|
.withStartAction {
|
||||||
|
isVisible = true
|
||||||
|
}
|
||||||
|
.withEndAction {
|
||||||
|
viewPropertyAnimator = null
|
||||||
|
}
|
||||||
|
viewPropertyAnimator?.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
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 { region ->
|
||||||
|
|
||||||
|
val paint = paintForRegion(region.type)
|
||||||
|
|
||||||
|
val rect = region.rectF
|
||||||
|
|
||||||
|
canvas?.save()
|
||||||
|
|
||||||
|
// Scale rect from 1f,1f to screen width and height
|
||||||
|
canvas?.scale(width.toFloat(), height.toFloat())
|
||||||
|
canvas?.drawRect(rect, paint)
|
||||||
|
|
||||||
|
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(region.type.name, x, y, textBorderPaint)
|
||||||
|
canvas?.drawText(region.type.name, x, y, textPaint)
|
||||||
|
|
||||||
|
canvas?.restore()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun paintForRegion(type: ViewerNavigation.NavigationRegion): Paint {
|
||||||
|
return Paint().apply {
|
||||||
|
when (type) {
|
||||||
|
ViewerNavigation.NavigationRegion.NEXT -> {
|
||||||
|
color = ContextCompat.getColor(context, R.color.navigation_next)
|
||||||
|
}
|
||||||
|
ViewerNavigation.NavigationRegion.PREV -> {
|
||||||
|
color = ContextCompat.getColor(context, R.color.navigation_prev)
|
||||||
|
}
|
||||||
|
ViewerNavigation.NavigationRegion.MENU -> {
|
||||||
|
color = ContextCompat.getColor(context, R.color.navigation_menu)
|
||||||
|
}
|
||||||
|
ViewerNavigation.NavigationRegion.RIGHT -> {
|
||||||
|
color = ContextCompat.getColor(context, R.color.navigation_right)
|
||||||
|
}
|
||||||
|
ViewerNavigation.NavigationRegion.LEFT -> {
|
||||||
|
color = ContextCompat.getColor(context, R.color.navigation_left)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun performClick(): Boolean {
|
||||||
|
super.performClick()
|
||||||
|
|
||||||
|
if (viewPropertyAnimator == null && isVisible) {
|
||||||
|
viewPropertyAnimator = animate()
|
||||||
|
.alpha(0f)
|
||||||
|
.setDuration(1000L)
|
||||||
|
.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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,8 @@ abstract class ViewerConfig(preferences: PreferencesHelper, private val scope: C
|
||||||
|
|
||||||
var imagePropertyChangedListener: (() -> Unit)? = null
|
var imagePropertyChangedListener: (() -> Unit)? = null
|
||||||
|
|
||||||
|
var navigationModeChangedListener: (() -> Unit)? = null
|
||||||
|
|
||||||
var tappingEnabled = true
|
var tappingEnabled = true
|
||||||
var tappingInverted = TappingInvertMode.NONE
|
var tappingInverted = TappingInvertMode.NONE
|
||||||
var longTapEnabled = true
|
var longTapEnabled = true
|
||||||
|
@ -27,6 +29,10 @@ abstract class ViewerConfig(preferences: PreferencesHelper, private val scope: C
|
||||||
var navigationMode = 0
|
var navigationMode = 0
|
||||||
protected set
|
protected set
|
||||||
|
|
||||||
|
var forceNavigationOverlay = false
|
||||||
|
|
||||||
|
var navigationOverlayOnStart = false
|
||||||
|
|
||||||
var dualPageSplit = false
|
var dualPageSplit = false
|
||||||
protected set
|
protected set
|
||||||
|
|
||||||
|
@ -60,6 +66,14 @@ abstract class ViewerConfig(preferences: PreferencesHelper, private val scope: C
|
||||||
|
|
||||||
preferences.alwaysShowChapterTransition()
|
preferences.alwaysShowChapterTransition()
|
||||||
.register({ alwaysShowChapterTransition = it })
|
.register({ alwaysShowChapterTransition = it })
|
||||||
|
|
||||||
|
forceNavigationOverlay = preferences.showNavigationOverlayNewUser().get()
|
||||||
|
if (forceNavigationOverlay) {
|
||||||
|
preferences.showNavigationOverlayNewUser().set(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
preferences.showNavigationOverlayOnStart()
|
||||||
|
.register({ navigationOverlayOnStart = it })
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun defaultNavigation(): ViewerNavigation
|
protected abstract fun defaultNavigation(): ViewerNavigation
|
||||||
|
|
|
@ -90,6 +90,7 @@ class PagerConfig(
|
||||||
4 -> RightAndLeftNavigation()
|
4 -> RightAndLeftNavigation()
|
||||||
else -> defaultNavigation()
|
else -> defaultNavigation()
|
||||||
}
|
}
|
||||||
|
navigationModeChangedListener?.invoke()
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class ZoomType {
|
enum class ZoomType {
|
||||||
|
|
|
@ -119,6 +119,11 @@ abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer {
|
||||||
config.imagePropertyChangedListener = {
|
config.imagePropertyChangedListener = {
|
||||||
refreshAdapter()
|
refreshAdapter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.navigationModeChangedListener = {
|
||||||
|
val showOnStart = config.navigationOverlayOnStart || config.forceNavigationOverlay
|
||||||
|
activity.binding.navigationOverlay.setNavigation(config.navigator, showOnStart)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun destroy() {
|
override fun destroy() {
|
||||||
|
|
|
@ -63,5 +63,6 @@ class WebtoonConfig(
|
||||||
4 -> RightAndLeftNavigation()
|
4 -> RightAndLeftNavigation()
|
||||||
else -> defaultNavigation()
|
else -> defaultNavigation()
|
||||||
}
|
}
|
||||||
|
navigationModeChangedListener?.invoke()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,6 +136,11 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
|
||||||
refreshAdapter()
|
refreshAdapter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config.navigationModeChangedListener = {
|
||||||
|
val showOnStart = config.navigationOverlayOnStart || config.forceNavigationOverlay
|
||||||
|
activity.binding.navigationOverlay.setNavigation(config.navigator, showOnStart)
|
||||||
|
}
|
||||||
|
|
||||||
frame.layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)
|
frame.layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)
|
||||||
frame.addView(recycler)
|
frame.addView(recycler)
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,12 @@ class SettingsReaderController : SettingsController() {
|
||||||
summaryRes = R.string.pref_show_reading_mode_summary
|
summaryRes = R.string.pref_show_reading_mode_summary
|
||||||
defaultValue = true
|
defaultValue = true
|
||||||
}
|
}
|
||||||
|
switchPreference {
|
||||||
|
key = Keys.showNavigationOverlayOnStart
|
||||||
|
titleRes = R.string.pref_show_navigation_mode
|
||||||
|
summaryRes = R.string.pref_show_navigation_mode_summary
|
||||||
|
defaultValue = false
|
||||||
|
}
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
switchPreference {
|
switchPreference {
|
||||||
key = Keys.trueColor
|
key = Keys.trueColor
|
||||||
|
|
|
@ -43,6 +43,12 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<eu.kanade.tachiyomi.ui.reader.ReaderNavigationOverlayView
|
||||||
|
android:id="@+id/navigation_overlay"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/reader_menu"
|
android:id="@+id/reader_menu"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -76,4 +76,11 @@
|
||||||
|
|
||||||
<color name="green">#47a84a</color>
|
<color name="green">#47a84a</color>
|
||||||
|
|
||||||
|
<!-- Navigation overlay colors -->
|
||||||
|
<color name="navigation_next">#CB84E296</color>
|
||||||
|
<color name="navigation_prev">#CCFF7733</color>
|
||||||
|
<color name="navigation_menu">#CC95818D</color>
|
||||||
|
<color name="navigation_right">#CCA6CFD5</color>
|
||||||
|
<color name="navigation_left">#CC7D1128</color>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -252,6 +252,8 @@
|
||||||
|
|
||||||
<!-- Reader section -->
|
<!-- Reader section -->
|
||||||
<string name="pref_fullscreen">Fullscreen</string>
|
<string name="pref_fullscreen">Fullscreen</string>
|
||||||
|
<string name="pref_show_navigation_mode">Show navigation layout overlay</string>
|
||||||
|
<string name="pref_show_navigation_mode_summary">Show overlay when reader is opened</string>
|
||||||
<string name="pref_dual_page_split">Dual page split (ALPHA)</string>
|
<string name="pref_dual_page_split">Dual page split (ALPHA)</string>
|
||||||
<string name="pref_dual_page_invert">Invert dual page split placement</string>
|
<string name="pref_dual_page_invert">Invert dual page split placement</string>
|
||||||
<string name="pref_dual_page_invert_summary">If the placement of the dual page split doesn\'t match reading direction</string>
|
<string name="pref_dual_page_invert_summary">If the placement of the dual page split doesn\'t match reading direction</string>
|
||||||
|
|
Loading…
Reference in a new issue