Update reader actions bar
This commit is contained in:
@@ -170,7 +170,7 @@ private fun SpannableStringBuilder.appendTagsSummary(filter: MangaListFilter) {
|
||||
}
|
||||
}
|
||||
|
||||
fun MangaChapter.getLocalizedTitle(resources: Resources): String {
|
||||
fun MangaChapter.getLocalizedTitle(resources: Resources, index: Int = -1): String {
|
||||
title?.let {
|
||||
if (it.isNotBlank()) {
|
||||
return it
|
||||
@@ -181,6 +181,12 @@ fun MangaChapter.getLocalizedTitle(resources: Resources): String {
|
||||
return when {
|
||||
num != null && vol != null -> resources.getString(R.string.chapter_volume_number, vol, num)
|
||||
num != null -> resources.getString(R.string.chapter_number, num)
|
||||
else -> resources.getString(R.string.unnamed_chapter) // TODO fallback to manga title + index
|
||||
index > 0 -> resources.getString(
|
||||
R.string.chapters_time_pattern,
|
||||
resources.getString(R.string.unnamed_chapter),
|
||||
index.toString(),
|
||||
)
|
||||
|
||||
else -> resources.getString(R.string.unnamed_chapter)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ data class MangaDetails(
|
||||
|
||||
val coverUrl: String?
|
||||
get() = manga.largeCoverUrl
|
||||
.ifNullOrEmpty { manga.largeCoverUrl }
|
||||
.ifNullOrEmpty { manga.coverUrl }
|
||||
.ifNullOrEmpty { localManga?.manga?.coverUrl }
|
||||
?.nullIfEmpty()
|
||||
|
||||
|
||||
@@ -69,7 +69,6 @@ data class ChapterListItem(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun buildDescription(): String {
|
||||
val joiner = StringJoiner(" • ")
|
||||
chapter.numberString()?.let {
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
package org.koitharu.kotatsu.reader.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.database.ContentObserver
|
||||
import android.provider.Settings
|
||||
import android.util.AttributeSet
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.LinearLayout
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import com.google.android.material.slider.Slider
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.nav.AppRouter
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderControl
|
||||
import org.koitharu.kotatsu.core.util.ext.isRtl
|
||||
import org.koitharu.kotatsu.core.util.ext.setValueRounded
|
||||
import org.koitharu.kotatsu.databinding.LayoutReaderActionsBinding
|
||||
import org.koitharu.kotatsu.details.ui.pager.ChaptersPagesSheet.Companion.TAB_PAGES
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderControlDelegate.OnInteractionListener
|
||||
import javax.inject.Inject
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ReaderActionsView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
@AttrRes defStyleAttr: Int = 0,
|
||||
) : LinearLayout(context, attrs, defStyleAttr),
|
||||
View.OnClickListener,
|
||||
SharedPreferences.OnSharedPreferenceChangeListener,
|
||||
Slider.OnChangeListener,
|
||||
Slider.OnSliderTouchListener {
|
||||
|
||||
@Inject
|
||||
lateinit var settings: AppSettings
|
||||
|
||||
private val binding = LayoutReaderActionsBinding.inflate(LayoutInflater.from(context), this)
|
||||
private val rotationObserver = object : ContentObserver(handler) {
|
||||
override fun onChange(selfChange: Boolean) {
|
||||
updateRotationButton()
|
||||
}
|
||||
}
|
||||
private var isSliderChanged = false
|
||||
private var isSliderTracking = false
|
||||
|
||||
var isSliderEnabled: Boolean
|
||||
get() = binding.slider.isEnabled
|
||||
set(value) {
|
||||
binding.slider.isEnabled = value
|
||||
binding.slider.setThumbVisible(value)
|
||||
}
|
||||
|
||||
var isNextEnabled: Boolean
|
||||
get() = binding.buttonNext.isEnabled
|
||||
set(value) {
|
||||
binding.buttonNext.isEnabled = value
|
||||
}
|
||||
|
||||
var isPrevEnabled: Boolean
|
||||
get() = binding.buttonPrev.isEnabled
|
||||
set(value) {
|
||||
binding.buttonPrev.isEnabled = value
|
||||
}
|
||||
|
||||
var listener: OnInteractionListener? = null
|
||||
|
||||
init {
|
||||
orientation = HORIZONTAL
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
binding.buttonNext.initAction()
|
||||
binding.buttonPrev.initAction()
|
||||
binding.buttonSave.initAction()
|
||||
binding.buttonOptions.initAction()
|
||||
binding.buttonScreenRotation.initAction()
|
||||
binding.buttonPagesThumbs.initAction()
|
||||
binding.slider.setLabelFormatter(PageLabelFormatter())
|
||||
binding.slider.addOnChangeListener(this)
|
||||
binding.slider.addOnSliderTouchListener(this)
|
||||
updateControlsVisibility()
|
||||
updatePagesSheetButton()
|
||||
updateRotationButton()
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
settings.subscribe(this)
|
||||
context.contentResolver.registerContentObserver(
|
||||
Settings.System.CONTENT_URI, true, rotationObserver,
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
settings.unsubscribe(this)
|
||||
context.contentResolver.unregisterContentObserver(rotationObserver)
|
||||
super.onDetachedFromWindow()
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.button_prev -> listener?.switchChapterBy(-1)
|
||||
R.id.button_next -> listener?.switchChapterBy(1)
|
||||
R.id.button_save -> listener?.onSavePageClick()
|
||||
R.id.button_pages_thumbs -> AppRouter.from(this)?.showChapterPagesSheet()
|
||||
R.id.button_screen_rotation -> listener?.toggleScreenOrientation()
|
||||
R.id.button_options -> listener?.openMenu()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
if (isSliderTracking) {
|
||||
isSliderChanged = true
|
||||
} else {
|
||||
listener?.switchPageTo(value.toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(slider: Slider) {
|
||||
isSliderChanged = false
|
||||
isSliderTracking = true
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(slider: Slider) {
|
||||
isSliderTracking = false
|
||||
if (isSliderChanged) {
|
||||
listener?.switchPageTo(slider.value.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
when (key) {
|
||||
AppSettings.KEY_READER_CONTROLS -> updateControlsVisibility()
|
||||
AppSettings.KEY_PAGES_TAB,
|
||||
AppSettings.KEY_DETAILS_TAB,
|
||||
AppSettings.KEY_DETAILS_LAST_TAB -> updatePagesSheetButton()
|
||||
}
|
||||
}
|
||||
|
||||
fun setSliderValue(value: Int, max: Int) {
|
||||
binding.slider.valueTo = max.toFloat()
|
||||
binding.slider.setValueRounded(value.toFloat())
|
||||
}
|
||||
|
||||
fun setSliderReversed(reversed: Boolean) {
|
||||
binding.slider.isRtl = reversed != isRtl
|
||||
}
|
||||
|
||||
private fun updateControlsVisibility() {
|
||||
val controls = settings.readerControls
|
||||
binding.buttonPrev.isVisible = ReaderControl.PREV_CHAPTER in controls
|
||||
binding.buttonNext.isVisible = ReaderControl.NEXT_CHAPTER in controls
|
||||
binding.buttonPagesThumbs.isVisible = ReaderControl.PAGES_SHEET in controls
|
||||
binding.buttonScreenRotation.isVisible = ReaderControl.SCREEN_ROTATION in controls
|
||||
binding.buttonSave.isVisible = ReaderControl.SAVE_PAGE in controls
|
||||
binding.slider.isVisible = ReaderControl.SLIDER in controls
|
||||
adjustLayoutParams()
|
||||
}
|
||||
|
||||
private fun updatePagesSheetButton() {
|
||||
val isPagesMode = settings.defaultDetailsTab == TAB_PAGES
|
||||
val button = binding.buttonPagesThumbs
|
||||
button.setIconResource(
|
||||
if (isPagesMode) R.drawable.ic_grid else R.drawable.ic_list,
|
||||
)
|
||||
button.setTitle(
|
||||
if (isPagesMode) R.string.pages else R.string.chapters,
|
||||
)
|
||||
}
|
||||
|
||||
private fun adjustLayoutParams() {
|
||||
val isSliderVisible = binding.slider.isVisible
|
||||
repeat(childCount) { i ->
|
||||
val child = getChildAt(i)
|
||||
if (child is FrameLayout) {
|
||||
child.updateLayoutParams<LayoutParams> {
|
||||
width = if (isSliderVisible) LayoutParams.WRAP_CONTENT else 0
|
||||
weight = if (isSliderVisible) 0f else 1f
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateRotationButton() {
|
||||
val button = binding.buttonScreenRotation
|
||||
when {
|
||||
!button.isVisible -> return
|
||||
isAutoRotationEnabled() -> {
|
||||
button.setTitle(R.string.lock_screen_rotation)
|
||||
button.setIconResource(R.drawable.ic_screen_rotation_lock)
|
||||
}
|
||||
|
||||
else -> {
|
||||
button.setTitle(R.string.rotate_screen)
|
||||
button.setIconResource(R.drawable.ic_screen_rotation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Button.initAction() {
|
||||
setOnClickListener(this@ReaderActionsView)
|
||||
ViewCompat.setTooltipText(this, contentDescription)
|
||||
}
|
||||
|
||||
private fun Button.setTitle(@StringRes titleResId: Int) {
|
||||
val text = resources.getString(titleResId)
|
||||
contentDescription = text
|
||||
ViewCompat.setTooltipText(this, text)
|
||||
}
|
||||
|
||||
private fun isAutoRotationEnabled(): Boolean = Settings.System.getInt(
|
||||
context.contentResolver,
|
||||
Settings.System.ACCELEROMETER_ROTATION,
|
||||
0,
|
||||
) == 1
|
||||
|
||||
private fun Slider.setThumbVisible(visible: Boolean) {
|
||||
thumbWidth = if (visible) {
|
||||
resources.getDimensionPixelSize(materialR.dimen.m3_comp_slider_active_handle_width)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
thumbHeight = if (visible) {
|
||||
resources.getDimensionPixelSize(materialR.dimen.m3_comp_slider_active_handle_height)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,10 @@ import android.view.Gravity
|
||||
import android.view.KeyEvent
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.MenuHost
|
||||
import androidx.core.view.OnApplyWindowInsetsListener
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
@@ -35,7 +34,6 @@ import org.koitharu.kotatsu.core.exceptions.resolve.DialogErrorObserver
|
||||
import org.koitharu.kotatsu.core.nav.AppRouter
|
||||
import org.koitharu.kotatsu.core.nav.router
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderControl
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderMode
|
||||
import org.koitharu.kotatsu.core.ui.BaseFullscreenActivity
|
||||
import org.koitharu.kotatsu.core.ui.util.MenuInvalidator
|
||||
@@ -43,11 +41,9 @@ import org.koitharu.kotatsu.core.ui.widgets.ZoomControl
|
||||
import org.koitharu.kotatsu.core.util.IdlingDetector
|
||||
import org.koitharu.kotatsu.core.util.ext.hasGlobalPoint
|
||||
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.observeEvent
|
||||
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.databinding.ActivityReaderBinding
|
||||
import org.koitharu.kotatsu.details.ui.pager.pages.PagesSavedObserver
|
||||
@@ -100,9 +96,6 @@ class ReaderActivity :
|
||||
scrollTimer.isEnabled = value
|
||||
}
|
||||
|
||||
private val secondaryMenuHost: MenuHost
|
||||
get() = viewBinding.toolbarBottom ?: this
|
||||
|
||||
private lateinit var scrollTimer: ScrollTimer
|
||||
private lateinit var pageSaveHelper: PageSaveHelper
|
||||
private lateinit var touchHelper: TapGridDispatcher
|
||||
@@ -120,13 +113,11 @@ class ReaderActivity :
|
||||
scrollTimer = scrollTimerFactory.create(this, this)
|
||||
pageSaveHelper = pageSaveHelperFactory.create(this)
|
||||
controlDelegate = ReaderControlDelegate(resources, settings, tapGridSettings, this)
|
||||
viewBinding.slider.setLabelFormatter(PageLabelFormatter())
|
||||
viewBinding.zoomControl.listener = this
|
||||
ReaderSliderListener(viewModel, this).attachToSlider(viewBinding.slider)
|
||||
viewBinding.actionsView.listener = this
|
||||
idlingDetector.bindToLifecycle(this)
|
||||
viewBinding.buttonPrev.setOnClickListener(controlDelegate)
|
||||
viewBinding.buttonNext.setOnClickListener(controlDelegate)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(viewBinding.root, this)
|
||||
screenOrientationHelper.applySettings()
|
||||
|
||||
viewModel.onError.observeEvent(
|
||||
this,
|
||||
@@ -150,17 +141,13 @@ class ReaderActivity :
|
||||
viewModel.content.observe(this) {
|
||||
onLoadingStateChanged(viewModel.isLoading.value)
|
||||
}
|
||||
viewModel.readerControls.observe(this, ::onReaderControlsChanged)
|
||||
viewModel.isKeepScreenOnEnabled.observe(this, this::setKeepScreenOn)
|
||||
viewModel.isInfoBarTransparent.observe(this) { viewBinding.infoBar.drawBackground = !it }
|
||||
viewModel.isInfoBarEnabled.observe(this, ::onReaderBarChanged)
|
||||
viewModel.isBookmarkAdded.observe(this, MenuInvalidator(this))
|
||||
val bottomMenuInvalidator = MenuInvalidator(secondaryMenuHost)
|
||||
viewModel.isPagesSheetEnabled.observe(this, bottomMenuInvalidator)
|
||||
screenOrientationHelper.observeAutoOrientation().observe(this, bottomMenuInvalidator)
|
||||
viewModel.onShowToast.observeEvent(this) { msgId ->
|
||||
Snackbar.make(viewBinding.container, msgId, Snackbar.LENGTH_SHORT)
|
||||
.setAnchorView(viewBinding.appbarBottom)
|
||||
.setAnchorView(viewBinding.toolbarDocked)
|
||||
.show()
|
||||
}
|
||||
viewModel.readerSettings.observe(this) {
|
||||
@@ -169,10 +156,7 @@ class ReaderActivity :
|
||||
viewModel.isZoomControlsEnabled.observe(this) {
|
||||
viewBinding.zoomControl.isVisible = it
|
||||
}
|
||||
addMenuProvider(ReaderMenuTopProvider(viewModel))
|
||||
secondaryMenuHost.addMenuProvider(
|
||||
ReaderMenuBottomProvider(this, readerManager, screenOrientationHelper, this, viewModel),
|
||||
)
|
||||
addMenuProvider(ReaderMenuProvider(viewModel))
|
||||
}
|
||||
|
||||
override fun getParentActivityIntent(): Intent? {
|
||||
@@ -215,7 +199,7 @@ class ReaderActivity :
|
||||
if (viewBinding.appbarTop.isVisible) {
|
||||
lifecycle.postDelayed(TimeUnit.SECONDS.toMillis(1), hideUiRunnable)
|
||||
}
|
||||
viewBinding.slider.isRtl = mode == ReaderMode.REVERSED
|
||||
viewBinding.actionsView.setSliderReversed(mode == ReaderMode.REVERSED)
|
||||
}
|
||||
|
||||
private fun onLoadingStateChanged(isLoading: Boolean) {
|
||||
@@ -226,7 +210,6 @@ class ReaderActivity :
|
||||
} else {
|
||||
viewBinding.toastView.hide()
|
||||
}
|
||||
secondaryMenuHost.invalidateMenu()
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
|
||||
@@ -247,7 +230,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.toolbarDocked?.hasGlobalPoint(rawX, rawY) == true
|
||||
) {
|
||||
false
|
||||
} else {
|
||||
@@ -263,7 +246,7 @@ class ReaderActivity :
|
||||
}
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
return controlDelegate.onKeyDown(keyCode) || super.onKeyDown(keyCode, event)
|
||||
return controlDelegate.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event)
|
||||
}
|
||||
|
||||
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
@@ -307,13 +290,6 @@ class ReaderActivity :
|
||||
}
|
||||
}
|
||||
|
||||
private fun onReaderControlsChanged(controls: Set<ReaderControl>) = with(viewBinding) {
|
||||
buttonPrev.isVisible = ReaderControl.PREV_CHAPTER in controls
|
||||
buttonNext.isVisible = ReaderControl.NEXT_CHAPTER in controls
|
||||
slider.isVisible = ReaderControl.SLIDER in controls
|
||||
secondaryMenuHost.invalidateMenu()
|
||||
}
|
||||
|
||||
private fun setUiIsVisible(isUiVisible: Boolean) {
|
||||
if (viewBinding.appbarTop.isVisible != isUiVisible) {
|
||||
if (isAnimationsEnabled) {
|
||||
@@ -321,12 +297,12 @@ class ReaderActivity :
|
||||
.setOrdering(TransitionSet.ORDERING_TOGETHER)
|
||||
.addTransition(Slide(Gravity.TOP).addTarget(viewBinding.appbarTop))
|
||||
.addTransition(Fade().addTarget(viewBinding.infoBar))
|
||||
.addTransition(Slide(Gravity.BOTTOM).addTarget(viewBinding.appbarBottom))
|
||||
.addTransition(Slide(Gravity.BOTTOM).addTarget(viewBinding.toolbarDocked))
|
||||
TransitionManager.beginDelayedTransition(viewBinding.root, transition)
|
||||
}
|
||||
val isFullscreen = settings.isReaderFullscreenEnabled
|
||||
viewBinding.appbarTop.isVisible = isUiVisible
|
||||
viewBinding.appbarBottom?.isVisible = isUiVisible
|
||||
viewBinding.toolbarDocked?.isVisible = isUiVisible
|
||||
viewBinding.infoBar.isGone = isUiVisible || (!viewModel.isInfoBarEnabled.value)
|
||||
viewBinding.infoBar.isTimeVisible = isFullscreen
|
||||
systemUiController.setSystemUiVisible(isUiVisible || !isFullscreen)
|
||||
@@ -341,10 +317,12 @@ class ReaderActivity :
|
||||
right = systemBars.right,
|
||||
left = systemBars.left,
|
||||
)
|
||||
viewBinding.appbarBottom?.updateLayoutParams<MarginLayoutParams> {
|
||||
bottomMargin = systemBars.bottom + topMargin
|
||||
rightMargin = systemBars.right + topMargin
|
||||
leftMargin = systemBars.left + topMargin
|
||||
if (viewBinding.toolbarDocked != null) {
|
||||
viewBinding.actionsView.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
bottomMargin = systemBars.bottom
|
||||
rightMargin = systemBars.right
|
||||
leftMargin = systemBars.left
|
||||
}
|
||||
}
|
||||
viewBinding.infoBar.updatePadding(
|
||||
top = systemBars.top,
|
||||
@@ -385,6 +363,28 @@ class ReaderActivity :
|
||||
viewModel.saveCurrentPage(pageSaveHelper)
|
||||
}
|
||||
|
||||
override fun toggleScreenOrientation() {
|
||||
if (screenOrientationHelper.toggleScreenOrientation()) {
|
||||
Snackbar.make(
|
||||
viewBinding.container,
|
||||
if (screenOrientationHelper.isLocked) {
|
||||
R.string.screen_rotation_locked
|
||||
} else {
|
||||
R.string.screen_rotation_unlocked
|
||||
},
|
||||
Snackbar.LENGTH_SHORT,
|
||||
).setAnchorView(viewBinding.toolbarDocked)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun switchPageTo(index: Int) {
|
||||
val pages = viewModel.getCurrentChapterPages()
|
||||
val page = pages?.getOrNull(index) ?: return
|
||||
val chapterId = viewModel.getCurrentState()?.chapterId ?: return
|
||||
onPageSelected(ReaderPage(page, index, chapterId))
|
||||
}
|
||||
|
||||
private fun onReaderBarChanged(isBarEnabled: Boolean) {
|
||||
viewBinding.infoBar.isVisible = isBarEnabled && viewBinding.appbarTop.isGone
|
||||
}
|
||||
@@ -395,27 +395,29 @@ class ReaderActivity :
|
||||
viewBinding.infoBar.update(uiState)
|
||||
if (uiState == null) {
|
||||
supportActionBar?.subtitle = null
|
||||
viewBinding.layoutSlider.isVisible = false
|
||||
viewBinding.actionsView.setSliderValue(0, 1)
|
||||
viewBinding.actionsView.isSliderEnabled = false
|
||||
return
|
||||
}
|
||||
val chapterTitle = uiState.getChapterTitle(resources)
|
||||
supportActionBar?.subtitle = when {
|
||||
uiState.incognito -> getString(R.string.incognito_mode)
|
||||
else -> uiState.chapterName
|
||||
else -> chapterTitle
|
||||
}
|
||||
if (uiState.chapterName != previous?.chapterName && !uiState.chapterName.isNullOrEmpty()) {
|
||||
viewBinding.toastView.showTemporary(uiState.chapterName, TOAST_DURATION)
|
||||
if (chapterTitle != previous?.getChapterTitle(resources) && chapterTitle.isNotEmpty()) {
|
||||
viewBinding.toastView.showTemporary(chapterTitle, TOAST_DURATION)
|
||||
}
|
||||
if (uiState.isSliderAvailable()) {
|
||||
viewBinding.slider.valueTo = uiState.totalPages.toFloat() - 1
|
||||
viewBinding.slider.setValueRounded(uiState.currentPage.toFloat())
|
||||
viewBinding.actionsView.setSliderValue(
|
||||
value = uiState.currentPage,
|
||||
max = uiState.totalPages - 1,
|
||||
)
|
||||
} else {
|
||||
viewBinding.slider.valueTo = 1f
|
||||
viewBinding.slider.value = 0f
|
||||
viewBinding.actionsView.setSliderValue(0, 1)
|
||||
}
|
||||
viewBinding.slider.isEnabled = uiState.isSliderAvailable()
|
||||
viewBinding.buttonNext.isEnabled = uiState.hasNextChapter()
|
||||
viewBinding.buttonPrev.isEnabled = uiState.hasPreviousChapter()
|
||||
viewBinding.layoutSlider.isVisible = true
|
||||
viewBinding.actionsView.isSliderEnabled = uiState.isSliderAvailable()
|
||||
viewBinding.actionsView.isNextEnabled = uiState.hasNextChapter()
|
||||
viewBinding.actionsView.isPrevEnabled = uiState.hasPreviousChapter()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.koitharu.kotatsu.core.prefs.ReaderMode
|
||||
import org.koitharu.kotatsu.reader.data.TapGridSettings
|
||||
import org.koitharu.kotatsu.reader.domain.TapGridArea
|
||||
import org.koitharu.kotatsu.reader.ui.tapgrid.TapAction
|
||||
import kotlin.math.sign
|
||||
|
||||
class ReaderControlDelegate(
|
||||
resources: Resources,
|
||||
@@ -43,77 +44,48 @@ class ReaderControlDelegate(
|
||||
processAction(action)
|
||||
}
|
||||
|
||||
fun onKeyDown(keyCode: Int): Boolean = when (keyCode) {
|
||||
fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
when (keyCode) {
|
||||
KeyEvent.KEYCODE_NAVIGATE_NEXT,
|
||||
KeyEvent.KEYCODE_SPACE -> switchBy(1, event, false)
|
||||
|
||||
KeyEvent.KEYCODE_R -> {
|
||||
listener.switchPageBy(1)
|
||||
true
|
||||
}
|
||||
KeyEvent.KEYCODE_PAGE_DOWN -> switchBy(1, event, false)
|
||||
|
||||
KeyEvent.KEYCODE_L -> {
|
||||
listener.switchPageBy(-1)
|
||||
true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_VOLUME_UP -> if (settings.isReaderVolumeButtonsEnabled) {
|
||||
listener.switchPageBy(-1)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
KeyEvent.KEYCODE_NAVIGATE_PREVIOUS -> switchBy(-1, event, false)
|
||||
KeyEvent.KEYCODE_PAGE_UP -> switchBy(-1, event, false)
|
||||
|
||||
KeyEvent.KEYCODE_VOLUME_DOWN -> if (settings.isReaderVolumeButtonsEnabled) {
|
||||
listener.switchPageBy(1)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
KeyEvent.KEYCODE_R -> switchBy(1, null, false)
|
||||
|
||||
KeyEvent.KEYCODE_SPACE,
|
||||
KeyEvent.KEYCODE_PAGE_DOWN,
|
||||
-> {
|
||||
listener.switchPageBy(1)
|
||||
true
|
||||
}
|
||||
KeyEvent.KEYCODE_L -> switchBy(-1, null, false)
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT -> {
|
||||
listener.switchPageBy(if (isReaderTapsReversed()) -1 else 1)
|
||||
true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_PAGE_UP,
|
||||
-> {
|
||||
listener.switchPageBy(-1)
|
||||
true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_LEFT -> {
|
||||
listener.switchPageBy(if (isReaderTapsReversed()) 1 else -1)
|
||||
true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_CENTER -> {
|
||||
listener.toggleUiVisibility()
|
||||
true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP,
|
||||
KeyEvent.KEYCODE_DPAD_UP -> {
|
||||
if (!listener.scrollBy(-minScrollDelta, smooth = true)) {
|
||||
listener.switchPageBy(-1)
|
||||
KeyEvent.KEYCODE_VOLUME_UP -> if (settings.isReaderVolumeButtonsEnabled) {
|
||||
switchBy(-1, event, false)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN,
|
||||
KeyEvent.KEYCODE_DPAD_DOWN -> {
|
||||
if (!listener.scrollBy(minScrollDelta, smooth = true)) {
|
||||
listener.switchPageBy(1)
|
||||
KeyEvent.KEYCODE_VOLUME_DOWN -> if (settings.isReaderVolumeButtonsEnabled) {
|
||||
switchBy(1, event, false)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT -> switchByRelative(-1, event)
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_LEFT -> switchByRelative(1, event)
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_CENTER -> listener.toggleUiVisibility()
|
||||
|
||||
KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP,
|
||||
KeyEvent.KEYCODE_DPAD_UP -> switchBy(-1, event, true)
|
||||
|
||||
KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN,
|
||||
KeyEvent.KEYCODE_DPAD_DOWN -> switchBy(1, event, true)
|
||||
|
||||
else -> return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun onKeyUp(keyCode: Int, @Suppress("UNUSED_PARAMETER") event: KeyEvent?): Boolean {
|
||||
@@ -136,12 +108,30 @@ class ReaderControlDelegate(
|
||||
return settings.isReaderControlAlwaysLTR && listener.readerMode == ReaderMode.REVERSED
|
||||
}
|
||||
|
||||
private fun switchBy(delta: Int, event: KeyEvent?, scroll: Boolean) {
|
||||
if (event?.isCtrlPressed == true) {
|
||||
listener.switchChapterBy(delta)
|
||||
} else if (scroll) {
|
||||
if (!listener.scrollBy(minScrollDelta * delta.sign, smooth = true)) {
|
||||
listener.switchPageBy(delta)
|
||||
}
|
||||
} else {
|
||||
listener.switchPageBy(delta)
|
||||
}
|
||||
}
|
||||
|
||||
private fun switchByRelative(delta: Int, event: KeyEvent?) {
|
||||
return switchBy(if (isReaderTapsReversed()) -delta else delta, event, scroll = false)
|
||||
}
|
||||
|
||||
interface OnInteractionListener {
|
||||
|
||||
val readerMode: ReaderMode?
|
||||
|
||||
fun switchPageBy(delta: Int)
|
||||
|
||||
fun switchPageTo(index: Int)
|
||||
|
||||
fun switchChapterBy(delta: Int)
|
||||
|
||||
fun scrollBy(delta: Int, smooth: Boolean): Boolean
|
||||
@@ -150,6 +140,10 @@ class ReaderControlDelegate(
|
||||
|
||||
fun openMenu()
|
||||
|
||||
fun onSavePageClick()
|
||||
|
||||
fun toggleScreenOrientation()
|
||||
|
||||
fun isReaderResumed(): Boolean
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
package org.koitharu.kotatsu.reader.ui
|
||||
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.nav.router
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderControl
|
||||
import org.koitharu.kotatsu.reader.ui.config.ReaderConfigSheet
|
||||
|
||||
class ReaderMenuBottomProvider(
|
||||
private val activity: FragmentActivity,
|
||||
private val readerManager: ReaderManager,
|
||||
private val screenOrientationHelper: ScreenOrientationHelper,
|
||||
private val configCallback: ReaderConfigSheet.Callback,
|
||||
private val viewModel: ReaderViewModel,
|
||||
) : MenuProvider {
|
||||
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.opt_reader_bottom, menu)
|
||||
onPrepareMenu(menu) // fix, not called in toolbar
|
||||
}
|
||||
|
||||
override fun onPrepareMenu(menu: Menu) {
|
||||
val readerControls = viewModel.readerControls.value
|
||||
val hasPages = viewModel.content.value.pages.isNotEmpty()
|
||||
val isPagesSheetEnabled = hasPages && ReaderControl.PAGES_SHEET in readerControls
|
||||
menu.findItem(R.id.action_pages_thumbs).run {
|
||||
isVisible = isPagesSheetEnabled
|
||||
if (isPagesSheetEnabled) {
|
||||
setIcon(if (viewModel.isPagesSheetEnabled.value) R.drawable.ic_grid else R.drawable.ic_list)
|
||||
}
|
||||
}
|
||||
menu.findItem(R.id.action_screen_rotation).run {
|
||||
isVisible = ReaderControl.SCREEN_ROTATION in readerControls
|
||||
when {
|
||||
!isVisible -> Unit
|
||||
!screenOrientationHelper.isAutoRotationEnabled -> {
|
||||
setTitle(R.string.rotate_screen)
|
||||
setIcon(R.drawable.ic_screen_rotation)
|
||||
}
|
||||
|
||||
else -> {
|
||||
setTitle(R.string.lock_screen_rotation)
|
||||
setIcon(R.drawable.ic_screen_rotation_lock)
|
||||
}
|
||||
}
|
||||
}
|
||||
menu.findItem(R.id.action_save_page)?.run {
|
||||
isVisible = hasPages && ReaderControl.SAVE_PAGE in readerControls
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
return when (menuItem.itemId) {
|
||||
R.id.action_screen_rotation -> {
|
||||
toggleScreenRotation()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_save_page -> {
|
||||
configCallback.onSavePageClick()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_pages_thumbs -> {
|
||||
activity.router.showChapterPagesSheet()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_options -> {
|
||||
viewModel.saveCurrentState(readerManager.currentReader?.getCurrentState())
|
||||
val currentMode = readerManager.currentMode ?: return false
|
||||
activity.router.showReaderConfigSheet(currentMode)
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_bookmark -> {
|
||||
if (viewModel.isBookmarkAdded.value) {
|
||||
viewModel.removeBookmark()
|
||||
} else {
|
||||
viewModel.addBookmark()
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleScreenRotation() = with(screenOrientationHelper) {
|
||||
if (isAutoRotationEnabled) {
|
||||
val newValue = !isLocked
|
||||
isLocked = newValue
|
||||
Toast.makeText(
|
||||
activity,
|
||||
if (newValue) {
|
||||
R.string.screen_rotation_locked
|
||||
} else {
|
||||
R.string.screen_rotation_unlocked
|
||||
},
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
} else {
|
||||
isLandscape = !isLandscape
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,12 +6,12 @@ import android.view.MenuItem
|
||||
import androidx.core.view.MenuProvider
|
||||
import org.koitharu.kotatsu.R
|
||||
|
||||
class ReaderMenuTopProvider(
|
||||
class ReaderMenuProvider(
|
||||
private val viewModel: ReaderViewModel,
|
||||
) : MenuProvider {
|
||||
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.opt_reader_top, menu)
|
||||
menuInflater.inflate(R.menu.opt_reader, menu)
|
||||
}
|
||||
|
||||
override fun onPrepareMenu(menu: Menu) {
|
||||
@@ -1,47 +0,0 @@
|
||||
package org.koitharu.kotatsu.reader.ui
|
||||
|
||||
import com.google.android.material.slider.Slider
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
|
||||
class ReaderSliderListener(
|
||||
private val viewModel: ReaderViewModel,
|
||||
private val callback: ReaderNavigationCallback,
|
||||
) : Slider.OnChangeListener, Slider.OnSliderTouchListener {
|
||||
|
||||
private var isChanged = false
|
||||
private var isTracking = false
|
||||
|
||||
override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
if (isTracking) {
|
||||
isChanged = true
|
||||
} else {
|
||||
switchPageToIndex(value.toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(slider: Slider) {
|
||||
isChanged = false
|
||||
isTracking = true
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(slider: Slider) {
|
||||
isTracking = false
|
||||
if (isChanged) {
|
||||
switchPageToIndex(slider.value.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
fun attachToSlider(slider: Slider) {
|
||||
slider.addOnChangeListener(this)
|
||||
slider.addOnSliderTouchListener(this)
|
||||
}
|
||||
|
||||
private fun switchPageToIndex(index: Int) {
|
||||
val pages = viewModel.getCurrentChapterPages()
|
||||
val page = pages?.getOrNull(index) ?: return
|
||||
val chapterId = viewModel.getCurrentState()?.chapterId ?: return
|
||||
callback.onPageSelected(ReaderPage(page, index, chapterId))
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
@@ -43,7 +42,6 @@ import org.koitharu.kotatsu.core.util.ext.requireValue
|
||||
import org.koitharu.kotatsu.details.data.MangaDetails
|
||||
import org.koitharu.kotatsu.details.domain.DetailsInteractor
|
||||
import org.koitharu.kotatsu.details.domain.DetailsLoadUseCase
|
||||
import org.koitharu.kotatsu.details.ui.pager.ChaptersPagesSheet.Companion.TAB_PAGES
|
||||
import org.koitharu.kotatsu.details.ui.pager.ChaptersPagesViewModel
|
||||
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
|
||||
import org.koitharu.kotatsu.history.data.HistoryRepository
|
||||
@@ -120,8 +118,6 @@ class ReaderViewModel @Inject constructor(
|
||||
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, false)
|
||||
}
|
||||
|
||||
val isPagesSheetEnabled = observeIsPagesSheetEnabled()
|
||||
|
||||
val content = MutableStateFlow(ReaderContent(emptyList(), null))
|
||||
|
||||
val pageAnimation = settings.observeAsStateFlow(
|
||||
@@ -136,12 +132,6 @@ class ReaderViewModel @Inject constructor(
|
||||
valueProducer = { isReaderBarEnabled },
|
||||
)
|
||||
|
||||
val readerControls = settings.observeAsStateFlow(
|
||||
scope = viewModelScope + Dispatchers.Default,
|
||||
key = AppSettings.KEY_READER_CONTROLS,
|
||||
valueProducer = { readerControls },
|
||||
)
|
||||
|
||||
val isInfoBarTransparent = settings.observeAsStateFlow(
|
||||
scope = viewModelScope + Dispatchers.Default,
|
||||
key = AppSettings.KEY_READER_BAR_TRANSPARENT,
|
||||
@@ -449,9 +439,8 @@ class ReaderViewModel @Inject constructor(
|
||||
val chapterIndex = m.chapters[chapter.branch]?.indexOfFirst { it.id == chapter.id } ?: -1
|
||||
val newState = ReaderUiState(
|
||||
mangaName = m.toManga().title,
|
||||
branch = chapter.branch,
|
||||
chapterName = chapter.name,
|
||||
chapterNumber = chapterIndex + 1,
|
||||
chapter = chapter,
|
||||
chapterIndex = chapterIndex,
|
||||
chaptersTotal = m.chapters[chapter.branch].sizeOrZero(),
|
||||
totalPages = chaptersLoader.getPagesCount(chapter.id),
|
||||
currentPage = state.page,
|
||||
@@ -493,11 +482,6 @@ class ReaderViewModel @Inject constructor(
|
||||
valueProducer = { isReaderZoomButtonsEnabled },
|
||||
)
|
||||
|
||||
private fun observeIsPagesSheetEnabled() = settings.observe()
|
||||
.filter { it == AppSettings.KEY_PAGES_TAB || it == AppSettings.KEY_DETAILS_TAB || it == AppSettings.KEY_DETAILS_LAST_TAB }
|
||||
.map { settings.defaultDetailsTab == TAB_PAGES }
|
||||
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, settings.defaultDetailsTab == TAB_PAGES)
|
||||
|
||||
private suspend fun getStateFromIntent(manga: Manga): ReaderState {
|
||||
val history = historyRepository.getOne(manga)
|
||||
val preselectedBranch = selectedBranch.value
|
||||
|
||||
@@ -19,7 +19,7 @@ import javax.inject.Inject
|
||||
@ActivityScoped
|
||||
class ScreenOrientationHelper @Inject constructor(
|
||||
private val activity: Activity,
|
||||
settings: AppSettings,
|
||||
private val settings: AppSettings,
|
||||
) {
|
||||
|
||||
val isAutoRotationEnabled: Boolean
|
||||
@@ -49,7 +49,7 @@ class ScreenOrientationHelper @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
fun applySettings() {
|
||||
if (activity.requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
|
||||
// https://developer.android.com/reference/android/R.attr.html#screenOrientation
|
||||
activity.requestedOrientation = settings.readerScreenOrientation
|
||||
@@ -72,4 +72,13 @@ class ScreenOrientationHelper @Inject constructor(
|
||||
emit(isAutoRotationEnabled)
|
||||
}.distinctUntilChanged()
|
||||
.conflate()
|
||||
|
||||
fun toggleScreenOrientation(): Boolean = if (isAutoRotationEnabled) {
|
||||
val newValue = !isLocked
|
||||
isLocked = newValue
|
||||
true
|
||||
} else {
|
||||
isLandscape = !isLandscape
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package org.koitharu.kotatsu.reader.ui.pager
|
||||
|
||||
import android.content.res.Resources
|
||||
import org.koitharu.kotatsu.core.model.getLocalizedTitle
|
||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
|
||||
data class ReaderUiState(
|
||||
val mangaName: String?,
|
||||
val branch: String?,
|
||||
val chapterName: String?,
|
||||
val chapterNumber: Int,
|
||||
val chapter: MangaChapter,
|
||||
val chapterIndex: Int,
|
||||
val chaptersTotal: Int,
|
||||
val currentPage: Int,
|
||||
val totalPages: Int,
|
||||
@@ -12,9 +15,14 @@ data class ReaderUiState(
|
||||
val incognito: Boolean,
|
||||
) {
|
||||
|
||||
val chapterNumber: Int
|
||||
get() = chapterIndex + 1
|
||||
|
||||
fun hasNextChapter(): Boolean = chapterNumber < chaptersTotal
|
||||
|
||||
fun hasPreviousChapter(): Boolean = chapterNumber > 1
|
||||
fun hasPreviousChapter(): Boolean = chapterIndex > 0
|
||||
|
||||
fun isSliderAvailable(): Boolean = totalPages > 1 && currentPage < totalPages
|
||||
|
||||
fun getChapterTitle(resources: Resources) = chapter.getLocalizedTitle(resources, chapterIndex)
|
||||
}
|
||||
|
||||
@@ -49,45 +49,13 @@
|
||||
android:elevation="@dimen/m3_card_elevated_elevation"
|
||||
app:elevation="@dimen/m3_card_elevated_elevation"
|
||||
app:popupTheme="@style/ThemeOverlay.Kotatsu"
|
||||
tools:menu="@menu/opt_reader_top">
|
||||
tools:menu="@menu/opt_reader">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_slider"
|
||||
android:layout_width="wrap_content"
|
||||
<org.koitharu.kotatsu.reader.ui.ReaderActionsView
|
||||
android:id="@+id/actionsView"
|
||||
android:layout_width="400dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:gravity="center_vertical|end">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/button_prev"
|
||||
style="?actionButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
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="260dp"
|
||||
android:layout_height="wrap_content"
|
||||
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:contentDescription="@string/next_chapter"
|
||||
android:src="@drawable/ic_next"
|
||||
android:tooltipText="@string/next_chapter" />
|
||||
|
||||
</LinearLayout>
|
||||
android:layout_gravity="center_vertical|end" />
|
||||
|
||||
</com.google.android.material.appbar.MaterialToolbar>
|
||||
|
||||
|
||||
@@ -49,68 +49,26 @@
|
||||
android:elevation="@dimen/m3_card_elevated_elevation"
|
||||
app:elevation="@dimen/m3_card_elevated_elevation"
|
||||
app:popupTheme="@style/ThemeOverlay.Kotatsu"
|
||||
tools:menu="@menu/opt_reader_top" />
|
||||
tools:menu="@menu/opt_reader" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/appbar_bottom"
|
||||
style="?materialCardViewElevatedStyle"
|
||||
<com.google.android.material.dockedtoolbar.DockedToolbarLayout
|
||||
android:id="@+id/toolbar_docked"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_margin="8dp"
|
||||
android:fitsSystemWindows="false"
|
||||
app:cardBackgroundColor="?colorSurfaceContainer"
|
||||
app:layout_insetEdge="bottom">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar_bottom"
|
||||
<org.koitharu.kotatsu.reader.ui.ReaderActionsView
|
||||
android:id="@+id/actionsView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:menu="@menu/opt_reader_bottom">
|
||||
android:minHeight="?actionBarSize" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_slider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:gravity="center_vertical|end">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/button_prev"
|
||||
style="?actionButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
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_weight="1"
|
||||
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:contentDescription="@string/next_chapter"
|
||||
android:src="@drawable/ic_next"
|
||||
android:tooltipText="@string/next_chapter" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.appbar.MaterialToolbar>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</com.google.android.material.dockedtoolbar.DockedToolbarLayout>
|
||||
|
||||
<org.koitharu.kotatsu.reader.ui.ReaderToastView
|
||||
android:id="@+id/toastView"
|
||||
|
||||
101
app/src/main/res/layout/layout_reader_actions.xml
Normal file
101
app/src/main/res/layout/layout_reader_actions.xml
Normal file
@@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge
|
||||
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"
|
||||
tools:layout_height="wrap_content"
|
||||
tools:layout_width="match_parent"
|
||||
tools:orientation="horizontal"
|
||||
tools:parentTag="android.widget.LinearLayout">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_prev"
|
||||
style="@style/Widget.Kotatsu.IconButton.Action"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:contentDescription="@string/prev_chapter"
|
||||
app:icon="@drawable/ic_prev" />
|
||||
</FrameLayout>
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/slider"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:stepSize="1.0"
|
||||
android:valueFrom="0"
|
||||
android:visibility="visible"
|
||||
app:labelBehavior="floating"
|
||||
tools:value="6"
|
||||
tools:valueTo="20" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_next"
|
||||
style="@style/Widget.Kotatsu.IconButton.Action"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:contentDescription="@string/next_chapter"
|
||||
app:icon="@drawable/ic_next" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_save"
|
||||
style="@style/Widget.Kotatsu.IconButton.Action"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/save_page"
|
||||
app:icon="@drawable/ic_save" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_screen_rotation"
|
||||
style="@style/Widget.Kotatsu.IconButton.Action"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:contentDescription="@string/screen_orientation"
|
||||
app:icon="@drawable/ic_screen_rotation" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_pages_thumbs"
|
||||
style="@style/Widget.Kotatsu.IconButton.Action"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:contentDescription="@string/pages"
|
||||
app:icon="@drawable/ic_grid" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/button_options"
|
||||
style="@style/Widget.Kotatsu.IconButton.Action"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:contentDescription="@string/options"
|
||||
app:icon="@drawable/abc_ic_menu_overflow_material" />
|
||||
</FrameLayout>
|
||||
|
||||
</merge>
|
||||
@@ -1,39 +0,0 @@
|
||||
<?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"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:ignore="AlwaysShowAction">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_save_page"
|
||||
android:icon="@drawable/ic_save"
|
||||
android:title="@string/save_page"
|
||||
android:visible="false"
|
||||
app:showAsAction="always"
|
||||
tools:visible="true" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_screen_rotation"
|
||||
android:icon="@drawable/ic_screen_rotation"
|
||||
android:title="@string/screen_orientation"
|
||||
android:visible="false"
|
||||
app:showAsAction="always"
|
||||
tools:visible="true" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_pages_thumbs"
|
||||
android:icon="@drawable/ic_grid"
|
||||
android:title="@string/pages"
|
||||
android:visible="false"
|
||||
app:showAsAction="always"
|
||||
tools:visible="true" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_options"
|
||||
android:icon="@drawable/abc_ic_menu_overflow_material"
|
||||
android:title="@string/options"
|
||||
app:showAsAction="always"
|
||||
tools:visible="true" />
|
||||
|
||||
</menu>
|
||||
@@ -111,6 +111,10 @@
|
||||
<item name="android:minHeight">42dp</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Kotatsu.IconButton.Action" parent="Widget.Material3.Button.IconButton">
|
||||
<item name="iconTint">?colorControlNormal</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.Kotatsu.ToggleButton" parent="Widget.Material3.Button.OutlinedButton">
|
||||
<item name="android:checkable">true</item>
|
||||
<item name="android:textAlignment">textStart</item>
|
||||
|
||||
Reference in New Issue
Block a user