From 25ae23963eca681e7afde38e0f947251b76350e1 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sat, 14 Dec 2024 09:26:01 +0200 Subject: [PATCH] Update reader interface --- .idea/AndroidProjectSystem.xml | 6 ++ .../kotatsu/core/prefs/AppSettings.kt | 3 +- .../kotatsu/details/ui/ReadButtonDelegate.kt | 26 +++++--- .../kotatsu/reader/ui/ReaderActivity.kt | 22 +++++-- .../reader/ui/ReaderBottomMenuProvider.kt | 23 +++++++ .../reader/ui/ReaderControlDelegate.kt | 14 +++- .../kotatsu/reader/ui/ReaderViewModel.kt | 10 +++ .../main/res/drawable/ic_move_horizontal.xml | 11 ++++ app/src/main/res/drawable/ic_next.xml | 12 ++++ app/src/main/res/drawable/ic_prev.xml | 12 ++++ .../layout-w600dp-land/activity_reader.xml | 65 +++++++++++++++---- app/src/main/res/layout/activity_reader.xml | 49 ++++++++++++-- app/src/main/res/menu/opt_reader_bottom.xml | 13 ++++ app/src/main/res/values/strings.xml | 3 + app/src/main/res/values/styles.xml | 6 ++ app/src/main/res/xml/pref_reader.xml | 5 -- 16 files changed, 240 insertions(+), 40 deletions(-) create mode 100644 .idea/AndroidProjectSystem.xml create mode 100644 app/src/main/res/drawable/ic_move_horizontal.xml create mode 100644 app/src/main/res/drawable/ic_next.xml create mode 100644 app/src/main/res/drawable/ic_prev.xml diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml new file mode 100644 index 000000000..4a53bee8c --- /dev/null +++ b/.idea/AndroidProjectSystem.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt index e677c4b13..05ccaa494 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt @@ -362,8 +362,9 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) { val isReaderBarEnabled: Boolean get() = prefs.getBoolean(KEY_READER_BAR, true) - val isReaderSliderEnabled: Boolean + 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) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ReadButtonDelegate.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ReadButtonDelegate.kt index 48a8d22c5..5457fbd44 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ReadButtonDelegate.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/ReadButtonDelegate.kt @@ -20,6 +20,7 @@ import androidx.lifecycle.LifecycleOwner import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialSplitButton import com.google.android.material.snackbar.Snackbar +import kotlinx.coroutines.flow.combine import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.core.util.ext.findActivity @@ -30,7 +31,7 @@ import org.koitharu.kotatsu.download.ui.dialog.DownloadDialogFragment import org.koitharu.kotatsu.reader.ui.ReaderActivity class ReadButtonDelegate( - splitButton: MaterialSplitButton, + private val splitButton: MaterialSplitButton, private val viewModel: DetailsViewModel, ) : View.OnClickListener, PopupMenu.OnMenuItemClickListener, PopupMenu.OnDismissListener { @@ -73,7 +74,10 @@ class ReadButtonDelegate( fun attach(lifecycleOwner: LifecycleOwner) { buttonRead.setOnClickListener(this) buttonMenu.setOnClickListener(this) - viewModel.historyInfo.observe(lifecycleOwner, this::onHistoryChanged) + combine(viewModel.isLoading, viewModel.historyInfo, ::Pair) + .observe(lifecycleOwner) { (isLoading, historyInfo) -> + onHistoryChanged(isLoading, historyInfo) + } } private fun showMenu() { @@ -97,16 +101,15 @@ class ReadButtonDelegate( } private fun openReader(isIncognitoMode: Boolean) { - val detailsViewModel = viewModel as? DetailsViewModel ?: return val manga = viewModel.manga.value ?: return - if (detailsViewModel.historyInfo.value.isChapterMissing) { + if (viewModel.historyInfo.value.isChapterMissing) { Snackbar.make(buttonRead, R.string.chapter_is_missing, Snackbar.LENGTH_SHORT) .show() // TODO } else { context.startActivity( ReaderActivity.IntentBuilder(context) .manga(manga) - .branch(detailsViewModel.selectedBranchValue) + .branch(viewModel.selectedBranchValue) .incognito(isIncognitoMode) .build(), ) @@ -116,9 +119,16 @@ class ReadButtonDelegate( } } - private fun onHistoryChanged(info: HistoryInfo) { - buttonRead.setText(if (info.canContinue) R.string._continue else R.string.read) - buttonRead.isEnabled = info.isValid + private fun onHistoryChanged(isLoading: Boolean, info: HistoryInfo) { + buttonRead.setText( + when { + isLoading -> R.string.loading_ + info.isIncognitoMode -> R.string.incognito + info.canContinue -> R.string._continue + else -> R.string.read + }, + ) + splitButton.isEnabled = !isLoading && info.isValid } private fun Menu.populateBranchList() { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt index 4adbad36a..98bccb250 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt @@ -2,7 +2,6 @@ package org.koitharu.kotatsu.reader.ui import android.content.Context import android.content.Intent -import android.net.Uri import android.os.Bundle import android.transition.Fade import android.transition.Slide @@ -42,7 +41,6 @@ import org.koitharu.kotatsu.core.ui.BaseFullscreenActivity import org.koitharu.kotatsu.core.ui.util.MenuInvalidator import org.koitharu.kotatsu.core.ui.widgets.ZoomControl import org.koitharu.kotatsu.core.util.IdlingDetector -import org.koitharu.kotatsu.core.util.ShareHelper import org.koitharu.kotatsu.core.util.ext.hasGlobalPoint import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled import org.koitharu.kotatsu.core.util.ext.isRtl @@ -127,6 +125,8 @@ class ReaderActivity : ReaderSliderListener(viewModel, this).attachToSlider(viewBinding.slider) insetsDelegate.interceptingWindowInsetsListener = this idlingDetector.bindToLifecycle(this) + viewBinding.buttonPrev.setOnClickListener(controlDelegate) + viewBinding.buttonNext.setOnClickListener(controlDelegate) viewModel.onError.observeEvent( this, @@ -150,10 +150,12 @@ class ReaderActivity : viewModel.content.observe(this) { onLoadingStateChanged(viewModel.isLoading.value) } + viewModel.isSliderVisible.observe(this, this::onSliderVisibilityChanged) viewModel.isKeepScreenOnEnabled.observe(this, this::setKeepScreenOn) viewModel.isInfoBarEnabled.observe(this, ::onReaderBarChanged) - viewModel.isBookmarkAdded.observe(this, MenuInvalidator(this)) - viewModel.isPagesSheetEnabled.observe(this, MenuInvalidator(viewBinding.toolbarBottom)) + val bottomMenuInvalidator = MenuInvalidator(viewBinding.toolbarBottom) + viewModel.isBookmarkAdded.observe(this, bottomMenuInvalidator) + viewModel.isPagesSheetEnabled.observe(this, bottomMenuInvalidator) viewModel.onShowToast.observeEvent(this) { msgId -> Snackbar.make(viewBinding.container, msgId, Snackbar.LENGTH_SHORT) .setAnchorView(viewBinding.appbarBottom) @@ -243,7 +245,7 @@ class ReaderActivity : false } else { val touchables = window.peekDecorView()?.touchables - touchables?.none { it.hasGlobalPoint(rawX, rawY) } ?: true + touchables?.none { it.hasGlobalPoint(rawX, rawY) } != false } } @@ -307,6 +309,9 @@ class ReaderActivity : .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)) } TransitionManager.beginDelayedTransition(viewBinding.root, transition) } @@ -315,10 +320,15 @@ class ReaderActivity : viewBinding.appbarBottom?.isVisible = isUiVisible viewBinding.infoBar.isGone = isUiVisible || (!viewModel.isInfoBarEnabled.value) viewBinding.infoBar.isTimeVisible = isFullscreen + viewBinding.floatingToolbar.isVisible = isUiVisible && viewModel.isSliderVisible.value systemUiController.setSystemUiVisible(isUiVisible || !isFullscreen) } } + private fun onSliderVisibilityChanged(isSliderVisible: Boolean) { + viewBinding.floatingToolbar.isVisible = isSliderVisible && viewBinding.appbarTop.isVisible + } + override fun onApplyWindowInsets(v: View, insets: WindowInsetsCompat): WindowInsetsCompat { gestureInsets = insets.getInsets(WindowInsetsCompat.Type.systemGestures()) val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) @@ -357,7 +367,7 @@ class ReaderActivity : } override fun scrollBy(delta: Int, smooth: Boolean): Boolean { - return readerManager.currentReader?.scrollBy(delta, smooth) ?: false + return readerManager.currentReader?.scrollBy(delta, smooth) == true } override fun toggleUiVisibility() { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderBottomMenuProvider.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderBottomMenuProvider.kt index ab1013374..d02125ede 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderBottomMenuProvider.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderBottomMenuProvider.kt @@ -28,6 +28,15 @@ class ReaderBottomMenuProvider( 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 { @@ -44,6 +53,20 @@ class ReaderBottomMenuProvider( true } + R.id.action_slider -> { + viewModel.setSliderVisibility(!viewModel.isSliderVisible.value) + true + } + + R.id.action_bookmark -> { + if (viewModel.isBookmarkAdded.value) { + viewModel.removeBookmark() + } else { + viewModel.addBookmark() + } + true + } + else -> false } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderControlDelegate.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderControlDelegate.kt index 53421d319..2076a50b5 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderControlDelegate.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderControlDelegate.kt @@ -2,6 +2,7 @@ package org.koitharu.kotatsu.reader.ui import android.content.res.Resources import android.view.KeyEvent +import android.view.View import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.ReaderMode @@ -14,10 +15,17 @@ class ReaderControlDelegate( private val settings: AppSettings, private val tapGridSettings: TapGridSettings, private val listener: OnInteractionListener, -) { +) : View.OnClickListener { private var minScrollDelta = resources.getDimensionPixelSize(R.dimen.reader_scroll_delta_min) + override fun onClick(v: View) { + when (v.id) { + R.id.button_prev -> listener.switchChapterBy(-1) + R.id.button_next -> listener.switchChapterBy(1) + } + } + fun onGridTouch(area: TapGridArea): Boolean { val action = tapGridSettings.getTapAction( area = area, @@ -63,7 +71,7 @@ class ReaderControlDelegate( KeyEvent.KEYCODE_SPACE, KeyEvent.KEYCODE_PAGE_DOWN, - -> { + -> { listener.switchPageBy(1) true } @@ -74,7 +82,7 @@ class ReaderControlDelegate( } KeyEvent.KEYCODE_PAGE_UP, - -> { + -> { listener.switchPageBy(-1) true } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt index f7649b4b7..93b2e42d7 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt @@ -131,6 +131,12 @@ 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, @@ -213,6 +219,10 @@ class ReaderViewModel @Inject constructor( } } + fun setSliderVisibility(visible: Boolean) { + settings.isReaderSliderEnabled = visible + } + fun switchMode(newMode: ReaderMode) { launchJob { val manga = checkNotNull(getMangaOrNull()) diff --git a/app/src/main/res/drawable/ic_move_horizontal.xml b/app/src/main/res/drawable/ic_move_horizontal.xml new file mode 100644 index 000000000..5c67c54df --- /dev/null +++ b/app/src/main/res/drawable/ic_move_horizontal.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/ic_next.xml b/app/src/main/res/drawable/ic_next.xml new file mode 100644 index 000000000..6c80ccb83 --- /dev/null +++ b/app/src/main/res/drawable/ic_next.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_prev.xml b/app/src/main/res/drawable/ic_prev.xml new file mode 100644 index 000000000..8822aa243 --- /dev/null +++ b/app/src/main/res/drawable/ic_prev.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/layout-w600dp-land/activity_reader.xml b/app/src/main/res/layout-w600dp-land/activity_reader.xml index 4ee41c34a..92ec14325 100644 --- a/app/src/main/res/layout-w600dp-land/activity_reader.xml +++ b/app/src/main/res/layout-w600dp-land/activity_reader.xml @@ -59,23 +59,64 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" - tools:menu="@menu/opt_reader_bottom"> - - - - + tools:menu="@menu/opt_reader_bottom" /> + + + + + + + + + + + + + + + tools:menu="@menu/opt_reader_bottom" /> + + + + + + + + - + - + + + + + + + Source Translation %1$s (%2$s) + Show slider + + Incognito diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index b48a87d7a..fd3ffe310 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -144,6 +144,12 @@ ?shapeAppearanceCornerMedium + + diff --git a/app/src/main/res/xml/pref_reader.xml b/app/src/main/res/xml/pref_reader.xml index 0c7386ea1..531fe26fb 100644 --- a/app/src/main/res/xml/pref_reader.xml +++ b/app/src/main/res/xml/pref_reader.xml @@ -121,11 +121,6 @@ android:title="@string/reader_info_bar" app:allowDividerAbove="true" /> - -