Bring back reader slider

This commit is contained in:
Koitharu
2024-04-23 09:23:19 +03:00
parent e520e695f9
commit fc2ab3f795
8 changed files with 63 additions and 147 deletions

View File

@@ -45,9 +45,11 @@ import org.koitharu.kotatsu.core.util.IdlingDetector
import org.koitharu.kotatsu.core.util.ShareHelper import org.koitharu.kotatsu.core.util.ShareHelper
import org.koitharu.kotatsu.core.util.ext.hasGlobalPoint import org.koitharu.kotatsu.core.util.ext.hasGlobalPoint
import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled
import org.koitharu.kotatsu.core.util.ext.isRtl
import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.core.util.ext.observeEvent
import org.koitharu.kotatsu.core.util.ext.postDelayed import org.koitharu.kotatsu.core.util.ext.postDelayed
import org.koitharu.kotatsu.core.util.ext.setValueRounded
import org.koitharu.kotatsu.core.util.ext.zipWithPrevious import org.koitharu.kotatsu.core.util.ext.zipWithPrevious
import org.koitharu.kotatsu.databinding.ActivityReaderBinding import org.koitharu.kotatsu.databinding.ActivityReaderBinding
import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.details.ui.DetailsActivity
@@ -103,7 +105,6 @@ class ReaderActivity :
private var gestureInsets: Insets = Insets.NONE private var gestureInsets: Insets = Insets.NONE
private lateinit var readerManager: ReaderManager private lateinit var readerManager: ReaderManager
private val hideUiRunnable = Runnable { setUiIsVisible(false) } private val hideUiRunnable = Runnable { setUiIsVisible(false) }
private lateinit var bottomMenuProvider: ReaderBottomMenuProvider
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -113,8 +114,9 @@ class ReaderActivity :
touchHelper = TapGridDispatcher(this, this) touchHelper = TapGridDispatcher(this, this)
scrollTimer = scrollTimerFactory.create(this, this) scrollTimer = scrollTimerFactory.create(this, this)
controlDelegate = ReaderControlDelegate(resources, settings, tapGridSettings, this) controlDelegate = ReaderControlDelegate(resources, settings, tapGridSettings, this)
bottomMenuProvider = ReaderBottomMenuProvider(this, readerManager, viewModel, this) viewBinding.slider.setLabelFormatter(PageLabelFormatter())
viewBinding.zoomControl.listener = this viewBinding.zoomControl.listener = this
ReaderSliderListener(viewModel, this).attachToSlider(viewBinding.slider)
insetsDelegate.interceptingWindowInsetsListener = this insetsDelegate.interceptingWindowInsetsListener = this
idlingDetector.bindToLifecycle(this) idlingDetector.bindToLifecycle(this)
@@ -153,8 +155,7 @@ class ReaderActivity :
viewBinding.zoomControl.isVisible = it viewBinding.zoomControl.isVisible = it
} }
addMenuProvider(ReaderTopMenuProvider(this, viewModel)) addMenuProvider(ReaderTopMenuProvider(this, viewModel))
viewBinding.toolbarBottom.addMenuProvider(bottomMenuProvider) viewBinding.toolbarBottom.addMenuProvider(ReaderBottomMenuProvider(this, readerManager, viewModel))
onBackPressedDispatcher.addCallback(bottomMenuProvider)
} }
override fun onActivityResult(result: Uri?) { override fun onActivityResult(result: Uri?) {
@@ -196,9 +197,10 @@ class ReaderActivity :
if (readerManager.currentMode != mode) { if (readerManager.currentMode != mode) {
readerManager.replace(mode) readerManager.replace(mode)
} }
if (viewBinding.appbarTop.isVisible && !bottomMenuProvider.isSliderExpanded()) { if (viewBinding.appbarTop.isVisible) {
lifecycle.postDelayed(TimeUnit.SECONDS.toMillis(1), hideUiRunnable) lifecycle.postDelayed(TimeUnit.SECONDS.toMillis(1), hideUiRunnable)
} }
viewBinding.slider.isRtl = mode == ReaderMode.REVERSED
} }
private fun onLoadingStateChanged(isLoading: Boolean) { private fun onLoadingStateChanged(isLoading: Boolean) {
@@ -210,7 +212,6 @@ class ReaderActivity :
viewBinding.toastView.hide() viewBinding.toastView.hide()
} }
viewBinding.toolbarBottom.invalidateMenu() viewBinding.toolbarBottom.invalidateMenu()
invalidateMenu()
} }
override fun onGridTouch(area: TapGridArea): Boolean { override fun onGridTouch(area: TapGridArea): Boolean {
@@ -328,9 +329,6 @@ class ReaderActivity :
viewBinding.infoBar.isGone = isUiVisible || (!viewModel.isInfoBarEnabled.value) viewBinding.infoBar.isGone = isUiVisible || (!viewModel.isInfoBarEnabled.value)
viewBinding.infoBar.isTimeVisible = isFullscreen viewBinding.infoBar.isTimeVisible = isFullscreen
systemUiController.setSystemUiVisible(isUiVisible || !isFullscreen) systemUiController.setSystemUiVisible(isUiVisible || !isFullscreen)
if (!isUiVisible) {
bottomMenuProvider.collapseSlider()
}
} }
} }
@@ -396,20 +394,28 @@ class ReaderActivity :
private fun onUiStateChanged(pair: Pair<ReaderUiState?, ReaderUiState?>) { private fun onUiStateChanged(pair: Pair<ReaderUiState?, ReaderUiState?>) {
val (previous: ReaderUiState?, uiState: ReaderUiState?) = pair val (previous: ReaderUiState?, uiState: ReaderUiState?) = pair
title = uiState?.mangaName ?: getString(R.string.loading_) title = uiState?.mangaName ?: getString(R.string.loading_)
supportActionBar?.subtitle = uiState?.chapterName
with(viewBinding.toolbarBottom) {
title = uiState?.resolveSummary(context)
subtitle = uiState?.resolveSubtitle(context)
}
viewBinding.infoBar.update(uiState) viewBinding.infoBar.update(uiState)
if (!bottomMenuProvider.updateState(uiState)) { if (uiState == null) {
viewBinding.toolbarBottom.invalidateMenu() supportActionBar?.subtitle = null
viewBinding.slider.isVisible = false
return
} }
if (uiState != null && previous?.chapterName != null && uiState.chapterName != previous.chapterName) { supportActionBar?.subtitle = when {
uiState.incognito -> getString(R.string.incognito_mode)
else -> uiState.chapterName
}
if (previous?.chapterName != null && uiState.chapterName != previous.chapterName) {
if (!uiState.chapterName.isNullOrEmpty()) { if (!uiState.chapterName.isNullOrEmpty()) {
viewBinding.toastView.showTemporary(uiState.chapterName, TOAST_DURATION) viewBinding.toastView.showTemporary(uiState.chapterName, TOAST_DURATION)
} }
} }
if (uiState.isSliderAvailable()) {
viewBinding.slider.valueTo = uiState.totalPages.toFloat() - 1
viewBinding.slider.setValueRounded(uiState.currentPage.toFloat())
viewBinding.slider.isVisible = true
} else {
viewBinding.slider.isVisible = false
}
} }
class IntentBuilder(context: Context) { class IntentBuilder(context: Context) {

View File

@@ -3,29 +3,17 @@ package org.koitharu.kotatsu.reader.ui
import android.view.Menu import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import androidx.activity.OnBackPressedCallback
import androidx.core.view.MenuProvider import androidx.core.view.MenuProvider
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import com.google.android.material.slider.LabelFormatter
import com.google.android.material.slider.Slider
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.prefs.ReaderMode
import org.koitharu.kotatsu.core.util.ext.isRtl
import org.koitharu.kotatsu.core.util.ext.setValueRounded
import org.koitharu.kotatsu.details.ui.pager.ChaptersPagesSheet import org.koitharu.kotatsu.details.ui.pager.ChaptersPagesSheet
import org.koitharu.kotatsu.reader.ui.config.ReaderConfigSheet import org.koitharu.kotatsu.reader.ui.config.ReaderConfigSheet
import org.koitharu.kotatsu.reader.ui.pager.ReaderUiState
import org.koitharu.kotatsu.settings.SettingsActivity
import java.lang.ref.WeakReference
class ReaderBottomMenuProvider( class ReaderBottomMenuProvider(
private val activity: FragmentActivity, private val activity: FragmentActivity,
private val readerManager: ReaderManager, private val readerManager: ReaderManager,
private val viewModel: ReaderViewModel, private val viewModel: ReaderViewModel,
private val callback: ReaderNavigationCallback, ) : MenuProvider {
) : OnBackPressedCallback(false), MenuProvider, MenuItem.OnActionExpandListener {
private var expandedItemRef: WeakReference<MenuItem>? = null
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.opt_reader_bottom, menu) menuInflater.inflate(R.menu.opt_reader_bottom, menu)
@@ -33,31 +21,12 @@ class ReaderBottomMenuProvider(
} }
override fun onPrepareMenu(menu: Menu) { override fun onPrepareMenu(menu: Menu) {
val shouldExpandSlider = expandedItemRef != null
val hasPages = viewModel.content.value.pages.isNotEmpty() val hasPages = viewModel.content.value.pages.isNotEmpty()
menu.findItem(R.id.action_pages_thumbs).isVisible = hasPages menu.findItem(R.id.action_pages_thumbs).isVisible = hasPages
menu.findItem(R.id.action_slider)?.run {
val state = viewModel.uiState.value?.takeIf { it.isSliderAvailable() }
isVisible = state != null
setOnActionExpandListener(this@ReaderBottomMenuProvider)
if (state != null) {
(actionView as? Slider)?.setupPagesSlider(state)
}
if (shouldExpandSlider) {
expandActionView()
}
}
} }
override fun onMenuItemSelected(menuItem: MenuItem): Boolean { override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return when (menuItem.itemId) { return when (menuItem.itemId) {
R.id.action_settings -> {
activity.startActivity(SettingsActivity.newReaderSettingsIntent(activity))
true
}
R.id.action_pages_thumbs -> { R.id.action_pages_thumbs -> {
ChaptersPagesSheet.show(activity.supportFragmentManager, ChaptersPagesSheet.TAB_PAGES) ChaptersPagesSheet.show(activity.supportFragmentManager, ChaptersPagesSheet.TAB_PAGES)
true true
@@ -73,48 +42,4 @@ class ReaderBottomMenuProvider(
else -> false else -> false
} }
} }
override fun handleOnBackPressed() {
expandedItemRef?.get()?.collapseActionView()
}
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
expandedItemRef = WeakReference(item)
isEnabled = true
return true
}
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
expandedItemRef = null
isEnabled = false
return true
}
fun collapseSlider() {
expandedItemRef?.get()?.collapseActionView()
}
fun isSliderExpanded(): Boolean {
return expandedItemRef?.get()?.isActionViewExpanded == true
}
fun updateState(state: ReaderUiState?): Boolean {
if (state == null || !state.isSliderAvailable()) {
return false
}
val slider = (expandedItemRef?.get()?.actionView as? Slider) ?: return false
slider.valueTo = state.totalPages.toFloat() - 1
slider.setValueRounded(state.currentPage.toFloat())
return true
}
private fun Slider.setupPagesSlider(state: ReaderUiState) {
isRtl = viewModel.readerMode.value == ReaderMode.REVERSED
valueTo = state.totalPages.toFloat() - 1
setValueRounded(state.currentPage.toFloat())
labelBehavior = LabelFormatter.LABEL_FLOATING
isTickVisible = true
setLabelFormatter(PageLabelFormatter())
ReaderSliderListener(viewModel, callback).attachToSlider(this)
}
} }

View File

@@ -169,7 +169,13 @@ class ReaderInfoBarView @JvmOverloads constructor(
fun update(state: ReaderUiState?) { fun update(state: ReaderUiState?) {
text = if (state != null) { text = if (state != null) {
state.resolveSummary(context) + if (state.percent in 0f..1f) { context.getString(
R.string.reader_info_pattern,
state.chapterNumber,
state.chaptersTotal,
state.currentPage + 1,
state.totalPages,
) + if (state.percent in 0f..1f) {
" " + context.getString(R.string.percent_string_pattern, (state.percent * 100).format()) " " + context.getString(R.string.percent_string_pattern, (state.percent * 100).format())
} else { } else {
"" ""

View File

@@ -1,9 +1,5 @@
package org.koitharu.kotatsu.reader.ui.pager package org.koitharu.kotatsu.reader.ui.pager
import android.content.Context
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.parsers.util.format
data class ReaderUiState( data class ReaderUiState(
val mangaName: String?, val mangaName: String?,
val branch: String?, val branch: String?,
@@ -20,28 +16,4 @@ data class ReaderUiState(
fun isSliderAvailable(): Boolean { fun isSliderAvailable(): Boolean {
return isSliderEnabled && totalPages > 1 && currentPage < totalPages return isSliderEnabled && totalPages > 1 && currentPage < totalPages
} }
fun resolveSubtitle(context: Context): String? {
val firstPart = branch
val secondPart = if (incognito) {
context.getString(R.string.incognito_mode)
} else if (percent in 0f..1f) {
context.getString(R.string.percent_string_pattern, (percent * 100).format())
} else {
null
}
return if (firstPart != null && secondPart != null) {
context.getString(R.string.download_summary_pattern, firstPart, secondPart)
} else {
firstPart ?: secondPart
}
}
fun resolveSummary(context: Context) = context.getString(
R.string.reader_info_pattern,
chapterNumber,
chaptersTotal,
currentPage + 1,
totalPages,
)
} }

View File

@@ -1,11 +0,0 @@
<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="#000000"
android:pathData="M21,9L17,5V8H10V10H17V13M7,11L3,15L7,19V16H14V14H7V11Z" />
</vector>

View File

@@ -60,6 +60,15 @@
android:layout_weight="1" android:layout_weight="1"
tools:menu="@menu/opt_reader_bottom"> tools:menu="@menu/opt_reader_bottom">
<com.google.android.material.slider.Slider
android:id="@+id/slider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stepSize="1"
android:valueFrom="0"
app:labelBehavior="floating"
app:tickVisible="false" />
</com.google.android.material.appbar.MaterialToolbar> </com.google.android.material.appbar.MaterialToolbar>
</LinearLayout> </LinearLayout>

View File

@@ -65,7 +65,20 @@
android:id="@+id/toolbar_bottom" android:id="@+id/toolbar_bottom"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
tools:menu="@menu/opt_reader_bottom" /> tools:menu="@menu/opt_reader_bottom">
<com.google.android.material.slider.Slider
android:id="@+id/slider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stepSize="1.0"
android:valueFrom="0"
app:labelBehavior="floating"
app:tickVisible="true"
tools:value="6"
tools:valueTo="20" />
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>
@@ -88,8 +101,12 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
android:background="@drawable/bg_card"
android:backgroundTint="?colorBackgroundFloating"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:orientation="vertical"> android:orientation="vertical"
android:outlineProvider="background"
android:padding="@dimen/screen_padding">
<com.google.android.material.progressindicator.CircularProgressIndicator <com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/progressBar" android:id="@+id/progressBar"
@@ -101,9 +118,9 @@
android:id="@+id/textView_loading" android:id="@+id/textView_loading"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginTop="10dp"
android:text="@string/loading_" android:text="@string/loading_"
android:textAppearance="?attr/textAppearanceBody2" /> android:textAppearance="?attr/textAppearanceTitleMedium" />
</LinearLayout> </LinearLayout>

View File

@@ -5,15 +5,6 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
tools:ignore="AlwaysShowAction"> tools:ignore="AlwaysShowAction">
<item
android:id="@+id/action_slider"
android:icon="@drawable/ic_slider"
android:title="@string/reader_slider"
android:visible="false"
app:actionViewClass="com.google.android.material.slider.Slider"
app:showAsAction="always|collapseActionView"
tools:visible="true" />
<item <item
android:id="@+id/action_pages_thumbs" android:id="@+id/action_pages_thumbs"
android:icon="@drawable/ic_grid" android:icon="@drawable/ic_grid"
@@ -26,6 +17,7 @@
android:id="@+id/action_options" android:id="@+id/action_options"
android:icon="@drawable/abc_ic_menu_overflow_material" android:icon="@drawable/abc_ic_menu_overflow_material"
android:title="@string/options" android:title="@string/options"
app:showAsAction="always" /> app:showAsAction="always"
tools:visible="true" />
</menu> </menu>