Migrate bottom reader menu to Compose

(cherry picked from commit 8680accd8e6f458a662dd5454bbcdcde482ce0a7)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt
#	app/src/main/res/layout/reader_activity.xml
This commit is contained in:
arkon 2023-08-04 18:05:02 -04:00 committed by Jobobby04
parent eb8685fa7e
commit ebb690cbe7
14 changed files with 466 additions and 645 deletions

View File

@ -0,0 +1,155 @@
package eu.kanade.presentation.reader
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
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.FormatListNumbered
import androidx.compose.material.icons.outlined.Public
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.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReaderBottomButton
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
@Composable
fun BottomReaderBar(
// SY -->
enabledButtons: Set<String>,
// SY <--
readingMode: ReadingModeType,
onClickReadingMode: () -> Unit,
orientationMode: OrientationType,
onClickOrientationMode: () -> Unit,
cropEnabled: Boolean,
onClickCropBorder: () -> Unit,
onClickSettings: () -> Unit,
// SY -->
isHttpSource: Boolean,
dualPageSplitEnabled: Boolean,
doublePages: Boolean,
onClickChapterList: () -> 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()
.background(backgroundColor)
.padding(8.dp),
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically,
) {
// SY -->
if (ReaderBottomButton.ViewChapters.isIn(enabledButtons)) {
IconButton(onClick = onClickChapterList) {
Icon(
imageVector = Icons.Outlined.FormatListNumbered,
contentDescription = stringResource(R.string.chapters),
)
}
}
if (ReaderBottomButton.WebView.isIn(enabledButtons) && isHttpSource) {
IconButton(onClick = onClickWebView) {
Icon(
imageVector = Icons.Outlined.Public,
contentDescription = stringResource(R.string.action_open_in_web_view),
)
}
}
if (ReaderBottomButton.Share.isIn(enabledButtons) && isHttpSource) {
IconButton(onClick = onClickShare) {
Icon(
imageVector = Icons.Outlined.Share,
contentDescription = stringResource(R.string.action_share),
)
}
}
if (ReaderBottomButton.ReadingMode.isIn(enabledButtons)) {
IconButton(onClick = onClickReadingMode) {
Icon(
painter = painterResource(readingMode.iconRes),
contentDescription = stringResource(R.string.viewer),
)
}
}
val cropBorders = when (readingMode) {
ReadingModeType.WEBTOON -> ReaderBottomButton.CropBordersWebtoon
ReadingModeType.CONTINUOUS_VERTICAL -> ReaderBottomButton.CropBordersContinuesVertical
else -> ReaderBottomButton.CropBordersPager
}
if (cropBorders.isIn(enabledButtons)) {
IconButton(onClick = onClickCropBorder) {
Icon(
painter = painterResource(if (cropEnabled) R.drawable.ic_crop_24dp else R.drawable.ic_crop_off_24dp),
contentDescription = stringResource(R.string.pref_crop_borders),
)
}
}
if (ReaderBottomButton.Rotation.isIn(enabledButtons)) {
IconButton(onClick = onClickOrientationMode) {
Icon(
painter = painterResource(orientationMode.iconRes),
contentDescription = stringResource(R.string.pref_rotation_type),
)
}
}
if (
!dualPageSplitEnabled &&
ReaderBottomButton.PageLayout.isIn(enabledButtons) &&
ReadingModeType.isPagerType(readingMode.prefValue)
) {
IconButton(onClick = onClickPageLayout) {
Icon(
painter = painterResource(R.drawable.ic_book_open_variant_24dp),
contentDescription = stringResource(R.string.page_layout),
)
}
}
if (doublePages) {
IconButton(onClick = onClickShiftPage) {
Icon(
painter = painterResource(R.drawable.ic_page_next_outline_24dp),
contentDescription = stringResource(R.string.shift_double_pages),
)
}
}
IconButton(onClick = onClickSettings) {
Icon(
imageVector = Icons.Outlined.Settings,
contentDescription = stringResource(R.string.action_settings),
)
}
// SY <--
}
}

View File

@ -0,0 +1,84 @@
package eu.kanade.presentation.reader
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import eu.kanade.presentation.components.AdaptiveSheet
import eu.kanade.presentation.manga.components.MangaChapterListItem
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.ui.reader.chapter.ReaderChapterItem
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
import eu.kanade.tachiyomi.util.lang.toRelativeString
import exh.metadata.MetadataUtil
import exh.source.isEhBasedManga
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.library.service.LibraryPreferences
import java.util.Date
@Composable
fun ChapterListDialog(
onDismissRequest: () -> Unit,
screenModel: ReaderSettingsScreenModel,
chapters: List<ReaderChapterItem>,
onClickChapter: (Chapter) -> Unit,
onBookmark: (Chapter) -> Unit,
) {
val manga by screenModel.mangaFlow.collectAsState()
val context = LocalContext.current
val state = rememberLazyListState(chapters.indexOfFirst { it.isCurrent }.coerceAtLeast(0))
AdaptiveSheet(
onDismissRequest = onDismissRequest,
) {
LazyColumn(
state = state,
modifier = Modifier.defaultMinSize(minHeight = 200.dp),
contentPadding = PaddingValues(vertical = 16.dp),
) {
items(
items = chapters,
key = { "chapter-${it.chapter.id}" },
) { chapterItem ->
MangaChapterListItem(
title = chapterItem.chapter.name,
date = chapterItem.chapter.dateUpload
.takeIf { it > 0L }
?.let {
// SY -->
if (manga?.isEhBasedManga() == true) {
MetadataUtil.EX_DATE_FORMAT.format(Date(it))
} else {
Date(it).toRelativeString(context, chapterItem.dateFormat)
}
// SY <--
},
readProgress = null,
scanlator = chapterItem.chapter.scanlator,
sourceName = null,
read = false,
bookmark = chapterItem.chapter.bookmark,
selected = false,
downloadIndicatorEnabled = false,
downloadStateProvider = { Download.State.NOT_DOWNLOADED },
downloadProgressProvider = { 0 },
chapterSwipeStartAction = LibraryPreferences.ChapterSwipeAction.ToggleBookmark,
chapterSwipeEndAction = LibraryPreferences.ChapterSwipeAction.ToggleBookmark,
onLongClick = { /*TODO*/ },
onClick = { onClickChapter(chapterItem.chapter) },
onDownloadClick = null,
onChapterSwipe = {
onBookmark(chapterItem.chapter)
},
)
}
}
}
}

View File

@ -79,14 +79,6 @@ fun ChapterNavigator(
val layoutDirection = if (isRtl) LayoutDirection.Rtl else LayoutDirection.Ltr
val haptic = LocalHapticFeedback.current
// We explicitly handle direction based on the reader viewer rather than the system direction
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = horizontalPadding),
verticalAlignment = Alignment.CenterVertically,
) {
// Match with toolbar background color set in ReaderActivity
val backgroundColor = MaterialTheme.colorScheme
.surfaceColorAtElevation(3.dp)
@ -95,6 +87,15 @@ fun ChapterNavigator(
containerColor = backgroundColor,
disabledContainerColor = backgroundColor,
)
// We explicitly handle direction based on the reader viewer rather than the system direction
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = horizontalPadding),
verticalAlignment = Alignment.CenterVertically,
) {
FilledIconButton(
enabled = if (isRtl) enabledNext else enabledPrevious,
onClick = if (isRtl) onNextChapter else onPreviousChapter,

View File

@ -31,8 +31,8 @@ import eu.kanade.tachiyomi.databinding.EditMangaDialogBinding
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.lang.chop
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.widget.materialdialogs.setTextInput
import exh.ui.metadata.adapters.MetadataUIUtil.getResourceColor
import exh.util.dropBlank
import exh.util.trimOrNull
import kotlinx.coroutines.CoroutineScope

View File

@ -7,7 +7,7 @@ import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.EditMergedSettingsItemBinding
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.getResourceColor
import exh.ui.metadata.adapters.MetadataUIUtil.getResourceColor
import tachiyomi.domain.manga.model.MergedMangaReference
import tachiyomi.domain.source.service.SourceManager
import uy.kohesive.injekt.Injekt

View File

@ -31,6 +31,7 @@ import android.widget.TextView
import android.widget.Toast
import androidx.activity.viewModels
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.CircularProgressIndicator
@ -38,8 +39,11 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
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.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.graphics.ColorUtils
@ -60,6 +64,8 @@ import com.google.android.material.transition.platform.MaterialContainerTransfor
import dev.chrisbanes.insetter.applyInsetter
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.manga.model.readingModeType
import eu.kanade.presentation.reader.BottomReaderBar
import eu.kanade.presentation.reader.ChapterListDialog
import eu.kanade.presentation.reader.ChapterNavigator
import eu.kanade.presentation.reader.OrientationModeSelectDialog
import eu.kanade.presentation.reader.PageIndicatorText
@ -71,19 +77,16 @@ 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
import eu.kanade.tachiyomi.ui.reader.ReaderViewModel.SetAsCoverResult.Error
import eu.kanade.tachiyomi.ui.reader.ReaderViewModel.SetAsCoverResult.Success
import eu.kanade.tachiyomi.ui.reader.chapter.ReaderChapterDialog
import eu.kanade.tachiyomi.ui.reader.loader.HttpPageLoader
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReaderBottomButton
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
@ -100,9 +103,7 @@ import eu.kanade.tachiyomi.util.system.isLTR
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.copy
import eu.kanade.tachiyomi.util.view.setComposeContent
import eu.kanade.tachiyomi.util.view.setTooltip
import eu.kanade.tachiyomi.widget.listener.SimpleAnimationListener
import exh.source.isEhBasedSource
import exh.util.defaultReaderType
@ -126,13 +127,13 @@ import kotlinx.coroutines.flow.sample
import kotlinx.coroutines.launch
import logcat.LogPriority
import tachiyomi.core.Constants
import tachiyomi.core.preference.toggle
import tachiyomi.core.util.lang.launchIO
import tachiyomi.core.util.lang.launchNonCancellable
import tachiyomi.core.util.lang.withUIContext
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.source.service.SourceManager
import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import kotlin.math.abs
@ -527,8 +528,6 @@ class ReaderActivity : BaseActivity() {
if (!readerPreferences.showReadingMode().get()) {
menuToggleToast = toast(stringRes)
}
updateCropBordersShortcut()
},
)
}
@ -553,6 +552,30 @@ class ReaderActivity : BaseActivity() {
hasExtraPage = (state.dialog as? ReaderViewModel.Dialog.PageActions)?.extraPage != null,
)
}
is ReaderViewModel.Dialog.ChapterList -> {
var chapters by remember {
mutableStateOf(viewModel.getChapters())
}
ChapterListDialog(
onDismissRequest = onDismissRequest,
screenModel = settingsScreenModel,
chapters = chapters,
onClickChapter = {
viewModel.loadNewChapterFromDialog(it)
onDismissRequest()
},
onBookmark = { chapter ->
viewModel.toggleBookmark(chapter.id, !chapter.bookmark)
chapters = chapters.map {
if (it.chapter.id == chapter.id) {
it.copy(chapter = chapter.copy(bookmark = !chapter.bookmark))
} else {
it
}
}
},
)
}
null -> {}
}
}
@ -560,8 +583,19 @@ class ReaderActivity : BaseActivity() {
// SY -->
val sliderContent: @Composable (Boolean) -> Unit = a@{ isVertical ->
val state by viewModel.state.collectAsState()
if (state.viewer == null) return@a
val forceHorizontalSeekbar by readerPreferences.forceHorizontalSeekbar().collectAsState()
val landscapeVerticalSeekbar by readerPreferences.landscapeVerticalSeekbar().collectAsState()
val configuration = LocalConfiguration.current
val verticalSeekbarLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && landscapeVerticalSeekbar
val verticalSeekbarHorizontal = configuration.orientation == Configuration.ORIENTATION_PORTRAIT
val viewerIsVertical = (state.viewer is WebtoonViewer || state.viewer is VerticalPagerViewer)
val showVerticalSeekbar = !forceHorizontalSeekbar && (verticalSeekbarLandscape || verticalSeekbarHorizontal) && viewerIsVertical
if ((isVertical && !showVerticalSeekbar) || (!isVertical && showVerticalSeekbar)) {
return@a
}
val isRtl = state.viewer is R2LPagerViewer
ChapterNavigator(
@ -586,26 +620,103 @@ class ReaderActivity : BaseActivity() {
}
// Init listeners on bottom menu
binding.readerNavHorz.setComposeContent {
sliderContent(false)
}
binding.readerNavVert.setComposeContent {
sliderContent(true)
}
initBottomShortcuts()
binding.readerMenuBottom.setComposeContent {
val state by viewModel.state.collectAsState()
if (state.viewer == null) return@setComposeContent
val cropBorderPaged by readerPreferences.cropBorders().collectAsState()
val cropBorderWebtoon by readerPreferences.cropBordersWebtoon().collectAsState()
val cropBorderContinuousVertical by readerPreferences.cropBordersContinuousVertical().collectAsState()
val readingMode = viewModel.getMangaReadingMode()
val isPagerType = ReadingModeType.isPagerType(readingMode)
val isWebtoon = ReadingModeType.WEBTOON.prefValue == readingMode
val cropEnabled = if (isPagerType) {
cropBorderPaged
} else if (isWebtoon) {
cropBorderWebtoon
} else {
cropBorderContinuousVertical
}
val readerBottomButtons by readerPreferences.readerBottomButtons().collectAsState()
val dualPageSplitPaged by readerPreferences.dualPageSplitPaged().collectAsState()
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
// SY -->
sliderContent(false)
// SY <--
updateBottomButtons()
BottomReaderBar(
enabledButtons = readerBottomButtons,
readingMode = ReadingModeType.fromPreference(
viewModel.getMangaReadingMode(
resolveDefault = false,
),
),
onClickReadingMode = viewModel::openReadingModeSelectDialog,
orientationMode = OrientationType.fromPreference(
viewModel.getMangaOrientationType(
resolveDefault = false,
),
),
onClickOrientationMode = viewModel::openOrientationModeSelectDialog,
cropEnabled = cropEnabled,
onClickCropBorder = {
val enabled = viewModel.toggleCropBorders()
menuToggleToast?.cancel()
menuToggleToast = toast(
if (enabled) {
R.string.on
} else {
R.string.off
},
)
},
onClickSettings = viewModel::openSettingsDialog,
// SY -->
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 ->
config.doublePages = !config.doublePages
reloadChapters(config.doublePages, true)
}
} else {
readerPreferences.pageLayout().set(1 - readerPreferences.pageLayout().get())
}
},
onClickShiftPage = ::shiftDoublePages,
// 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
}
binding.toolbarBottom.background = toolbarBackground.copy(this@ReaderActivity)
val toolbarColor = ColorUtils.setAlphaComponent(
toolbarBackground.resolvedTintColor,
toolbarBackground.alpha,
@ -628,125 +739,6 @@ class ReaderActivity : BaseActivity() {
}
// EXH -->
fun initBottomShortcuts() {
// Reading mode
with(binding.actionReadingMode) {
setTooltip(R.string.viewer)
setOnClickListener {
viewModel.openReadingModeSelectDialog()
}
}
// Crop borders
with(binding.actionCropBorders) {
setTooltip(R.string.pref_crop_borders)
setOnClickListener {
// SY -->
val mangaViewer = viewModel.getMangaReadingMode()
// SY <--
val isPagerType = ReadingModeType.isPagerType(mangaViewer)
val enabled = if (isPagerType) {
readerPreferences.cropBorders().toggle()
} else {
// SY -->
if (ReadingModeType.fromPreference(mangaViewer) == ReadingModeType.CONTINUOUS_VERTICAL) {
readerPreferences.cropBordersContinuousVertical().toggle()
} else {
readerPreferences.cropBordersWebtoon().toggle()
}
// SY <--
}
menuToggleToast?.cancel()
menuToggleToast = toast(
if (enabled) {
R.string.on
} else {
R.string.off
},
)
}
}
updateCropBordersShortcut()
listOf(readerPreferences.cropBorders(), readerPreferences.cropBordersWebtoon() /* SY --> */, readerPreferences.cropBordersContinuousVertical()/* SY <-- */)
.forEach { pref ->
pref.changes()
.onEach { updateCropBordersShortcut() }
.launchIn(lifecycleScope)
}
// Rotation
with(binding.actionRotation) {
setTooltip(R.string.rotation_type)
setOnClickListener {
viewModel.openOrientationModeSelectDialog()
}
}
// Settings sheet
with(binding.actionSettings) {
setTooltip(R.string.action_settings)
setOnClickListener {
viewModel.openSettingsDialog()
}
}
// --> EH
with(binding.actionWebView) {
setTooltip(R.string.action_open_in_web_view)
setOnClickListener {
openChapterInWebView()
}
}
with(binding.actionChapterList) {
setTooltip(R.string.chapters)
setOnClickListener {
ReaderChapterDialog(this@ReaderActivity)
}
}
with(binding.actionShare) {
setTooltip(R.string.action_share)
setOnClickListener {
assistUrl?.let {
val intent = it.toUri().toShareIntent(this@ReaderActivity, type = "text/plain")
startActivity(Intent.createChooser(intent, getString(R.string.action_share)))
}
}
}
with(binding.doublePage) {
setTooltip(R.string.page_layout)
setOnClickListener {
if (readerPreferences.pageLayout().get() == PagerConfig.PageLayout.AUTOMATIC) {
(viewModel.state.value.viewer as? PagerViewer)?.config?.let { config ->
config.doublePages = !config.doublePages
reloadChapters(config.doublePages, true)
}
updateBottomButtons()
} else {
readerPreferences.pageLayout().set(1 - readerPreferences.pageLayout().get())
}
}
}
with(binding.shiftPageButton) {
setTooltip(R.string.shift_double_pages)
setOnClickListener {
shiftDoublePages()
}
}
}
fun initDropdownMenu() {
binding.expandEhButton.setOnClickListener {
val newValue = !viewModel.state.value.ehUtilsVisible
@ -904,36 +896,6 @@ class ReaderActivity : BaseActivity() {
return currentPage?.let { viewModel.state.value.viewerChapters?.currChapter?.pages?.getOrNull(it) }
}
fun updateBottomButtons() {
val viewer = viewModel.state.value.viewer
val enabledButtons = readerPreferences.readerBottomButtons().get()
with(binding) {
actionReadingMode.isVisible = ReaderBottomButton.ReadingMode.isIn(enabledButtons)
actionRotation.isVisible =
ReaderBottomButton.Rotation.isIn(enabledButtons)
doublePage.isVisible =
viewer is PagerViewer && ReaderBottomButton.PageLayout.isIn(enabledButtons) && !readerPreferences.dualPageSplitPaged().get()
actionCropBorders.isVisible =
if (viewer is PagerViewer) {
ReaderBottomButton.CropBordersPager.isIn(enabledButtons)
} else {
val continuous = (viewer as? WebtoonViewer)?.isContinuous ?: false
if (continuous) {
ReaderBottomButton.CropBordersWebtoon.isIn(enabledButtons)
} else {
ReaderBottomButton.CropBordersContinuesVertical.isIn(enabledButtons)
}
}
actionWebView.isVisible =
ReaderBottomButton.WebView.isIn(enabledButtons) && viewModel.getSource() is HttpSource
actionShare.isVisible =
ReaderBottomButton.Share.isIn(enabledButtons) && viewModel.getSource() is HttpSource
actionChapterList.isVisible =
ReaderBottomButton.ViewChapters.isIn(enabledButtons)
shiftPageButton.isVisible = (viewer as? PagerViewer)?.config?.doublePages ?: false
}
}
fun reloadChapters(doublePages: Boolean, force: Boolean = false) {
val viewer = viewModel.state.value.viewer as? PagerViewer ?: return
viewer.updateShifting()
@ -941,6 +903,7 @@ class ReaderActivity : BaseActivity() {
setDoublePageMode(viewer)
} else {
viewer.config.doublePages = doublePages
viewModel.setDoublePages(viewer.config.doublePages)
}
val currentChapter = viewModel.getCurrentChapter()
if (doublePages) {
@ -958,6 +921,7 @@ class ReaderActivity : BaseActivity() {
private fun setDoublePageMode(viewer: PagerViewer) {
val currentOrientation = resources.configuration.orientation
viewer.config.doublePages = currentOrientation == Configuration.ORIENTATION_LANDSCAPE
viewModel.setDoublePages(viewer.config.doublePages)
}
private fun shiftDoublePages() {
@ -973,35 +937,6 @@ class ReaderActivity : BaseActivity() {
}
// EXH <--
private fun updateOrientationShortcut(preference: Int) {
val orientation = OrientationType.fromPreference(preference)
binding.actionRotation.setImageResource(orientation.iconRes)
}
private fun updateCropBordersShortcut() {
val mangaViewer = viewModel.getMangaReadingMode()
val isPagerType = ReadingModeType.isPagerType(mangaViewer)
val enabled = if (isPagerType) {
readerPreferences.cropBorders().get()
} else {
// SY -->
if (ReadingModeType.fromPreference(mangaViewer) == ReadingModeType.CONTINUOUS_VERTICAL) {
readerPreferences.cropBordersContinuousVertical().get()
} else {
readerPreferences.cropBordersWebtoon().get()
}
// SY <--
}
binding.actionCropBorders.setImageResource(
if (enabled) {
R.drawable.ic_crop_24dp
} else {
R.drawable.ic_crop_off_24dp
},
)
}
/**
* Sets the visibility of the menu according to [visible] and with an optional parameter to
* [animate] the views.
@ -1088,13 +1023,8 @@ class ReaderActivity : BaseActivity() {
*/
private fun setManga(manga: Manga) {
val prevViewer = viewModel.state.value.viewer
val viewerMode = ReadingModeType.fromPreference(viewModel.getMangaReadingMode(resolveDefault = false))
binding.actionReadingMode.setImageResource(viewerMode.iconRes)
val newViewer = ReadingModeType.toViewer(viewModel.getMangaReadingMode(), this)
updateCropBordersShortcut()
if (window.sharedElementEnterTransition is MaterialContainerTransform) {
// Wait until transition is complete to avoid crash on API 26
window.sharedElementEnterTransition.doOnEnd {
@ -1131,11 +1061,7 @@ class ReaderActivity : BaseActivity() {
}
// SY -->
// --> Vertical seekbar hide on landscape
if (
!readerPreferences.forceHorizontalSeekbar().get() &&
binding.readerNavVert.isVisible = !readerPreferences.forceHorizontalSeekbar().get() &&
(
(
resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && readerPreferences.landscapeVerticalSeekbar().get()
@ -1143,27 +1069,12 @@ class ReaderActivity : BaseActivity() {
resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
) &&
(viewModel.state.value.viewer is WebtoonViewer || viewModel.state.value.viewer is VerticalPagerViewer)
) {
binding.readerNavVert.isVisible = true
binding.readerNavHorz.isVisible = false
} else {
binding.readerNavVert.isVisible = false
binding.readerNavHorz.isVisible = true
}
// <-- Vertical seekbar hide on landscape
// --> Left-handed vertical seekbar
val params = binding.readerNavVert.layoutParams as RelativeLayout.LayoutParams
if (readerPreferences.leftVerticalSeekbar().get() && binding.readerNavVert.isVisible) {
params.removeRule(RelativeLayout.ALIGN_PARENT_END)
binding.readerNavVert.layoutParams = params
}
// <-- Left-handed vertical seekbar
updateBottomButtons()
// SY <--
supportActionBar?.title = manga.title
@ -1416,7 +1327,6 @@ class ReaderActivity : BaseActivity() {
if (newOrientation.flag != requestedOrientation) {
requestedOrientation = newOrientation.flag
}
updateOrientationShortcut(viewModel.getMangaOrientationType(resolveDefault = false))
}
/**
@ -1524,14 +1434,20 @@ class ReaderActivity : BaseActivity() {
// SY -->
readerPreferences.pageLayout().changes()
.drop(1)
.onEach { updateBottomButtons() }
.onEach {
viewModel.setDoublePages(
(viewModel.state.value.viewer as? PagerViewer)
?.config
?.doublePages
?: false,
)
}
.launchIn(lifecycleScope)
readerPreferences.dualPageSplitPaged().changes()
.drop(1)
.onEach {
if (viewModel.state.value.viewer !is PagerViewer) return@onEach
updateBottomButtons()
reloadChapters(
!it && when (readerPreferences.pageLayout().get()) {
PagerConfig.PageLayout.DOUBLE_PAGES -> true

View File

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.ui.reader
import android.app.Application
import android.content.Context
import android.net.Uri
import androidx.annotation.ColorInt
import androidx.compose.runtime.Immutable
@ -71,6 +70,7 @@ import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.runBlocking
import logcat.LogPriority
import tachiyomi.core.preference.toggle
import tachiyomi.core.util.lang.launchIO
import tachiyomi.core.util.lang.launchNonCancellable
import tachiyomi.core.util.lang.withIOContext
@ -97,8 +97,6 @@ import tachiyomi.domain.source.service.SourceManager
import tachiyomi.source.local.isLocal
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import java.util.Date
/**
@ -333,22 +331,15 @@ class ReaderViewModel(
}
// SY -->
fun getChapters(context: Context): List<ReaderChapterItem> {
fun getChapters(): List<ReaderChapterItem> {
val currentChapter = getCurrentChapter()
val decimalFormat = DecimalFormat(
"#.###",
DecimalFormatSymbols()
.apply { decimalSeparator = '.' },
)
return chapterList.map {
ReaderChapterItem(
it.chapter.toDomainChapter()!!,
manga!!,
it.chapter.id == currentChapter?.chapter?.id,
context,
UiPreferences.dateFormat(uiPreferences.dateFormat().get()),
decimalFormat,
chapter = it.chapter.toDomainChapter()!!,
manga = manga!!,
isCurrent = it.chapter.id == currentChapter?.chapter?.id,
dateFormat = UiPreferences.dateFormat(uiPreferences.dateFormat().get()),
)
}
}
@ -411,10 +402,12 @@ class ReaderViewModel(
}
}
suspend fun loadNewChapterFromDialog(chapter: Chapter) {
val newChapter = chapterList.firstOrNull { it.chapter.id == chapter.id } ?: return
fun loadNewChapterFromDialog(chapter: Chapter) {
viewModelScope.launchIO {
val newChapter = chapterList.firstOrNull { it.chapter.id == chapter.id } ?: return@launchIO
loadAdjacent(newChapter)
}
}
/**
* Called when the user is going to load the prev/next chapter through the toolbar buttons.
@ -716,7 +709,7 @@ class ReaderViewModel(
viewModelScope.launchNonCancellable {
updateChapter.await(
ChapterUpdate(
id = chapter.id!!.toLong(),
id = chapterId,
bookmark = bookmarked,
),
)
@ -804,6 +797,21 @@ class ReaderViewModel(
}
}
// SY -->
fun toggleCropBorders(): Boolean {
val readingMode = getMangaReadingMode()
val isPagerType = ReadingModeType.isPagerType(readingMode)
val isWebtoon = ReadingModeType.WEBTOON.prefValue == readingMode
return if (isPagerType) {
readerPreferences.cropBorders().toggle()
} else if (isWebtoon) {
readerPreferences.cropBordersWebtoon().toggle()
} else {
readerPreferences.cropBordersContinuousVertical().toggle()
}
}
// SY <--
/**
* Generate a filename for the given [manga] and [page]
*/
@ -834,6 +842,14 @@ class ReaderViewModel(
fun setIndexPageToShift(index: Int?) {
mutableState.update { it.copy(indexPageToShift = index) }
}
fun openChapterListDialog() {
mutableState.update { it.copy(dialog = Dialog.ChapterList) }
}
fun setDoublePages(doublePages: Boolean) {
mutableState.update { it.copy(doublePages = doublePages) }
}
// SY <--
fun showLoadingDialog() {
@ -1158,6 +1174,7 @@ class ReaderViewModel(
val lastShiftDoubleState: Boolean? = null,
val indexPageToShift: Int? = null,
val indexChapterToShift: Long? = null,
val doublePages: Boolean = false,
// SY <--
) {
val totalPages: Int
@ -1169,6 +1186,11 @@ class ReaderViewModel(
data object Settings : Dialog
data object ReadingModeSelect : Dialog
data object OrientationModeSelect : Dialog
// SY -->
data object ChapterList : Dialog
// SY <--
data class PageActions(val page: ReaderPage/* SY --> */, val extraPage: ReaderPage? = null /* SY <-- */) : Dialog
}

View File

@ -1,21 +0,0 @@
package eu.kanade.tachiyomi.ui.reader.chapter
import eu.davidea.flexibleadapter.FlexibleAdapter
import tachiyomi.domain.chapter.model.Chapter
class ReaderChapterAdapter(
dialog: OnBookmarkClickListener,
) : FlexibleAdapter<ReaderChapterItem>(null, dialog, true) {
/**
* Listener for browse item clicks.
*/
val clickListener: OnBookmarkClickListener = dialog
/**
* Listener which should be called when user clicks the download icons.
*/
interface OnBookmarkClickListener {
fun bookmarkChapter(chapter: Chapter)
}
}

View File

@ -1,80 +0,0 @@
package eu.kanade.tachiyomi.ui.reader.chapter
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.ReaderChaptersDialogBinding
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.ui.reader.ReaderViewModel
import eu.kanade.tachiyomi.util.system.dpToPx
import kotlinx.coroutines.launch
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.chapter.service.getChapterSort
class ReaderChapterDialog(private val activity: ReaderActivity) : ReaderChapterAdapter.OnBookmarkClickListener {
private val binding = ReaderChaptersDialogBinding.inflate(activity.layoutInflater)
var viewModel: ReaderViewModel = activity.viewModel
var adapter: FlexibleAdapter<ReaderChapterItem>? = null
var dialog: AlertDialog
init {
dialog = MaterialAlertDialogBuilder(activity)
.setTitle(R.string.chapters)
.setView(binding.root)
.setNegativeButton(android.R.string.cancel, null)
.setOnDismissListener { destroy() }
.create()
adapter = ReaderChapterAdapter(this@ReaderChapterDialog)
binding.chapterRecycler.adapter = adapter
adapter?.mItemClickListener = FlexibleAdapter.OnItemClickListener { _, position ->
val item = adapter?.getItem(position)
if (item != null && item.chapter.id != viewModel.getCurrentChapter()?.chapter?.id) {
dialog.dismiss()
activity.lifecycleScope.launch {
viewModel.loadNewChapterFromDialog(item.chapter)
}
}
true
}
binding.chapterRecycler.layoutManager = LinearLayoutManager(activity)
activity.lifecycleScope.launch {
refreshList()
}
dialog.show()
}
private fun refreshList(scroll: Boolean = true) {
val chapterSort = getChapterSort(viewModel.manga!!)
val chapters = viewModel.getChapters(activity)
.sortedWith { a, b ->
chapterSort(a.chapter, b.chapter)
}
adapter?.clear()
adapter?.updateDataSet(chapters)
if (scroll) {
(binding.chapterRecycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(
adapter?.getGlobalPositionOf(chapters.find { it.isCurrent }) ?: 0,
(binding.chapterRecycler.height / 2).dpToPx,
)
}
}
fun destroy() {
adapter = null
}
override fun bookmarkChapter(chapter: Chapter) {
viewModel.toggleBookmark(chapter.id, !chapter.bookmark)
refreshList(scroll = false)
}
}

View File

@ -1,133 +1,12 @@
package eu.kanade.tachiyomi.ui.reader.chapter
import android.content.Context
import android.graphics.Typeface
import android.text.SpannableStringBuilder
import android.view.View
import android.widget.ImageView
import androidx.annotation.AttrRes
import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources
import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.ReaderChapterItemBinding
import eu.kanade.tachiyomi.util.system.getResourceColor
import exh.source.isEhBasedManga
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.manga.model.Manga
import java.text.DateFormat
import java.text.DecimalFormat
import java.util.Date
class ReaderChapterItem(val chapter: Chapter, val manga: Manga, val isCurrent: Boolean, context: Context, val dateFormat: DateFormat, val decimalFormat: DecimalFormat) :
AbstractFlexibleItem<ReaderChapterItem.ViewHolder>() {
val readColor = context.getResourceColor(R.attr.colorOnSurface, 0.38f)
val unreadColor = context.getResourceColor(R.attr.colorOnSurface)
val bookmarkedColor = context.getResourceColor(R.attr.colorAccent)
override fun getLayoutRes(): Int {
return R.layout.reader_chapter_item
}
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): ViewHolder {
return ViewHolder(view, adapter as ReaderChapterAdapter)
}
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
holder: ViewHolder,
position: Int,
payloads: List<Any?>?,
) {
holder.bind(this)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ReaderChapterItem
if (chapter.id != other.chapter.id) return false
return true
}
override fun hashCode(): Int {
return chapter.id.hashCode()
}
inner class ViewHolder(view: View, private val adapter: ReaderChapterAdapter) : FlexibleViewHolder(view, adapter) {
val binding = ReaderChapterItemBinding.bind(itemView)
fun bind(item: ReaderChapterItem) {
val manga = item.manga
val chapter = item.chapter
binding.chapterTitle.text = when (manga.displayMode) {
Manga.CHAPTER_DISPLAY_NUMBER -> {
val number = item.decimalFormat.format(chapter.chapterNumber.toDouble())
itemView.context.getString(R.string.display_mode_chapter, number)
}
else -> chapter.name
}
// Set correct text color
val chapterColor = when {
chapter.read -> item.readColor
chapter.bookmark -> item.bookmarkedColor
else -> item.unreadColor
}
binding.chapterTitle.setTextColor(chapterColor)
binding.chapterScanlator.setTextColor(chapterColor)
// bookmarkImage.isVisible = item.bookmark
val descriptions = mutableListOf<CharSequence>()
if (chapter.dateUpload > 0) {
descriptions.add(item.dateFormat.format(Date(chapter.dateUpload)))
}
if (!chapter.scanlator.isNullOrBlank() && !manga.isEhBasedManga()) {
descriptions.add(chapter.scanlator!!)
}
if (descriptions.isNotEmpty()) {
binding.chapterScanlator.text = descriptions.joinTo(SpannableStringBuilder(), "")
} else {
binding.chapterScanlator.text = ""
}
if (chapter.bookmark) {
binding.bookmarkImage.setVectorCompat(R.drawable.ic_bookmark_24dp, R.attr.colorAccent)
} else {
binding.bookmarkImage.setVectorCompat(R.drawable.ic_bookmark_border_24dp, R.attr.colorOnSurface)
}
if (item.isCurrent) {
binding.chapterTitle.setTypeface(null, Typeface.BOLD_ITALIC)
binding.chapterScanlator.setTypeface(null, Typeface.BOLD_ITALIC)
} else {
binding.chapterTitle.setTypeface(null, Typeface.NORMAL)
binding.chapterScanlator.setTypeface(null, Typeface.NORMAL)
}
binding.bookmarkLayout.setOnClickListener {
adapter.clickListener.bookmarkChapter(chapter)
}
}
}
}
fun ImageView.setVectorCompat(@DrawableRes drawable: Int, @AttrRes tint: Int? = null) {
val vector = AppCompatResources.getDrawable(context, drawable)
if (tint != null) {
vector?.mutate()
vector?.setTint(context.getResourceColor(tint))
}
setImageDrawable(vector)
}
data class ReaderChapterItem(
val chapter: Chapter,
val manga: Manga,
val isCurrent: Boolean,
val dateFormat: DateFormat,
)

View File

@ -2,10 +2,8 @@
package eu.kanade.tachiyomi.util.view
import android.content.Context
import android.content.res.Resources
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.view.Gravity
import android.view.Menu
import android.view.MenuItem
@ -13,9 +11,7 @@ import android.view.View
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.annotation.MenuRes
import androidx.annotation.StringRes
import androidx.appcompat.widget.PopupMenu
import androidx.appcompat.widget.TooltipCompat
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
@ -24,7 +20,6 @@ import androidx.compose.runtime.CompositionContext
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import com.google.android.material.shape.MaterialShapeDrawable
import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.tachiyomi.R
@ -60,24 +55,6 @@ fun ComposeView.setComposeContent(
}
}
/**
* Adds a tooltip shown on long press.
*
* @param stringRes String resource for tooltip.
*/
inline fun View.setTooltip(@StringRes stringRes: Int) {
setTooltip(context.getString(stringRes))
}
/**
* Adds a tooltip shown on long press.
*
* @param text Text for tooltip.
*/
inline fun View.setTooltip(text: String) {
TooltipCompat.setTooltipText(this, text)
}
/**
* Shows a popup menu on top of this view.
*
@ -105,17 +82,6 @@ inline fun View.popupMenu(
return popup
}
/**
* Returns a deep copy of the provided [Drawable]
*/
inline fun <reified T : Drawable> T.copy(context: Context): T? {
return (constantState?.newDrawable()?.mutate() as? T).apply {
if (this is MaterialShapeDrawable) {
initializeElevationOverlay(context)
}
}
}
fun View?.isVisibleOnScreen(): Boolean {
if (this == null) {
return false

View File

@ -1,13 +1,19 @@
package exh.ui.metadata.adapters
import android.content.Context
import android.graphics.Color
import android.widget.TextView
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.annotation.FloatRange
import androidx.core.content.ContextCompat
import androidx.core.graphics.alpha
import androidx.core.graphics.blue
import androidx.core.graphics.green
import androidx.core.graphics.red
import eu.kanade.tachiyomi.source.R
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.getResourceColor
import exh.util.SourceTagsUtil
import kotlin.math.roundToInt
@ -56,4 +62,24 @@ object MetadataUIUtil {
setCompoundDrawables(this, null, null, null)
}
}
/**
* Returns the color for the given attribute.
*
* @param resource the attribute.
* @param alphaFactor the alpha number [0,1].
*/
@ColorInt
fun Context.getResourceColor(@AttrRes resource: Int, alphaFactor: Float = 1f): Int {
val typedArray = obtainStyledAttributes(intArrayOf(resource))
val color = typedArray.getColor(0, 0)
typedArray.recycle()
if (alphaFactor < 1f) {
val alpha = (color.alpha * alphaFactor).roundToInt()
return Color.argb(alpha, color.red, color.green, color.blue)
}
return color
}
}

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="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98 0,-0.34 -0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.09,-0.16 -0.26,-0.25 -0.44,-0.25 -0.06,0 -0.12,0.01 -0.17,0.03l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.06,-0.02 -0.12,-0.03 -0.18,-0.03 -0.17,0 -0.34,0.09 -0.43,0.25l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98 0,0.33 0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.09,0.16 0.26,0.25 0.44,0.25 0.06,0 0.12,-0.01 0.17,-0.03l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.06,0.02 0.12,0.03 0.18,0.03 0.17,0 0.34,-0.09 0.43,-0.25l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM17.45,11.27c0.04,0.31 0.05,0.52 0.05,0.73 0,0.21 -0.02,0.43 -0.05,0.73l-0.14,1.13 0.89,0.7 1.08,0.84 -0.7,1.21 -1.27,-0.51 -1.04,-0.42 -0.9,0.68c-0.43,0.32 -0.84,0.56 -1.25,0.73l-1.06,0.43 -0.16,1.13 -0.2,1.35h-1.4l-0.19,-1.35 -0.16,-1.13 -1.06,-0.43c-0.43,-0.18 -0.83,-0.41 -1.23,-0.71l-0.91,-0.7 -1.06,0.43 -1.27,0.51 -0.7,-1.21 1.08,-0.84 0.89,-0.7 -0.14,-1.13c-0.03,-0.31 -0.05,-0.54 -0.05,-0.74s0.02,-0.43 0.05,-0.73l0.14,-1.13 -0.89,-0.7 -1.08,-0.84 0.7,-1.21 1.27,0.51 1.04,0.42 0.9,-0.68c0.43,-0.32 0.84,-0.56 1.25,-0.73l1.06,-0.43 0.16,-1.13 0.2,-1.35h1.39l0.19,1.35 0.16,1.13 1.06,0.43c0.43,0.18 0.83,0.41 1.23,0.71l0.91,0.7 1.06,-0.43 1.27,-0.51 0.7,1.21 -1.07,0.85 -0.89,0.7 0.14,1.13zM12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM12,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z" />
</vector>

View File

@ -218,133 +218,15 @@
app:layout_constraintGuide_percent="0.85"
app:layout_constraintBottom_toTopOf="@id/reader_menu_bottom"/>
<LinearLayout
<androidx.compose.ui.platform.ComposeView
android:id="@+id/reader_menu_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:orientation="vertical"
android:minHeight="52dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<androidx.compose.ui.platform.ComposeView
android:id="@+id/reader_nav_horz"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layoutDirection="ltr" />
<LinearLayout
android:id="@+id/toolbar_bottom"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="bottom"
android:clickable="true"
tools:ignore="KeyboardInaccessibleWidget">
<ImageButton
android:id="@+id/action_chapter_list"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/chapters"
android:padding="@dimen/screen_edge_margin"
app:srcCompat="@drawable/ic_format_list_numbered_24dp"
app:tint="?attr/colorOnSurface" />
<ImageButton
android:id="@+id/action_web_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/action_open_in_web_view"
android:padding="@dimen/screen_edge_margin"
android:layout_weight="1"
app:srcCompat="@drawable/ic_webview_24dp"
app:tint="?attr/colorOnSurface" />
<ImageButton
android:id="@+id/action_share"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/action_share"
android:padding="@dimen/screen_edge_margin"
android:layout_weight="1"
app:srcCompat="@drawable/ic_share_24dp"
app:tint="?attr/colorOnSurface" />
<ImageButton
android:id="@+id/action_reading_mode"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/viewer"
android:padding="@dimen/screen_edge_margin"
android:layout_weight="1"
app:srcCompat="@drawable/ic_reader_default_24dp"
app:tint="?attr/colorOnSurface" />
<ImageButton
android:id="@+id/action_crop_borders"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/pref_crop_borders"
android:padding="@dimen/screen_edge_margin"
android:layout_weight="1"
app:srcCompat="@drawable/ic_crop_24dp"
app:tint="?attr/colorOnSurface" />
<ImageButton
android:id="@+id/action_rotation"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/pref_rotation_type"
android:padding="@dimen/screen_edge_margin"
android:layout_weight="1"
app:srcCompat="@drawable/ic_screen_rotation_24dp"
app:tint="?attr/colorOnSurface" />
<ImageButton
android:id="@+id/double_page"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/page_layout"
android:padding="@dimen/screen_edge_margin"
android:layout_weight="1"
app:srcCompat="@drawable/ic_book_open_variant_24dp"
app:tint="?attr/colorOnSurface" />
<ImageButton
android:id="@+id/shift_page_button"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/shift_double_pages"
android:padding="@dimen/screen_edge_margin"
android:layout_weight="1"
app:srcCompat="@drawable/ic_page_next_outline_24dp"
app:tint="?attr/colorOnSurface" />
<ImageButton
android:id="@+id/action_settings"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/action_settings"
android:padding="@dimen/screen_edge_margin"
android:layout_weight="1"
app:srcCompat="@drawable/ic_settings_24dp"
app:tint="?attr/colorOnSurface" />
</LinearLayout>
</LinearLayout>
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>