From 6c43881cf45f43a88d5691ab4a79f52f7ae9887e Mon Sep 17 00:00:00 2001 From: Koitharu Date: Tue, 2 Aug 2022 12:34:50 +0300 Subject: [PATCH] Autoscroll in reader #204 --- .../kotatsu/reader/ui/ReaderActivity.kt | 14 ++++ .../reader/ui/ReaderControlDelegate.kt | 10 ++- .../reader/ui/config/PageSwitchTimer.kt | 74 +++++++++++++++++++ .../ui/config/ReaderConfigBottomSheet.kt | 18 ++++- app/src/main/res/drawable/ic_timer.xml | 12 +++ .../main/res/layout/sheet_reader_config.xml | 45 +++++++++-- app/src/main/res/values/strings.xml | 3 + 7 files changed, 164 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/reader/ui/config/PageSwitchTimer.kt create mode 100644 app/src/main/res/drawable/ic_timer.xml diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt index fe9c19d19..867938511 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt @@ -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) diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderControlDelegate.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderControlDelegate.kt index dbe853894..98c14c98c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderControlDelegate.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderControlDelegate.kt @@ -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() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/PageSwitchTimer.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/PageSwitchTimer.kt new file mode 100644 index 000000000..2bc82623c --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/PageSwitchTimer.kt @@ -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 + } + } +} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/ReaderConfigBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/ReaderConfigBottomSheet.kt index e89430bc6..e7f088c94 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/ReaderConfigBottomSheet.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/config/ReaderConfigBottomSheet.kt @@ -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(), ActivityResultCallback, View.OnClickListener, - MaterialButtonToggleGroup.OnButtonCheckedListener { + MaterialButtonToggleGroup.OnButtonCheckedListener, + Slider.OnSliderTouchListener { private val viewModel by activityViewModels() 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) } diff --git a/app/src/main/res/drawable/ic_timer.xml b/app/src/main/res/drawable/ic_timer.xml new file mode 100644 index 000000000..e6b135831 --- /dev/null +++ b/app/src/main/res/drawable/ic_timer.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/layout/sheet_reader_config.xml b/app/src/main/res/layout/sheet_reader_config.xml index 5a173a268..34e0efcad 100644 --- a/app/src/main/res/layout/sheet_reader_config.xml +++ b/app/src/main/res/layout/sheet_reader_config.xml @@ -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" /> + android:orientation="horizontal" + app:selectionRequired="true" + app:singleSelection="true"> + + + + + + + + + android:textAppearance="?attr/textAppearanceButton" + app:drawableStartCompat="@drawable/ic_settings" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4907a48b9..b2e231578 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -361,4 +361,7 @@ Incognito mode Application update available: %s No chapters + Automatic scroll + Off + %ss