mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-03 23:58:55 +01: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:
		@@ -79,6 +79,10 @@ object PreferenceKeys {
 | 
			
		||||
 | 
			
		||||
    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 portraitColumns = "pref_library_columns_portrait_key"
 | 
			
		||||
 
 | 
			
		||||
@@ -149,6 +149,10 @@ class PreferencesHelper(val context: Context) {
 | 
			
		||||
 | 
			
		||||
    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 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 navigationModeChangedListener: (() -> Unit)? = null
 | 
			
		||||
 | 
			
		||||
    var tappingEnabled = true
 | 
			
		||||
    var tappingInverted = TappingInvertMode.NONE
 | 
			
		||||
    var longTapEnabled = true
 | 
			
		||||
@@ -27,6 +29,10 @@ abstract class ViewerConfig(preferences: PreferencesHelper, private val scope: C
 | 
			
		||||
    var navigationMode = 0
 | 
			
		||||
        protected set
 | 
			
		||||
 | 
			
		||||
    var forceNavigationOverlay = false
 | 
			
		||||
 | 
			
		||||
    var navigationOverlayOnStart = false
 | 
			
		||||
 | 
			
		||||
    var dualPageSplit = false
 | 
			
		||||
        protected set
 | 
			
		||||
 | 
			
		||||
@@ -60,6 +66,14 @@ abstract class ViewerConfig(preferences: PreferencesHelper, private val scope: C
 | 
			
		||||
 | 
			
		||||
        preferences.alwaysShowChapterTransition()
 | 
			
		||||
            .register({ alwaysShowChapterTransition = it })
 | 
			
		||||
 | 
			
		||||
        forceNavigationOverlay = preferences.showNavigationOverlayNewUser().get()
 | 
			
		||||
        if (forceNavigationOverlay) {
 | 
			
		||||
            preferences.showNavigationOverlayNewUser().set(false)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        preferences.showNavigationOverlayOnStart()
 | 
			
		||||
            .register({ navigationOverlayOnStart = it })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected abstract fun defaultNavigation(): ViewerNavigation
 | 
			
		||||
 
 | 
			
		||||
@@ -90,6 +90,7 @@ class PagerConfig(
 | 
			
		||||
            4 -> RightAndLeftNavigation()
 | 
			
		||||
            else -> defaultNavigation()
 | 
			
		||||
        }
 | 
			
		||||
        navigationModeChangedListener?.invoke()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    enum class ZoomType {
 | 
			
		||||
 
 | 
			
		||||
@@ -119,6 +119,11 @@ abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer {
 | 
			
		||||
        config.imagePropertyChangedListener = {
 | 
			
		||||
            refreshAdapter()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        config.navigationModeChangedListener = {
 | 
			
		||||
            val showOnStart = config.navigationOverlayOnStart || config.forceNavigationOverlay
 | 
			
		||||
            activity.binding.navigationOverlay.setNavigation(config.navigator, showOnStart)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun destroy() {
 | 
			
		||||
 
 | 
			
		||||
@@ -63,5 +63,6 @@ class WebtoonConfig(
 | 
			
		||||
            4 -> RightAndLeftNavigation()
 | 
			
		||||
            else -> defaultNavigation()
 | 
			
		||||
        }
 | 
			
		||||
        navigationModeChangedListener?.invoke()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -136,6 +136,11 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
 | 
			
		||||
            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.addView(recycler)
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -50,6 +50,12 @@ class SettingsReaderController : SettingsController() {
 | 
			
		||||
            summaryRes = R.string.pref_show_reading_mode_summary
 | 
			
		||||
            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) {
 | 
			
		||||
            switchPreference {
 | 
			
		||||
                key = Keys.trueColor
 | 
			
		||||
 
 | 
			
		||||
@@ -43,6 +43,12 @@
 | 
			
		||||
        android:layout_height="match_parent"
 | 
			
		||||
        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
 | 
			
		||||
        android:id="@+id/reader_menu"
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
 
 | 
			
		||||
@@ -76,4 +76,11 @@
 | 
			
		||||
 | 
			
		||||
    <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -252,6 +252,8 @@
 | 
			
		||||
 | 
			
		||||
      <!-- Reader section -->
 | 
			
		||||
    <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_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>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user