Improve keyboard control in reader

pull/500/head
Koitharu 3 years ago
parent 835c49ae79
commit 8398c01929
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -132,7 +132,7 @@ dependencies {
implementation 'io.coil-kt:coil-base:2.4.0' implementation 'io.coil-kt:coil-base:2.4.0'
implementation 'io.coil-kt:coil-svg:2.4.0' implementation 'io.coil-kt:coil-svg:2.4.0'
implementation 'com.github.KotatsuApp:subsampling-scale-image-view:9b1d20be67' implementation 'com.github.KotatsuApp:subsampling-scale-image-view:169806d928'
implementation 'com.github.solkin:disk-lru-cache:1.4' implementation 'com.github.solkin:disk-lru-cache:1.4'
implementation 'io.noties.markwon:core:4.6.2' implementation 'io.noties.markwon:core:4.6.2'

@ -107,7 +107,7 @@ class ReaderActivity :
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
touchHelper = GridTouchHelper(this, this) touchHelper = GridTouchHelper(this, this)
scrollTimer = scrollTimerFactory.create(this, this) scrollTimer = scrollTimerFactory.create(this, this)
controlDelegate = ReaderControlDelegate(settings, this, this) controlDelegate = ReaderControlDelegate(resources, settings, this, this)
viewBinding.toolbarBottom.setOnMenuItemClickListener(::onOptionsItemSelected) viewBinding.toolbarBottom.setOnMenuItemClickListener(::onOptionsItemSelected)
viewBinding.slider.setLabelFormatter(PageLabelFormatter()) viewBinding.slider.setLabelFormatter(PageLabelFormatter())
ReaderSliderListener(this, viewModel).attachToSlider(viewBinding.slider) ReaderSliderListener(this, viewModel).attachToSlider(viewBinding.slider)
@ -347,8 +347,8 @@ class ReaderActivity :
readerManager.currentReader?.switchPageBy(delta) readerManager.currentReader?.switchPageBy(delta)
} }
override fun scrollBy(delta: Int): Boolean { override fun scrollBy(delta: Int, smooth: Boolean): Boolean {
return readerManager.currentReader?.scrollBy(delta) ?: false return readerManager.currentReader?.scrollBy(delta, smooth) ?: false
} }
override fun toggleUiVisibility() { override fun toggleUiVisibility() {

@ -1,16 +1,19 @@
package org.koitharu.kotatsu.reader.ui package org.koitharu.kotatsu.reader.ui
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.res.Resources
import android.view.KeyEvent import android.view.KeyEvent
import android.view.SoundEffectConstants import android.view.SoundEffectConstants
import android.view.View import android.view.View
import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.ReaderMode import org.koitharu.kotatsu.core.prefs.ReaderMode
import org.koitharu.kotatsu.core.util.GridTouchHelper import org.koitharu.kotatsu.core.util.GridTouchHelper
class ReaderControlDelegate( class ReaderControlDelegate(
resources: Resources,
private val settings: AppSettings, private val settings: AppSettings,
private val listener: OnInteractionListener, private val listener: OnInteractionListener,
owner: LifecycleOwner, owner: LifecycleOwner,
@ -19,6 +22,7 @@ class ReaderControlDelegate(
private var isTapSwitchEnabled: Boolean = true private var isTapSwitchEnabled: Boolean = true
private var isVolumeKeysSwitchEnabled: Boolean = false private var isVolumeKeysSwitchEnabled: Boolean = false
private var isReaderTapsAdaptive: Boolean = true private var isReaderTapsAdaptive: Boolean = true
private var minScrollDelta = resources.getDimensionPixelSize(R.dimen.reader_scroll_delta_min)
init { init {
owner.lifecycle.addObserver(this) owner.lifecycle.addObserver(this)
@ -82,8 +86,6 @@ class ReaderControlDelegate(
KeyEvent.KEYCODE_SPACE, KeyEvent.KEYCODE_SPACE,
KeyEvent.KEYCODE_PAGE_DOWN, KeyEvent.KEYCODE_PAGE_DOWN,
KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN,
KeyEvent.KEYCODE_DPAD_DOWN,
-> { -> {
listener.switchPageBy(1) listener.switchPageBy(1)
true true
@ -95,8 +97,6 @@ class ReaderControlDelegate(
} }
KeyEvent.KEYCODE_PAGE_UP, KeyEvent.KEYCODE_PAGE_UP,
KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP,
KeyEvent.KEYCODE_DPAD_UP,
-> { -> {
listener.switchPageBy(-1) listener.switchPageBy(-1)
true true
@ -112,6 +112,22 @@ class ReaderControlDelegate(
true true
} }
KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP,
KeyEvent.KEYCODE_DPAD_UP -> {
if (!listener.scrollBy(-minScrollDelta, smooth = true)) {
listener.switchPageBy(-1)
}
true
}
KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN,
KeyEvent.KEYCODE_DPAD_DOWN -> {
if (!listener.scrollBy(minScrollDelta, smooth = true)) {
listener.switchPageBy(1)
}
true
}
else -> false else -> false
} }
@ -139,7 +155,7 @@ class ReaderControlDelegate(
fun switchPageBy(delta: Int) fun switchPageBy(delta: Int)
fun scrollBy(delta: Int): Boolean fun scrollBy(delta: Int, smooth: Boolean): Boolean
fun toggleUiVisibility() fun toggleUiVisibility()

@ -96,7 +96,7 @@ class ScrollTimer @AssistedInject constructor(
if (!listener.isReaderResumed()) { if (!listener.isReaderResumed()) {
continue continue
} }
if (!listener.scrollBy(1)) { if (!listener.scrollBy(1, false)) {
accumulator += delayMs accumulator += delayMs
} }
if (accumulator >= pageSwitchDelay) { if (accumulator >= pageSwitchDelay) {

@ -66,7 +66,7 @@ abstract class BaseReaderFragment<B : ViewBinding> : BaseFragment<B>() {
abstract fun switchPageTo(position: Int, smooth: Boolean) abstract fun switchPageTo(position: Int, smooth: Boolean)
open fun scrollBy(delta: Int): Boolean = false open fun scrollBy(delta: Int, smooth: Boolean): Boolean = false
abstract fun getCurrentState(): ReaderState? abstract fun getCurrentState(): ReaderState?

@ -1,5 +1,6 @@
package org.koitharu.kotatsu.reader.ui.pager.reversed package org.koitharu.kotatsu.reader.ui.pager.reversed
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.InputDevice import android.view.InputDevice
import android.view.KeyEvent import android.view.KeyEvent
@ -27,6 +28,7 @@ import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderFragment import org.koitharu.kotatsu.reader.ui.pager.BaseReaderFragment
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
import org.koitharu.kotatsu.reader.ui.pager.standard.NoAnimPageTransformer import org.koitharu.kotatsu.reader.ui.pager.standard.NoAnimPageTransformer
import org.koitharu.kotatsu.reader.ui.pager.standard.PagerEventSupplier
import org.koitharu.kotatsu.reader.ui.pager.standard.PagerReaderFragment import org.koitharu.kotatsu.reader.ui.pager.standard.PagerReaderFragment
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
@ -54,6 +56,10 @@ class ReversedReaderFragment : BaseReaderFragment<FragmentReaderStandardBinding>
offscreenPageLimit = 2 offscreenPageLimit = 2
doOnPageChanged(::notifyPageChanged) doOnPageChanged(::notifyPageChanged)
setOnGenericMotionListener(this@ReversedReaderFragment) setOnGenericMotionListener(this@ReversedReaderFragment)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
recyclerView?.defaultFocusHighlightEnabled = false
}
PagerEventSupplier(this).attach()
} }
viewModel.pageAnimation.observe(viewLifecycleOwner) { viewModel.pageAnimation.observe(viewLifecycleOwner) {

@ -35,7 +35,6 @@ open class PageHolder(
binding.ssiv.bindToLifecycle(owner) binding.ssiv.bindToLifecycle(owner)
binding.ssiv.isEagerLoadingEnabled = !context.isLowRamDevice() binding.ssiv.isEagerLoadingEnabled = !context.isLowRamDevice()
binding.ssiv.addOnImageEventListener(delegate) binding.ssiv.addOnImageEventListener(delegate)
binding.ssiv.setOnGenericMotionListener(SsivZoomListener())
@Suppress("LeakingThis") @Suppress("LeakingThis")
bindingInfo.buttonRetry.setOnClickListener(this) bindingInfo.buttonRetry.setOnClickListener(this)
@Suppress("LeakingThis") @Suppress("LeakingThis")

@ -0,0 +1,24 @@
package org.koitharu.kotatsu.reader.ui.pager.standard
import android.view.KeyEvent
import android.view.View
import android.view.ViewGroup
import androidx.core.view.children
import androidx.viewpager2.widget.ViewPager2
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import org.koitharu.kotatsu.core.util.ext.recyclerView
class PagerEventSupplier(private val pager: ViewPager2) : View.OnKeyListener {
fun attach() {
pager.recyclerView?.setOnKeyListener(this)
}
override fun onKey(v: View?, keyCode: Int, event: KeyEvent?): Boolean {
val rootView = pager.recyclerView?.findViewHolderForAdapterPosition(pager.currentItem)?.itemView as? ViewGroup
?: return false
return rootView.children.firstNotNullOfOrNull { x ->
x as? SubsamplingScaleImageView
}?.dispatchKeyEvent(event) == true
}
}

@ -1,5 +1,6 @@
package org.koitharu.kotatsu.reader.ui.pager.standard package org.koitharu.kotatsu.reader.ui.pager.standard
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.InputDevice import android.view.InputDevice
import android.view.KeyEvent import android.view.KeyEvent
@ -55,6 +56,10 @@ class PagerReaderFragment : BaseReaderFragment<FragmentReaderStandardBinding>(),
offscreenPageLimit = 2 offscreenPageLimit = 2
doOnPageChanged(::notifyPageChanged) doOnPageChanged(::notifyPageChanged)
setOnGenericMotionListener(this@PagerReaderFragment) setOnGenericMotionListener(this@PagerReaderFragment)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
recyclerView?.defaultFocusHighlightEnabled = false
}
PagerEventSupplier(this).attach()
} }
viewModel.pageAnimation.observe(viewLifecycleOwner) { viewModel.pageAnimation.observe(viewLifecycleOwner) {

@ -1,32 +0,0 @@
package org.koitharu.kotatsu.reader.ui.pager.standard
import android.graphics.PointF
import android.view.InputDevice
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.view.View.OnGenericMotionListener
import android.view.animation.DecelerateInterpolator
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
class SsivZoomListener : OnGenericMotionListener {
override fun onGenericMotion(v: View?, event: MotionEvent): Boolean {
val ssiv = v as? SubsamplingScaleImageView ?: return false
if (event.source and InputDevice.SOURCE_CLASS_POINTER != 0) {
if (event.actionMasked == MotionEvent.ACTION_SCROLL) {
val axisValue = event.getAxisValue(MotionEvent.AXIS_VSCROLL)
val withCtrl = event.metaState and KeyEvent.META_CTRL_MASK != 0
if (withCtrl || ssiv.scale > ssiv.minScale) {
val center = PointF(event.x, event.y)
val scale = ssiv.scale + axisValue * 1.6f
(ssiv.animateScaleAndCenter(scale, center) ?: return false)
.withInterpolator(DecelerateInterpolator())
.start()
return true
}
}
}
return false
}
}

@ -12,8 +12,8 @@ class WebtoonFrameLayout @JvmOverloads constructor(
@AttrRes defStyleAttr: Int = 0, @AttrRes defStyleAttr: Int = 0,
) : FrameLayout(context, attrs, defStyleAttr) { ) : FrameLayout(context, attrs, defStyleAttr) {
val target by lazy(LazyThreadSafetyMode.NONE) { val target: WebtoonImageView by lazy(LazyThreadSafetyMode.NONE) {
findViewById<WebtoonImageView>(R.id.ssiv) findViewById(R.id.ssiv)
} }
fun dispatchVerticalScroll(dy: Int): Int { fun dispatchVerticalScroll(dy: Int): Int {

@ -3,7 +3,7 @@ package org.koitharu.kotatsu.reader.ui.pager.webtoon
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.view.animation.AccelerateDecelerateInterpolator import android.view.animation.DecelerateInterpolator
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.async import kotlinx.coroutines.async
@ -31,7 +31,7 @@ class WebtoonReaderFragment : BaseReaderFragment<FragmentReaderWebtoonBinding>()
@Inject @Inject
lateinit var pageLoader: PageLoader lateinit var pageLoader: PageLoader
private val scrollInterpolator = AccelerateDecelerateInterpolator() private val scrollInterpolator = DecelerateInterpolator()
override fun onCreateViewBinding( override fun onCreateViewBinding(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -122,8 +122,12 @@ class WebtoonReaderFragment : BaseReaderFragment<FragmentReaderWebtoonBinding>()
requireViewBinding().recyclerView.firstVisibleItemPosition = position requireViewBinding().recyclerView.firstVisibleItemPosition = position
} }
override fun scrollBy(delta: Int): Boolean { override fun scrollBy(delta: Int, smooth: Boolean): Boolean {
if (smooth && isAnimationEnabled()) {
requireViewBinding().recyclerView.smoothScrollBy(0, delta, scrollInterpolator)
} else {
requireViewBinding().recyclerView.nestedScrollBy(0, delta) requireViewBinding().recyclerView.nestedScrollBy(0, delta)
}
return true return true
} }

@ -11,14 +11,17 @@ import android.view.InputDevice
import android.view.KeyEvent import android.view.KeyEvent
import android.view.MotionEvent import android.view.MotionEvent
import android.view.ScaleGestureDetector import android.view.ScaleGestureDetector
import android.view.ViewConfiguration
import android.view.animation.AccelerateDecelerateInterpolator import android.view.animation.AccelerateDecelerateInterpolator
import android.view.animation.DecelerateInterpolator
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.OverScroller import android.widget.OverScroller
import androidx.core.view.GestureDetectorCompat import androidx.core.view.GestureDetectorCompat
import androidx.core.view.ViewConfigurationCompat
import org.koitharu.kotatsu.core.util.ext.getAnimationDuration
private const val MAX_SCALE = 2.5f private const val MAX_SCALE = 2.5f
private const val MIN_SCALE = 0.5f private const val MIN_SCALE = 0.5f
private const val WHEEL_SCALE_FACTOR = 0.2f
class WebtoonScalingFrame @JvmOverloads constructor( class WebtoonScalingFrame @JvmOverloads constructor(
context: Context, context: Context,
@ -43,6 +46,7 @@ class WebtoonScalingFrame @JvmOverloads constructor(
private var halfHeight = 0f private var halfHeight = 0f
private val translateBounds = RectF() private val translateBounds = RectF()
private val targetHitRect = Rect() private val targetHitRect = Rect()
private var animator: ValueAnimator? = null
var isZoomEnable = true var isZoomEnable = true
set(value) { set(value) {
@ -81,13 +85,15 @@ class WebtoonScalingFrame @JvmOverloads constructor(
} }
override fun onGenericMotionEvent(event: MotionEvent): Boolean { override fun onGenericMotionEvent(event: MotionEvent): Boolean {
if (event.source and InputDevice.SOURCE_CLASS_POINTER != 0) { if (isZoomEnable && event.source and InputDevice.SOURCE_CLASS_POINTER != 0) {
if (event.actionMasked == MotionEvent.ACTION_SCROLL) { if (event.actionMasked == MotionEvent.ACTION_SCROLL) {
val withCtrl = event.metaState and KeyEvent.META_CTRL_MASK != 0 val withCtrl = event.metaState and KeyEvent.META_CTRL_MASK != 0
if (withCtrl) { if (withCtrl) {
val axisValue = event.getAxisValue(MotionEvent.AXIS_VSCROLL) val axisValue =
val newScale = event.getAxisValue(MotionEvent.AXIS_VSCROLL) * ViewConfigurationCompat.getScaledVerticalScrollFactor(
(scale + axisValue * WHEEL_SCALE_FACTOR).coerceIn(MIN_SCALE, MAX_SCALE) ViewConfiguration.get(context), context,
)
val newScale = (scale + axisValue).coerceIn(MIN_SCALE, MAX_SCALE)
scaleChild(newScale, event.x, event.y) scaleChild(newScale, event.x, event.y)
return true return true
} }
@ -96,6 +102,49 @@ class WebtoonScalingFrame @JvmOverloads constructor(
return super.onGenericMotionEvent(event) return super.onGenericMotionEvent(event)
} }
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
if (!isZoomEnable) {
return super.onKeyDown(keyCode, event)
}
return when (keyCode) {
KeyEvent.KEYCODE_ZOOM_IN,
KeyEvent.KEYCODE_NUMPAD_ADD,
KeyEvent.KEYCODE_PLUS -> {
smoothScaleTo(scale * 1.1f)
true
}
KeyEvent.KEYCODE_ZOOM_OUT,
KeyEvent.KEYCODE_NUMPAD_SUBTRACT,
KeyEvent.KEYCODE_MINUS -> {
smoothScaleTo(scale * 0.9f)
true
}
KeyEvent.KEYCODE_ESCAPE -> {
smoothScaleTo(1f)
true
}
else -> super.onKeyDown(keyCode, event)
}
}
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
return if (isZoomEnable) {
keyCode == KeyEvent.KEYCODE_NUMPAD_ADD
|| keyCode == KeyEvent.KEYCODE_PLUS
|| keyCode == KeyEvent.KEYCODE_NUMPAD_SUBTRACT
|| keyCode == KeyEvent.KEYCODE_MINUS
|| keyCode == KeyEvent.KEYCODE_ZOOM_IN
|| keyCode == KeyEvent.KEYCODE_ZOOM_OUT
|| keyCode == KeyEvent.KEYCODE_ESCAPE
|| super.onKeyUp(keyCode, event)
} else {
super.onKeyUp(keyCode, event)
}
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh) super.onSizeChanged(w, h, oldw, oldh)
halfWidth = w / 2f halfWidth = w / 2f
@ -173,10 +222,24 @@ class WebtoonScalingFrame @JvmOverloads constructor(
return true return true
} }
override fun onScaleBegin(detector: ScaleGestureDetector): Boolean = true override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
animator?.cancel()
animator = null
return true
}
override fun onScaleEnd(p0: ScaleGestureDetector) = Unit override fun onScaleEnd(p0: ScaleGestureDetector) = Unit
private fun smoothScaleTo(target: Float) {
val newScale = target.coerceIn(MIN_SCALE, MAX_SCALE)
animator?.cancel()
animator = ValueAnimator.ofFloat(scale, newScale).apply {
setDuration(context.getAnimationDuration(android.R.integer.config_shortAnimTime))
interpolator = DecelerateInterpolator()
addUpdateListener { scaleChild(it.animatedValue as Float, halfWidth, halfHeight) }
start()
}
}
private inner class GestureListener : GestureDetector.SimpleOnGestureListener(), Runnable { private inner class GestureListener : GestureDetector.SimpleOnGestureListener(), Runnable {
@ -231,7 +294,7 @@ class WebtoonScalingFrame @JvmOverloads constructor(
if (overScroller.computeScrollOffset()) { if (overScroller.computeScrollOffset()) {
transformMatrix.postTranslate( transformMatrix.postTranslate(
overScroller.currX - transX, overScroller.currX - transX,
overScroller.currY - transY overScroller.currY - transY,
) )
invalidateTarget() invalidateTarget()
postOnAnimation(this) postOnAnimation(this)

@ -3,4 +3,5 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager" android:id="@+id/pager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent"
android:defaultFocusHighlightEnabled="false" />

@ -4,12 +4,15 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/frame" android:id="@+id/frame"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:focusable="true"
android:defaultFocusHighlightEnabled="false">
<org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonRecyclerView <org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonRecyclerView
android:id="@+id/recyclerView" android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:defaultFocusHighlightEnabled="false"
android:orientation="vertical" android:orientation="vertical"
app:layoutManager="org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonLayoutManager" /> app:layoutManager="org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonLayoutManager" />
</org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonScalingFrame> </org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonScalingFrame>

@ -10,6 +10,8 @@
android:id="@+id/ssiv" android:id="@+id/ssiv"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:defaultFocusHighlightEnabled="false"
android:focusable="true"
app:restoreStrategy="deferred" /> app:restoreStrategy="deferred" />
<TextView <TextView

@ -3,12 +3,14 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:defaultFocusHighlightEnabled="false">
<org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonImageView <org.koitharu.kotatsu.reader.ui.pager.webtoon.WebtoonImageView
android:id="@+id/ssiv" android:id="@+id/ssiv"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:defaultFocusHighlightEnabled="false"
android:minHeight="1dp" android:minHeight="1dp"
app:panEnabled="false" app:panEnabled="false"
app:quickScaleEnabled="false" app:quickScaleEnabled="false"

@ -80,4 +80,6 @@
<dimen name="fastscroll_scrollbar_padding_end">6dp</dimen> <dimen name="fastscroll_scrollbar_padding_end">6dp</dimen>
<dimen name="m3_side_sheet_width">400dp</dimen> <dimen name="m3_side_sheet_width">400dp</dimen>
<dimen name="reader_scroll_delta_min">200dp</dimen>
</resources> </resources>

Loading…
Cancel
Save