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

View File

@ -1,7 +1,6 @@
package eu.kanade.presentation.reader.appbars package eu.kanade.presentation.reader.appbars
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth 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.material.icons.outlined.Share
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -31,6 +29,7 @@ fun BottomReaderBar(
// SY --> // SY -->
enabledButtons: Set<String>, enabledButtons: Set<String>,
// SY <-- // SY <--
backgroundColor: Color,
readingMode: ReadingModeType, readingMode: ReadingModeType,
onClickReadingMode: () -> Unit, onClickReadingMode: () -> Unit,
orientationMode: OrientationType, orientationMode: OrientationType,
@ -39,21 +38,15 @@ fun BottomReaderBar(
onClickCropBorder: () -> Unit, onClickCropBorder: () -> Unit,
onClickSettings: () -> Unit, onClickSettings: () -> Unit,
// SY --> // SY -->
isHttpSource: Boolean,
dualPageSplitEnabled: Boolean, dualPageSplitEnabled: Boolean,
doublePages: Boolean, doublePages: Boolean,
onClickChapterList: () -> Unit, onClickChapterList: () -> Unit,
onClickWebView: () -> Unit, onClickWebView: (() -> Unit)?,
onClickShare: () -> Unit, onClickShare: (() -> Unit)?,
onClickPageLayout: () -> Unit, onClickPageLayout: () -> Unit,
onClickShiftPage: () -> Unit, onClickShiftPage: () -> Unit,
// SY <-- // 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( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -72,7 +65,7 @@ fun BottomReaderBar(
} }
} }
if (ReaderBottomButton.WebView.isIn(enabledButtons) && isHttpSource) { if (ReaderBottomButton.WebView.isIn(enabledButtons) && onClickWebView != null) {
IconButton(onClick = onClickWebView) { IconButton(onClick = onClickWebView) {
Icon( Icon(
imageVector = Icons.Outlined.Public, 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) { IconButton(onClick = onClickShare) {
Icon( Icon(
imageVector = Icons.Outlined.Share, 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.slideInVertically
import androidx.compose.animation.slideOutHorizontally import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.slideOutVertically 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.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer 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.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding 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.Composable
import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment 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.IntOffset
import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp 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.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import eu.kanade.tachiyomi.ui.reader.viewer.Viewer import eu.kanade.tachiyomi.ui.reader.viewer.Viewer
@ -56,8 +66,18 @@ fun BoxIgnoreLayoutDirection(modifier: Modifier, content: @Composable BoxScope.(
@Composable @Composable
fun ReaderAppBars( fun ReaderAppBars(
visible: Boolean, 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, onNextChapter: () -> Unit,
enabledNext: Boolean, enabledNext: Boolean,
onPreviousChapter: () -> Unit, onPreviousChapter: () -> Unit,
@ -74,20 +94,40 @@ fun ReaderAppBars(
onClickCropBorder: () -> Unit, onClickCropBorder: () -> Unit,
onClickSettings: () -> Unit, onClickSettings: () -> Unit,
// SY --> // 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, navBarType: NavBarType,
currentPageText: String, currentPageText: String,
enabledButtons: Set<String>, enabledButtons: Set<String>,
isHttpSource: Boolean,
dualPageSplitEnabled: Boolean, dualPageSplitEnabled: Boolean,
doublePages: Boolean, doublePages: Boolean,
onClickChapterList: () -> Unit, onClickChapterList: () -> Unit,
onClickWebView: () -> Unit,
onClickShare: () -> Unit,
onClickPageLayout: () -> Unit, onClickPageLayout: () -> Unit,
onClickShiftPage: () -> Unit, onClickShiftPage: () -> Unit,
) { ) {
val isRtl = viewer is R2LPagerViewer 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 --> // SY -->
@ -104,7 +144,8 @@ fun ReaderAppBars(
targetOffsetX = { -it }, targetOffsetX = { -it },
animationSpec = animationSpec, animationSpec = animationSpec,
), ),
modifier = Modifier.padding(bottom = 48.dp, top = 120.dp) modifier = Modifier
.padding(bottom = 48.dp, top = 120.dp)
.align(Alignment.TopStart) .align(Alignment.TopStart)
) { ) {
ChapterNavigator( ChapterNavigator(
@ -131,7 +172,8 @@ fun ReaderAppBars(
targetOffsetX = { it }, targetOffsetX = { it },
animationSpec = animationSpec, animationSpec = animationSpec,
), ),
modifier = Modifier.padding(bottom = 48.dp, top = 120.dp) modifier = Modifier
.padding(bottom = 48.dp, top = 120.dp)
.align(Alignment.TopEnd) .align(Alignment.TopEnd)
) { ) {
ChapterNavigator( ChapterNavigator(
@ -152,6 +194,72 @@ fun ReaderAppBars(
modifier = Modifier.fillMaxHeight(), modifier = Modifier.fillMaxHeight(),
verticalArrangement = Arrangement.SpaceBetween, 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)) Spacer(modifier = Modifier.weight(1f))
AnimatedVisibility( AnimatedVisibility(
@ -187,6 +295,7 @@ fun ReaderAppBars(
// SY --> // SY -->
enabledButtons = enabledButtons, enabledButtons = enabledButtons,
// SY <-- // SY <--
backgroundColor = backgroundColor,
readingMode = readingMode, readingMode = readingMode,
onClickReadingMode = onClickReadingMode, onClickReadingMode = onClickReadingMode,
orientationMode = orientationMode, orientationMode = orientationMode,
@ -195,12 +304,11 @@ fun ReaderAppBars(
onClickCropBorder = onClickCropBorder, onClickCropBorder = onClickCropBorder,
onClickSettings = onClickSettings, onClickSettings = onClickSettings,
// SY --> // SY -->
isHttpSource = isHttpSource,
dualPageSplitEnabled = dualPageSplitEnabled, dualPageSplitEnabled = dualPageSplitEnabled,
doublePages = doublePages, doublePages = doublePages,
onClickChapterList = onClickChapterList, onClickChapterList = onClickChapterList,
onClickWebView = onClickWebView, onClickWebView = onOpenInWebView,
onClickShare = onClickShare, onClickShare = onShare,
onClickPageLayout = onClickPageLayout, onClickPageLayout = onClickPageLayout,
onClickShiftPage = onClickShiftPage onClickShiftPage = onClickShiftPage
) )

View File

@ -5,7 +5,6 @@ import android.annotation.TargetApi
import android.app.assist.AssistContent import android.app.assist.AssistContent
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.ColorStateList
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Color import android.graphics.Color
@ -15,19 +14,11 @@ import android.graphics.Paint
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.text.Editable
import android.text.TextUtils
import android.text.TextWatcher
import android.view.KeyEvent import android.view.KeyEvent
import android.view.Menu
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.View.LAYER_TYPE_HARDWARE import android.view.View.LAYER_TYPE_HARDWARE
import android.view.WindowManager 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 android.widget.Toast
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.compose.foundation.layout.Arrangement 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.AlertDialog
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -55,9 +47,7 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.elevation.SurfaceColors
import com.google.android.material.internal.ToolbarUtils
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.transition.platform.MaterialContainerTransform import com.google.android.material.transition.platform.MaterialContainerTransform
import dev.chrisbanes.insetter.applyInsetter import dev.chrisbanes.insetter.applyInsetter
import eu.kanade.domain.base.BasePreferences 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.data.notification.Notifications
import eu.kanade.tachiyomi.databinding.ReaderActivityBinding import eu.kanade.tachiyomi.databinding.ReaderActivityBinding
import eu.kanade.tachiyomi.source.model.Page 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.base.activity.BaseActivity
import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.reader.ReaderViewModel.SetAsCoverResult.AddToLibraryFirst 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.pager.VerticalPagerViewer
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
import eu.kanade.tachiyomi.ui.webview.WebViewActivity 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.hasDisplayCutout
import eu.kanade.tachiyomi.util.system.isNightMode import eu.kanade.tachiyomi.util.system.isNightMode
import eu.kanade.tachiyomi.util.system.toShareIntent import eu.kanade.tachiyomi.util.system.toShareIntent
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.setComposeContent import eu.kanade.tachiyomi.util.view.setComposeContent
import eu.kanade.tachiyomi.widget.listener.SimpleAnimationListener
import exh.source.isEhBasedSource import exh.source.isEhBasedSource
import exh.util.defaultReaderType import exh.util.defaultReaderType
import exh.util.mangaType import exh.util.mangaType
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.filterNotNull
@ -118,7 +103,6 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.sample import kotlinx.coroutines.flow.sample
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import logcat.LogPriority import logcat.LogPriority
@ -271,19 +255,6 @@ class ReaderActivity : BaseActivity() {
.launchIn(lifecycleScope) .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. * 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() } 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 * Called when the user clicks the back key or the button on the toolbar. The call is
* delegated to the presenter. * delegated to the presenter.
@ -405,69 +335,16 @@ class ReaderActivity : BaseActivity() {
return handled || super.dispatchGenericMotionEvent(event) 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. * Initializes the reader menu. It sets up click listeners and the initial visibility.
*/ */
private fun initializeMenu() { 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 { binding.dialogRoot.applyInsetter {
type(navigationBars = true) { type(navigationBars = true) {
margin(vertical = true, horizontal = 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 { binding.pageNumber.setComposeContent {
val state by viewModel.state.collectAsState() val state by viewModel.state.collectAsState()
val showPageNumber by viewModel.readerPreferences.showPageNumber().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 cropBorderPaged by readerPreferences.cropBorders().collectAsState()
val cropBorderWebtoon by readerPreferences.cropBordersWebtoon().collectAsState() val cropBorderWebtoon by readerPreferences.cropBordersWebtoon().collectAsState()
// SY --> // SY -->
@ -526,11 +406,20 @@ class ReaderActivity : BaseActivity() {
} }
// SY <-- // SY <--
ReaderAppBars( ReaderAppBars(
visible = state.menuVisible, 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, onNextChapter = ::loadNextChapter,
enabledNext = state.viewerChapters?.nextChapter != null, enabledNext = state.viewerChapters?.nextChapter != null,
onPreviousChapter = ::loadPreviousChapter, onPreviousChapter = ::loadPreviousChapter,
@ -557,34 +446,29 @@ class ReaderActivity : BaseActivity() {
cropEnabled = cropEnabled, cropEnabled = cropEnabled,
onClickCropBorder = { onClickCropBorder = {
val enabled = viewModel.toggleCropBorders() val enabled = viewModel.toggleCropBorders()
menuToggleToast?.cancel() menuToggleToast?.cancel()
menuToggleToast = toast( menuToggleToast = toast(if (enabled) R.string.on else R.string.off)
if (enabled) {
R.string.on
} else {
R.string.off
},
)
}, },
onClickSettings = viewModel::openSettingsDialog, onClickSettings = viewModel::openSettingsDialog,
// SY --> // 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, currentPageText = state.currentPageText,
navBarType = navBarType, navBarType = navBarType,
enabledButtons = readerBottomButtons, enabledButtons = readerBottomButtons,
isHttpSource = remember {
viewModel.getSource() != null
},
dualPageSplitEnabled = dualPageSplitPaged, dualPageSplitEnabled = dualPageSplitPaged,
doublePages = state.doublePages, doublePages = state.doublePages,
onClickChapterList = viewModel::openChapterListDialog, 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 = { onClickPageLayout = {
if (readerPreferences.pageLayout().get() == PagerConfig.PageLayout.AUTOMATIC) { if (readerPreferences.pageLayout().get() == PagerConfig.PageLayout.AUTOMATIC) {
(viewModel.state.value.viewer as? PagerViewer)?.config?.let { config -> (viewModel.state.value.viewer as? PagerViewer)?.config?.let { config ->
@ -682,188 +566,154 @@ class ReaderActivity : BaseActivity() {
state.dateRelativeTime 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 -> {} 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( val toolbarColor = ColorUtils.setAlphaComponent(
toolbarBackground.resolvedTintColor, SurfaceColors.SURFACE_2.getColor(this),
toolbarBackground.alpha, if (isNightMode()) 230 else 242, // 90% dark 95% light
) )
window.statusBarColor = toolbarColor window.statusBarColor = toolbarColor
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
window.navigationBarColor = toolbarColor window.navigationBarColor = toolbarColor
} }
// SY -->
binding.toolbar.background.alpha = 0
(binding.header.background as MaterialShapeDrawable).fillColor = ColorStateList.valueOf(toolbarColor)
// SY <--
// Set initial visibility // Set initial visibility
setMenuVisibility(viewModel.state.value.menuVisible) setMenuVisibility(viewModel.state.value.menuVisible)
// --> EH enableExhAutoScroll()
setEhUtilsVisibility(viewModel.state.value.ehUtilsVisible)
// <-- EH
} }
// EXH --> private fun enableExhAutoScroll() {
fun initDropdownMenu() { readerPreferences.autoscrollInterval().changes()
binding.expandEhButton.setOnClickListener { .combine(viewModel.state.map { it.autoScroll }.distinctUntilChanged()) { interval, enabled ->
val newValue = !viewModel.state.value.ehUtilsVisible interval.toDouble() to enabled
viewModel.showEhUtils(newValue) }.mapLatest { (intervalFloat, enabled) ->
setEhUtilsVisibility(newValue) if (enabled) {
} repeatOnLifecycle(Lifecycle.State.STARTED) {
val interval = intervalFloat.seconds
binding.ehAutoscrollFreq.setText( while (true) {
readerPreferences.autoscrollInterval().get().let { if (!viewModel.state.value.menuVisible) {
if (it == -1f) { viewModel.state.value.viewer.let { v ->
"" when (v) {
} else { is PagerViewer -> v.moveToNext()
it.toString() is WebtoonViewer -> {
} if (readerPreferences.smoothAutoScroll().get()) {
}, v.linearScroll(interval)
) } else {
v.scrollDown()
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()
}
} }
} }
} }
delay(interval)
} else {
delay(100)
} }
delay(interval)
} else {
delay(100)
} }
} }
} }
} }
} }
.launchIn(lifecycleScope) .launchIn(lifecycleScope)
}
binding.ehAutoscrollHelp.setOnClickListener { private fun exhRetryAll() {
MaterialAlertDialogBuilder(this) var retried = 0
.setTitle(R.string.eh_autoscroll_help)
.setMessage(R.string.eh_autoscroll_help_message)
.setPositiveButton(R.string.action_ok, null)
.show()
}
binding.ehRetryAll.setOnClickListener { viewModel.state.value.viewerChapters
var retried = 0 ?.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 if (shouldQueuePage) {
?.currChapter page.status = Page.State.QUEUE
?.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)
} else { } 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 { if (curPage.status == Page.State.ERROR) {
MaterialAlertDialogBuilder(this) toast(R.string.eh_boost_page_errored)
.setTitle(R.string.eh_boost_page_help) } else if (curPage.status == Page.State.LOAD_PAGE || curPage.status == Page.State.DOWNLOAD_IMAGE) {
.setMessage(R.string.eh_boost_page_help_message) toast(R.string.eh_boost_page_downloading)
.setPositiveButton(R.string.action_ok, null) } else if (curPage.status == Page.State.READY) {
.show() 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 viewer.config.doublePages = doublePages
viewModel.setDoublePages(viewer.config.doublePages) viewModel.setDoublePages(viewer.config.doublePages)
} }
val currentChapter = viewModel.getCurrentChapter() val currentChapter = viewModel.state.value.currentChapter
if (doublePages) { if (doublePages) {
// If we're moving from singe to double, we want the current page to be the first page // If we're moving from singe to double, we want the current page to be the first page
val currentPage = viewModel.state.value.currentPage val currentPage = viewModel.state.value.currentPage
@ -921,40 +771,12 @@ class ReaderActivity : BaseActivity() {
viewModel.showMenus(visible) viewModel.showMenus(visible)
if (visible) { if (visible) {
windowInsetsController.show(WindowInsetsCompat.Type.systemBars()) windowInsetsController.show(WindowInsetsCompat.Type.systemBars())
binding.readerMenu.isVisible = true window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
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 <--
} else { } else {
if (readerPreferences.fullscreen().get()) { if (readerPreferences.fullscreen().get()) {
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars()) windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE 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()) showReadingModeToast(viewModel.getMangaReadingMode())
} }
supportActionBar?.title = manga.title
loadingIndicator = ReaderProgressIndicator(this) loadingIndicator = ReaderProgressIndicator(this)
binding.readerContainer.addView(loadingIndicator) binding.readerContainer.addView(loadingIndicator)
startPostponedEnterTransition() 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() { private fun openChapterInWebView() {
val manga = viewModel.manga ?: return val manga = viewModel.manga ?: return
val source = viewModel.getSource() ?: 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) { private fun showReadingModeToast(mode: Int) {
try { try {
readingModeToast?.cancel() readingModeToast?.cancel()
@ -1057,15 +896,6 @@ class ReaderActivity : BaseActivity() {
viewModel.state.value.viewer?.setChapters(viewerChapters) 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 { lifecycleScope.launchIO {
viewModel.getChapterUrl()?.let { url -> viewModel.getChapterUrl()?.let { url ->
assistUrl = url assistUrl = url
@ -1103,7 +933,7 @@ class ReaderActivity : BaseActivity() {
*/ */
private fun moveToPageIndex(index: Int) { private fun moveToPageIndex(index: Int) {
val viewer = viewModel.state.value.viewer ?: return 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 val page = currentChapter.pages?.getOrNull(index) ?: return
viewer.moveToPage(page) viewer.moveToPage(page)
} }

View File

@ -64,6 +64,7 @@ import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
@ -271,6 +272,24 @@ class ReaderViewModel @JvmOverloads constructor(
chapterId = currentChapter.chapter.id!! chapterId = currentChapter.chapter.id!!
} }
.launchIn(viewModelScope) .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() { 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 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 mergedManga = if (source is MergedSource) runBlocking { getMergedMangaById.await(manga.id) }.associateBy { it.id } else emptyMap()
val relativeTime = uiPreferences.relativeTime().get() val relativeTime = uiPreferences.relativeTime().get()
val autoScrollFreq = readerPreferences.autoscrollInterval().get()
// SY <-- // 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 if (chapterId == -1L) chapterId = initialChapterId
val context = Injekt.get<Application>() val context = Injekt.get<Application>()
@ -384,7 +419,10 @@ class ReaderViewModel @JvmOverloads constructor(
it.viewerChapters?.unref() it.viewerChapters?.unref()
chapterToDownload = cancelQueuedDownloads(newChapters.currChapter) chapterToDownload = cancelQueuedDownloads(newChapters.currChapter)
it.copy(viewerChapters = newChapters) it.copy(
viewerChapters = newChapters,
bookmarked = newChapters.currChapter.chapter.bookmark,
)
} }
} }
return newChapters return newChapters
@ -686,8 +724,8 @@ class ReaderViewModel @JvmOverloads constructor(
/** /**
* Returns the currently active chapter. * Returns the currently active chapter.
*/ */
fun getCurrentChapter(): ReaderChapter? { private fun getCurrentChapter(): ReaderChapter? {
return state.value.viewerChapters?.currChapter return state.value.currentChapter
} }
fun getSource() = manga?.source?.let { sourceManager.getOrStub(it) } as? HttpSource fun getSource() = manga?.source?.let { sourceManager.getOrStub(it) } as? HttpSource
@ -707,9 +745,11 @@ class ReaderViewModel @JvmOverloads constructor(
/** /**
* Bookmarks the currently active chapter. * Bookmarks the currently active chapter.
*/ */
fun bookmarkCurrentChapter(bookmarked: Boolean) { fun toggleChapterBookmark() {
val chapter = getCurrentChapter()?.chapter ?: return val chapter = getCurrentChapter()?.chapter ?: return
chapter.bookmark = bookmarked // Otherwise the bookmark icon doesn't update val bookmarked = !chapter.bookmark
chapter.bookmark = bookmarked
viewModelScope.launchNonCancellable { viewModelScope.launchNonCancellable {
updateChapter.await( updateChapter.await(
ChapterUpdate( ChapterUpdate(
@ -718,6 +758,12 @@ class ReaderViewModel @JvmOverloads constructor(
), ),
) )
} }
mutableState.update {
it.copy(
bookmarked = bookmarked,
)
}
} }
// SY --> // SY -->
@ -868,6 +914,26 @@ class ReaderViewModel @JvmOverloads constructor(
fun setDoublePages(doublePages: Boolean) { fun setDoublePages(doublePages: Boolean) {
mutableState.update { it.copy(doublePages = doublePages) } 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 <-- // SY <--
fun showLoadingDialog() { fun showLoadingDialog() {
@ -1168,6 +1234,7 @@ class ReaderViewModel @JvmOverloads constructor(
data class State( data class State(
val manga: Manga? = null, val manga: Manga? = null,
val viewerChapters: ViewerChapters? = null, val viewerChapters: ViewerChapters? = null,
val bookmarked: Boolean = false,
val isLoadingAdjacentChapter: Boolean = false, val isLoadingAdjacentChapter: Boolean = false,
val currentPage: Int = -1, val currentPage: Int = -1,
@ -1188,10 +1255,16 @@ class ReaderViewModel @JvmOverloads constructor(
val indexChapterToShift: Long? = null, val indexChapterToShift: Long? = null,
val doublePages: Boolean = false, val doublePages: Boolean = false,
val dateRelativeTime: Boolean = true, val dateRelativeTime: Boolean = true,
val autoScroll: Boolean = false,
val isAutoScrollEnabled: Boolean = false,
val ehAutoscrollFreq: String = "",
// SY <-- // SY <--
) { ) {
val currentChapter: ReaderChapter?
get() = viewerChapters?.currChapter
val totalPages: Int val totalPages: Int
get() = viewerChapters?.currChapter?.pages?.size ?: -1 get() = currentChapter?.pages?.size ?: -1
} }
sealed interface Dialog { sealed interface Dialog {
@ -1205,6 +1278,12 @@ class ReaderViewModel @JvmOverloads constructor(
// SY <-- // SY <--
data class PageActions(val page: ReaderPage/* SY --> */, val extraPage: ReaderPage? = null /* SY <-- */) : Dialog 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 { 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" <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_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -44,145 +41,6 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:visibility="gone" /> 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 <androidx.compose.ui.platform.ComposeView
android:id="@+id/dialog_root" android:id="@+id/dialog_root"
android:layout_width="match_parent" 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>