diff --git a/app/src/main/java/eu/kanade/presentation/reader/ChapterNavigator.kt b/app/src/main/java/eu/kanade/presentation/reader/ChapterNavigator.kt new file mode 100644 index 000000000..029c759e4 --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/reader/ChapterNavigator.kt @@ -0,0 +1,255 @@ +package eu.kanade.presentation.reader + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.SkipNext +import androidx.compose.material.icons.outlined.SkipPrevious +import androidx.compose.material3.FilledIconButton +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Slider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.graphics.TransformOrigin +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.layout.layout +import androidx.compose.ui.platform.LocalHapticFeedback +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import eu.kanade.presentation.util.isTabletUi +import eu.kanade.tachiyomi.R + +@Composable +fun ChapterNavigator( + isRtl: Boolean, + isVerticalSlider: Boolean, + onNextChapter: () -> Unit, + enabledNext: Boolean, + onPreviousChapter: () -> Unit, + enabledPrevious: Boolean, + currentPage: Int, + // SY --> + currentPageText: String, + // SY <-- + totalPages: Int, + onSliderValueChange: (Int) -> Unit, +) { + // SY --> + if (isVerticalSlider) { + ChapterNavigatorVert( + onNextChapter = onNextChapter, + enabledNext = enabledNext, + onPreviousChapter = onPreviousChapter, + enabledPrevious = enabledPrevious, + currentPage = currentPage, + currentPageText = currentPageText, + totalPages = totalPages, + onSliderValueChange = onSliderValueChange, + ) + return + } + // SY <-- + val isTabletUi = isTabletUi() + val horizontalPadding = if (isTabletUi) 24.dp else 16.dp + val layoutDirection = if (isRtl) LayoutDirection.Rtl else LayoutDirection.Ltr + + val backgroundColor = MaterialTheme.colorScheme.surface.copy(alpha = 0.9f) + val haptic = LocalHapticFeedback.current + + // We explicitly handle direction based on the reader viewer rather than the system direction + CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = horizontalPadding), + verticalAlignment = Alignment.CenterVertically, + ) { + val isLeftEnabled = if (isRtl) enabledNext else enabledPrevious + if (isLeftEnabled) { + FilledIconButton( + onClick = if (isRtl) onNextChapter else onPreviousChapter, + colors = IconButtonDefaults.filledIconButtonColors( + containerColor = backgroundColor, + ), + ) { + Icon( + imageVector = Icons.Outlined.SkipPrevious, + contentDescription = stringResource(if (isRtl) R.string.action_next_chapter else R.string.action_previous_chapter), + ) + } + } + + if (totalPages > 1) { + CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) { + Row( + modifier = Modifier + .weight(1f) + .clip(RoundedCornerShape(24.dp)) + .background(backgroundColor) + .padding(horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + // SY --> + Text(text = currentPageText) + // SY <-- + + Slider( + modifier = Modifier + .weight(1f) + .padding(horizontal = 8.dp), + value = currentPage.toFloat(), + valueRange = 1f..totalPages.toFloat(), + steps = totalPages, + onValueChange = { + onSliderValueChange(it.toInt() - 1) + haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove) + }, + ) + + Text(text = totalPages.toString()) + } + } + } else { + Spacer(Modifier.weight(1f)) + } + + val isRightEnabled = if (isRtl) enabledPrevious else enabledNext + if (isRightEnabled) { + FilledIconButton( + onClick = if (isRtl) onPreviousChapter else onNextChapter, + colors = IconButtonDefaults.filledIconButtonColors( + containerColor = backgroundColor, + ), + ) { + Icon( + imageVector = Icons.Outlined.SkipNext, + contentDescription = stringResource(if (isRtl) R.string.action_previous_chapter else R.string.action_next_chapter), + ) + } + } + } + } +} + +@Composable +fun ChapterNavigatorVert( + onNextChapter: () -> Unit, + enabledNext: Boolean, + onPreviousChapter: () -> Unit, + enabledPrevious: Boolean, + currentPage: Int, + // SY --> + currentPageText: String, + // SY <-- + totalPages: Int, + onSliderValueChange: (Int) -> Unit, +) { + val isTabletUi = isTabletUi() + val verticalPadding = if (isTabletUi) 24.dp else 16.dp + + val backgroundColor = MaterialTheme.colorScheme.surface.copy(alpha = 0.9f) + val haptic = LocalHapticFeedback.current + + Column( + modifier = Modifier + .fillMaxHeight() + .padding(vertical = verticalPadding, horizontal = 8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + if (enabledPrevious) { + FilledIconButton( + onClick = onPreviousChapter, + colors = IconButtonDefaults.filledIconButtonColors( + containerColor = backgroundColor, + ), + ) { + Icon( + imageVector = Icons.Outlined.SkipPrevious, + contentDescription = stringResource(R.string.action_previous_chapter), + modifier = Modifier.rotate(90f), + ) + } + } + + if (totalPages > 1) { + Column( + modifier = Modifier + .weight(1f) + .clip(RoundedCornerShape(24.dp)) + .background(backgroundColor) + .padding(vertical = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + // SY --> + Text(text = currentPageText) + // SY <-- + + Slider( + modifier = Modifier + .padding(vertical = 8.dp) + .graphicsLayer { + rotationZ = 90f + transformOrigin = TransformOrigin(0f, 0f) + } + .layout { measurable, constraints -> + val placeable = measurable.measure( + Constraints( + minWidth = constraints.minHeight, + maxWidth = constraints.maxHeight, + minHeight = constraints.minWidth, + maxHeight = constraints.maxWidth, + ), + ) + layout(placeable.height, placeable.width) { + placeable.place(0, -placeable.height) + } + } + .weight(1f), + value = currentPage.toFloat(), + valueRange = 1f..totalPages.toFloat(), + steps = totalPages, + onValueChange = { + onSliderValueChange(it.toInt() - 1) + haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove) + }, + ) + + Text(text = totalPages.toString()) + } + } else { + Spacer(Modifier.weight(1f)) + } + + if (enabledNext) { + FilledIconButton( + onClick = onNextChapter, + colors = IconButtonDefaults.filledIconButtonColors( + containerColor = backgroundColor, + ), + ) { + Icon( + imageVector = Icons.Outlined.SkipNext, + contentDescription = stringResource(R.string.action_next_chapter), + modifier = Modifier.rotate(90f), + ) + } + } + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt index eaa73031e..4ebf08071 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt @@ -13,14 +13,12 @@ import android.graphics.Color import android.graphics.ColorMatrix import android.graphics.ColorMatrixColorFilter import android.graphics.Paint -import android.graphics.drawable.RippleDrawable import android.net.Uri import android.os.Build import android.os.Bundle import android.text.Editable import android.text.TextWatcher import android.view.Gravity -import android.view.HapticFeedbackConstants import android.view.KeyEvent import android.view.Menu import android.view.MotionEvent @@ -35,6 +33,7 @@ import android.widget.TextView import android.widget.Toast import androidx.activity.viewModels import androidx.annotation.ColorInt +import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.core.graphics.ColorUtils @@ -43,7 +42,6 @@ import androidx.core.transition.doOnEnd import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat -import androidx.core.view.isInvisible import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.lifecycle.Lifecycle @@ -52,12 +50,12 @@ import androidx.lifecycle.repeatOnLifecycle import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.shape.MaterialShapeDrawable -import com.google.android.material.slider.Slider import com.google.android.material.transition.platform.MaterialContainerTransform import dev.chrisbanes.insetter.applyInsetter import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.manga.model.orientationType import eu.kanade.domain.manga.model.readingModeType +import eu.kanade.presentation.reader.ChapterNavigator import eu.kanade.presentation.reader.PageIndicatorText import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.notification.NotificationReceiver @@ -80,7 +78,6 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderBottomButton import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsSheet import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType -import eu.kanade.tachiyomi.ui.reader.viewer.BaseViewer import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressIndicator import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerViewer @@ -91,7 +88,6 @@ import eu.kanade.tachiyomi.ui.webview.WebViewActivity import eu.kanade.tachiyomi.util.preference.toggle import eu.kanade.tachiyomi.util.system.applySystemAnimatorScale import eu.kanade.tachiyomi.util.system.createReaderThemeContext -import eu.kanade.tachiyomi.util.system.getThemeColor import eu.kanade.tachiyomi.util.system.hasDisplayCutout import eu.kanade.tachiyomi.util.system.isLTR import eu.kanade.tachiyomi.util.system.isNightMode @@ -105,7 +101,6 @@ import eu.kanade.tachiyomi.widget.listener.SimpleAnimationListener import exh.log.xLogE import exh.source.isEhBasedSource import exh.util.defaultReaderType -import exh.util.floor import exh.util.mangaType import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.delay @@ -134,7 +129,6 @@ import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.service.SourceManager import uy.kohesive.injekt.injectLazy import kotlin.math.abs -import kotlin.math.max import kotlin.time.Duration.Companion.seconds class ReaderActivity : BaseActivity() { @@ -155,9 +149,6 @@ class ReaderActivity : BaseActivity() { const val SHIFT_DOUBLE_PAGES = "shiftingDoublePages" const val SHIFTED_PAGE_INDEX = "shiftedPageIndex" const val SHIFTED_CHAP_INDEX = "shiftedChapterIndex" - - private const val ENABLED_BUTTON_IMAGE_ALPHA = 255 - private const val DISABLED_BUTTON_IMAGE_ALPHA = 64 } private val readerPreferences: ReaderPreferences by injectLazy() @@ -170,12 +161,6 @@ class ReaderActivity : BaseActivity() { val hasCutout by lazy { hasDisplayCutout() } - /** - * Viewer used to display the pages (pager, webtoon, ...). - */ - var viewer: BaseViewer? = null - private set - /** * Whether the menu is currently visible. */ @@ -330,8 +315,7 @@ class ReaderActivity : BaseActivity() { */ override fun onDestroy() { super.onDestroy() - viewer?.destroy() - viewer = null + viewModel.state.value.viewer?.destroy() config = null menuToggleToast?.cancel() readingModeToast?.cancel() @@ -349,7 +333,7 @@ class ReaderActivity : BaseActivity() { outState.putBoolean(::ehUtilsVisible.name, ehUtilsVisible) // EXH <-- // SY --> - (viewer as? PagerViewer)?.let { pViewer -> + (viewModel.state.value.viewer as? PagerViewer)?.let { pViewer -> val config = pViewer.config outState.putBoolean(SHIFT_DOUBLE_PAGES, config.shiftDoublePage) if (config.shiftDoublePage && config.doublePages) { @@ -455,7 +439,7 @@ class ReaderActivity : BaseActivity() { * Dispatches a key event. If the viewer doesn't handle it, call the default implementation. */ override fun dispatchKeyEvent(event: KeyEvent): Boolean { - val handled = viewer?.handleKeyEvent(event) ?: false + val handled = viewModel.state.value.viewer?.handleKeyEvent(event) ?: false return handled || super.dispatchKeyEvent(event) } @@ -464,7 +448,7 @@ class ReaderActivity : BaseActivity() { * implementation. */ override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean { - val handled = viewer?.handleGenericMotionEvent(event) ?: false + val handled = viewModel.state.value.viewer?.handleGenericMotionEvent(event) ?: false return handled || super.dispatchGenericMotionEvent(event) } @@ -535,60 +519,47 @@ class ReaderActivity : BaseActivity() { val state by viewModel.state.collectAsState() PageIndicatorText( - currentPage = state.currentPage, - totalPages = state.viewerChapters?.currChapter?.pages?.size ?: -1, + // SY --> + currentPage = state.currentPageText, + // SY <-- + totalPages = state.totalPages, ) } // SY --> + val sliderContent: @Composable (Boolean) -> Unit = a@{ isVertical -> + val state by viewModel.state.collectAsState() + + if (state.viewer == null) return@a + val isRtl = state.viewer is R2LPagerViewer + + ChapterNavigator( + isRtl = isRtl, + // SY --> + isVerticalSlider = isVertical, + // SY <-- + onNextChapter = ::loadNextChapter, + enabledNext = state.viewerChapters?.nextChapter != null, + onPreviousChapter = ::loadPreviousChapter, + enabledPrevious = state.viewerChapters?.prevChapter != null, + currentPage = state.currentPage, + // SY --> + currentPageText = state.currentPageText, + // SY <-- + totalPages = state.totalPages, + onSliderValueChange = { + isScrollingThroughPages = true + moveToPageIndex(it) + }, + ) + } + // Init listeners on bottom menu - val listener = object : Slider.OnSliderTouchListener { - override fun onStartTrackingTouch(slider: Slider) { - isScrollingThroughPages = true - } - - override fun onStopTrackingTouch(slider: Slider) { - isScrollingThroughPages = false - } + binding.readerNavHorz.setComposeContent { + sliderContent(false) } - val onChangeListener = Slider.OnChangeListener { slider, value, fromUser -> - if (viewer != null && fromUser) { - isScrollingThroughPages = true - moveToPageIndex(value.toInt()) - slider.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY) - } - } - listOf(binding.pageSlider, binding.pageSliderVert) - .forEach { - it.addOnSliderTouchListener(listener) - it.addOnChangeListener(onChangeListener) - } - // SY <-- - - // Extra menu buttons - - // SY --> - listOf(binding.leftChapter, binding.aboveChapter).forEach { - it.setOnClickListener { - if (viewer != null) { - if (viewer is R2LPagerViewer) { - loadNextChapter() - } else { - loadPreviousChapter() - } - } - } - } - listOf(binding.rightChapter, binding.belowChapter).forEach { - it.setOnClickListener { - if (viewer != null) { - if (viewer is R2LPagerViewer) { - loadPreviousChapter() - } else { - loadNextChapter() - } - } - } + binding.readerNavVert.setComposeContent { + sliderContent(true) } initBottomShortcuts() @@ -604,23 +575,6 @@ class ReaderActivity : BaseActivity() { } binding.toolbarBottom.background = toolbarBackground.copy(this@ReaderActivity) - binding.readerSeekbar.background = toolbarBackground.copy(this@ReaderActivity)?.apply { - setCornerSize(999F) - } - // SY --> - binding.readerSeekbarVert.background = toolbarBackground.copy(this@ReaderActivity)?.apply { - setCornerSize(999F) - } - // SY <-- - listOf(binding.leftChapter, binding.rightChapter /* SY --> */, binding.belowChapter, binding.aboveChapter /* SY <-- */).forEach { - it.background = binding.readerSeekbar.background.copy(this) - it.foreground = RippleDrawable( - ColorStateList.valueOf(getThemeColor(android.R.attr.colorControlHighlight)), - null, - it.background, - ) - } - val toolbarColor = ColorUtils.setAlphaComponent( toolbarBackground.resolvedTintColor, toolbarBackground.alpha, @@ -766,7 +720,7 @@ class ReaderActivity : BaseActivity() { setOnClickListener { if (readerPreferences.pageLayout().get() == PagerConfig.PageLayout.AUTOMATIC) { - (viewer as? PagerViewer)?.config?.let { config -> + (viewModel.state.value.viewer as? PagerViewer)?.config?.let { config -> config.doublePages = !config.doublePages reloadChapters(config.doublePages, true) } @@ -821,7 +775,7 @@ class ReaderActivity : BaseActivity() { val interval = parsed.seconds while (true) { if (!binding.readerMenu.isVisible) { - viewer.let { v -> + viewModel.state.value.viewer.let { v -> when (v) { is PagerViewer -> v.moveToNext() is WebtoonViewer -> { @@ -903,7 +857,7 @@ class ReaderActivity : BaseActivity() { } binding.ehBoostPage.setOnClickListener { - viewer ?: return@setOnClickListener + viewModel.state.value.viewer ?: return@setOnClickListener val curPage = exhCurrentpage() ?: run { toast(R.string.eh_boost_page_invalid) return@setOnClickListener @@ -936,11 +890,13 @@ class ReaderActivity : BaseActivity() { } private fun exhCurrentpage(): ReaderPage? { + val viewer = viewModel.state.value.viewer val currentPage = (((viewer as? PagerViewer)?.currentPage ?: (viewer as? WebtoonViewer)?.currentPage) as? ReaderPage)?.index return currentPage?.let { viewModel.state.value.viewerChapters?.currChapter?.pages?.getOrNull(it) } } fun updateBottomButtons() { + val viewer = viewModel.state.value.viewer val enabledButtons = readerPreferences.readerBottomButtons().get() with(binding) { actionReadingMode.isVisible = ReaderBottomButton.ReadingMode.isIn(enabledButtons) @@ -968,23 +924,23 @@ class ReaderActivity : BaseActivity() { } fun reloadChapters(doublePages: Boolean, force: Boolean = false) { - val pViewer = viewer as? PagerViewer ?: return - pViewer.updateShifting() - if (!force && pViewer.config.autoDoublePages) { - setDoublePageMode(pViewer) + val viewer = viewModel.state.value.viewer as? PagerViewer ?: return + viewer.updateShifting() + if (!force && viewer.config.autoDoublePages) { + setDoublePageMode(viewer) } else { - pViewer.config.doublePages = doublePages + viewer.config.doublePages = doublePages } val currentChapter = viewModel.getCurrentChapter() if (doublePages) { // If we're moving from singe to double, we want the current page to be the first page - pViewer.config.shiftDoublePage = ( - binding.pageSlider.value.floor() + - (currentChapter?.pages?.take(binding.pageSlider.value.floor())?.count { it.fullPage || it.isolatedPage } ?: 0) + val currentPage = viewModel.state.value.currentPage + viewer.config.shiftDoublePage = ( + currentPage + (currentChapter?.pages?.take(currentPage)?.count { it.fullPage || it.isolatedPage } ?: 0) ) % 2 != 0 } viewModel.state.value.viewerChapters?.let { - pViewer.setChaptersDoubleShift(it) + viewer.setChaptersDoubleShift(it) } } @@ -994,11 +950,12 @@ class ReaderActivity : BaseActivity() { } private fun shiftDoublePages() { - (viewer as? PagerViewer)?.config?.let { config -> + val viewer = viewModel.state.value.viewer as? PagerViewer ?: return + viewer.config.let { config -> config.shiftDoublePage = !config.shiftDoublePage viewModel.state.value.viewerChapters?.let { - (viewer as? PagerViewer)?.updateShifting() - (viewer as? PagerViewer)?.setChaptersDoubleShift(it) + viewer.updateShifting() + viewer.setChaptersDoubleShift(it) invalidateOptionsMenu() } } @@ -1119,7 +1076,7 @@ class ReaderActivity : BaseActivity() { * and the toolbar title. */ private fun setManga(manga: Manga) { - val prevViewer = viewer + val prevViewer = viewModel.state.value.viewer val viewerMode = ReadingModeType.fromPreference(viewModel.getMangaReadingMode(resolveDefault = false)) binding.actionReadingMode.setImageResource(viewerMode.iconRes) @@ -1141,7 +1098,7 @@ class ReaderActivity : BaseActivity() { prevViewer.destroy() binding.viewerContainer.removeAllViews() } - viewer = newViewer + viewModel.onViewerLoaded(newViewer) updateViewerInset(readerPreferences.fullscreen().get()) binding.viewerContainer.addView(newViewer.getView()) @@ -1174,7 +1131,7 @@ class ReaderActivity : BaseActivity() { ) || resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT ) && - (viewer is WebtoonViewer || viewer is VerticalPagerViewer) + (viewModel.state.value.viewer is WebtoonViewer || viewModel.state.value.viewer is VerticalPagerViewer) ) { binding.readerNavVert.isVisible = true binding.readerNavHorz.isVisible = false @@ -1199,17 +1156,6 @@ class ReaderActivity : BaseActivity() { // SY <-- supportActionBar?.title = manga.title - binding.pageSlider.isRTL = newViewer is R2LPagerViewer - if (newViewer is R2LPagerViewer) { - binding.leftChapter.setTooltip(R.string.action_next_chapter) - binding.rightChapter.setTooltip(R.string.action_previous_chapter) - } else { - binding.leftChapter.setTooltip(R.string.action_previous_chapter) - binding.rightChapter.setTooltip(R.string.action_next_chapter) - } - binding.aboveChapter.setTooltip(R.string.action_previous_chapter) - binding.belowChapter.setTooltip(R.string.action_next_chapter) - val loadingIndicatorContext = createReaderThemeContext() loadingIndicator = ReaderProgressIndicator(loadingIndicatorContext).apply { updateLayoutParams { @@ -1252,13 +1198,13 @@ class ReaderActivity : BaseActivity() { // SY --> if (indexChapterToShift != null && indexPageToShift != null) { viewerChapters.currChapter.pages?.find { it.index == indexPageToShift && it.chapter.chapter.id == indexChapterToShift }?.let { - (viewer as? PagerViewer)?.updateShifting(it) + (viewModel.state.value.viewer as? PagerViewer)?.updateShifting(it) } indexChapterToShift = null indexPageToShift = null } else if (lastShiftDoubleState != null) { val currentChapter = viewerChapters.currChapter - (viewer as? PagerViewer)?.config?.shiftDoublePage = ( + (viewModel.state.value.viewer as? PagerViewer)?.config?.shiftDoublePage = ( currentChapter.requestedPage + ( currentChapter.pages?.take(currentChapter.requestedPage) @@ -1268,35 +1214,9 @@ class ReaderActivity : BaseActivity() { } // SY <-- - viewer?.setChapters(viewerChapters) + viewModel.state.value.viewer?.setChapters(viewerChapters) binding.toolbar.subtitle = viewerChapters.currChapter.chapter.name - val currentChapterPageCount = viewerChapters.currChapter.pages?.size ?: 1 - binding.readerSeekbar.isInvisible = currentChapterPageCount == 1 - binding.readerSeekbarVert.isInvisible = currentChapterPageCount == 1 - - val leftChapterObject = if (viewer is R2LPagerViewer) viewerChapters.nextChapter else viewerChapters.prevChapter - val rightChapterObject = if (viewer is R2LPagerViewer) viewerChapters.prevChapter else viewerChapters.nextChapter - - if (leftChapterObject == null && rightChapterObject == null) { - binding.leftChapter.isVisible = false - binding.rightChapter.isVisible = false - binding.aboveChapter.isVisible = false - binding.belowChapter.isVisible = false - } else { - binding.leftChapter.isEnabled = leftChapterObject != null - binding.leftChapter.imageAlpha = if (leftChapterObject != null) ENABLED_BUTTON_IMAGE_ALPHA else DISABLED_BUTTON_IMAGE_ALPHA - - binding.rightChapter.isEnabled = rightChapterObject != null - binding.rightChapter.imageAlpha = if (rightChapterObject != null) ENABLED_BUTTON_IMAGE_ALPHA else DISABLED_BUTTON_IMAGE_ALPHA - - binding.aboveChapter.isEnabled = leftChapterObject != null - binding.aboveChapter.imageAlpha = if (leftChapterObject != null) ENABLED_BUTTON_IMAGE_ALPHA else DISABLED_BUTTON_IMAGE_ALPHA - - binding.belowChapter.isEnabled = rightChapterObject != null - binding.belowChapter.imageAlpha = if (rightChapterObject != null) ENABLED_BUTTON_IMAGE_ALPHA else DISABLED_BUTTON_IMAGE_ALPHA - } - // Invalidate menu to show proper chapter bookmark state invalidateOptionsMenu() @@ -1338,7 +1258,7 @@ class ReaderActivity : BaseActivity() { * page is not found. */ private fun moveToPageIndex(index: Int) { - val viewer = viewer ?: return + val viewer = viewModel.state.value.viewer ?: return val currentChapter = viewModel.getCurrentChapter() ?: return val page = currentChapter.pages?.getOrNull(index) ?: return viewer.moveToPage(page) @@ -1374,38 +1294,13 @@ class ReaderActivity : BaseActivity() { fun onPageSelected(page: ReaderPage, hasExtraPage: Boolean = false) { // SY --> val currentPage = if (hasExtraPage) { - val invertDoublePage = (viewer as? PagerViewer)?.config?.invertDoublePages ?: false + val invertDoublePage = (viewModel.state.value.viewer as? PagerViewer)?.config?.invertDoublePages ?: false if (resources.isLTR xor invertDoublePage) "${page.number}-${page.number + 1}" else "${page.number + 1}-${page.number}" } else { "${page.number}" } viewModel.onPageSelected(page, hasExtraPage, currentPage) // SY <-- - val pages = page.chapter.pages ?: return - - // Set page numbers - if (viewer !is R2LPagerViewer) { - binding.leftPageText.text = currentPage - binding.rightPageText.text = "${pages.size}" - } else { - binding.rightPageText.text = currentPage - binding.leftPageText.text = "${pages.size}" - } - - // Set slider progress - binding.pageSlider.isEnabled = pages.size > 1 - binding.pageSlider.valueTo = max(pages.lastIndex.toFloat(), 1f) - binding.pageSlider.value = page.index.toFloat() - - // SY --> - binding.pageSliderVert.valueTo = max(pages.lastIndex.toFloat(), 1f) - binding.pageSliderVert.value = page.index.toFloat() - // SY <-- - - // SY --> - binding.abovePageText.text = currentPage - binding.belowPageText.text = "${pages.size}" - // SY <-- } /** @@ -1415,7 +1310,7 @@ class ReaderActivity : BaseActivity() { fun onPageLongTap(page: ReaderPage, extraPage: ReaderPage? = null) { // SY --> try { - val viewer = viewer as? PagerViewer + val viewer = viewModel.state.value.viewer as? PagerViewer ReaderPageSheet( this, page, @@ -1566,7 +1461,7 @@ class ReaderActivity : BaseActivity() { * Updates viewer inset depending on fullscreen reader preferences. */ fun updateViewerInset(fullscreen: Boolean) { - viewer?.getView()?.applyInsetter { + viewModel.state.value.viewer?.getView()?.applyInsetter { if (!fullscreen) { type(navigationBars = true, statusBars = true) { padding() @@ -1671,7 +1566,7 @@ class ReaderActivity : BaseActivity() { readerPreferences.dualPageSplitPaged().changes() .drop(1) .onEach { - if (viewer !is PagerViewer) return@onEach + if (viewModel.state.value.viewer !is PagerViewer) return@onEach updateBottomButtons() reloadChapters( !it && when (readerPreferences.pageLayout().get()) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderSlider.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderSlider.kt deleted file mode 100644 index 94f857785..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderSlider.kt +++ /dev/null @@ -1,30 +0,0 @@ -package eu.kanade.tachiyomi.ui.reader - -import android.content.Context -import android.util.AttributeSet -import com.google.android.material.slider.Slider - -/** - * Slider to show current chapter progress. - */ -class ReaderSlider @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, -) : Slider(context, attrs) { - - init { - stepSize = 1f - setLabelFormatter { value -> - (value.toInt() + 1).toString() - } - } - - /** - * Whether the slider should draw from right to left. - */ - var isRTL: Boolean - set(value) { - layoutDirection = if (value) LAYOUT_DIRECTION_RTL else LAYOUT_DIRECTION_LTR - } - get() = layoutDirection == LAYOUT_DIRECTION_RTL -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt index 54c4deead..e5b86c269 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt @@ -39,6 +39,7 @@ import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters import eu.kanade.tachiyomi.ui.reader.setting.OrientationType import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType +import eu.kanade.tachiyomi.ui.reader.viewer.Viewer import eu.kanade.tachiyomi.util.chapter.removeDuplicates import eu.kanade.tachiyomi.util.editCover import eu.kanade.tachiyomi.util.lang.byteSize @@ -480,6 +481,14 @@ class ReaderViewModel( eventChannel.trySend(Event.ReloadViewerChapters) } + fun onViewerLoaded(viewer: Viewer?) { + mutableState.update { + it.copy( + viewer = viewer, + ) + } + } + /** * Called every time a page changes on the reader. Used to mark the flag of chapters being * read, update tracking services, enqueue downloaded chapter deletion, and updating the active chapter if this @@ -498,8 +507,9 @@ class ReaderViewModel( // Save last page read and mark as read if needed mutableState.update { it.copy( + currentPage = page.number, // SY --> - currentPage = currentPage, + currentPageText = currentPage, // SY <-- ) } @@ -1108,12 +1118,21 @@ class ReaderViewModel( val manga: Manga? = null, val viewerChapters: ViewerChapters? = null, val isLoadingAdjacentChapter: Boolean = false, + val currentPage: Int = -1, + /** + * Viewer used to display the pages (pager, webtoon, ...). + */ + val viewer: Viewer? = null, + // SY --> - val currentPage: String = "", + val currentPageText: String = "", val meta: RaisedSearchMetadata? = null, val mergedManga: Map? = null, // SY <-- - ) + ) { + val totalPages: Int + get() = viewerChapters?.currChapter?.pages?.size ?: -1 + } sealed class Event { object ReloadViewerChapters : Event() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt index b9377a657..485dd9e12 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt @@ -34,7 +34,7 @@ class ReaderReadingModeSettings @JvmOverloads constructor(context: Context, attr initGeneralPreferences() - when ((context as ReaderActivity).viewer) { + when ((context as ReaderActivity).viewModel.state.value.viewer) { is PagerViewer -> initPagerPreferences() is WebtoonViewer -> initWebtoonPreferences() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingModeType.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingModeType.kt index 5617c7aaa..88e1a3f02 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingModeType.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReadingModeType.kt @@ -4,7 +4,7 @@ import androidx.annotation.DrawableRes import androidx.annotation.StringRes import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.reader.ReaderActivity -import eu.kanade.tachiyomi.ui.reader.viewer.BaseViewer +import eu.kanade.tachiyomi.ui.reader.viewer.Viewer import eu.kanade.tachiyomi.ui.reader.viewer.pager.L2RPagerViewer import eu.kanade.tachiyomi.ui.reader.viewer.pager.R2LPagerViewer import eu.kanade.tachiyomi.ui.reader.viewer.pager.VerticalPagerViewer @@ -31,7 +31,7 @@ enum class ReadingModeType(val prefValue: Int, @StringRes val stringRes: Int, @D fun fromSpinner(position: Int?) = values().find { value -> value.prefValue == position } ?: DEFAULT - fun toViewer(preference: Int?, activity: ReaderActivity): BaseViewer { + fun toViewer(preference: Int?, activity: ReaderActivity): Viewer { return when (fromPreference(preference)) { LEFT_TO_RIGHT -> L2RPagerViewer(activity) RIGHT_TO_LEFT -> R2LPagerViewer(activity) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/BaseViewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/Viewer.kt similarity index 98% rename from app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/BaseViewer.kt rename to app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/Viewer.kt index 223cb087f..00834563c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/BaseViewer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/Viewer.kt @@ -9,7 +9,7 @@ import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters /** * Interface for implementing a viewer. */ -interface BaseViewer { +interface Viewer { /** * Returns the view this viewer uses. diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt index 0982dfcb5..44a63c0d4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt @@ -18,7 +18,7 @@ import eu.kanade.tachiyomi.ui.reader.model.InsertPage import eu.kanade.tachiyomi.ui.reader.model.ReaderItem import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters -import eu.kanade.tachiyomi.ui.reader.viewer.BaseViewer +import eu.kanade.tachiyomi.ui.reader.viewer.Viewer import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation.NavigationRegion import kotlinx.coroutines.MainScope import kotlinx.coroutines.cancel @@ -27,10 +27,10 @@ import uy.kohesive.injekt.injectLazy import kotlin.math.min /** - * Implementation of a [BaseViewer] to display pages with a [ViewPager]. + * Implementation of a [Viewer] to display pages with a [ViewPager]. */ @Suppress("LeakingThis") -abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer { +abstract class PagerViewer(val activity: ReaderActivity) : Viewer { val downloadManager: DownloadManager by injectLazy() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt index 6e1cf9735..0f1113e3c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt @@ -19,7 +19,7 @@ import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.StencilPage import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences -import eu.kanade.tachiyomi.ui.reader.viewer.BaseViewer +import eu.kanade.tachiyomi.ui.reader.viewer.Viewer import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation.NavigationRegion import kotlinx.coroutines.MainScope import kotlinx.coroutines.cancel @@ -32,9 +32,9 @@ import kotlin.math.min import kotlin.time.Duration /** - * Implementation of a [BaseViewer] to display pages with a [RecyclerView]. + * Implementation of a [Viewer] to display pages with a [RecyclerView]. */ -class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = true, private val tapByPage: Boolean = false) : BaseViewer { +class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = true, private val tapByPage: Boolean = false) : Viewer { val downloadManager: DownloadManager by injectLazy() diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt index 79da5aea3..9124619f8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt @@ -12,7 +12,6 @@ import android.graphics.drawable.Drawable import android.net.Uri import android.os.Build import android.os.PowerManager -import android.util.TypedValue import androidx.annotation.AttrRes import androidx.annotation.ColorInt import androidx.appcompat.view.ContextThemeWrapper @@ -89,19 +88,6 @@ fun Context.hasPermission(permission: String) = PermissionChecker.checkSelfPermi return color } -@ColorInt fun Context.getThemeColor(attr: Int): Int { - val tv = TypedValue() - return if (this.theme.resolveAttribute(attr, tv, true)) { - if (tv.resourceId != 0) { - getColor(tv.resourceId) - } else { - tv.data - } - } else { - 0 - } -} - val Context.powerManager: PowerManager get() = getSystemService()!! diff --git a/app/src/main/res/drawable/ic_skip_next_24dp.xml b/app/src/main/res/drawable/ic_skip_next_24dp.xml deleted file mode 100644 index 78ae4da4f..000000000 --- a/app/src/main/res/drawable/ic_skip_next_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/ic_skip_previous_24dp.xml b/app/src/main/res/drawable/ic_skip_previous_24dp.xml deleted file mode 100644 index 2dad0ad04..000000000 --- a/app/src/main/res/drawable/ic_skip_previous_24dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/layout/reader_activity.xml b/app/src/main/res/layout/reader_activity.xml index cb6d83975..635735579 100755 --- a/app/src/main/res/layout/reader_activity.xml +++ b/app/src/main/res/layout/reader_activity.xml @@ -195,104 +195,13 @@ app:layout_constraintTop_toBottomOf="@id/above_guideline" tools:ignore="NotSibling"> - - - - - - - - - - - - - - - - - - - - - - - - - - + android:gravity="center"/> @@ -314,82 +223,12 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"> - - - - - - - - - - - - - - - - - - + android:layoutDirection="ltr" />