mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-03 23:58:55 +01:00 
			
		
		
		
	Migrate reader slider and next/prev buttons to Compose
This commit is contained in:
		@@ -0,0 +1,121 @@
 | 
			
		||||
package eu.kanade.presentation.reader
 | 
			
		||||
 | 
			
		||||
import androidx.compose.foundation.background
 | 
			
		||||
import androidx.compose.foundation.layout.Row
 | 
			
		||||
import androidx.compose.foundation.layout.Spacer
 | 
			
		||||
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.hapticfeedback.HapticFeedbackType
 | 
			
		||||
import androidx.compose.ui.platform.LocalHapticFeedback
 | 
			
		||||
import androidx.compose.ui.platform.LocalLayoutDirection
 | 
			
		||||
import androidx.compose.ui.res.stringResource
 | 
			
		||||
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,
 | 
			
		||||
    onNextChapter: () -> Unit,
 | 
			
		||||
    enabledNext: Boolean,
 | 
			
		||||
    onPreviousChapter: () -> Unit,
 | 
			
		||||
    enabledPrevious: Boolean,
 | 
			
		||||
    currentPage: Int,
 | 
			
		||||
    totalPages: Int,
 | 
			
		||||
    onSliderValueChange: (Int) -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
    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,
 | 
			
		||||
                    ) {
 | 
			
		||||
                        Text(text = currentPage.toString())
 | 
			
		||||
 | 
			
		||||
                        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),
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -6,18 +6,15 @@ import android.app.ProgressDialog
 | 
			
		||||
import android.app.assist.AssistContent
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.content.res.ColorStateList
 | 
			
		||||
import android.graphics.Bitmap
 | 
			
		||||
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.view.Gravity
 | 
			
		||||
import android.view.HapticFeedbackConstants
 | 
			
		||||
import android.view.KeyEvent
 | 
			
		||||
import android.view.Menu
 | 
			
		||||
import android.view.MenuItem
 | 
			
		||||
@@ -37,17 +34,16 @@ 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.lifecycleScope
 | 
			
		||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
 | 
			
		||||
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.presentation.reader.ChapterNavigator
 | 
			
		||||
import eu.kanade.presentation.reader.PageIndicatorText
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
 | 
			
		||||
@@ -66,14 +62,12 @@ import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
 | 
			
		||||
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.R2LPagerViewer
 | 
			
		||||
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.isNightMode
 | 
			
		||||
import eu.kanade.tachiyomi.util.system.toShareIntent
 | 
			
		||||
@@ -101,7 +95,6 @@ import tachiyomi.core.util.system.logcat
 | 
			
		||||
import tachiyomi.domain.manga.model.Manga
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import kotlin.math.abs
 | 
			
		||||
import kotlin.math.max
 | 
			
		||||
 | 
			
		||||
class ReaderActivity : BaseActivity() {
 | 
			
		||||
 | 
			
		||||
@@ -113,9 +106,6 @@ class ReaderActivity : BaseActivity() {
 | 
			
		||||
                addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private const val ENABLED_BUTTON_IMAGE_ALPHA = 255
 | 
			
		||||
        private const val DISABLED_BUTTON_IMAGE_ALPHA = 64
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private val readerPreferences: ReaderPreferences by injectLazy()
 | 
			
		||||
@@ -128,12 +118,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.
 | 
			
		||||
     */
 | 
			
		||||
@@ -255,8 +239,7 @@ class ReaderActivity : BaseActivity() {
 | 
			
		||||
     */
 | 
			
		||||
    override fun onDestroy() {
 | 
			
		||||
        super.onDestroy()
 | 
			
		||||
        viewer?.destroy()
 | 
			
		||||
        viewer = null
 | 
			
		||||
        viewModel.state.value.viewer?.destroy()
 | 
			
		||||
        config = null
 | 
			
		||||
        menuToggleToast?.cancel()
 | 
			
		||||
        readingModeToast?.cancel()
 | 
			
		||||
@@ -365,7 +348,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)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -374,7 +357,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)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -416,46 +399,30 @@ class ReaderActivity : BaseActivity() {
 | 
			
		||||
 | 
			
		||||
            PageIndicatorText(
 | 
			
		||||
                currentPage = state.currentPage,
 | 
			
		||||
                totalPages = state.viewerChapters?.currChapter?.pages?.size ?: -1,
 | 
			
		||||
                totalPages = state.totalPages,
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Init listeners on bottom menu
 | 
			
		||||
        binding.pageSlider.addOnSliderTouchListener(
 | 
			
		||||
            object : Slider.OnSliderTouchListener {
 | 
			
		||||
                override fun onStartTrackingTouch(slider: Slider) {
 | 
			
		||||
                    isScrollingThroughPages = true
 | 
			
		||||
                }
 | 
			
		||||
        binding.readerNav.setComposeContent {
 | 
			
		||||
            val state by viewModel.state.collectAsState()
 | 
			
		||||
 | 
			
		||||
                override fun onStopTrackingTouch(slider: Slider) {
 | 
			
		||||
                    isScrollingThroughPages = false
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
        binding.pageSlider.addOnChangeListener { slider, value, fromUser ->
 | 
			
		||||
            if (viewer != null && fromUser) {
 | 
			
		||||
                isScrollingThroughPages = true
 | 
			
		||||
                moveToPageIndex(value.toInt())
 | 
			
		||||
                slider.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        binding.leftChapter.setOnClickListener {
 | 
			
		||||
            if (viewer != null) {
 | 
			
		||||
                if (viewer is R2LPagerViewer) {
 | 
			
		||||
                    loadNextChapter()
 | 
			
		||||
                } else {
 | 
			
		||||
                    loadPreviousChapter()
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        binding.rightChapter.setOnClickListener {
 | 
			
		||||
            if (viewer != null) {
 | 
			
		||||
                if (viewer is R2LPagerViewer) {
 | 
			
		||||
                    loadPreviousChapter()
 | 
			
		||||
                } else {
 | 
			
		||||
                    loadNextChapter()
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (state.viewer == null) return@setComposeContent
 | 
			
		||||
            val isRtl = state.viewer is R2LPagerViewer
 | 
			
		||||
 | 
			
		||||
            ChapterNavigator(
 | 
			
		||||
                isRtl = isRtl,
 | 
			
		||||
                onNextChapter = ::loadNextChapter,
 | 
			
		||||
                enabledNext = state.viewerChapters?.nextChapter != null,
 | 
			
		||||
                onPreviousChapter = ::loadPreviousChapter,
 | 
			
		||||
                enabledPrevious = state.viewerChapters?.prevChapter != null,
 | 
			
		||||
                currentPage = state.currentPage,
 | 
			
		||||
                totalPages = state.totalPages,
 | 
			
		||||
                onSliderValueChange = {
 | 
			
		||||
                    isScrollingThroughPages = true
 | 
			
		||||
                    moveToPageIndex(it)
 | 
			
		||||
                },
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        initBottomShortcuts()
 | 
			
		||||
@@ -466,18 +433,6 @@ class ReaderActivity : BaseActivity() {
 | 
			
		||||
        }
 | 
			
		||||
        binding.toolbarBottom.background = toolbarBackground.copy(this@ReaderActivity)
 | 
			
		||||
 | 
			
		||||
        binding.readerSeekbar.background = toolbarBackground.copy(this@ReaderActivity)?.apply {
 | 
			
		||||
            setCornerSize(999F)
 | 
			
		||||
        }
 | 
			
		||||
        listOf(binding.leftChapter, binding.rightChapter).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,
 | 
			
		||||
@@ -671,7 +626,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)
 | 
			
		||||
@@ -693,7 +648,7 @@ class ReaderActivity : BaseActivity() {
 | 
			
		||||
            prevViewer.destroy()
 | 
			
		||||
            binding.viewerContainer.removeAllViews()
 | 
			
		||||
        }
 | 
			
		||||
        viewer = newViewer
 | 
			
		||||
        viewModel.onViewerLoaded(newViewer)
 | 
			
		||||
        updateViewerInset(readerPreferences.fullscreen().get())
 | 
			
		||||
        binding.viewerContainer.addView(newViewer.getView())
 | 
			
		||||
 | 
			
		||||
@@ -703,15 +658,6 @@ class ReaderActivity : BaseActivity() {
 | 
			
		||||
 | 
			
		||||
        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)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val loadingIndicatorContext = createReaderThemeContext()
 | 
			
		||||
        loadingIndicator = ReaderProgressIndicator(loadingIndicatorContext).apply {
 | 
			
		||||
            updateLayoutParams<FrameLayout.LayoutParams> {
 | 
			
		||||
@@ -751,26 +697,9 @@ class ReaderActivity : BaseActivity() {
 | 
			
		||||
     */
 | 
			
		||||
    private fun setChapters(viewerChapters: ViewerChapters) {
 | 
			
		||||
        binding.readerContainer.removeView(loadingIndicator)
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
        } 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
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Invalidate menu to show proper chapter bookmark state
 | 
			
		||||
        invalidateOptionsMenu()
 | 
			
		||||
 | 
			
		||||
@@ -812,7 +741,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)
 | 
			
		||||
@@ -847,21 +776,6 @@ class ReaderActivity : BaseActivity() {
 | 
			
		||||
    @SuppressLint("SetTextI18n")
 | 
			
		||||
    fun onPageSelected(page: ReaderPage) {
 | 
			
		||||
        viewModel.onPageSelected(page)
 | 
			
		||||
        val pages = page.chapter.pages ?: return
 | 
			
		||||
 | 
			
		||||
        // Set page numbers
 | 
			
		||||
        if (viewer !is R2LPagerViewer) {
 | 
			
		||||
            binding.leftPageText.text = "${page.number}"
 | 
			
		||||
            binding.rightPageText.text = "${pages.size}"
 | 
			
		||||
        } else {
 | 
			
		||||
            binding.rightPageText.text = "${page.number}"
 | 
			
		||||
            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()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -989,7 +903,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()
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
}
 | 
			
		||||
@@ -33,6 +33,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
 | 
			
		||||
@@ -396,6 +397,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
 | 
			
		||||
@@ -881,7 +890,15 @@ class ReaderViewModel(
 | 
			
		||||
        val viewerChapters: ViewerChapters? = null,
 | 
			
		||||
        val isLoadingAdjacentChapter: Boolean = false,
 | 
			
		||||
        val currentPage: Int = -1,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Viewer used to display the pages (pager, webtoon, ...).
 | 
			
		||||
         */
 | 
			
		||||
        val viewer: Viewer? = null,
 | 
			
		||||
    ) {
 | 
			
		||||
        val totalPages: Int
 | 
			
		||||
            get() = viewerChapters?.currChapter?.pages?.size ?: -1
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    sealed class Event {
 | 
			
		||||
        object ReloadViewerChapters : Event()
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
@@ -17,7 +17,7 @@ import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition
 | 
			
		||||
import eu.kanade.tachiyomi.ui.reader.model.InsertPage
 | 
			
		||||
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
 | 
			
		||||
@@ -26,10 +26,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()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,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
 | 
			
		||||
@@ -30,9 +30,9 @@ import kotlin.math.max
 | 
			
		||||
import kotlin.math.min
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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) : BaseViewer {
 | 
			
		||||
class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = true) : Viewer {
 | 
			
		||||
 | 
			
		||||
    val downloadManager: DownloadManager by injectLazy()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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()!!
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user