Update reader ui
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
4
app/src/main/res/color-v23/bg_floating_button.xml
Normal file
4
app/src/main/res/color-v23/bg_floating_button.xml
Normal 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>
|
||||
5
app/src/main/res/color/bg_floating_button.xml
Normal file
5
app/src/main/res/color/bg_floating_button.xml
Normal 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>
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
13
app/src/main/res/menu/opt_reader_top.xml
Normal file
13
app/src/main/res/menu/opt_reader_top.xml
Normal 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>
|
||||
@@ -141,6 +141,7 @@
|
||||
|
||||
<declare-styleable name="ChipsView">
|
||||
<attr name="chipStyle" />
|
||||
<attr name="chipIconVisible" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="ReaderInfoBarView">
|
||||
|
||||
Reference in New Issue
Block a user