diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ChapterNavigator.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ChapterNavigator.kt new file mode 100644 index 000000000..d011e1a4c --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ChapterNavigator.kt @@ -0,0 +1,84 @@ +package eu.kanade.tachiyomi.ui.reader + +import androidx.compose.foundation.background +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.collectIsDraggedAsState +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Slider +import androidx.compose.material3.Text +import androidx.compose.material3.surfaceColorAtElevation +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +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.LocalConfiguration +import androidx.compose.ui.platform.LocalHapticFeedback +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import eu.kanade.tachiyomi.util.system.isTabletUi +import kotlin.math.roundToInt + +@Composable +fun ChapterNavigator( + isRtl: Boolean, + currentPage: Int, + totalPages: Int, + onSliderValueChange: (Int) -> Unit, +) { + val isTabletUi = LocalConfiguration.current.isTabletUi() + val horizontalPadding = if (isTabletUi) 24.dp else 16.dp + val layoutDirection = if (isRtl) LayoutDirection.Rtl else LayoutDirection.Ltr + 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, + ) { + // Match with toolbar background color set in ReaderActivity + val backgroundColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp).copy(alpha = if (isSystemInDarkTheme()) 0.9f else 0.95f) + 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()) + + val interactionSource = remember { MutableInteractionSource() } + val sliderDragged by interactionSource.collectIsDraggedAsState() + LaunchedEffect(currentPage) { + if (sliderDragged) { + haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove) + } + } + Slider( + modifier = Modifier.weight(1f).padding(horizontal = 8.dp), + value = currentPage.toFloat(), + valueRange = 1f..totalPages.toFloat(), + steps = totalPages - 2, + onValueChange = { + onSliderValueChange(it.roundToInt() - 1) + }, + interactionSource = interactionSource, + ) + + Text(text = totalPages.toString()) + } + } + } + } + } +} 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 c033a3797..1317f986a 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 @@ -35,7 +35,6 @@ import android.os.Environment import android.os.ParcelFileDescriptor import android.os.ParcelFileDescriptor.MODE_READ_ONLY import android.provider.MediaStore -import android.view.HapticFeedbackConstants import android.view.KeyEvent import android.view.MotionEvent import android.view.View.LAYER_TYPE_HARDWARE @@ -49,6 +48,7 @@ import androidx.activity.result.contract.ActivityResultContracts.CreateDocument import androidx.appcompat.app.AlertDialog import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.core.content.ContextCompat import androidx.core.content.FileProvider @@ -59,7 +59,6 @@ import androidx.core.view.WindowInsetsControllerCompat import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope import com.google.android.material.shape.MaterialShapeDrawable -import com.google.android.material.slider.Slider import com.google.android.material.transition.MaterialContainerTransform import com.hippo.ehviewer.AppConfig import com.hippo.ehviewer.BuildConfig @@ -113,7 +112,6 @@ import java.util.concurrent.atomic.AtomicReference import kotlin.coroutines.Continuation import kotlin.coroutines.resume import kotlin.math.abs -import kotlin.math.max class ReaderActivity : EhActivity() { lateinit var binding: ReaderActivityBinding @@ -352,7 +350,7 @@ class ReaderActivity : EhActivity() { binding.actionReadingMode.setImageResource(viewerMode.iconRes) viewer?.destroy() viewer = ReadingModeType.toViewer(ReaderPreferences.defaultReadingMode().get(), this) - binding.pageSlider.isRTL = viewer is R2LPagerViewer + isRtl = viewer is R2LPagerViewer updateViewerInset(ReaderPreferences.fullscreen().get()) binding.viewerContainer.removeAllViews() setOrientation(ReaderPreferences.defaultOrientationType().get()) @@ -660,6 +658,7 @@ class ReaderActivity : EhActivity() { } private var currentPage by mutableIntStateOf(-1) + private var isRtl by mutableStateOf(false) /** * Initializes the reader menu. It sets up click listeners and the initial visibility. @@ -672,31 +671,26 @@ class ReaderActivity : EhActivity() { } } + val totalPages = mGalleryProvider?.takeIf { it.isReady }?.size ?: -1 + binding.pageNumber.setReaderMD3Content { PageIndicatorText( currentPage = currentPage, - totalPages = mGalleryProvider?.takeIf { it.isReady }?.size ?: -1, + totalPages = totalPages, ) } // Init listeners on bottom menu - binding.pageSlider.addOnSliderTouchListener( - object : Slider.OnSliderTouchListener { - override fun onStartTrackingTouch(slider: Slider) { + binding.readerNav.setReaderMD3Content { + ChapterNavigator( + isRtl = isRtl, + currentPage = currentPage, + totalPages = totalPages, + onSliderValueChange = { isScrollingThroughPages = true - } - - 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.CLOCK_TICK) - } + moveToPageIndex(it) + }, + ) } initBottomShortcuts() @@ -708,10 +702,6 @@ class ReaderActivity : EhActivity() { } binding.toolbarBottom.background = toolbarBackground.copy(this@ReaderActivity) - binding.readerSeekbar.background = toolbarBackground.copy(this@ReaderActivity)?.apply { - setCornerSize(999F) - } - val toolbarColor = ColorUtils.setAlphaComponent( toolbarBackground.resolvedTintColor, toolbarBackground.alpha, @@ -866,25 +856,9 @@ class ReaderActivity : EhActivity() { */ @SuppressLint("SetTextI18n") fun onPageSelected(page: ReaderPage) { - val pages = mGalleryProvider?.mPages ?: return - // Set bottom page number currentPage = page.number - // 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() - mCurrentIndex = page.index mGalleryProvider?.putStartPage(mCurrentIndex) } @@ -1064,7 +1038,7 @@ class ReaderActivity : EhActivity() { } fun setReaderSeekbarVisibility(visible: Boolean) { - binding.readerSeekbar.isVisible = visible + binding.readerNav.isVisible = visible } /** 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/res/layout/reader_activity.xml b/app/src/main/res/layout/reader_activity.xml index b564d854a..009b34a27 100644 --- a/app/src/main/res/layout/reader_activity.xml +++ b/app/src/main/res/layout/reader_activity.xml @@ -54,58 +54,12 @@ android:layout_gravity="bottom" android:orientation="vertical"> - - - - - - - - - - - - - + android:layoutDirection="ltr" />