Update reader ui

This commit is contained in:
Koitharu
2024-12-23 09:48:50 +02:00
parent 22d203fc60
commit 099362d198
22 changed files with 234 additions and 361 deletions

View File

@@ -363,10 +363,6 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
val isReaderBarEnabled: Boolean
get() = prefs.getBoolean(KEY_READER_BAR, true)
var isReaderSliderEnabled: Boolean
get() = prefs.getBoolean(KEY_READER_SLIDER, true)
set(value) = prefs.edit { putBoolean(KEY_READER_SLIDER, value) }
val isReaderKeepScreenOn: Boolean
get() = prefs.getBoolean(KEY_READER_SCREEN_ON, true)
@@ -671,7 +667,6 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
const val KEY_SYNC = "sync"
const val KEY_SYNC_SETTINGS = "sync_settings"
const val KEY_READER_BAR = "reader_bar"
const val KEY_READER_SLIDER = "reader_slider"
const val KEY_READER_BACKGROUND = "reader_background"
const val KEY_READER_SCREEN_ON = "reader_screen_on"
const val KEY_SHORTCUTS = "dynamic_shortcuts"

View File

@@ -3,7 +3,6 @@ package org.koitharu.kotatsu.core.ui.widgets
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.View.OnClickListener
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
@@ -34,7 +33,7 @@ import com.google.android.material.R as materialR
class ChipsView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = com.google.android.material.R.attr.chipGroupStyle,
defStyleAttr: Int = materialR.attr.chipGroupStyle,
) : ChipGroup(context, attrs, defStyleAttr) {
@Inject
@@ -49,6 +48,7 @@ class ChipsView @JvmOverloads constructor(
onChipCloseClickListener?.onChipCloseClick(chip, data) ?: onChipClickListener?.onChipClick(chip, data)
}
private val chipStyle: Int
private val iconsVisible: Boolean
var onChipClickListener: OnChipClickListener? = null
set(value) {
field = value
@@ -60,6 +60,7 @@ class ChipsView @JvmOverloads constructor(
init {
val ta = context.obtainStyledAttributes(attrs, R.styleable.ChipsView, defStyleAttr, 0)
chipStyle = ta.getResourceId(R.styleable.ChipsView_chipStyle, R.style.Widget_Kotatsu_Chip)
iconsVisible = ta.getBoolean(R.styleable.ChipsView_chipIconVisible, true)
ta.recycle()
if (isInEditMode) {
@@ -170,12 +171,7 @@ class ChipsView @JvmOverloads constructor(
private fun bindIcon(model: ChipModel) {
when {
model.isChecked -> {
imageRequest?.dispose()
imageRequest = null
chipIcon = null
isChipIconVisible = false
}
model.isChecked -> disposeIcon()
model.isLoading -> {
imageRequest?.dispose()
@@ -184,6 +180,8 @@ class ChipsView @JvmOverloads constructor(
setProgressIcon()
}
!iconsVisible -> disposeIcon()
model.iconData != null -> {
val placeholder = model.icon.ifZero { materialR.drawable.navigation_empty_icon }
imageRequest = ImageRequest.Builder(context)
@@ -207,14 +205,16 @@ class ChipsView @JvmOverloads constructor(
isChipIconVisible = true
}
else -> {
imageRequest?.dispose()
imageRequest = null
chipIcon = null
isChipIconVisible = false
}
else -> disposeIcon()
}
}
private fun disposeIcon() {
imageRequest?.dispose()
imageRequest = null
chipIcon = null
isChipIconVisible = false
}
}
private inner class InternalChipClickListener : OnClickListener {

View File

@@ -4,14 +4,14 @@ import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
import com.google.android.material.button.MaterialButtonGroup
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.databinding.ViewZoomBinding
class ZoomControl @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
) : LinearLayout(context, attrs), View.OnClickListener {
) : MaterialButtonGroup(context, attrs), View.OnClickListener {
private val binding = ViewZoomBinding.inflate(LayoutInflater.from(context), this)

View File

@@ -1,6 +1,7 @@
package org.koitharu.kotatsu.core.util
import androidx.core.os.LocaleListCompat
import org.koitharu.kotatsu.core.util.ext.indexOfContains
import org.koitharu.kotatsu.core.util.ext.iterator
class LocaleStringComparator : Comparator<String?> {
@@ -14,7 +15,7 @@ class LocaleStringComparator : Comparator<String?> {
val set = HashSet<String?>(localeList.size() + 1)
set.add(null)
for (locale in localeList) {
val lang = locale.getDisplayLanguage(locale).lowercase()
val lang = locale.getDisplayLanguage(locale)
if (set.add(lang)) {
add(lang)
}
@@ -23,8 +24,8 @@ class LocaleStringComparator : Comparator<String?> {
}
override fun compare(a: String?, b: String?): Int {
val indexA = deviceLocales.indexOf(a?.lowercase())
val indexB = deviceLocales.indexOf(b?.lowercase())
val indexA = deviceLocales.indexOfContains(a, true)
val indexB = deviceLocales.indexOfContains(b, true)
return when {
indexA < 0 && indexB < 0 -> compareValues(a, b)
indexA < 0 -> 1

View File

@@ -108,3 +108,7 @@ fun <T, R> Collection<T>.mapSortedByCount(isDescending: Boolean = true, mapper:
}
return sorted.map { it.first }
}
fun Collection<CharSequence?>.indexOfContains(element: CharSequence?, ignoreCase: Boolean): Int = indexOfFirst { x ->
(x == null && element == null) || (x != null && element != null && x.contains(element, ignoreCase))
}

View File

@@ -91,8 +91,8 @@ class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(), Actio
}
val binding = viewBinding ?: return
val isActionModeStarted = actionModeDelegate?.isActionModeStarted == true
binding.toolbar.menuView?.isVisible = newState != STATE_COLLAPSED && !isActionModeStarted
binding.splitButtonRead.isVisible = newState == STATE_COLLAPSED && !isActionModeStarted
binding.toolbar.menuView?.isVisible = newState == STATE_EXPANDED && !isActionModeStarted
binding.splitButtonRead.isVisible = newState != STATE_EXPANDED && !isActionModeStarted
&& viewModel is DetailsViewModel
}

View File

@@ -145,12 +145,10 @@ class ReaderActivity :
viewModel.content.observe(this) {
onLoadingStateChanged(viewModel.isLoading.value)
}
viewModel.isSliderVisible.observe(this) { updateSliderVisibility() }
viewModel.isKeepScreenOnEnabled.observe(this, this::setKeepScreenOn)
viewModel.isInfoBarEnabled.observe(this, ::onReaderBarChanged)
val bottomMenuInvalidator = MenuInvalidator(viewBinding.toolbarBottom)
viewModel.isBookmarkAdded.observe(this, bottomMenuInvalidator)
viewModel.isPagesSheetEnabled.observe(this, bottomMenuInvalidator)
viewModel.isBookmarkAdded.observe(this, MenuInvalidator(this))
viewModel.isPagesSheetEnabled.observe(this, MenuInvalidator(viewBinding.toolbarBottom))
viewModel.onShowToast.observeEvent(this) { msgId ->
Snackbar.make(viewBinding.container, msgId, Snackbar.LENGTH_SHORT)
.setAnchorView(viewBinding.appbarBottom)
@@ -159,7 +157,8 @@ class ReaderActivity :
viewModel.isZoomControlsEnabled.observe(this) {
viewBinding.zoomControl.isVisible = it
}
viewBinding.toolbarBottom.addMenuProvider(ReaderMenuProvider(this, readerManager, viewModel))
addMenuProvider(ReaderMenuTopProvider(viewModel))
viewBinding.toolbarBottom.addMenuProvider(ReaderMenuBottomProvider(this, readerManager, viewModel))
}
override fun getParentActivityIntent(): Intent? {
@@ -234,7 +233,7 @@ class ReaderActivity :
rawX >= viewBinding.root.width - gestureInsets.right ||
rawY >= viewBinding.root.height - gestureInsets.bottom ||
viewBinding.appbarTop.hasGlobalPoint(rawX, rawY) ||
viewBinding.appbarBottom?.hasGlobalPoint(rawX, rawY) == true
viewBinding.appbarBottom.hasGlobalPoint(rawX, rawY) == true
) {
false
} else {
@@ -301,20 +300,14 @@ class ReaderActivity :
.setOrdering(TransitionSet.ORDERING_TOGETHER)
.addTransition(Slide(Gravity.TOP).addTarget(viewBinding.appbarTop))
.addTransition(Fade().addTarget(viewBinding.infoBar))
viewBinding.appbarBottom?.let { bottomBar ->
transition.addTransition(Slide(Gravity.BOTTOM).addTarget(bottomBar))
transition.addTransition(Slide(Gravity.BOTTOM).addTarget(viewBinding.floatingToolbar))
} ?: run {
transition.addTransition(Slide(Gravity.END).addTarget(viewBinding.floatingToolbar))
}
.addTransition(Slide(Gravity.BOTTOM).addTarget(viewBinding.appbarBottom))
TransitionManager.beginDelayedTransition(viewBinding.root, transition)
}
val isFullscreen = settings.isReaderFullscreenEnabled
viewBinding.appbarTop.isVisible = isUiVisible
viewBinding.appbarBottom?.isVisible = isUiVisible
viewBinding.appbarBottom.isVisible = isUiVisible
viewBinding.infoBar.isGone = isUiVisible || (!viewModel.isInfoBarEnabled.value)
viewBinding.infoBar.isTimeVisible = isFullscreen
updateSliderVisibility()
systemUiController.setSystemUiVisible(isUiVisible || !isFullscreen)
}
}
@@ -327,7 +320,7 @@ class ReaderActivity :
right = systemBars.right,
left = systemBars.left,
)
viewBinding.appbarBottom?.updateLayoutParams<MarginLayoutParams> {
viewBinding.appbarBottom.updateLayoutParams<MarginLayoutParams> {
bottomMargin = systemBars.bottom + topMargin
rightMargin = systemBars.right + topMargin
leftMargin = systemBars.left + topMargin
@@ -383,33 +376,31 @@ class ReaderActivity :
viewBinding.infoBar.update(uiState)
if (uiState == null) {
supportActionBar?.subtitle = null
updateSliderVisibility()
viewBinding.layoutSlider.isVisible = false
return
}
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()) {
viewBinding.toastView.showTemporary(uiState.chapterName, TOAST_DURATION)
}
if (uiState.chapterName != previous?.chapterName && !uiState.chapterName.isNullOrEmpty()) {
viewBinding.toastView.showTemporary(uiState.chapterName, TOAST_DURATION)
}
if (uiState.isSliderAvailable()) {
viewBinding.slider.valueTo = uiState.totalPages.toFloat() - 1
viewBinding.slider.setValueRounded(uiState.currentPage.toFloat())
} else {
viewBinding.slider.valueTo = 1f
viewBinding.slider.value = 0f
}
updateSliderVisibility()
}
private fun updateSliderVisibility() {
viewBinding.floatingToolbar.isVisible = viewBinding.appbarTop.isVisible &&
viewModel.isSliderVisible.value &&
viewModel.uiState.value?.isSliderAvailable() == true
viewBinding.slider.isEnabled = uiState.isSliderAvailable()
viewBinding.buttonNext.isEnabled = uiState.hasNextChapter()
viewBinding.buttonPrev.isEnabled = uiState.hasPreviousChapter()
viewBinding.layoutSlider.isVisible = true
}
companion object {
private const val TOAST_DURATION = 1500L
private const val TOAST_DURATION = 2000L
}
}

View File

@@ -8,14 +8,14 @@ import androidx.fragment.app.FragmentActivity
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.nav.router
class ReaderMenuProvider(
class ReaderMenuBottomProvider(
private val activity: FragmentActivity,
private val readerManager: ReaderManager,
private val viewModel: ReaderViewModel,
) : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.opt_reader, menu)
menuInflater.inflate(R.menu.opt_reader_bottom, menu)
onPrepareMenu(menu) // fix, not called in toolbar
}
@@ -27,15 +27,6 @@ class ReaderMenuProvider(
setIcon(if (viewModel.isPagesSheetEnabled.value) R.drawable.ic_grid else R.drawable.ic_list)
}
}
menu.findItem(R.id.action_bookmark)?.let { bookmarkItem ->
val hasPages = viewModel.content.value.pages.isNotEmpty()
bookmarkItem.isEnabled = hasPages
if (hasPages) {
val hasBookmark = viewModel.isBookmarkAdded.value
bookmarkItem.setTitle(if (hasBookmark) R.string.bookmark_remove else R.string.bookmark_add)
bookmarkItem.setIcon(if (hasBookmark) R.drawable.ic_bookmark_added else R.drawable.ic_bookmark)
}
}
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
@@ -52,11 +43,6 @@ class ReaderMenuProvider(
true
}
R.id.action_slider -> {
viewModel.setSliderVisibility(!viewModel.isSliderVisible.value)
true
}
R.id.action_bookmark -> {
if (viewModel.isBookmarkAdded.value) {
viewModel.removeBookmark()

View File

@@ -0,0 +1,43 @@
package org.koitharu.kotatsu.reader.ui
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import androidx.core.view.MenuProvider
import org.koitharu.kotatsu.R
class ReaderMenuTopProvider(
private val viewModel: ReaderViewModel,
) : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.opt_reader_top, menu)
}
override fun onPrepareMenu(menu: Menu) {
menu.findItem(R.id.action_bookmark)?.let { bookmarkItem ->
val hasPages = viewModel.content.value.pages.isNotEmpty()
bookmarkItem.isEnabled = hasPages
if (hasPages) {
val hasBookmark = viewModel.isBookmarkAdded.value
bookmarkItem.setTitle(if (hasBookmark) R.string.bookmark_remove else R.string.bookmark_add)
bookmarkItem.setIcon(if (hasBookmark) R.drawable.ic_bookmark_added else R.drawable.ic_bookmark)
}
}
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
return when (menuItem.itemId) {
R.id.action_bookmark -> {
if (viewModel.isBookmarkAdded.value) {
viewModel.removeBookmark()
} else {
viewModel.addBookmark()
}
true
}
else -> false
}
}
}

View File

@@ -1,16 +1,19 @@
package org.koitharu.kotatsu.reader.ui
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.content.Context
import android.util.AttributeSet
import android.view.Gravity
import android.view.ViewGroup
import android.view.ViewPropertyAnimator
import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator
import androidx.annotation.StringRes
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.transition.Fade
import androidx.transition.Slide
import androidx.transition.TransitionManager
import androidx.transition.TransitionSet
import com.google.android.material.textview.MaterialTextView
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.util.ext.getAnimationDuration
import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled
class ReaderToastView @JvmOverloads constructor(
context: Context,
@@ -18,6 +21,8 @@ class ReaderToastView @JvmOverloads constructor(
defStyleAttr: Int = 0,
) : MaterialTextView(context, attrs, defStyleAttr) {
private var currentAnimator: ViewPropertyAnimator? = null
private var hideRunnable = Runnable {
hide()
}
@@ -25,8 +30,7 @@ class ReaderToastView @JvmOverloads constructor(
fun show(message: CharSequence) {
removeCallbacks(hideRunnable)
text = message
setupTransition()
isVisible = true
showImpl()
}
fun show(@StringRes messageId: Int) {
@@ -40,8 +44,7 @@ class ReaderToastView @JvmOverloads constructor(
fun hide() {
removeCallbacks(hideRunnable)
setupTransition()
isVisible = false
hideImpl()
}
override fun onDetachedFromWindow() {
@@ -49,13 +52,41 @@ class ReaderToastView @JvmOverloads constructor(
super.onDetachedFromWindow()
}
private fun setupTransition() {
val parentView = parent as? ViewGroup ?: return
val transition = TransitionSet()
.setOrdering(TransitionSet.ORDERING_TOGETHER)
.addTarget(this)
.addTransition(Slide(Gravity.BOTTOM))
.addTransition(Fade())
TransitionManager.beginDelayedTransition(parentView, transition)
private fun showImpl() {
currentAnimator?.cancel()
clearAnimation()
if (!context.isAnimationsEnabled) {
currentAnimator = null
isVisible = true
return
}
alpha = 0f
isVisible = true
currentAnimator = animate()
.alpha(1f)
.setInterpolator(DecelerateInterpolator())
.setDuration(context.getAnimationDuration(R.integer.config_shorterAnimTime))
.setListener(null)
}
}
private fun hideImpl() {
currentAnimator?.cancel()
clearAnimation()
if (!context.isAnimationsEnabled) {
currentAnimator = null
isGone = true
return
}
currentAnimator = animate()
.alpha(0f)
.setInterpolator(AccelerateInterpolator())
.setDuration(context.getAnimationDuration(R.integer.config_shorterAnimTime))
.setListener(
object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
isGone = true
}
},
)
}
}

View File

@@ -21,9 +21,7 @@ import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.plus
@@ -132,12 +130,6 @@ class ReaderViewModel @Inject constructor(
valueProducer = { readerAnimation },
)
val isSliderVisible = settings.observeAsStateFlow(
scope = viewModelScope + Dispatchers.Default,
key = AppSettings.KEY_READER_SLIDER,
valueProducer = { isReaderSliderEnabled },
)
val isInfoBarEnabled = settings.observeAsStateFlow(
scope = viewModelScope + Dispatchers.Default,
key = AppSettings.KEY_READER_BAR,
@@ -199,10 +191,6 @@ class ReaderViewModel @Inject constructor(
init {
loadImpl()
settings.observe()
.onEach { key ->
if (key == AppSettings.KEY_READER_SLIDER) notifyStateChanged()
}.launchIn(viewModelScope + Dispatchers.Default)
launchJob(Dispatchers.Default) {
val mangaId = manga.filterNotNull().first().id
appShortcutManager.notifyMangaOpened(mangaId)
@@ -220,10 +208,6 @@ class ReaderViewModel @Inject constructor(
}
}
fun setSliderVisibility(visible: Boolean) {
settings.isReaderSliderEnabled = visible
}
fun switchMode(newMode: ReaderMode) {
launchJob {
val manga = checkNotNull(getMangaOrNull())
@@ -459,7 +443,6 @@ class ReaderViewModel @Inject constructor(
chaptersTotal = m.chapters[chapter.branch].sizeOrZero(),
totalPages = chaptersLoader.getPagesCount(chapter.id),
currentPage = state.page,
isSliderEnabled = settings.isReaderSliderEnabled,
percent = computePercent(state.chapterId, state.page),
incognito = incognitoMode.value,
)

View File

@@ -10,10 +10,11 @@ data class ReaderUiState(
val totalPages: Int,
val percent: Float,
val incognito: Boolean,
private val isSliderEnabled: Boolean,
) {
fun isSliderAvailable(): Boolean {
return isSliderEnabled && totalPages > 1 && currentPage < totalPages
}
fun hasNextChapter(): Boolean = chapterNumber < chaptersTotal
fun hasPreviousChapter(): Boolean = chapterNumber > 1
fun isSliderAvailable(): Boolean = totalPages > 1 && currentPage < totalPages
}

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="0.6" android:color="?colorSurfaceBright" />
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- https://stackoverflow.com/questions/54685474/theme-attributes-in-color-selector-for-api-22 -->
<item android:alpha="0.6" android:color="@color/kotatsu_surfaceBright" />
</selector>

View File

@@ -1,158 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<org.koitharu.kotatsu.reader.ui.ReaderInfoBarView
android:id="@+id/infoBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:paddingHorizontal="6dp"
android:paddingTop="8dp"
android:textSize="12sp"
android:visibility="gone"
tools:visibility="visible" />
<org.koitharu.kotatsu.core.ui.widgets.ZoomControl
android:id="@+id/zoomControl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:orientation="vertical"
android:visibility="gone"
app:layout_dodgeInsetEdges="bottom"
tools:visibility="visible" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="@dimen/m3_card_elevated_elevation"
app:elevation="@dimen/m3_card_elevated_elevation"
app:liftOnScroll="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<com.google.android.material.appbar.MaterialToolbar
android:id="@id/toolbar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
tools:menu="@menu/opt_reader_top" />
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar_bottom"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
tools:menu="@menu/opt_reader" />
</LinearLayout>
</com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.floatingtoolbar.FloatingToolbarLayout
android:id="@+id/floating_toolbar"
style="@style/Widget.Material3.FloatingToolbar.Vertical.Vibrant"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical">
<RelativeLayout
android:id="@+id/floating_toolbar_child"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical">
<com.google.android.material.slider.Slider
android:id="@+id/slider"
android:layout_width="wrap_content"
android:layout_height="240dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:orientation="vertical"
android:stepSize="1.0"
android:valueFrom="0"
app:labelBehavior="floating"
tools:value="6"
tools:valueTo="20" />
<ImageButton
android:id="@+id/button_prev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/slider"
android:layout_centerHorizontal="true"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/prev_chapter"
android:padding="@dimen/margin_small"
android:src="@drawable/ic_prev" />
<ImageButton
android:id="@+id/button_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/button_prev"
android:layout_centerHorizontal="true"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/next_chapter"
android:padding="@dimen/margin_small"
android:src="@drawable/ic_next" />
</RelativeLayout>
</com.google.android.material.floatingtoolbar.FloatingToolbarLayout>
<org.koitharu.kotatsu.reader.ui.ReaderToastView
android:id="@+id/toastView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="20dp"
android:background="@drawable/bg_reader_indicator"
android:drawablePadding="6dp"
android:singleLine="true"
android:textAppearance="?attr/textAppearanceBodySmall"
android:theme="@style/ThemeOverlay.Material3.Dark"
app:layout_dodgeInsetEdges="bottom"
tools:text="@string/loading_" />
<LinearLayout
android:id="@+id/layout_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_horizontal"
android:orientation="vertical">
<com.google.android.material.progressindicator.CircularProgressIndicator
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true" />
<TextView
android:id="@+id/textView_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/loading_"
android:textAppearance="?attr/textAppearanceBody2" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -19,6 +19,7 @@
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:orientation="vertical"
android:spacing="2dp"
android:visibility="gone"
app:layout_dodgeInsetEdges="bottom"
tools:visibility="visible" />
@@ -66,62 +67,56 @@
android:id="@+id/toolbar_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:menu="@menu/opt_reader" />
tools:menu="@menu/opt_reader_bottom">
<RelativeLayout
android:id="@+id/layout_slider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="2dp">
<ImageButton
android:id="@+id/button_prev"
style="?actionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:contentDescription="@string/prev_chapter"
android:src="@drawable/ic_prev"
android:tooltipText="@string/prev_chapter" />
<com.google.android.material.slider.Slider
android:id="@+id/slider"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toStartOf="@id/button_next"
android:layout_toEndOf="@id/button_prev"
android:stepSize="1.0"
android:valueFrom="0"
app:labelBehavior="floating"
tools:value="6"
tools:valueTo="20" />
<ImageButton
android:id="@+id/button_next"
style="?actionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:contentDescription="@string/next_chapter"
android:src="@drawable/ic_next"
android:tooltipText="@string/next_chapter" />
</RelativeLayout>
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.floatingtoolbar.FloatingToolbarLayout
android:id="@+id/floating_toolbar"
style="@style/Widget.Material3.FloatingToolbar.Horizontal.Vibrant"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_dodgeInsetEdges="bottom">
<RelativeLayout
android:id="@+id/floating_toolbar_child"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical">
<ImageButton
android:id="@+id/button_prev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/prev_chapter"
android:padding="@dimen/margin_small"
android:src="@drawable/ic_prev" />
<com.google.android.material.slider.Slider
android:id="@+id/slider"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toStartOf="@id/button_next"
android:layout_toEndOf="@id/button_prev"
android:stepSize="1.0"
android:valueFrom="0"
app:labelBehavior="floating"
tools:value="6"
tools:valueTo="20" />
<ImageButton
android:id="@+id/button_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/next_chapter"
android:padding="@dimen/margin_small"
android:src="@drawable/ic_next" />
</RelativeLayout>
</com.google.android.material.floatingtoolbar.FloatingToolbarLayout>
<org.koitharu.kotatsu.reader.ui.ReaderToastView
android:id="@+id/toastView"
android:layout_width="wrap_content"
@@ -130,6 +125,7 @@
android:layout_marginBottom="20dp"
android:background="@drawable/bg_reader_indicator"
android:drawablePadding="6dp"
android:elevation="1000dp"
android:singleLine="true"
android:textAppearance="?attr/textAppearanceBodySmall"
android:theme="@style/ThemeOverlay.Material3.Dark"

View File

@@ -25,6 +25,7 @@
android:clipToPadding="false"
android:paddingTop="2dp"
android:paddingBottom="6dp"
app:chipIconVisible="false"
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter"
app:singleLine="true" />

View File

@@ -5,39 +5,28 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:parentTag="android.widget.LinearLayout">
tools:orientation="vertical"
tools:parentTag="com.google.android.material.button.MaterialButtonGroup">
<com.google.android.material.imageview.ShapeableImageView
<com.google.android.material.button.MaterialButton
android:id="@+id/button_zoom_in"
android:layout_width="?minTouchTargetSize"
android:layout_height="?minTouchTargetSize"
android:layout_margin="4dp"
android:alpha="0.8"
android:background="@drawable/bg_circle_button"
style="@style/Widget.Material3.Button.IconButton.Outlined"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/zoom_in"
android:padding="1dp"
android:scaleType="centerInside"
android:src="@drawable/ic_zoom_in"
android:tooltipText="@string/zoom_in"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Circle"
app:strokeColor="?colorOutline"
app:strokeWidth="1dp" />
app:backgroundTint="@color/bg_floating_button"
app:icon="@drawable/ic_zoom_in" />
<com.google.android.material.imageview.ShapeableImageView
<com.google.android.material.button.MaterialButton
android:id="@+id/button_zoom_out"
android:layout_width="?minTouchTargetSize"
android:layout_height="?minTouchTargetSize"
android:layout_margin="4dp"
android:alpha="0.8"
android:background="@drawable/bg_circle_button"
style="@style/Widget.Material3.Button.IconButton.Outlined"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/zoom_out"
android:padding="1dp"
android:scaleType="centerInside"
android:src="@drawable/ic_zoom_out"
android:tooltipText="@string/zoom_out"
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Circle"
app:strokeColor="?colorOutline"
app:strokeWidth="1dp" />
app:backgroundTint="@color/bg_floating_button"
app:icon="@drawable/ic_zoom_out" />
</merge>

View File

@@ -5,19 +5,6 @@
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="AlwaysShowAction">
<item
android:id="@+id/action_slider"
android:icon="@drawable/ic_move_horizontal"
android:title="@string/show_slider"
app:showAsAction="always" />
<item
android:id="@+id/action_bookmark"
android:enabled="false"
android:icon="@drawable/ic_bookmark"
android:title="@string/bookmark_add"
app:showAsAction="always" />
<item
android:id="@+id/action_pages_thumbs"
android:icon="@drawable/ic_grid"

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_bookmark"
android:enabled="false"
android:icon="@drawable/ic_bookmark"
android:title="@string/bookmark_add"
app:showAsAction="always" />
</menu>

View File

@@ -141,6 +141,7 @@
<declare-styleable name="ChipsView">
<attr name="chipStyle" />
<attr name="chipIconVisible" />
</declare-styleable>
<declare-styleable name="ReaderInfoBarView">