diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 943ae0560..ea17af131 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -280,7 +280,6 @@ dependencies { implementation(libs.directionalviewpager) { exclude(group = "androidx.viewpager", module = "viewpager") } - implementation(libs.insetter) implementation(libs.richeditor.compose) implementation(libs.aboutLibraries.compose) implementation(libs.bundles.voyager) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt index ad901d735..30f09d905 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt @@ -1,6 +1,5 @@ package eu.kanade.presentation.more.settings.screen -import android.os.Build import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.getValue @@ -14,6 +13,7 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences.Companion.zoomWid import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences.WebtoonScaleType import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig +import eu.kanade.tachiyomi.util.system.hasDisplayCutout import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.toImmutableMap @@ -148,9 +148,7 @@ object SettingsReaderScreen : SearchableSettings { Preference.PreferenceItem.SwitchPreference( preference = readerPreferences.cutoutShort(), title = stringResource(MR.strings.pref_cutout_short), - enabled = fullscreen && - Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && - LocalView.current.rootWindowInsets?.displayCutout != null, // has cutout + enabled = LocalView.current.hasDisplayCutout() && fullscreen, ), Preference.PreferenceItem.SwitchPreference( preference = readerPreferences.keepScreenOn(), diff --git a/app/src/main/java/eu/kanade/presentation/reader/PageIndicatorText.kt b/app/src/main/java/eu/kanade/presentation/reader/ReaderPageIndicator.kt similarity index 86% rename from app/src/main/java/eu/kanade/presentation/reader/PageIndicatorText.kt rename to app/src/main/java/eu/kanade/presentation/reader/ReaderPageIndicator.kt index 40cd15768..e3efffa54 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/PageIndicatorText.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/ReaderPageIndicator.kt @@ -6,6 +6,7 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.text.TextStyle @@ -15,11 +16,12 @@ import androidx.compose.ui.unit.sp import eu.kanade.presentation.theme.TachiyomiPreviewTheme @Composable -fun PageIndicatorText( +fun ReaderPageIndicator( // SY --> currentPage: String, // SY <-- totalPages: Int, + modifier: Modifier = Modifier, ) { if (currentPage.isEmpty() || totalPages <= 0) return @@ -40,6 +42,7 @@ fun PageIndicatorText( Box( contentAlignment = Alignment.Center, + modifier = modifier, ) { Text( text = text, @@ -54,10 +57,10 @@ fun PageIndicatorText( @PreviewLightDark @Composable -private fun PageIndicatorTextPreview() { +private fun ReaderPageIndicatorPreview() { TachiyomiPreviewTheme { Surface { - PageIndicatorText(currentPage = "10", totalPages = 69) + ReaderPageIndicator(currentPage = "10", totalPages = 69) } } } diff --git a/app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderAppBars.kt b/app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderAppBars.kt index d50a5919b..ccbc5a985 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderAppBars.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderAppBars.kt @@ -2,10 +2,13 @@ package eu.kanade.presentation.reader.appbars import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut import androidx.compose.animation.slideInHorizontally import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideOutHorizontally import androidx.compose.animation.slideOutVertically +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement @@ -13,13 +16,12 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.systemBarsPadding -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.Bookmark -import androidx.compose.material.icons.outlined.BookmarkBorder +import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.material3.MaterialTheme import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable @@ -30,20 +32,16 @@ import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp -import eu.kanade.presentation.components.AppBar -import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.reader.components.ChapterNavigator import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode import eu.kanade.tachiyomi.ui.reader.viewer.Viewer import eu.kanade.tachiyomi.ui.reader.viewer.pager.R2LPagerViewer import kotlinx.collections.immutable.ImmutableSet -import kotlinx.collections.immutable.persistentListOf -import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.padding -import tachiyomi.presentation.core.i18n.stringResource -private val animationSpec = tween(200) +private val readerBarsSlideAnimationSpec = tween(200) +private val readerBarsFadeAnimationSpec = tween(150) // SY --> enum class NavBarType { @@ -72,7 +70,6 @@ fun BoxIgnoreLayoutDirection(modifier: Modifier, content: @Composable BoxScope.( @Composable fun ReaderAppBars( visible: Boolean, - fullscreen: Boolean, mangaTitle: String?, chapterTitle: String?, @@ -129,28 +126,18 @@ fun ReaderAppBars( .surfaceColorAtElevation(3.dp) .copy(alpha = if (isSystemInDarkTheme()) 0.9f else 0.95f) - val modifierWithInsetsPadding = if (fullscreen) { - Modifier.systemBarsPadding() - } else { - Modifier - } - // SY --> BoxIgnoreLayoutDirection( Modifier.fillMaxWidth(), ) { AnimatedVisibility( visible = visible && navBarType == NavBarType.VerticalLeft, - enter = slideInHorizontally( - initialOffsetX = { -it }, - animationSpec = animationSpec, - ), - exit = slideOutHorizontally( - targetOffsetX = { -it }, - animationSpec = animationSpec, - ), - modifier = modifierWithInsetsPadding - .padding(bottom = 64.dp, top = 112.dp) + enter = slideInHorizontally(initialOffsetX = { -it }, animationSpec = readerBarsSlideAnimationSpec) + + fadeIn(animationSpec = readerBarsFadeAnimationSpec), + exit = slideOutHorizontally(targetOffsetX = { -it }, animationSpec = readerBarsSlideAnimationSpec) + + fadeOut(animationSpec = readerBarsFadeAnimationSpec), + modifier = Modifier + .padding(top = 164.dp, bottom = 70.dp) .align(Alignment.TopStart), ) { ChapterNavigator( @@ -162,23 +149,21 @@ fun ReaderAppBars( currentPage = currentPage, totalPages = totalPages, onPageIndexChange = onPageIndexChange, + // SY --> isVerticalSlider = true, currentPageText = currentPageText, + // SY <-- ) } AnimatedVisibility( visible = visible && navBarType == NavBarType.VerticalRight, - enter = slideInHorizontally( - initialOffsetX = { it }, - animationSpec = animationSpec, - ), - exit = slideOutHorizontally( - targetOffsetX = { it }, - animationSpec = animationSpec, - ), - modifier = modifierWithInsetsPadding - .padding(bottom = 64.dp, top = 112.dp) + enter = slideInHorizontally(initialOffsetX = { it }, animationSpec = readerBarsSlideAnimationSpec) + + fadeIn(animationSpec = readerBarsFadeAnimationSpec), + exit = slideOutHorizontally(targetOffsetX = { it }, animationSpec = readerBarsSlideAnimationSpec) + + fadeOut(animationSpec = readerBarsFadeAnimationSpec), + modifier = Modifier + .padding(top = 164.dp, bottom = 70.dp) .align(Alignment.TopEnd), ) { ChapterNavigator( @@ -190,87 +175,38 @@ fun ReaderAppBars( currentPage = currentPage, totalPages = totalPages, onPageIndexChange = onPageIndexChange, + // SY --> isVerticalSlider = true, currentPageText = currentPageText, + // SY <-- ) } // SY <-- - Column( - modifier = Modifier.fillMaxHeight(), - verticalArrangement = Arrangement.SpaceBetween, - ) { + Column(modifier = Modifier.fillMaxHeight()) { AnimatedVisibility( visible = visible, - enter = slideInVertically( - initialOffsetY = { -it }, - animationSpec = animationSpec, - ), - exit = slideOutVertically( - targetOffsetY = { -it }, - animationSpec = animationSpec, - ), + enter = slideInVertically(initialOffsetY = { -it }, animationSpec = readerBarsSlideAnimationSpec) + + fadeIn(animationSpec = readerBarsFadeAnimationSpec), + exit = slideOutVertically(targetOffsetY = { -it }, animationSpec = readerBarsSlideAnimationSpec) + + fadeOut(animationSpec = readerBarsFadeAnimationSpec), ) { // SY --> - Column(modifierWithInsetsPadding) { + Column { // SY <-- - AppBar( - modifier = /*SY --> */ Modifier /*SY <-- */ + ReaderTopBar( + modifier = Modifier + .background(backgroundColor) .clickable(onClick = onClickTopAppBar), - backgroundColor = backgroundColor, - title = mangaTitle, - subtitle = chapterTitle, + mangaTitle = mangaTitle, + chapterTitle = chapterTitle, navigateUp = navigateUp, - actions = { - AppBarActions( - actions = persistentListOf().builder() - .apply { - add( - AppBar.Action( - title = stringResource( - if (bookmarked) { - MR.strings.action_remove_bookmark - } else { - MR.strings.action_bookmark - }, - ), - icon = if (bookmarked) { - Icons.Outlined.Bookmark - } else { - Icons.Outlined.BookmarkBorder - }, - onClick = onToggleBookmarked, - ), - ) - /* SY --> - onOpenInWebView?.let { - add( - AppBar.OverflowAction( - title = stringResource(MR.strings.action_open_in_web_view), - onClick = it, - ), - ) - } - onOpenInBrowser?.let { - add( - AppBar.OverflowAction( - title = stringResource(MR.strings.action_open_in_browser), - onClick = it, - ), - ) - } - onShare?.let { - add( - AppBar.OverflowAction( - title = stringResource(MR.strings.action_share), - onClick = it, - ), - ) - } - SY <-- */ - } - .build(), - ) - }, + bookmarked = bookmarked, + onToggleBookmarked = onToggleBookmarked, + // SY --> + onOpenInWebView = null, // onOpenInWebView, + onOpenInBrowser = null, // onOpenInBrowser, + onShare = null, // onShare, + // SY <-- ) // SY --> ExhUtils( @@ -288,28 +224,23 @@ fun ReaderAppBars( onClickBoostPage = onClickBoostPage, onClickBoostPageHelp = onClickBoostPageHelp, ) - // SY <-- } + // SY <-- } Spacer(modifier = Modifier.weight(1f)) AnimatedVisibility( visible = visible, - enter = slideInVertically( - initialOffsetY = { it }, - animationSpec = animationSpec, - ), - exit = slideOutVertically( - targetOffsetY = { it }, - animationSpec = animationSpec, - ), + enter = slideInVertically(initialOffsetY = { it }, animationSpec = readerBarsSlideAnimationSpec) + + fadeIn(animationSpec = readerBarsFadeAnimationSpec), + exit = slideOutVertically(targetOffsetY = { it }, animationSpec = readerBarsSlideAnimationSpec) + + fadeOut(animationSpec = readerBarsFadeAnimationSpec), ) { - Column( - modifier = modifierWithInsetsPadding, - verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small), - ) { + Column(verticalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small)) { + // SY --> if (navBarType == NavBarType.Bottom) { + // SY <-- ChapterNavigator( isRtl = isRtl, onNextChapter = onNextChapter, @@ -319,15 +250,18 @@ fun ReaderAppBars( currentPage = currentPage, totalPages = totalPages, onPageIndexChange = onPageIndexChange, + // SY --> isVerticalSlider = false, currentPageText = currentPageText, + // SY <-- ) } - BottomReaderBar( - // SY --> - enabledButtons = enabledButtons, - // SY <-- - backgroundColor = backgroundColor, + ReaderBottomBar( + modifier = Modifier + .fillMaxWidth() + .background(backgroundColor) + .padding(horizontal = MaterialTheme.padding.small) + .windowInsetsPadding(WindowInsets.navigationBars), readingMode = readingMode, onClickReadingMode = onClickReadingMode, orientation = orientation, @@ -336,6 +270,7 @@ fun ReaderAppBars( onClickCropBorder = onClickCropBorder, onClickSettings = onClickSettings, // SY --> + enabledButtons = enabledButtons, currentReadingMode = currentReadingMode, dualPageSplitEnabled = dualPageSplitEnabled, doublePages = doublePages, diff --git a/app/src/main/java/eu/kanade/presentation/reader/appbars/BottomReaderBar.kt b/app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderBottomBar.kt similarity index 94% rename from app/src/main/java/eu/kanade/presentation/reader/appbars/BottomReaderBar.kt rename to app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderBottomBar.kt index 281b09040..49822833f 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/appbars/BottomReaderBar.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderBottomBar.kt @@ -1,10 +1,7 @@ package eu.kanade.presentation.reader.appbars -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Explore import androidx.compose.material.icons.outlined.FormatListNumbered @@ -17,9 +14,8 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.dp import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.reader.setting.ReaderBottomButton import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation @@ -30,11 +26,10 @@ import tachiyomi.i18n.sy.SYMR import tachiyomi.presentation.core.i18n.stringResource @Composable -fun BottomReaderBar( +fun ReaderBottomBar( // SY --> enabledButtons: ImmutableSet, // SY <-- - backgroundColor: Color, readingMode: ReadingMode, onClickReadingMode: () -> Unit, orientation: ReaderOrientation, @@ -53,15 +48,14 @@ fun BottomReaderBar( onClickPageLayout: () -> Unit, onClickShiftPage: () -> Unit, // SY <-- + modifier: Modifier = Modifier, ) { // KMK --> val iconColor = MaterialTheme.colorScheme.primary // KMK <-- Row( - modifier = Modifier - .fillMaxWidth() - .background(backgroundColor) - .padding(8.dp), + modifier = modifier + .pointerInput(Unit) {}, horizontalArrangement = Arrangement.SpaceEvenly, verticalAlignment = Alignment.CenterVertically, ) { diff --git a/app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderTopBar.kt b/app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderTopBar.kt new file mode 100644 index 000000000..9d783af7b --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderTopBar.kt @@ -0,0 +1,83 @@ +package eu.kanade.presentation.reader.appbars + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Bookmark +import androidx.compose.material.icons.outlined.BookmarkBorder +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import eu.kanade.presentation.components.AppBar +import eu.kanade.presentation.components.AppBarActions +import kotlinx.collections.immutable.persistentListOf +import tachiyomi.i18n.MR +import tachiyomi.presentation.core.i18n.stringResource + +@Composable +fun ReaderTopBar( + mangaTitle: String?, + chapterTitle: String?, + navigateUp: () -> Unit, + bookmarked: Boolean, + onToggleBookmarked: () -> Unit, + onOpenInWebView: (() -> Unit)?, + onOpenInBrowser: (() -> Unit)?, + onShare: (() -> Unit)?, + modifier: Modifier = Modifier, +) { + AppBar( + modifier = modifier, + backgroundColor = Color.Transparent, + title = mangaTitle, + subtitle = chapterTitle, + navigateUp = navigateUp, + actions = { + AppBarActions( + actions = persistentListOf().builder() + .apply { + add( + AppBar.Action( + title = stringResource( + if (bookmarked) { + MR.strings.action_remove_bookmark + } else { + MR.strings.action_bookmark + }, + ), + icon = if (bookmarked) { + Icons.Outlined.Bookmark + } else { + Icons.Outlined.BookmarkBorder + }, + onClick = onToggleBookmarked, + ), + ) + onOpenInWebView?.let { + add( + AppBar.OverflowAction( + title = stringResource(MR.strings.action_open_in_web_view), + onClick = it, + ), + ) + } + onOpenInBrowser?.let { + add( + AppBar.OverflowAction( + title = stringResource(MR.strings.action_open_in_browser), + onClick = it, + ), + ) + } + onShare?.let { + add( + AppBar.OverflowAction( + title = stringResource(MR.strings.action_share), + onClick = it, + ), + ) + } + } + .build(), + ) + }, + ) +} diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt index fb16d3ae3..65919768c 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt @@ -5,8 +5,10 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.ui.platform.LocalView import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel +import eu.kanade.tachiyomi.util.system.hasDisplayCutout import tachiyomi.i18n.MR import tachiyomi.i18n.sy.SYMR import tachiyomi.presentation.core.components.CheckboxItem @@ -84,7 +86,8 @@ internal fun GeneralPage(screenModel: ReaderSettingsScreenModel) { pref = screenModel.preferences.fullscreen(), ) - if (screenModel.hasDisplayCutout && screenModel.preferences.fullscreen().get()) { + val isFullscreen by screenModel.preferences.fullscreen().collectAsState() + if (LocalView.current.hasDisplayCutout() && isFullscreen) { CheckboxItem( label = stringResource(MR.strings.pref_cutout_short), pref = screenModel.preferences.cutoutShort(), 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 a600e5130..5d56888bd 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 @@ -22,17 +22,21 @@ import android.view.View import android.view.View.LAYER_TYPE_HARDWARE import android.view.WindowManager import android.widget.Toast +import androidx.activity.SystemBarStyle import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.enableEdgeToEdge import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.material3.AlertDialog import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton -import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -40,14 +44,16 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.core.content.getSystemService -import androidx.core.graphics.ColorUtils +import androidx.core.graphics.Insets import androidx.core.net.toUri import androidx.core.transition.doOnEnd +import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat @@ -55,12 +61,8 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView -import com.google.android.material.elevation.SurfaceColors import com.google.android.material.transition.platform.MaterialContainerTransform import com.hippo.unifile.UniFile -import com.materialkolor.dynamicColorScheme -import com.materialkolor.dynamiccolor.ColorSpec -import dev.chrisbanes.insetter.applyInsetter import eu.kanade.core.util.ifSourcesLoaded import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.connections.service.ConnectionsPreferences @@ -69,9 +71,9 @@ import eu.kanade.domain.ui.UiPreferences import eu.kanade.presentation.reader.ChapterListDialog import eu.kanade.presentation.reader.DisplayRefreshHost import eu.kanade.presentation.reader.OrientationSelectDialog -import eu.kanade.presentation.reader.PageIndicatorText import eu.kanade.presentation.reader.ReaderContentOverlay import eu.kanade.presentation.reader.ReaderPageActionsDialog +import eu.kanade.presentation.reader.ReaderPageIndicator import eu.kanade.presentation.reader.ReadingModeSelectDialog import eu.kanade.presentation.reader.appbars.NavBarType import eu.kanade.presentation.reader.appbars.ReaderAppBars @@ -171,10 +173,7 @@ class ReaderActivity : BaseActivity() { private val preferences = Injekt.get() // KMK --> - private val uiPreferences = Injekt.get() - private val themeCoverBased = uiPreferences.themeCoverBased().get() - private val themeDarkAmoled = uiPreferences.themeDarkAmoled().get() - private val themeCoverBasedStyle = uiPreferences.themeCoverBasedStyle().get() + val themeCoverBased = Injekt.get().themeCoverBased().get() // KMK <-- // AM (CONNECTIONS) --> @@ -186,8 +185,6 @@ class ReaderActivity : BaseActivity() { val viewModel by viewModels() private var assistUrl: String? = null - private val hasCutout by lazy { hasDisplayCutout() } - // SY --> private val sourceManager = Injekt.get() // SY <-- @@ -201,7 +198,7 @@ class ReaderActivity : BaseActivity() { private var readingModeToast: Toast? = null private val displayRefreshHost = DisplayRefreshHost() - private val windowInsetsController by lazy { WindowInsetsControllerCompat(window, binding.root) } + private val windowInsetsController by lazy { WindowInsetsControllerCompat(window, window.decorView) } private var loadingIndicator: ReaderProgressIndicator? = null @@ -224,10 +221,17 @@ class ReaderActivity : BaseActivity() { overridePendingTransition(R.anim.shared_axis_x_push_enter, R.anim.shared_axis_x_push_exit) } + enableEdgeToEdge(navigationBarStyle = SystemBarStyle.auto(Color.TRANSPARENT, Color.TRANSPARENT)) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + window.isNavigationBarContrastEnforced = false + } + windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + super.onCreate(savedInstanceState) binding = ReaderActivityBinding.inflate(layoutInflater) setContentView(binding.root) + binding.setComposeOverlay() if (viewModel.needsInit()) { val manga = intent.extras?.getLong("manga", -1) ?: -1L @@ -253,7 +257,11 @@ class ReaderActivity : BaseActivity() { } config = ReaderConfig() - initializeMenu() + setMenuVisibility(viewModel.state.value.menuVisible) + + // EXH --> + enableExhAutoScroll() + // EXH <-- // Finish when incognito mode is disabled preferences.incognitoMode().changes() @@ -310,6 +318,210 @@ class ReaderActivity : BaseActivity() { .launchIn(lifecycleScope) } + private fun ReaderActivityBinding.setComposeOverlay(): Unit = composeOverlay.setComposeContent { + // KMK --> + TachiyomiTheme( + seedColor = seedColorState().takeIf { themeCoverBased }, + typography = MaterialTheme.typography.copy( + bodyLarge = MaterialTheme.typography.bodySmall, + ), + ) { + val context = LocalContext.current + // KMK <-- + val state by viewModel.state.collectAsState() + val showPageNumber by readerPreferences.showPageNumber().collectAsState() + val isFullscreen by readerPreferences.fullscreen().collectAsState() + val settingsScreenModel = remember { + ReaderSettingsScreenModel( + readerState = viewModel.state, + onChangeReadingMode = viewModel::setMangaReadingMode, + onChangeOrientation = viewModel::setMangaOrientationType, + ) + } + + Box(modifier = Modifier.fillMaxSize()) { + if (!state.menuVisible && showPageNumber) { + ReaderPageIndicator( + // SY --> + currentPage = state.currentPageText, + // SY <-- + totalPages = state.totalPages, + modifier = Modifier + .align(Alignment.BottomCenter) + .then(if (isFullscreen) Modifier else Modifier.navigationBarsPadding()), + ) + } + + ContentOverlay(state = state) + + AppBars(state = state) + } + + // KMK --> + val externalStoragePermissionNotGranted = Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && + context.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == + PackageManager.PERMISSION_DENIED + val permissionRequester = rememberLauncherForActivityResult( + contract = ActivityResultContracts.RequestPermission(), + onResult = { + toast(KMR.strings.permission_writing_external_storage_succeed) + }, + ) + // KMK <-- + + val onDismissRequest = viewModel::closeDialog + when (state.dialog) { + is ReaderViewModel.Dialog.Loading -> { + AlertDialog( + onDismissRequest = {}, + confirmButton = {}, + text = { + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + CircularProgressIndicator( + // KMK --> + color = MaterialTheme.colorScheme.primary, + // KMK <-- + ) + Text(stringResource(MR.strings.loading)) + } + }, + ) + } + + is ReaderViewModel.Dialog.Settings -> { + ReaderSettingsDialog( + onDismissRequest = onDismissRequest, + onShowMenus = { setMenuVisibility(true) }, + onHideMenus = { setMenuVisibility(false) }, + screenModel = settingsScreenModel, + ) + } + + is ReaderViewModel.Dialog.ReadingModeSelect -> { + ReadingModeSelectDialog( + onDismissRequest = onDismissRequest, + screenModel = settingsScreenModel, + onChange = { stringRes -> + menuToggleToast?.cancel() + if (!readerPreferences.showReadingMode().get()) { + menuToggleToast = toast(stringRes) + } + }, + ) + } + + is ReaderViewModel.Dialog.OrientationModeSelect -> { + OrientationSelectDialog( + onDismissRequest = onDismissRequest, + screenModel = settingsScreenModel, + onChange = { stringRes -> + menuToggleToast?.cancel() + menuToggleToast = toast(stringRes) + }, + ) + } + + is ReaderViewModel.Dialog.PageActions -> { + ReaderPageActionsDialog( + onDismissRequest = onDismissRequest, + onSetAsCover = viewModel::setAsCover, + onShare = viewModel::shareImage, + // SY --> + onSave = { extra -> + // KMK --> + if (externalStoragePermissionNotGranted) { + permissionRequester.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) + } else { + // KMK <-- + viewModel.saveImage(extra) + } + }, + onShareCombined = viewModel::shareImages, + onSaveCombined = { + // KMK --> + if (externalStoragePermissionNotGranted) { + permissionRequester.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) + } else { + // KMK <-- + viewModel.saveImages() + } + }, + hasExtraPage = (state.dialog as? ReaderViewModel.Dialog.PageActions)?.extraPage != null, + ) + } + + is ReaderViewModel.Dialog.ChapterList -> { + var chapters by remember { + mutableStateOf(viewModel.getChapters().toImmutableList()) + } + ChapterListDialog( + onDismissRequest = onDismissRequest, + screenModel = settingsScreenModel, + chapters = chapters, + onClickChapter = { + viewModel.loadNewChapterFromDialog(it) + onDismissRequest() + }, + onBookmark = { chapter -> + viewModel.toggleBookmark(chapter.id, !chapter.bookmark) + chapters = chapters.map { + if (it.chapter.id == chapter.id) { + it.copy(chapter = chapter.copy(bookmark = !chapter.bookmark)) + } else { + it + } + }.toImmutableList() + }, + state.dateRelativeTime, + // KMK --> + onDownloadAction = { chapter, action -> + viewModel.handleDownloadAction(chapter, action) + }, + // KMK <-- + ) + } + + ReaderViewModel.Dialog.AutoScrollHelp -> AlertDialog( + onDismissRequest = onDismissRequest, + confirmButton = { + TextButton(onClick = onDismissRequest) { + Text(text = stringResource(MR.strings.action_ok)) + } + }, + title = { Text(text = stringResource(SYMR.strings.eh_autoscroll_help)) }, + text = { Text(text = stringResource(SYMR.strings.eh_autoscroll_help_message)) }, + ) + + ReaderViewModel.Dialog.BoostPageHelp -> AlertDialog( + onDismissRequest = onDismissRequest, + confirmButton = { + TextButton(onClick = onDismissRequest) { + Text(text = stringResource(MR.strings.action_ok)) + } + }, + title = { Text(text = stringResource(SYMR.strings.eh_boost_page_help)) }, + text = { Text(text = stringResource(SYMR.strings.eh_boost_page_help_message)) }, + ) + + ReaderViewModel.Dialog.RetryAllHelp -> AlertDialog( + onDismissRequest = onDismissRequest, + confirmButton = { + TextButton(onClick = onDismissRequest) { + Text(text = stringResource(MR.strings.action_ok)) + } + }, + title = { Text(text = stringResource(SYMR.strings.eh_retry_all_help)) }, + text = { Text(text = stringResource(SYMR.strings.eh_retry_all_help_message)) }, + ) + // SY <-- + null -> {} + } + } + } + /** * Called when the activity is destroyed. Cleans up the viewer, configuration and any view. */ @@ -411,389 +623,146 @@ class ReaderActivity : BaseActivity() { return handled || super.dispatchGenericMotionEvent(event) } - /** - * Initializes the reader menu. It sets up click listeners and the initial visibility. - */ - private fun initializeMenu() { - // KMK --> - @Composable - fun pageNumberContent() { - // KMK <-- - val state by viewModel.state.collectAsState() - val showPageNumber by viewModel.readerPreferences.showPageNumber().collectAsState() + @Composable + private fun ContentOverlay(state: ReaderViewModel.State) { + val flashOnPageChange by readerPreferences.flashOnPageChange().collectAsState() - if (!state.menuVisible && showPageNumber) { - PageIndicatorText( - // SY --> - currentPage = state.currentPageText, - // SY <-- - totalPages = state.totalPages, - ) - } + val colorOverlayEnabled by readerPreferences.colorFilter().collectAsState() + val colorOverlay by readerPreferences.colorFilterValue().collectAsState() + val colorOverlayMode by readerPreferences.colorFilterMode().collectAsState() + val colorOverlayBlendMode = remember(colorOverlayMode) { + ReaderPreferences.ColorFilterMode.getOrNull(colorOverlayMode)?.second } - // KMK --> - binding.pageNumber.setComposeContent { - TachiyomiTheme( - seedColor = seedColorState().takeIf { themeCoverBased }, - ) { - pageNumberContent() - } - } - - @Composable - fun dialogRootContent() { - val context = LocalContext.current - // KMK <-- - val state by viewModel.state.collectAsState() - val settingsScreenModel = remember { - ReaderSettingsScreenModel( - readerState = viewModel.state, - hasDisplayCutout = hasCutout, - onChangeReadingMode = viewModel::setMangaReadingMode, - onChangeOrientation = viewModel::setMangaOrientationType, - ) - } - - if (!ifSourcesLoaded()) { - return - } - - val isHttpSource = viewModel.getSource() is HttpSource - val isFullscreen by readerPreferences.fullscreen().collectAsState() - val flashOnPageChange by readerPreferences.flashOnPageChange().collectAsState() - - val colorOverlayEnabled by readerPreferences.colorFilter().collectAsState() - val colorOverlay by readerPreferences.colorFilterValue().collectAsState() - val colorOverlayMode by readerPreferences.colorFilterMode().collectAsState() - val colorOverlayBlendMode = remember(colorOverlayMode) { - ReaderPreferences.ColorFilterMode.getOrNull(colorOverlayMode)?.second - } - - val cropBorderPaged by readerPreferences.cropBorders().collectAsState() - val cropBorderWebtoon by readerPreferences.cropBordersWebtoon().collectAsState() - // SY --> - val readingMode = viewModel.getMangaReadingMode() - val isPagerType = ReadingMode.isPagerType(readingMode) - val isWebtoon = ReadingMode.WEBTOON.flagValue == readingMode - val cropBorderContinuousVertical by readerPreferences.cropBordersContinuousVertical().collectAsState() - val cropEnabled = if (isPagerType) { - cropBorderPaged - } else if (isWebtoon) { - cropBorderWebtoon - } else { - cropBorderContinuousVertical - } - val readerBottomButtons by readerPreferences.readerBottomButtons().changes().map { it.toImmutableSet() } - .collectAsState(persistentSetOf()) - val dualPageSplitPaged by readerPreferences.dualPageSplitPaged().collectAsState() - - val forceHorizontalSeekbar by readerPreferences.forceHorizontalSeekbar().collectAsState() - val landscapeVerticalSeekbar by readerPreferences.landscapeVerticalSeekbar().collectAsState() - val leftHandedVerticalSeekbar by readerPreferences.leftVerticalSeekbar().collectAsState() - val configuration = LocalConfiguration.current - val verticalSeekbarLandscape = - configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && landscapeVerticalSeekbar - val verticalSeekbarHorizontal = configuration.orientation == Configuration.ORIENTATION_PORTRAIT - val viewerIsVertical = (state.viewer is WebtoonViewer || state.viewer is VerticalPagerViewer) - val showVerticalSeekbar = - !forceHorizontalSeekbar && (verticalSeekbarLandscape || verticalSeekbarHorizontal) && viewerIsVertical - val navBarType = when { - !showVerticalSeekbar -> NavBarType.Bottom - leftHandedVerticalSeekbar -> NavBarType.VerticalLeft - else -> NavBarType.VerticalRight - } - // SY <-- - - // KMK --> - val externalStoragePermissionNotGranted = Build.VERSION.SDK_INT < Build.VERSION_CODES.Q && - context.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == - PackageManager.PERMISSION_DENIED - val permissionRequester = rememberLauncherForActivityResult( - contract = ActivityResultContracts.RequestPermission(), - onResult = { - toast(KMR.strings.permission_writing_external_storage_succeed) - }, - ) - // KMK <-- - - ReaderContentOverlay( - brightness = state.brightnessOverlayValue, - color = colorOverlay.takeIf { colorOverlayEnabled }, - colorBlendMode = colorOverlayBlendMode, - ) - - ReaderAppBars( - visible = state.menuVisible, - fullscreen = isFullscreen, - - mangaTitle = state.manga?.title, - chapterTitle = state.currentChapter?.chapter?.name, - navigateUp = onBackPressedDispatcher::onBackPressed, - onClickTopAppBar = ::openMangaScreen, - bookmarked = state.bookmarked, - onToggleBookmarked = viewModel::toggleChapterBookmark, - onOpenInWebView = ::openChapterInWebView.takeIf { isHttpSource }, - onOpenInBrowser = ::openChapterInBrowser.takeIf { isHttpSource }, - onShare = ::shareChapter.takeIf { isHttpSource }, - - viewer = state.viewer, - onNextChapter = ::loadNextChapter, - enabledNext = state.viewerChapters?.nextChapter != null, - onPreviousChapter = ::loadPreviousChapter, - enabledPrevious = state.viewerChapters?.prevChapter != null, - currentPage = state.currentPage, - totalPages = state.totalPages, - onPageIndexChange = { - isScrollingThroughPages = true - moveToPageIndex(it) - }, - - readingMode = ReadingMode.fromPreference( - viewModel.getMangaReadingMode(resolveDefault = false), - ), - onClickReadingMode = viewModel::openReadingModeSelectDialog, - orientation = ReaderOrientation.fromPreference( - viewModel.getMangaOrientation(resolveDefault = false), - ), - onClickOrientation = viewModel::openOrientationModeSelectDialog, - cropEnabled = cropEnabled, - onClickCropBorder = { - val enabled = viewModel.toggleCropBorders() - menuToggleToast?.cancel() - menuToggleToast = toast(if (enabled) MR.strings.on else MR.strings.off) - }, - onClickSettings = viewModel::openSettingsDialog, - // SY --> - isExhToolsVisible = state.ehUtilsVisible, - onSetExhUtilsVisibility = viewModel::showEhUtils, - isAutoScroll = state.autoScroll, - isAutoScrollEnabled = state.isAutoScrollEnabled, - onToggleAutoscroll = viewModel::toggleAutoScroll, - autoScrollFrequency = state.ehAutoscrollFreq, - onSetAutoScrollFrequency = viewModel::setAutoScrollFrequency, - onClickAutoScrollHelp = viewModel::openAutoScrollHelpDialog, - onClickRetryAll = ::exhRetryAll, - onClickRetryAllHelp = viewModel::openRetryAllHelp, - onClickBoostPage = ::exhBoostPage, - onClickBoostPageHelp = viewModel::openBoostPageHelp, - currentPageText = state.currentPageText, - navBarType = navBarType, - enabledButtons = readerBottomButtons, - currentReadingMode = ReadingMode.fromPreference( - viewModel.getMangaReadingMode(resolveDefault = true), - ), - dualPageSplitEnabled = dualPageSplitPaged, - doublePages = state.doublePages, - onClickChapterList = viewModel::openChapterListDialog, - onClickPageLayout = { - if (readerPreferences.pageLayout().get() == PagerConfig.PageLayout.AUTOMATIC) { - (viewModel.state.value.viewer as? PagerViewer)?.config?.let { config -> - config.doublePages = !config.doublePages - reloadChapters(config.doublePages, true) - } - } else { - readerPreferences.pageLayout().set(1 - readerPreferences.pageLayout().get()) - } - }, - onClickShiftPage = ::shiftDoublePages, - // SY <-- - ) - - if (flashOnPageChange) { - DisplayRefreshHost( - hostState = displayRefreshHost, - ) - } - - val onDismissRequest = viewModel::closeDialog - when (state.dialog) { - is ReaderViewModel.Dialog.Loading -> { - AlertDialog( - onDismissRequest = {}, - confirmButton = {}, - text = { - Row( - horizontalArrangement = Arrangement.spacedBy(16.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - CircularProgressIndicator( - // KMK --> - color = MaterialTheme.colorScheme.primary, - // KMK <-- - ) - Text(stringResource(MR.strings.loading)) - } - }, - ) - } - is ReaderViewModel.Dialog.Settings -> { - ReaderSettingsDialog( - onDismissRequest = onDismissRequest, - onShowMenus = { setMenuVisibility(true) }, - onHideMenus = { setMenuVisibility(false) }, - screenModel = settingsScreenModel, - ) - } - is ReaderViewModel.Dialog.ReadingModeSelect -> { - ReadingModeSelectDialog( - onDismissRequest = onDismissRequest, - screenModel = settingsScreenModel, - onChange = { stringRes -> - menuToggleToast?.cancel() - if (!readerPreferences.showReadingMode().get()) { - menuToggleToast = toast(stringRes) - } - }, - ) - } - is ReaderViewModel.Dialog.OrientationModeSelect -> { - OrientationSelectDialog( - onDismissRequest = onDismissRequest, - screenModel = settingsScreenModel, - onChange = { stringRes -> - menuToggleToast?.cancel() - menuToggleToast = toast(stringRes) - }, - ) - } - is ReaderViewModel.Dialog.PageActions -> { - ReaderPageActionsDialog( - onDismissRequest = onDismissRequest, - onSetAsCover = viewModel::setAsCover, - onShare = viewModel::shareImage, - onSave = { extra -> - // KMK --> - if (externalStoragePermissionNotGranted) { - permissionRequester.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) - } else { - // KMK <-- - viewModel.saveImage(extra) - } - }, - onShareCombined = viewModel::shareImages, - onSaveCombined = { - // KMK --> - if (externalStoragePermissionNotGranted) { - permissionRequester.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE) - } else { - // KMK <-- - viewModel.saveImages() - } - }, - hasExtraPage = (state.dialog as? ReaderViewModel.Dialog.PageActions)?.extraPage != null, - ) - } - is ReaderViewModel.Dialog.ChapterList -> { - var chapters by remember { - mutableStateOf(viewModel.getChapters().toImmutableList()) - } - ChapterListDialog( - onDismissRequest = onDismissRequest, - screenModel = settingsScreenModel, - chapters = chapters, - onClickChapter = { - viewModel.loadNewChapterFromDialog(it) - onDismissRequest() - }, - onBookmark = { chapter -> - viewModel.toggleBookmark(chapter.id, !chapter.bookmark) - chapters = chapters.map { - if (it.chapter.id == chapter.id) { - it.copy(chapter = chapter.copy(bookmark = !chapter.bookmark)) - } else { - it - } - }.toImmutableList() - }, - state.dateRelativeTime, - // KMK --> - onDownloadAction = { chapter, action -> - viewModel.handleDownloadAction(chapter, action) - }, - // KMK <-- - ) - } - // SY --> - ReaderViewModel.Dialog.AutoScrollHelp -> AlertDialog( - onDismissRequest = onDismissRequest, - confirmButton = { - TextButton(onClick = onDismissRequest) { - Text(text = stringResource(MR.strings.action_ok)) - } - }, - title = { Text(text = stringResource(SYMR.strings.eh_autoscroll_help)) }, - text = { Text(text = stringResource(SYMR.strings.eh_autoscroll_help_message)) }, - ) - ReaderViewModel.Dialog.BoostPageHelp -> AlertDialog( - onDismissRequest = onDismissRequest, - confirmButton = { - TextButton(onClick = onDismissRequest) { - Text(text = stringResource(MR.strings.action_ok)) - } - }, - title = { Text(text = stringResource(SYMR.strings.eh_boost_page_help)) }, - text = { Text(text = stringResource(SYMR.strings.eh_boost_page_help_message)) }, - ) - ReaderViewModel.Dialog.RetryAllHelp -> AlertDialog( - onDismissRequest = onDismissRequest, - confirmButton = { - TextButton(onClick = onDismissRequest) { - Text(text = stringResource(MR.strings.action_ok)) - } - }, - title = { Text(text = stringResource(SYMR.strings.eh_retry_all_help)) }, - text = { Text(text = stringResource(SYMR.strings.eh_retry_all_help_message)) }, - ) - // SY <-- - null -> {} - } - } - - // KMK --> - binding.dialogRoot.setComposeContent { - TachiyomiTheme( - seedColor = seedColorState().takeIf { themeCoverBased }, - typography = MaterialTheme.typography.copy( - bodyLarge = MaterialTheme.typography.bodySmall, - ), - ) { - dialogRootContent() - } - } - - val colorScheme = seedColorStatic()?.let { - dynamicColorScheme( - seedColor = it, - isDark = isNightMode(), - isAmoled = themeDarkAmoled, - style = themeCoverBasedStyle, - specVersion = ColorSpec.SpecVersion.SPEC_2025, - ) - } - // KMK <-- - - val toolbarColor = ColorUtils.setAlphaComponent( - // KMK --> - if (themeCoverBased && colorScheme != null) { - colorScheme.surfaceColorAtElevation(3.dp).toArgb() - } else { - // KMK <-- - SurfaceColors.SURFACE_2.getColor(this) - }, - if (isNightMode()) 230 else 242, // 90% dark 95% light + ReaderContentOverlay( + brightness = state.brightnessOverlayValue, + color = colorOverlay.takeIf { colorOverlayEnabled }, + colorBlendMode = colorOverlayBlendMode, ) - @Suppress("DEPRECATION") - window.statusBarColor = toolbarColor - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { - @Suppress("DEPRECATION") - window.navigationBarColor = toolbarColor + + if (flashOnPageChange) { + DisplayRefreshHost(hostState = displayRefreshHost) + } + } + + @Composable + fun AppBars(state: ReaderViewModel.State) { + if (!ifSourcesLoaded()) { + return } - // Set initial visibility - setMenuVisibility(viewModel.state.value.menuVisible) + val isHttpSource = viewModel.getSource() is HttpSource - enableExhAutoScroll() + val cropBorderPaged by readerPreferences.cropBorders().collectAsState() + val cropBorderWebtoon by readerPreferences.cropBordersWebtoon().collectAsState() + // SY --> + val readingMode = viewModel.getMangaReadingMode() + val isPagerType = ReadingMode.isPagerType(readingMode) + val isWebtoon = ReadingMode.WEBTOON.flagValue == readingMode + val cropBorderContinuousVertical by readerPreferences.cropBordersContinuousVertical().collectAsState() + val cropEnabled = if (isPagerType) { + cropBorderPaged + } else if (isWebtoon) { + cropBorderWebtoon + } else { + cropBorderContinuousVertical + } + val readerBottomButtons by readerPreferences.readerBottomButtons().changes().map { it.toImmutableSet() } + .collectAsState(persistentSetOf()) + val dualPageSplitPaged by readerPreferences.dualPageSplitPaged().collectAsState() + + val forceHorizontalSeekbar by readerPreferences.forceHorizontalSeekbar().collectAsState() + val landscapeVerticalSeekbar by readerPreferences.landscapeVerticalSeekbar().collectAsState() + val leftHandedVerticalSeekbar by readerPreferences.leftVerticalSeekbar().collectAsState() + val configuration = LocalConfiguration.current + val verticalSeekbarLandscape = + configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && landscapeVerticalSeekbar + val verticalSeekbarHorizontal = configuration.orientation == Configuration.ORIENTATION_PORTRAIT + val viewerIsVertical = (state.viewer is WebtoonViewer || state.viewer is VerticalPagerViewer) + val showVerticalSeekbar = + !forceHorizontalSeekbar && (verticalSeekbarLandscape || verticalSeekbarHorizontal) && viewerIsVertical + val navBarType = when { + !showVerticalSeekbar -> NavBarType.Bottom + leftHandedVerticalSeekbar -> NavBarType.VerticalLeft + else -> NavBarType.VerticalRight + } + // SY <-- + + ReaderAppBars( + visible = state.menuVisible, + + mangaTitle = state.manga?.title, + chapterTitle = state.currentChapter?.chapter?.name, + navigateUp = onBackPressedDispatcher::onBackPressed, + onClickTopAppBar = ::openMangaScreen, + bookmarked = state.bookmarked, + onToggleBookmarked = viewModel::toggleChapterBookmark, + onOpenInWebView = ::openChapterInWebView.takeIf { isHttpSource }, + onOpenInBrowser = ::openChapterInBrowser.takeIf { isHttpSource }, + onShare = ::shareChapter.takeIf { isHttpSource }, + + viewer = state.viewer, + onNextChapter = ::loadNextChapter, + enabledNext = state.viewerChapters?.nextChapter != null, + onPreviousChapter = ::loadPreviousChapter, + enabledPrevious = state.viewerChapters?.prevChapter != null, + currentPage = state.currentPage, + totalPages = state.totalPages, + onPageIndexChange = { + isScrollingThroughPages = true + moveToPageIndex(it) + }, + + readingMode = ReadingMode.fromPreference( + viewModel.getMangaReadingMode(resolveDefault = false), + ), + onClickReadingMode = viewModel::openReadingModeSelectDialog, + orientation = ReaderOrientation.fromPreference( + viewModel.getMangaOrientation(resolveDefault = false), + ), + onClickOrientation = viewModel::openOrientationModeSelectDialog, + cropEnabled = cropEnabled, + onClickCropBorder = { + val enabled = viewModel.toggleCropBorders() + menuToggleToast?.cancel() + menuToggleToast = toast(if (enabled) MR.strings.on else MR.strings.off) + }, + onClickSettings = viewModel::openSettingsDialog, + // SY --> + isExhToolsVisible = state.ehUtilsVisible, + onSetExhUtilsVisibility = viewModel::showEhUtils, + isAutoScroll = state.autoScroll, + isAutoScrollEnabled = state.isAutoScrollEnabled, + onToggleAutoscroll = viewModel::toggleAutoScroll, + autoScrollFrequency = state.ehAutoscrollFreq, + onSetAutoScrollFrequency = viewModel::setAutoScrollFrequency, + onClickAutoScrollHelp = viewModel::openAutoScrollHelpDialog, + onClickRetryAll = ::exhRetryAll, + onClickRetryAllHelp = viewModel::openRetryAllHelp, + onClickBoostPage = ::exhBoostPage, + onClickBoostPageHelp = viewModel::openBoostPageHelp, + currentPageText = state.currentPageText, + navBarType = navBarType, + enabledButtons = readerBottomButtons, + currentReadingMode = ReadingMode.fromPreference( + viewModel.getMangaReadingMode(resolveDefault = true), + ), + dualPageSplitEnabled = dualPageSplitPaged, + doublePages = state.doublePages, + onClickChapterList = viewModel::openChapterListDialog, + onClickPageLayout = { + if (readerPreferences.pageLayout().get() == PagerConfig.PageLayout.AUTOMATIC) { + (viewModel.state.value.viewer as? PagerViewer)?.config?.let { config -> + config.doublePages = !config.doublePages + reloadChapters(config.doublePages, true) + } + } else { + readerPreferences.pageLayout().set(1 - readerPreferences.pageLayout().get()) + } + }, + onClickShiftPage = ::shiftDoublePages, + // SY <-- + ) } // KMK --> @@ -812,6 +781,7 @@ class ReaderActivity : BaseActivity() { } // KMK <-- + // EXH --> private fun enableExhAutoScroll() { readerPreferences.autoscrollInterval().changes() .combine(viewModel.state.map { it.autoScroll }.distinctUntilChanged()) { interval, enabled -> @@ -965,13 +935,8 @@ class ReaderActivity : BaseActivity() { viewModel.showMenus(visible) if (visible) { windowInsetsController.show(WindowInsetsCompat.Type.systemBars()) - window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) - } else { - if (readerPreferences.fullscreen().get()) { - windowInsetsController.hide(WindowInsetsCompat.Type.systemBars()) - windowInsetsController.systemBarsBehavior = - WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE - } + } else if (readerPreferences.fullscreen().get()) { + windowInsetsController.hide(WindowInsetsCompat.Type.systemBars()) } } @@ -1189,7 +1154,6 @@ class ReaderActivity : BaseActivity() { * Called from the viewer whenever a [page] is marked as active. It updates the values of the * bottom menu and delegates the change to the presenter. */ - @SuppressLint("SetTextI18n") fun onPageSelected(page: ReaderPage, hasExtraPage: Boolean = false) { // SY --> val currentPageText = if (hasExtraPage) { @@ -1204,18 +1168,16 @@ class ReaderActivity : BaseActivity() { } else { "${page.number}" } - viewModel.onPageSelected(page, currentPageText, hasExtraPage) // SY <-- + viewModel.onPageSelected(page, /* SY --> */ currentPageText, hasExtraPage /* SY <-- */) } /** * Called from the viewer whenever a [page] is long clicked. A bottom sheet with a list of * actions to perform is shown. */ - fun onPageLongTap(page: ReaderPage, extraPage: ReaderPage? = null) { - // SY --> - viewModel.openPageDialog(page, extraPage) - // SY <-- + fun onPageLongTap(page: ReaderPage, /* SY --> */ extraPage: ReaderPage? = null /* SY <-- */) { + viewModel.openPageDialog(page, /* SY --> */ extraPage /* SY <-- */) } /** @@ -1260,28 +1222,26 @@ class ReaderActivity : BaseActivity() { val manga = viewModel.manga ?: return val chapter = page.chapter.chapter - // SY --> - val text = if (secondPage != null) { - stringResource( - SYMR.strings.share_pages_info, - manga.title, - chapter.name, - if (resources.configuration.layoutDirection == - View.LAYOUT_DIRECTION_LTR - ) { - "${page.number}-${page.number + 1}" - } else { - "${page.number + 1}-${page.number}" - }, - ) - } else { - stringResource(MR.strings.share_page_info, manga.title, chapter.name, page.number) - } - // SY <-- - val intent = uri.toShareIntent( context = applicationContext, - message = /* SY --> */ text, // SY <-- + message = // SY --> + if (secondPage != null) { + stringResource( + SYMR.strings.share_pages_info, + manga.title, + chapter.name, + if (resources.configuration.layoutDirection == + View.LAYOUT_DIRECTION_LTR + ) { + "${page.number}-${page.number + 1}" + } else { + "${page.number + 1}-${page.number}" + }, + ) + } else { + // SY <-- + stringResource(MR.strings.share_page_info, manga.title, chapter.name, page.number) + }, ) startActivity(Intent.createChooser(intent, stringResource(MR.strings.action_share))) } @@ -1335,15 +1295,24 @@ class ReaderActivity : BaseActivity() { * Updates viewer inset depending on fullscreen reader preferences. */ private fun updateViewerInset(fullscreen: Boolean) { - viewModel.state.value.viewer?.getView()?.applyInsetter { - if (!fullscreen) { - type(navigationBars = true, statusBars = true) { - padding() - } - } + val view = viewModel.state.value.viewer?.getView() ?: return + + view.applyInsetsPadding(ViewCompat.getRootWindowInsets(view), fullscreen) + ViewCompat.setOnApplyWindowInsetsListener(view) { view, windowInsets -> + view.applyInsetsPadding(windowInsets, fullscreen) + windowInsets } } + private fun View.applyInsetsPadding(windowInsets: WindowInsetsCompat?, fullscreen: Boolean) { + val insets = if (!fullscreen) { + windowInsets?.getInsets(WindowInsetsCompat.Type.systemBars()) ?: Insets.NONE + } else { + Insets.NONE + } + setPadding(insets.left, insets.top, insets.right, insets.bottom) + } + /** * Class that handles the user preferences of the reader. */ @@ -1483,15 +1452,13 @@ class ReaderActivity : BaseActivity() { } private fun setCutoutShort(enabled: Boolean) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return + if (!window.decorView.hasDisplayCutout()) return + @SuppressLint("NewApi") window.attributes.layoutInDisplayCutoutMode = when (enabled) { true -> WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES false -> WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER } - - // Trigger relayout - setMenuVisibility(viewModel.state.value.menuVisible) } /** @@ -1546,6 +1513,7 @@ class ReaderActivity : BaseActivity() { } } + // KMK --> /** * Updates the Discord Rich Presence (RPC) status based on the current reader activity. * @@ -1587,4 +1555,5 @@ class ReaderActivity : BaseActivity() { } } } + // KMK <-- } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsScreenModel.kt index 5f107c388..790984bea 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsScreenModel.kt @@ -13,7 +13,6 @@ import uy.kohesive.injekt.api.get class ReaderSettingsScreenModel( readerState: StateFlow, - val hasDisplayCutout: Boolean, val onChangeReadingMode: (ReadingMode) -> Unit, val onChangeOrientation: (ReaderOrientation) -> Unit, val preferences: ReaderPreferences = Injekt.get(), diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/DisplayExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/DisplayExtensions.kt index f051ccbc8..fa5d6c2d8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/DisplayExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/DisplayExtensions.kt @@ -1,9 +1,9 @@ package eu.kanade.tachiyomi.util.system -import android.app.Activity import android.content.Context import android.content.res.Configuration import android.os.Build +import android.view.View import eu.kanade.domain.ui.UiPreferences import eu.kanade.domain.ui.model.TabletUiMode import uy.kohesive.injekt.Injekt @@ -57,11 +57,11 @@ fun Context.isNightMode(): Boolean { /** * Checks whether if the device has a display cutout (i.e. notch, camera cutout, etc.). * - * Only works in Android 9+. + * Only relevant from Android 9 to Android 14. */ -fun Activity.hasDisplayCutout(): Boolean { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && - window.decorView.rootWindowInsets?.displayCutout != null +fun View.hasDisplayCutout(): Boolean { + return Build.VERSION.SDK_INT in Build.VERSION_CODES.P..Build.VERSION_CODES.UPSIDE_DOWN_CAKE && + rootWindowInsets?.displayCutout != null } /** diff --git a/app/src/main/res/layout/reader_activity.xml b/app/src/main/res/layout/reader_activity.xml index ed289767a..e759ba454 100644 --- a/app/src/main/res/layout/reader_activity.xml +++ b/app/src/main/res/layout/reader_activity.xml @@ -13,12 +13,6 @@ android:layout_height="match_parent" android:descendantFocusability="blocksDescendants" /> - - diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 82214d8a7..48d612630 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -60,7 +60,6 @@ material = "com.google.android.material:material:1.13.0" flexible-adapter-core = "com.github.arkon.FlexibleAdapter:flexible-adapter:c8013533" photoview = "com.github.chrisbanes:PhotoView:2.3.0" directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0" -insetter = "dev.chrisbanes.insetter:insetter:0.6.1" compose-materialmotion = "io.github.fornewid:material-motion-compose-core:2.0.1" compose-webview = "io.github.kevinnzou:compose-webview:0.33.6" compose-grid = "io.woong.compose.grid:grid:1.2.2"