Autoscroll in reader #204
This commit is contained in:
@@ -35,6 +35,7 @@ import org.koitharu.kotatsu.databinding.ActivityReaderBinding
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
import org.koitharu.kotatsu.parsers.model.MangaPage
|
||||
import org.koitharu.kotatsu.reader.ui.config.PageSwitchTimer
|
||||
import org.koitharu.kotatsu.reader.ui.config.ReaderConfigBottomSheet
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderUiState
|
||||
import org.koitharu.kotatsu.reader.ui.thumbnails.OnPageSelectListener
|
||||
@@ -65,6 +66,13 @@ class ReaderActivity :
|
||||
)
|
||||
}
|
||||
|
||||
override var pageSwitchDelay: Float
|
||||
get() = pageSwitchTimer.delaySec
|
||||
set(value) {
|
||||
pageSwitchTimer.delaySec = value
|
||||
}
|
||||
|
||||
private lateinit var pageSwitchTimer: PageSwitchTimer
|
||||
private lateinit var touchHelper: GridTouchHelper
|
||||
private lateinit var controlDelegate: ReaderControlDelegate
|
||||
private var gestureInsets: Insets = Insets.NONE
|
||||
@@ -77,6 +85,7 @@ class ReaderActivity :
|
||||
readerManager = ReaderManager(supportFragmentManager, R.id.container)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
touchHelper = GridTouchHelper(this, this)
|
||||
pageSwitchTimer = PageSwitchTimer(this, this)
|
||||
controlDelegate = ReaderControlDelegate(lifecycleScope, settings, this)
|
||||
binding.toolbarBottom.setOnMenuItemClickListener(::onOptionsItemSelected)
|
||||
binding.slider.setLabelFormatter(PageLabelFormatter())
|
||||
@@ -100,6 +109,11 @@ class ReaderActivity :
|
||||
}
|
||||
}
|
||||
|
||||
override fun onUserInteraction() {
|
||||
super.onUserInteraction()
|
||||
pageSwitchTimer.onUserInteraction()
|
||||
}
|
||||
|
||||
private fun onInitReader(mode: ReaderMode) {
|
||||
if (readerManager.currentMode != mode) {
|
||||
readerManager.replace(mode)
|
||||
|
||||
@@ -15,7 +15,7 @@ import org.koitharu.kotatsu.utils.GridTouchHelper
|
||||
class ReaderControlDelegate(
|
||||
scope: LifecycleCoroutineScope,
|
||||
settings: AppSettings,
|
||||
private val listener: OnInteractionListener
|
||||
private val listener: OnInteractionListener,
|
||||
) {
|
||||
|
||||
private var isTapSwitchEnabled: Boolean = true
|
||||
@@ -72,14 +72,16 @@ class ReaderControlDelegate(
|
||||
KeyEvent.KEYCODE_PAGE_DOWN,
|
||||
KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN,
|
||||
KeyEvent.KEYCODE_DPAD_DOWN,
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT -> {
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT,
|
||||
-> {
|
||||
listener.switchPageBy(1)
|
||||
true
|
||||
}
|
||||
KeyEvent.KEYCODE_PAGE_UP,
|
||||
KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP,
|
||||
KeyEvent.KEYCODE_DPAD_UP,
|
||||
KeyEvent.KEYCODE_DPAD_LEFT -> {
|
||||
KeyEvent.KEYCODE_DPAD_LEFT,
|
||||
-> {
|
||||
listener.switchPageBy(-1)
|
||||
true
|
||||
}
|
||||
@@ -103,4 +105,4 @@ class ReaderControlDelegate(
|
||||
|
||||
fun toggleUiVisibility()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package org.koitharu.kotatsu.reader.ui.config
|
||||
|
||||
import android.content.res.Resources
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.coroutineScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.google.android.material.slider.LabelFormatter
|
||||
import kotlin.math.roundToLong
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.parsers.util.format
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderControlDelegate
|
||||
|
||||
class PageSwitchTimer(
|
||||
private val listener: ReaderControlDelegate.OnInteractionListener,
|
||||
private val lifecycleOwner: LifecycleOwner,
|
||||
) {
|
||||
|
||||
var delaySec: Float = 0f
|
||||
set(value) {
|
||||
field = value
|
||||
delayMs = mapDelay(value)
|
||||
restartJob()
|
||||
}
|
||||
private var delayMs = 0L
|
||||
|
||||
fun onUserInteraction() {
|
||||
restartJob()
|
||||
}
|
||||
|
||||
private var job: Job? = null
|
||||
|
||||
private fun restartJob() {
|
||||
job?.cancel()
|
||||
if (delayMs == 0L) {
|
||||
job = null
|
||||
return
|
||||
}
|
||||
job = lifecycleOwner.lifecycle.coroutineScope.launch {
|
||||
// FIXME: pause when bs is opened
|
||||
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||
while (isActive) {
|
||||
delay(delayMs)
|
||||
listener.switchPageBy(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DelayLabelFormatter(resources: Resources) : LabelFormatter {
|
||||
|
||||
private val textOff = resources.getString(R.string.off_short)
|
||||
private val textSec = resources.getString(R.string.seconds_pattern)
|
||||
|
||||
override fun getFormattedValue(value: Float): String {
|
||||
val ms = mapDelay(value)
|
||||
return if (ms == 0L) textOff else textSec.format((ms / 1000.0).format(1))
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val DELAY_MIN = 2000L
|
||||
|
||||
fun mapDelay(value: Float): Long {
|
||||
val delay = (value * 1000L).roundToLong()
|
||||
return if (delay < DELAY_MIN) 0L else delay
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.flowWithLifecycle
|
||||
import com.google.android.material.button.MaterialButtonToggleGroup
|
||||
import com.google.android.material.slider.Slider
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import org.koitharu.kotatsu.R
|
||||
@@ -28,7 +29,8 @@ class ReaderConfigBottomSheet :
|
||||
BaseBottomSheet<SheetReaderConfigBinding>(),
|
||||
ActivityResultCallback<Uri?>,
|
||||
View.OnClickListener,
|
||||
MaterialButtonToggleGroup.OnButtonCheckedListener {
|
||||
MaterialButtonToggleGroup.OnButtonCheckedListener,
|
||||
Slider.OnSliderTouchListener {
|
||||
|
||||
private val viewModel by activityViewModels<ReaderViewModel>()
|
||||
private val savePageRequest = registerForActivityResult(PageSaveContract(), this)
|
||||
@@ -57,6 +59,12 @@ class ReaderConfigBottomSheet :
|
||||
binding.buttonSavePage.setOnClickListener(this)
|
||||
binding.buttonScreenRotate.setOnClickListener(this)
|
||||
binding.buttonSettings.setOnClickListener(this)
|
||||
binding.sliderTimer.addOnSliderTouchListener(this)
|
||||
binding.sliderTimer.setLabelFormatter(PageSwitchTimer.DelayLabelFormatter(view.resources))
|
||||
|
||||
findCallback()?.run {
|
||||
binding.sliderTimer.value = pageSwitchDelay
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
@@ -92,6 +100,12 @@ class ReaderConfigBottomSheet :
|
||||
mode = newMode
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(slider: Slider) = Unit
|
||||
|
||||
override fun onStopTrackingTouch(slider: Slider) {
|
||||
findCallback()?.pageSwitchDelay = slider.value
|
||||
}
|
||||
|
||||
override fun onActivityResult(uri: Uri?) {
|
||||
viewModel.onActivityResult(uri)
|
||||
dismissAllowingStateLoss()
|
||||
@@ -113,6 +127,8 @@ class ReaderConfigBottomSheet :
|
||||
|
||||
interface Callback {
|
||||
|
||||
var pageSwitchDelay: Float
|
||||
|
||||
fun onReaderModeChanged(mode: ReaderMode)
|
||||
}
|
||||
|
||||
|
||||
12
app/src/main/res/drawable/ic_timer.xml
Normal file
12
app/src/main/res/drawable/ic_timer.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000"
|
||||
android:pathData="M12,20A7,7 0 0,1 5,13A7,7 0 0,1 12,6A7,7 0 0,1 19,13A7,7 0 0,1 12,20M19.03,7.39L20.45,5.97C20,5.46 19.55,5 19.04,4.56L17.62,6C16.07,4.74 14.12,4 12,4A9,9 0 0,0 3,13A9,9 0 0,0 12,22C17,22 21,17.97 21,13C21,10.88 20.26,8.93 19.03,7.39M11,14H13V8H11M15,1H9V3H15V1Z" />
|
||||
</vector>
|
||||
@@ -27,24 +27,24 @@
|
||||
android:id="@+id/button_save_page"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:listPreferredItemHeightSmall"
|
||||
android:drawableStart="@drawable/ic_save"
|
||||
android:drawablePadding="?android:listPreferredItemPaddingStart"
|
||||
android:paddingStart="?android:listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:listPreferredItemPaddingEnd"
|
||||
android:text="@string/save_page"
|
||||
android:textAppearance="?attr/textAppearanceButton" />
|
||||
android:textAppearance="?attr/textAppearanceButton"
|
||||
app:drawableStartCompat="@drawable/ic_save" />
|
||||
|
||||
<org.koitharu.kotatsu.base.ui.widgets.ListItemTextView
|
||||
android:id="@+id/button_screen_rotate"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:listPreferredItemHeightSmall"
|
||||
android:drawableStart="@drawable/ic_screen_rotation"
|
||||
android:drawablePadding="?android:listPreferredItemPaddingStart"
|
||||
android:paddingStart="?android:listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:listPreferredItemPaddingEnd"
|
||||
android:text="@string/rotate_screen"
|
||||
android:textAppearance="?attr/textAppearanceButton"
|
||||
android:visibility="gone"
|
||||
app:drawableStartCompat="@drawable/ic_screen_rotation"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
@@ -62,7 +62,9 @@
|
||||
android:layout_marginHorizontal="@dimen/margin_normal"
|
||||
android:layout_marginTop="@dimen/margin_small"
|
||||
android:baselineAligned="false"
|
||||
android:orientation="horizontal">
|
||||
android:orientation="horizontal"
|
||||
app:selectionRequired="true"
|
||||
app:singleSelection="true">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_standard"
|
||||
@@ -101,17 +103,46 @@
|
||||
android:text="@string/reader_mode_hint"
|
||||
android:textAppearance="?attr/textAppearanceBodySmall" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_normal"
|
||||
android:baselineAligned="false"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="?android:listPreferredItemHeightSmall"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="?android:listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:listPreferredItemPaddingEnd">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="?android:listPreferredItemPaddingStart"
|
||||
android:text="@string/automatic_scroll"
|
||||
android:textAppearance="?attr/textAppearanceButton"
|
||||
app:drawableStartCompat="@drawable/ic_timer" />
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/slider_timer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_normal"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="20"
|
||||
app:labelBehavior="floating" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<org.koitharu.kotatsu.base.ui.widgets.ListItemTextView
|
||||
android:id="@+id/button_settings"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:listPreferredItemHeightSmall"
|
||||
android:layout_marginTop="@dimen/margin_normal"
|
||||
android:drawableStart="@drawable/ic_settings"
|
||||
android:drawablePadding="?android:listPreferredItemPaddingStart"
|
||||
android:paddingStart="?android:listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?android:listPreferredItemPaddingEnd"
|
||||
android:text="@string/settings"
|
||||
android:textAppearance="?attr/textAppearanceButton" />
|
||||
android:textAppearance="?attr/textAppearanceButton"
|
||||
app:drawableStartCompat="@drawable/ic_settings" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -361,4 +361,7 @@
|
||||
<string name="incognito_mode">Incognito mode</string>
|
||||
<string name="app_update_available_s">Application update available: %s</string>
|
||||
<string name="no_chapters">No chapters</string>
|
||||
<string name="automatic_scroll">Automatic scroll</string>
|
||||
<string name="off_short">Off</string>
|
||||
<string name="seconds_pattern">%ss</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user