Migrate top reader app bar to Compose

(cherry picked from commit d18022c25990f1961fef77eabb3924c68f283250)

# Conflicts:
#	app/src/main/java/eu/kanade/presentation/reader/appbars/BottomReaderBar.kt
#	app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderAppBars.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt
#	app/src/main/java/eu/kanade/tachiyomi/widget/listener/SimpleAnimationListener.kt
#	app/src/main/res/anim/enter_from_top.xml
#	app/src/main/res/anim/exit_to_top.xml
#	app/src/main/res/layout/reader_activity.xml
#	app/src/main/res/menu/reader.xml
This commit is contained in:
arkon 2023-10-14 12:30:17 -04:00 committed by Jobobby04
parent 868a27110a
commit 3842056d9c
15 changed files with 626 additions and 657 deletions

View File

@ -1,5 +1,6 @@
package eu.kanade.presentation.components
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.RowScope
@ -60,6 +61,7 @@ const val SEARCH_DEBOUNCE_MILLIS = 250L
@Composable
fun AppBar(
modifier: Modifier = Modifier,
backgroundColor: Color? = null,
// Text
title: String?,
subtitle: String? = null,
@ -81,6 +83,7 @@ fun AppBar(
AppBar(
modifier = modifier,
backgroundColor = backgroundColor,
titleContent = {
if (isActionMode) {
AppBarTitle(actionModeCounter.toString())
@ -106,6 +109,7 @@ fun AppBar(
@Composable
fun AppBar(
modifier: Modifier = Modifier,
backgroundColor: Color? = null,
// Title
titleContent: @Composable () -> Unit,
// Up button
@ -142,7 +146,7 @@ fun AppBar(
title = titleContent,
actions = actions,
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
containerColor = backgroundColor ?: MaterialTheme.colorScheme.surfaceColorAtElevation(
elevation = if (isActionMode) 3.dp else 0.dp,
),
),
@ -170,6 +174,9 @@ fun AppBarTitle(
style = MaterialTheme.typography.bodyMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.basicMarquee(
delayMillis = 2_000,
),
)
}
}

View File

@ -1,7 +1,6 @@
package eu.kanade.presentation.reader.appbars
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
@ -13,11 +12,10 @@ import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material.icons.outlined.Share
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.surfaceColorAtElevation
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.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@ -31,6 +29,7 @@ fun BottomReaderBar(
// SY -->
enabledButtons: Set<String>,
// SY <--
backgroundColor: Color,
readingMode: ReadingModeType,
onClickReadingMode: () -> Unit,
orientationMode: OrientationType,
@ -39,21 +38,15 @@ fun BottomReaderBar(
onClickCropBorder: () -> Unit,
onClickSettings: () -> Unit,
// SY -->
isHttpSource: Boolean,
dualPageSplitEnabled: Boolean,
doublePages: Boolean,
onClickChapterList: () -> Unit,
onClickWebView: () -> Unit,
onClickShare: () -> Unit,
onClickWebView: (() -> Unit)?,
onClickShare: (() -> Unit)?,
onClickPageLayout: () -> Unit,
onClickShiftPage: () -> Unit,
// SY <--
) {
// Match with toolbar background color set in ReaderActivity
val backgroundColor = MaterialTheme.colorScheme
.surfaceColorAtElevation(3.dp)
.copy(alpha = if (isSystemInDarkTheme()) 0.9f else 0.95f)
Row(
modifier = Modifier
.fillMaxWidth()
@ -72,7 +65,7 @@ fun BottomReaderBar(
}
}
if (ReaderBottomButton.WebView.isIn(enabledButtons) && isHttpSource) {
if (ReaderBottomButton.WebView.isIn(enabledButtons) && onClickWebView != null) {
IconButton(onClick = onClickWebView) {
Icon(
imageVector = Icons.Outlined.Public,
@ -81,7 +74,7 @@ fun BottomReaderBar(
}
}
if (ReaderBottomButton.Share.isIn(enabledButtons) && isHttpSource) {
if (ReaderBottomButton.Share.isIn(enabledButtons) && onClickShare != null) {
IconButton(onClick = onClickShare) {
Icon(
imageVector = Icons.Outlined.Share,

View File

@ -0,0 +1,243 @@
package eu.kanade.presentation.reader.appbars
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.KeyboardArrowDown
import androidx.compose.material.icons.outlined.KeyboardArrowUp
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
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.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.util.ThemePreviews
@Composable
fun ExhUtils(
modifier: Modifier = Modifier,
isVisible: Boolean,
onSetExhUtilsVisibility: (Boolean) -> Unit,
backgroundColor: Color,
isAutoScroll: Boolean,
isAutoScrollEnabled: Boolean,
onToggleAutoscroll: (Boolean) -> Unit,
autoScrollFrequency: String,
onSetAutoScrollFrequency: (String) -> Unit,
onClickAutoScrollHelp: () -> Unit,
onClickRetryAll: () -> Unit,
onClickRetryAllHelp: () -> Unit,
onClickBoostPage: () -> Unit,
onClickBoostPageHelp: () -> Unit,
) {
Column(
modifier
.fillMaxWidth()
.background(backgroundColor)
) {
AnimatedVisibility(visible = isVisible) {
Column {
Row(
Modifier.fillMaxWidth().height(IntrinsicSize.Min),
verticalAlignment = Alignment.CenterVertically,
) {
Row(
Modifier
.fillMaxWidth(0.5f)
.fillMaxHeight()
.clickable(enabled = isAutoScrollEnabled) { onToggleAutoscroll(!isAutoScroll) },
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = stringResource(R.string.eh_autoscroll),
color = MaterialTheme.colorScheme.onSurface,
fontSize = 13.sp,
fontFamily = FontFamily.SansSerif,
style = MaterialTheme.typography.labelLarge,
modifier = Modifier.padding(start = 24.dp)
)
Switch(
checked = isAutoScroll,
onCheckedChange = null,
enabled = isAutoScrollEnabled
)
}
Row(
Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Column(Modifier.weight(3f)) {
var autoScrollFrequencyState by remember {
mutableStateOf(autoScrollFrequency)
}
TextField(
value = autoScrollFrequencyState,
onValueChange = {
autoScrollFrequencyState = it
onSetAutoScrollFrequency(it)
},
isError = !isAutoScrollEnabled,
singleLine = true,
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent,
focusedTextColor = MaterialTheme.colorScheme.onSurface,
unfocusedTextColor = MaterialTheme.colorScheme.onSurface
),
modifier = Modifier.fillMaxWidth(),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Decimal
),
)
AnimatedVisibility(!isAutoScrollEnabled) {
Text(
text = stringResource(R.string.eh_autoscroll_freq_invalid),
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.labelSmall
)
}
}
TextButton(
onClick = onClickAutoScrollHelp,
modifier = Modifier.weight(1f),
) {
Text(
text = "?",
color = MaterialTheme.colorScheme.onSurface,
fontSize = 15.sp,
fontWeight = FontWeight.Bold,
)
}
}
}
Row(
Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
Row(
Modifier.fillMaxWidth(0.5f),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
TextButton(
onClick = onClickRetryAll,
modifier = Modifier.weight(3f),
) {
Text(
text = stringResource(R.string.eh_retry_all),
color = MaterialTheme.colorScheme.onSurface,
fontSize = 13.sp,
fontFamily = FontFamily.SansSerif
)
}
TextButton(
onClick = onClickRetryAllHelp,
modifier = Modifier.weight(1f),
) {
Text(
text = "?",
color = MaterialTheme.colorScheme.onSurface,
fontSize = 15.sp,
fontWeight = FontWeight.Bold,
)
}
}
Row(
Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
TextButton(
onClick = onClickBoostPage,
modifier = Modifier.weight(3f),
) {
Text(
text = stringResource(R.string.eh_boost_page),
color = MaterialTheme.colorScheme.onSurface,
fontSize = 13.sp,
fontFamily = FontFamily.SansSerif
)
}
TextButton(
onClick = onClickBoostPageHelp,
modifier = Modifier.weight(1f),
) {
Text(
text = "?",
color = MaterialTheme.colorScheme.onSurface,
fontSize = 15.sp,
fontWeight = FontWeight.Bold,
)
}
}
}
}
}
IconButton(
onClick = { onSetExhUtilsVisibility(!isVisible) },
modifier = Modifier.fillMaxWidth()
) {
Icon(
imageVector = if (isVisible) {
Icons.Outlined.KeyboardArrowUp
} else {
Icons.Outlined.KeyboardArrowDown
},
contentDescription = null,
)
}
}
}
@Composable
@ThemePreviews
private fun ExhUtilsPreview() {
TachiyomiTheme {
ExhUtils(
isVisible = true,
onSetExhUtilsVisibility = {},
backgroundColor = Color.Black,
isAutoScroll = true,
isAutoScrollEnabled = true,
onToggleAutoscroll = {},
autoScrollFrequency = "3.0",
onSetAutoScrollFrequency = {},
onClickAutoScrollHelp = {},
onClickBoostPage = {},
onClickBoostPageHelp = {},
onClickRetryAll = {},
onClickRetryAllHelp = {}
)
}
}

View File

@ -6,14 +6,23 @@ import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.clickable
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
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.WindowInsetsSides
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
@ -22,6 +31,7 @@ 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.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import eu.kanade.tachiyomi.ui.reader.viewer.Viewer
@ -56,8 +66,18 @@ fun BoxIgnoreLayoutDirection(modifier: Modifier, content: @Composable BoxScope.(
@Composable
fun ReaderAppBars(
visible: Boolean,
viewer: Viewer?,
fullscreen: Boolean,
mangaTitle: String?,
chapterTitle: String?,
navigateUp: () -> Unit,
onClickTopAppBar: () -> Unit,
// bookmarked: Boolean,
// onToggleBookmarked: () -> Unit,
onOpenInWebView: (() -> Unit)?,
onShare: (() -> Unit)?,
viewer: Viewer?,
onNextChapter: () -> Unit,
enabledNext: Boolean,
onPreviousChapter: () -> Unit,
@ -74,20 +94,40 @@ fun ReaderAppBars(
onClickCropBorder: () -> Unit,
onClickSettings: () -> Unit,
// SY -->
isExhToolsVisible: Boolean,
onSetExhUtilsVisibility: (Boolean) -> Unit,
isAutoScroll: Boolean,
isAutoScrollEnabled: Boolean,
onToggleAutoscroll: (Boolean) -> Unit,
autoScrollFrequency: String,
onSetAutoScrollFrequency: (String) -> Unit,
onClickAutoScrollHelp: () -> Unit,
onClickRetryAll: () -> Unit,
onClickRetryAllHelp: () -> Unit,
onClickBoostPage: () -> Unit,
onClickBoostPageHelp: () -> Unit,
navBarType: NavBarType,
currentPageText: String,
enabledButtons: Set<String>,
isHttpSource: Boolean,
dualPageSplitEnabled: Boolean,
doublePages: Boolean,
onClickChapterList: () -> Unit,
onClickWebView: () -> Unit,
onClickShare: () -> Unit,
onClickPageLayout: () -> Unit,
onClickShiftPage: () -> Unit,
) {
val isRtl = viewer is R2LPagerViewer
val backgroundColor = MaterialTheme.colorScheme
.surfaceColorAtElevation(3.dp)
.copy(alpha = if (isSystemInDarkTheme()) 0.9f else 0.95f)
val appBarModifier = if (fullscreen) {
// SY -->
Modifier.windowInsetsPadding(WindowInsets.systemBars.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal))
// SY <--
} else {
Modifier
}
// SY -->
@ -104,7 +144,8 @@ fun ReaderAppBars(
targetOffsetX = { -it },
animationSpec = animationSpec,
),
modifier = Modifier.padding(bottom = 48.dp, top = 120.dp)
modifier = Modifier
.padding(bottom = 48.dp, top = 120.dp)
.align(Alignment.TopStart)
) {
ChapterNavigator(
@ -131,7 +172,8 @@ fun ReaderAppBars(
targetOffsetX = { it },
animationSpec = animationSpec,
),
modifier = Modifier.padding(bottom = 48.dp, top = 120.dp)
modifier = Modifier
.padding(bottom = 48.dp, top = 120.dp)
.align(Alignment.TopEnd)
) {
ChapterNavigator(
@ -152,6 +194,72 @@ fun ReaderAppBars(
modifier = Modifier.fillMaxHeight(),
verticalArrangement = Arrangement.SpaceBetween,
) {
AnimatedVisibility(
visible = visible,
enter = slideInVertically(
initialOffsetY = { -it },
animationSpec = animationSpec,
),
exit = slideOutVertically(
targetOffsetY = { -it },
animationSpec = animationSpec,
),
) {
// SY -->
Column(appBarModifier) {
// SY <--
AppBar(
modifier = /*SY --> */ Modifier /*SY <-- */
.clickable(onClick = onClickTopAppBar),
backgroundColor = backgroundColor,
title = mangaTitle,
subtitle = chapterTitle,
navigateUp = navigateUp,
/* SY --> actions = {
AppBarActions(
listOfNotNull(
AppBar.Action(
title = stringResource(if (bookmarked) R.string.action_remove_bookmark else R.string.action_bookmark),
icon = if (bookmarked) Icons.Outlined.Bookmark else Icons.Outlined.BookmarkBorder,
onClick = onToggleBookmarked,
),
onOpenInWebView?.let {
AppBar.OverflowAction(
title = stringResource(R.string.action_open_in_web_view),
onClick = it,
)
},
onShare?.let {
AppBar.OverflowAction(
title = stringResource(R.string.action_share),
onClick = it,
)
},
),
)
}, SY <-- */
)
// SY -->
ExhUtils(
isVisible = isExhToolsVisible,
onSetExhUtilsVisibility = onSetExhUtilsVisibility,
backgroundColor = backgroundColor,
isAutoScroll = isAutoScroll,
isAutoScrollEnabled = isAutoScrollEnabled,
onToggleAutoscroll = onToggleAutoscroll,
autoScrollFrequency = autoScrollFrequency,
onSetAutoScrollFrequency = onSetAutoScrollFrequency,
onClickAutoScrollHelp = onClickAutoScrollHelp,
onClickRetryAll = onClickRetryAll,
onClickRetryAllHelp = onClickRetryAllHelp,
onClickBoostPage = onClickBoostPage,
onClickBoostPageHelp = onClickBoostPageHelp
)
// SY <--
}
}
Spacer(modifier = Modifier.weight(1f))
AnimatedVisibility(
@ -187,6 +295,7 @@ fun ReaderAppBars(
// SY -->
enabledButtons = enabledButtons,
// SY <--
backgroundColor = backgroundColor,
readingMode = readingMode,
onClickReadingMode = onClickReadingMode,
orientationMode = orientationMode,
@ -195,12 +304,11 @@ fun ReaderAppBars(
onClickCropBorder = onClickCropBorder,
onClickSettings = onClickSettings,
// SY -->
isHttpSource = isHttpSource,
dualPageSplitEnabled = dualPageSplitEnabled,
doublePages = doublePages,
onClickChapterList = onClickChapterList,
onClickWebView = onClickWebView,
onClickShare = onClickShare,
onClickWebView = onOpenInWebView,
onClickShare = onShare,
onClickPageLayout = onClickPageLayout,
onClickShiftPage = onClickShiftPage
)

View File

@ -5,7 +5,6 @@ import android.annotation.TargetApi
import android.app.assist.AssistContent
import android.content.Context
import android.content.Intent
import android.content.res.ColorStateList
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Color
@ -15,19 +14,11 @@ import android.graphics.Paint
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.text.Editable
import android.text.TextUtils
import android.text.TextWatcher
import android.view.KeyEvent
import android.view.Menu
import android.view.MotionEvent
import android.view.View
import android.view.View.LAYER_TYPE_HARDWARE
import android.view.WindowManager
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.widget.CompoundButton
import android.widget.TextView
import android.widget.Toast
import androidx.activity.viewModels
import androidx.compose.foundation.layout.Arrangement
@ -35,6 +26,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@ -55,9 +47,7 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.internal.ToolbarUtils
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.elevation.SurfaceColors
import com.google.android.material.transition.platform.MaterialContainerTransform
import dev.chrisbanes.insetter.applyInsetter
import eu.kanade.domain.base.BasePreferences
@ -75,6 +65,7 @@ import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.databinding.ReaderActivityBinding
import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.reader.ReaderViewModel.SetAsCoverResult.AddToLibraryFirst
@ -94,22 +85,16 @@ import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerViewer
import eu.kanade.tachiyomi.ui.reader.viewer.pager.VerticalPagerViewer
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.system.applySystemAnimatorScale
import eu.kanade.tachiyomi.util.system.hasDisplayCutout
import eu.kanade.tachiyomi.util.system.isNightMode
import eu.kanade.tachiyomi.util.system.toShareIntent
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.setComposeContent
import eu.kanade.tachiyomi.widget.listener.SimpleAnimationListener
import exh.source.isEhBasedSource
import exh.util.defaultReaderType
import exh.util.mangaType
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.filterNotNull
@ -118,7 +103,6 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.sample
import kotlinx.coroutines.launch
import logcat.LogPriority
@ -271,19 +255,6 @@ class ReaderActivity : BaseActivity() {
.launchIn(lifecycleScope)
}
// SY -->
private fun setEhUtilsVisibility(visible: Boolean) {
viewModel.showEhUtils(visible)
if (visible) {
binding.ehUtils.isVisible = true
binding.expandEhButton.setImageResource(R.drawable.ic_keyboard_arrow_up_white_32dp)
} else {
binding.ehUtils.isVisible = false
binding.expandEhButton.setImageResource(R.drawable.ic_keyboard_arrow_down_white_32dp)
}
}
// SY <--
/**
* Called when the activity is destroyed. Cleans up the viewer, configuration and any view.
*/
@ -326,47 +297,6 @@ class ReaderActivity : BaseActivity() {
assistUrl?.let { outContent.webUri = it.toUri() }
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.reader, menu)
/*val isChapterBookmarked = viewModel.getCurrentChapter()?.chapter?.bookmark ?: false
menu.findItem(R.id.action_bookmark).isVisible = !isChapterBookmarked
menu.findItem(R.id.action_remove_bookmark).isVisible = isChapterBookmarked
val isHttpSource = viewModel.getSource() is HttpSource
menu.findItem(R.id.action_open_in_web_view).isVisible = isHttpSource
menu.findItem(R.id.action_share).isVisible = isHttpSource*/
return true
}
/**
* Called when an item of the options menu was clicked. Used to handle clicks on our menu
* entries.
*/
/*override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_open_in_web_view -> {
openChapterInWebView()
}
R.id.action_bookmark -> {
viewModel.bookmarkCurrentChapter(true)
invalidateOptionsMenu()
}
R.id.action_remove_bookmark -> {
viewModel.bookmarkCurrentChapter(false)
invalidateOptionsMenu()
}
R.id.action_share -> {
assistUrl?.let {
val intent = it.toUri().toShareIntent(this, type = "text/plain")
startActivity(Intent.createChooser(intent, getString(R.string.action_share)))
}
}
}
return super.onOptionsItemSelected(item)
}*/
/**
* Called when the user clicks the back key or the button on the toolbar. The call is
* delegated to the presenter.
@ -405,69 +335,16 @@ class ReaderActivity : BaseActivity() {
return handled || super.dispatchGenericMotionEvent(event)
}
// SY -->
fun TextView.textChanges(): Flow<CharSequence> = callbackFlow {
val listener = object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) = Unit
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
trySend(s)
}
override fun afterTextChanged(s: Editable) = Unit
}
addTextChangedListener(listener)
awaitClose { removeTextChangedListener(listener) }
}
.conflate()
.onStart { emit(text) }
fun CompoundButton.checkedChanges(): Flow<Boolean> = callbackFlow {
val listener = CompoundButton.OnCheckedChangeListener { _, isChecked ->
trySend(isChecked)
}
setOnCheckedChangeListener(listener)
awaitClose { setOnCheckedChangeListener(null) }
}
.conflate()
.onStart { emit(isChecked) }
// SY <--
/**
* Initializes the reader menu. It sets up click listeners and the initial visibility.
*/
private fun initializeMenu() {
setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
binding.toolbar.setNavigationOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
binding.header.applyInsetter {
type(navigationBars = true, statusBars = true) {
margin(top = true, horizontal = true)
}
}
binding.dialogRoot.applyInsetter {
type(navigationBars = true) {
margin(vertical = true, horizontal = true)
}
}
binding.toolbar.setOnClickListener {
viewModel.manga?.id?.let { id ->
startActivity(
Intent(this, MainActivity::class.java).apply {
action = Constants.SHORTCUT_MANGA
putExtra(Constants.MANGA_EXTRA, id)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
},
)
}
}
binding.pageNumber.setComposeContent {
val state by viewModel.state.collectAsState()
val showPageNumber by viewModel.readerPreferences.showPageNumber().collectAsState()
@ -493,6 +370,9 @@ class ReaderActivity : BaseActivity() {
)
}
val isHttpSource = viewModel.getSource() is HttpSource
val isFullscreen by readerPreferences.fullscreen().collectAsState()
val cropBorderPaged by readerPreferences.cropBorders().collectAsState()
val cropBorderWebtoon by readerPreferences.cropBordersWebtoon().collectAsState()
// SY -->
@ -526,11 +406,20 @@ class ReaderActivity : BaseActivity() {
}
// SY <--
ReaderAppBars(
visible = state.menuVisible,
viewer = state.viewer,
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 },
onShare = ::shareChapter.takeIf { isHttpSource },
viewer = state.viewer,
onNextChapter = ::loadNextChapter,
enabledNext = state.viewerChapters?.nextChapter != null,
onPreviousChapter = ::loadPreviousChapter,
@ -557,34 +446,29 @@ class ReaderActivity : BaseActivity() {
cropEnabled = cropEnabled,
onClickCropBorder = {
val enabled = viewModel.toggleCropBorders()
menuToggleToast?.cancel()
menuToggleToast = toast(
if (enabled) {
R.string.on
} else {
R.string.off
},
)
menuToggleToast = toast(if (enabled) R.string.on else R.string.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,
isHttpSource = remember {
viewModel.getSource() != null
},
dualPageSplitEnabled = dualPageSplitPaged,
doublePages = state.doublePages,
onClickChapterList = viewModel::openChapterListDialog,
onClickWebView = ::openChapterInWebView,
onClickShare = {
assistUrl?.let {
val intent = it.toUri().toShareIntent(this@ReaderActivity, type = "text/plain")
startActivity(Intent.createChooser(intent, getString(R.string.action_share)))
}
},
onClickPageLayout = {
if (readerPreferences.pageLayout().get() == PagerConfig.PageLayout.AUTOMATIC) {
(viewModel.state.value.viewer as? PagerViewer)?.config?.let { config ->
@ -682,188 +566,154 @@ class ReaderActivity : BaseActivity() {
state.dateRelativeTime
)
}
// SY -->
ReaderViewModel.Dialog.AutoScrollHelp -> AlertDialog(
onDismissRequest = onDismissRequest,
confirmButton = {
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(R.string.action_ok))
}
},
title = { Text(text = stringResource(R.string.eh_autoscroll_help)) },
text = { Text(text = stringResource(R.string.eh_autoscroll_help_message)) },
)
ReaderViewModel.Dialog.BoostPageHelp -> AlertDialog(
onDismissRequest = onDismissRequest,
confirmButton = {
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(R.string.action_ok))
}
},
title = { Text(text = stringResource(R.string.eh_boost_page_help)) },
text = { Text(text = stringResource(R.string.eh_boost_page_help_message)) },
)
ReaderViewModel.Dialog.RetryAllHelp -> AlertDialog(
onDismissRequest = onDismissRequest,
confirmButton = {
TextButton(onClick = onDismissRequest) {
Text(text = stringResource(R.string.action_ok))
}
},
title = { Text(text = stringResource(R.string.eh_retry_all_help)) },
text = { Text(text = stringResource(R.string.eh_retry_all_help_message)) },
)
// SY <--
null -> {}
}
}
// SY -->
initDropdownMenu()
// SY <--
val toolbarBackground = (binding.toolbar.background as MaterialShapeDrawable).apply {
elevation = resources.getDimension(R.dimen.m3_sys_elevation_level2)
alpha = if (isNightMode()) 230 else 242 // 90% dark 95% light
}
val toolbarColor = ColorUtils.setAlphaComponent(
toolbarBackground.resolvedTintColor,
toolbarBackground.alpha,
SurfaceColors.SURFACE_2.getColor(this),
if (isNightMode()) 230 else 242, // 90% dark 95% light
)
window.statusBarColor = toolbarColor
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
window.navigationBarColor = toolbarColor
}
// SY -->
binding.toolbar.background.alpha = 0
(binding.header.background as MaterialShapeDrawable).fillColor = ColorStateList.valueOf(toolbarColor)
// SY <--
// Set initial visibility
setMenuVisibility(viewModel.state.value.menuVisible)
// --> EH
setEhUtilsVisibility(viewModel.state.value.ehUtilsVisible)
// <-- EH
enableExhAutoScroll()
}
// EXH -->
fun initDropdownMenu() {
binding.expandEhButton.setOnClickListener {
val newValue = !viewModel.state.value.ehUtilsVisible
viewModel.showEhUtils(newValue)
setEhUtilsVisibility(newValue)
}
binding.ehAutoscrollFreq.setText(
readerPreferences.autoscrollInterval().get().let {
if (it == -1f) {
""
} else {
it.toString()
}
},
)
binding.ehAutoscroll.checkedChanges()
.combine(binding.ehAutoscrollFreq.textChanges()) { checked, text ->
checked to text
}
.mapLatest { (checked, text) ->
val parsed = text.toString().toDoubleOrNull()
if (parsed == null || parsed <= 0 || parsed > 9999) {
binding.ehAutoscrollFreq.error = getString(R.string.eh_autoscroll_freq_invalid)
readerPreferences.autoscrollInterval().set(-1f)
binding.ehAutoscroll.isEnabled = false
} else {
binding.ehAutoscrollFreq.error = null
readerPreferences.autoscrollInterval().set(parsed.toFloat())
binding.ehAutoscroll.isEnabled = true
if (checked) {
repeatOnLifecycle(Lifecycle.State.STARTED) {
val interval = parsed.seconds
while (true) {
if (!binding.readerMenu.isVisible) {
viewModel.state.value.viewer.let { v ->
when (v) {
is PagerViewer -> v.moveToNext()
is WebtoonViewer -> {
if (readerPreferences.smoothAutoScroll().get()) {
v.linearScroll(interval)
} else {
v.scrollDown()
}
private fun enableExhAutoScroll() {
readerPreferences.autoscrollInterval().changes()
.combine(viewModel.state.map { it.autoScroll }.distinctUntilChanged()) { interval, enabled ->
interval.toDouble() to enabled
}.mapLatest { (intervalFloat, enabled) ->
if (enabled) {
repeatOnLifecycle(Lifecycle.State.STARTED) {
val interval = intervalFloat.seconds
while (true) {
if (!viewModel.state.value.menuVisible) {
viewModel.state.value.viewer.let { v ->
when (v) {
is PagerViewer -> v.moveToNext()
is WebtoonViewer -> {
if (readerPreferences.smoothAutoScroll().get()) {
v.linearScroll(interval)
} else {
v.scrollDown()
}
}
}
delay(interval)
} else {
delay(100)
}
delay(interval)
} else {
delay(100)
}
}
}
}
}
.launchIn(lifecycleScope)
}
binding.ehAutoscrollHelp.setOnClickListener {
MaterialAlertDialogBuilder(this)
.setTitle(R.string.eh_autoscroll_help)
.setMessage(R.string.eh_autoscroll_help_message)
.setPositiveButton(R.string.action_ok, null)
.show()
}
private fun exhRetryAll() {
var retried = 0
binding.ehRetryAll.setOnClickListener {
var retried = 0
viewModel.state.value.viewerChapters
?.currChapter
?.pages
?.forEachIndexed { _, page ->
var shouldQueuePage = false
if (page.status == Page.State.ERROR) {
shouldQueuePage = true
} /*else if (page.status == Page.LOAD_PAGE ||
page.status == Page.DOWNLOAD_IMAGE) {
// Do nothing
}*/
viewModel.state.value.viewerChapters
?.currChapter
?.pages
?.forEachIndexed { _, page ->
var shouldQueuePage = false
if (page.status == Page.State.ERROR) {
shouldQueuePage = true
} /*else if (page.status == Page.LOAD_PAGE ||
page.status == Page.DOWNLOAD_IMAGE) {
// Do nothing
}*/
if (shouldQueuePage) {
page.status = Page.State.QUEUE
} else {
return@forEachIndexed
}
// If we are using EHentai/ExHentai, get a new image URL
viewModel.manga?.let { m ->
val src = sourceManager.get(m.source)
if (src?.isEhBasedSource() == true) {
page.imageUrl = null
}
}
val loader = page.chapter.pageLoader
if (page.index == exhCurrentpage()?.index && loader is HttpPageLoader) {
loader.boostPage(page)
} else {
loader?.retryPage(page)
}
retried++
}
toast(resources.getQuantityString(R.plurals.eh_retry_toast, retried, retried))
}
binding.ehRetryAllHelp.setOnClickListener {
MaterialAlertDialogBuilder(this)
.setTitle(R.string.eh_retry_all_help)
.setMessage(R.string.eh_retry_all_help_message)
.setPositiveButton(R.string.action_ok, null)
.show()
}
binding.ehBoostPage.setOnClickListener {
viewModel.state.value.viewer ?: return@setOnClickListener
val curPage = exhCurrentpage() ?: run {
toast(R.string.eh_boost_page_invalid)
return@setOnClickListener
}
if (curPage.status == Page.State.ERROR) {
toast(R.string.eh_boost_page_errored)
} else if (curPage.status == Page.State.LOAD_PAGE || curPage.status == Page.State.DOWNLOAD_IMAGE) {
toast(R.string.eh_boost_page_downloading)
} else if (curPage.status == Page.State.READY) {
toast(R.string.eh_boost_page_downloaded)
} else {
val loader = (viewModel.state.value.viewerChapters?.currChapter?.pageLoader as? HttpPageLoader)
if (loader != null) {
loader.boostPage(curPage)
toast(R.string.eh_boost_boosted)
if (shouldQueuePage) {
page.status = Page.State.QUEUE
} else {
toast(R.string.eh_boost_invalid_loader)
return@forEachIndexed
}
// If we are using EHentai/ExHentai, get a new image URL
viewModel.manga?.let { m ->
val src = sourceManager.get(m.source)
if (src?.isEhBasedSource() == true) {
page.imageUrl = null
}
}
val loader = page.chapter.pageLoader
if (page.index == exhCurrentpage()?.index && loader is HttpPageLoader) {
loader.boostPage(page)
} else {
loader?.retryPage(page)
}
retried++
}
toast(resources.getQuantityString(R.plurals.eh_retry_toast, retried, retried))
}
private fun exhBoostPage() {
viewModel.state.value.viewer ?: return
val curPage = exhCurrentpage() ?: run {
toast(R.string.eh_boost_page_invalid)
return
}
binding.ehBoostPageHelp.setOnClickListener {
MaterialAlertDialogBuilder(this)
.setTitle(R.string.eh_boost_page_help)
.setMessage(R.string.eh_boost_page_help_message)
.setPositiveButton(R.string.action_ok, null)
.show()
if (curPage.status == Page.State.ERROR) {
toast(R.string.eh_boost_page_errored)
} else if (curPage.status == Page.State.LOAD_PAGE || curPage.status == Page.State.DOWNLOAD_IMAGE) {
toast(R.string.eh_boost_page_downloading)
} else if (curPage.status == Page.State.READY) {
toast(R.string.eh_boost_page_downloaded)
} else {
val loader = (viewModel.state.value.viewerChapters?.currChapter?.pageLoader as? HttpPageLoader)
if (loader != null) {
loader.boostPage(curPage)
toast(R.string.eh_boost_boosted)
} else {
toast(R.string.eh_boost_invalid_loader)
}
}
}
@ -882,7 +732,7 @@ class ReaderActivity : BaseActivity() {
viewer.config.doublePages = doublePages
viewModel.setDoublePages(viewer.config.doublePages)
}
val currentChapter = viewModel.getCurrentChapter()
val currentChapter = viewModel.state.value.currentChapter
if (doublePages) {
// If we're moving from singe to double, we want the current page to be the first page
val currentPage = viewModel.state.value.currentPage
@ -921,40 +771,12 @@ class ReaderActivity : BaseActivity() {
viewModel.showMenus(visible)
if (visible) {
windowInsetsController.show(WindowInsetsCompat.Type.systemBars())
binding.readerMenu.isVisible = true
val toolbarAnimation = AnimationUtils.loadAnimation(this, R.anim.enter_from_top)
toolbarAnimation.applySystemAnimatorScale(this)
toolbarAnimation.setAnimationListener(
object : SimpleAnimationListener() {
override fun onAnimationStart(animation: Animation) {
// Fix status bar being translucent the first time it's opened.
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
}
},
)
// EXH -->
binding.header.startAnimation(toolbarAnimation)
// EXH <--
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
}
val toolbarAnimation = AnimationUtils.loadAnimation(this, R.anim.exit_to_top)
toolbarAnimation.applySystemAnimatorScale(this)
toolbarAnimation.setAnimationListener(
object : SimpleAnimationListener() {
override fun onAnimationEnd(animation: Animation) {
binding.readerMenu.isVisible = false
}
},
)
// EXH -->
binding.header.startAnimation(toolbarAnimation)
// EXH <--
}
}
@ -1001,14 +823,24 @@ class ReaderActivity : BaseActivity() {
showReadingModeToast(viewModel.getMangaReadingMode())
}
supportActionBar?.title = manga.title
loadingIndicator = ReaderProgressIndicator(this)
binding.readerContainer.addView(loadingIndicator)
startPostponedEnterTransition()
}
private fun openMangaScreen() {
viewModel.manga?.id?.let { id ->
startActivity(
Intent(this, MainActivity::class.java).apply {
action = Constants.SHORTCUT_MANGA
putExtra(Constants.MANGA_EXTRA, id)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
},
)
}
}
private fun openChapterInWebView() {
val manga = viewModel.manga ?: return
val source = viewModel.getSource() ?: return
@ -1018,6 +850,13 @@ class ReaderActivity : BaseActivity() {
}
}
private fun shareChapter() {
assistUrl?.let {
val intent = it.toUri().toShareIntent(this, type = "text/plain")
startActivity(Intent.createChooser(intent, getString(R.string.action_share)))
}
}
private fun showReadingModeToast(mode: Int) {
try {
readingModeToast?.cancel()
@ -1057,15 +896,6 @@ class ReaderActivity : BaseActivity() {
viewModel.state.value.viewer?.setChapters(viewerChapters)
binding.toolbar.subtitle = viewerChapters.currChapter.chapter.name
ToolbarUtils.getSubtitleTextView(binding.toolbar)?.let {
it.ellipsize = TextUtils.TruncateAt.MARQUEE
it.isSelected = true
}
// Invalidate menu to show proper chapter bookmark state
invalidateOptionsMenu()
lifecycleScope.launchIO {
viewModel.getChapterUrl()?.let { url ->
assistUrl = url
@ -1103,7 +933,7 @@ class ReaderActivity : BaseActivity() {
*/
private fun moveToPageIndex(index: Int) {
val viewer = viewModel.state.value.viewer ?: return
val currentChapter = viewModel.getCurrentChapter() ?: return
val currentChapter = viewModel.state.value.currentChapter ?: return
val page = currentChapter.pages?.getOrNull(index) ?: return
viewer.moveToPage(page)
}

View File

@ -64,6 +64,7 @@ import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update
@ -271,6 +272,24 @@ class ReaderViewModel @JvmOverloads constructor(
chapterId = currentChapter.chapter.id!!
}
.launchIn(viewModelScope)
// SY -->
state.mapLatest { it.ehAutoscrollFreq }
.distinctUntilChanged()
.drop(1)
.onEach { text ->
val parsed = text.toDoubleOrNull()
if (parsed == null || parsed <= 0 || parsed > 9999) {
readerPreferences.autoscrollInterval().set(-1f)
mutableState.update { it.copy(isAutoScrollEnabled = false) }
} else {
readerPreferences.autoscrollInterval().set(parsed.toFloat())
mutableState.update { it.copy(isAutoScrollEnabled = true) }
}
}
.launchIn(viewModelScope)
// SY <--
}
override fun onCleared() {
@ -319,8 +338,24 @@ class ReaderViewModel @JvmOverloads constructor(
val mergedReferences = if (source is MergedSource) runBlocking { getMergedReferencesById.await(manga.id) } else emptyList()
val mergedManga = if (source is MergedSource) runBlocking { getMergedMangaById.await(manga.id) }.associateBy { it.id } else emptyMap()
val relativeTime = uiPreferences.relativeTime().get()
val autoScrollFreq = readerPreferences.autoscrollInterval().get()
// SY <--
mutableState.update { it.copy(manga = manga /* SY --> */, meta = metadata, mergedManga = mergedManga, dateRelativeTime = relativeTime/* SY <-- */) }
mutableState.update {
it.copy(
manga = manga,
/* SY --> */
meta = metadata,
mergedManga = mergedManga,
dateRelativeTime = relativeTime,
ehAutoscrollFreq = if (autoScrollFreq == -1f) {
""
} else {
autoScrollFreq.toString()
},
isAutoScrollEnabled = autoScrollFreq != -1f
/* SY <-- */
)
}
if (chapterId == -1L) chapterId = initialChapterId
val context = Injekt.get<Application>()
@ -384,7 +419,10 @@ class ReaderViewModel @JvmOverloads constructor(
it.viewerChapters?.unref()
chapterToDownload = cancelQueuedDownloads(newChapters.currChapter)
it.copy(viewerChapters = newChapters)
it.copy(
viewerChapters = newChapters,
bookmarked = newChapters.currChapter.chapter.bookmark,
)
}
}
return newChapters
@ -686,8 +724,8 @@ class ReaderViewModel @JvmOverloads constructor(
/**
* Returns the currently active chapter.
*/
fun getCurrentChapter(): ReaderChapter? {
return state.value.viewerChapters?.currChapter
private fun getCurrentChapter(): ReaderChapter? {
return state.value.currentChapter
}
fun getSource() = manga?.source?.let { sourceManager.getOrStub(it) } as? HttpSource
@ -707,9 +745,11 @@ class ReaderViewModel @JvmOverloads constructor(
/**
* Bookmarks the currently active chapter.
*/
fun bookmarkCurrentChapter(bookmarked: Boolean) {
fun toggleChapterBookmark() {
val chapter = getCurrentChapter()?.chapter ?: return
chapter.bookmark = bookmarked // Otherwise the bookmark icon doesn't update
val bookmarked = !chapter.bookmark
chapter.bookmark = bookmarked
viewModelScope.launchNonCancellable {
updateChapter.await(
ChapterUpdate(
@ -718,6 +758,12 @@ class ReaderViewModel @JvmOverloads constructor(
),
)
}
mutableState.update {
it.copy(
bookmarked = bookmarked,
)
}
}
// SY -->
@ -868,6 +914,26 @@ class ReaderViewModel @JvmOverloads constructor(
fun setDoublePages(doublePages: Boolean) {
mutableState.update { it.copy(doublePages = doublePages) }
}
fun openAutoScrollHelpDialog() {
mutableState.update { it.copy(dialog = Dialog.AutoScrollHelp) }
}
fun openBoostPageHelp() {
mutableState.update { it.copy(dialog = Dialog.BoostPageHelp) }
}
fun openRetryAllHelp() {
mutableState.update { it.copy(dialog = Dialog.RetryAllHelp) }
}
fun toggleAutoScroll(enabled: Boolean) {
mutableState.update { it.copy(autoScroll = enabled) }
}
fun setAutoScrollFrequency(frequency: String) {
mutableState.update { it.copy(ehAutoscrollFreq = frequency) }
}
// SY <--
fun showLoadingDialog() {
@ -1168,6 +1234,7 @@ class ReaderViewModel @JvmOverloads constructor(
data class State(
val manga: Manga? = null,
val viewerChapters: ViewerChapters? = null,
val bookmarked: Boolean = false,
val isLoadingAdjacentChapter: Boolean = false,
val currentPage: Int = -1,
@ -1188,10 +1255,16 @@ class ReaderViewModel @JvmOverloads constructor(
val indexChapterToShift: Long? = null,
val doublePages: Boolean = false,
val dateRelativeTime: Boolean = true,
val autoScroll: Boolean = false,
val isAutoScrollEnabled: Boolean = false,
val ehAutoscrollFreq: String = "",
// SY <--
) {
val currentChapter: ReaderChapter?
get() = viewerChapters?.currChapter
val totalPages: Int
get() = viewerChapters?.currChapter?.pages?.size ?: -1
get() = currentChapter?.pages?.size ?: -1
}
sealed interface Dialog {
@ -1205,6 +1278,12 @@ class ReaderViewModel @JvmOverloads constructor(
// SY <--
data class PageActions(val page: ReaderPage/* SY --> */, val extraPage: ReaderPage? = null /* SY <-- */) : Dialog
// SY -->
data object AutoScrollHelp : Dialog
data object RetryAllHelp : Dialog
data object BoostPageHelp : Dialog
// SY <--
}
sealed interface Event {

View File

@ -1,11 +0,0 @@
package eu.kanade.tachiyomi.widget.listener
import android.view.animation.Animation
open class SimpleAnimationListener : Animation.AnimationListener {
override fun onAnimationRepeat(animation: Animation) {}
override fun onAnimationEnd(animation: Animation) {}
override fun onAnimationStart(animation: Animation) {}
}

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:duration="200"
android:fromYDelta="-100%"
android:toYDelta="0%" />
</set>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:duration="200"
android:fromYDelta="0%"
android:toYDelta="-100%" />
</set>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2z" />
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M17,3L7,3c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3L19,5c0,-1.1 -0.9,-2 -2,-2zM17,18l-5,-2.18L7,18L7,5h10v13z" />
</vector>

View File

@ -1,7 +1,4 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/reader_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -44,145 +41,6 @@
android:layout_height="match_parent"
android:visibility="gone" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/reader_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="?attr/actionBarTheme"
android:visibility="invisible"
tools:visibility="visible">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
app:elevation="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize" />
<LinearLayout
android:id="@+id/eh_utils"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/eh_autoscroll"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_horizontal|center_vertical"
android:paddingHorizontal="16dp"
android:paddingVertical="16dp"
android:text="@string/eh_autoscroll"
android:textColor="?attr/colorOnSurface"
android:textSize="13sp" />
<EditText
android:id="@+id/eh_autoscroll_freq"
android:layout_width="100dp"
android:layout_height="match_parent"
android:backgroundTint="?attr/colorAccent"
android:gravity="center_horizontal|center_vertical"
android:importantForAutofill="no"
android:inputType="numberDecimal"
android:maxLength="10"
android:singleLine="true" />
<Button
android:id="@+id/eh_autoscroll_help"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="50dp"
android:layout_height="match_parent"
android:text="\?"
android:textColor="?attr/colorOnSurface"
android:textSize="15sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal">
<Button
android:id="@+id/eh_retry_all"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:fontFamily="sans-serif"
android:text="@string/eh_retry_all"
android:textColor="?attr/colorOnSurface"
android:textSize="13sp" />
<Button
android:id="@+id/eh_retry_all_help"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="50dp"
android:layout_height="match_parent"
android:text="\?"
android:textColor="?attr/colorOnSurface"
android:textSize="15sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
<Button
android:id="@+id/eh_boost_page"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:fontFamily="sans-serif"
android:text="@string/eh_boost_page"
android:textColor="?attr/colorOnSurface"
android:textSize="13sp" />
<Button
android:id="@+id/eh_boost_page_help"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="50dp"
android:layout_height="match_parent"
android:text="\?"
android:textColor="?attr/colorOnSurface"
android:textSize="15sp"
android:textStyle="bold"
tools:ignore="HardcodedText" />
</LinearLayout>
</LinearLayout>
<ImageButton
android:id="@+id/expand_eh_button"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="0dp"
app:srcCompat="@drawable/ic_keyboard_arrow_down_white_32dp"
app:tint="?attr/colorOnSurface" />
</com.google.android.material.appbar.AppBarLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.compose.ui.platform.ComposeView
android:id="@+id/dialog_root"
android:layout_width="match_parent"

View File

@ -1,59 +0,0 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/reader_chapter_layout"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="@android:color/transparent">
<TextView
android:id="@+id/chapter_title"
style="@style/TextAppearance.MaterialComponents.Body2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="12dp"
android:ellipsize="end"
android:maxLines="1"
app:layout_constraintBottom_toTopOf="@id/chapter_scanlator"
app:layout_constraintEnd_toStartOf="@id/bookmark_layout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Chapter 123 - The Real One" />
<TextView
android:id="@+id/chapter_scanlator"
style="@style/TextAppearance.MaterialComponents.Caption"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginBottom="12dp"
android:ellipsize="end"
android:maxLines="1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/bookmark_layout"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/chapter_title"
tools:text="3 days ago • On page 45 • Scanlator" />
<FrameLayout
android:id="@+id/bookmark_layout"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginEnd="12dp"
android:clickable="true"
android:focusable="true"
android:src="@drawable/ic_bookmark_24dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/bookmark_image"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center"
android:src="@drawable/ic_bookmark_border_24dp"
app:tint="?attr/colorOnSurface" />
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="8dp">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/chapter_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
tools:listitem="@layout/reader_chapter_item" />
</LinearLayout>

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<!--<item
android:id="@+id/action_bookmark"
android:icon="@drawable/ic_bookmark_border_24dp"
android:title="@string/action_bookmark"
app:iconTint="?attr/colorOnSurface"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_remove_bookmark"
android:icon="@drawable/ic_bookmark_24dp"
android:title="@string/action_remove_bookmark"
app:iconTint="?attr/colorOnSurface"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_open_in_web_view"
android:title="@string/action_open_in_web_view"
app:showAsAction="never" />
<item
android:id="@+id/action_share"
android:title="@string/action_share"
app:showAsAction="never" />-->
</menu>