Refactor reader bottom bar to presentation package

(cherry picked from commit 7c012010558d5e7a2ea32a8aaef3c908fdc52799)

# 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-10-12 22:42:49 -04:00 committed by Jobobby04
parent 6e3dc97458
commit 7693076c89
5 changed files with 323 additions and 212 deletions

View File

@ -1,4 +1,4 @@
package eu.kanade.presentation.reader
package eu.kanade.presentation.reader.appbars
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme

View File

@ -1,4 +1,4 @@
package eu.kanade.presentation.reader
package eu.kanade.presentation.reader.appbars
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource

View File

@ -0,0 +1,211 @@
package eu.kanade.presentation.reader.appbars
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import eu.kanade.tachiyomi.ui.reader.viewer.Viewer
import eu.kanade.tachiyomi.ui.reader.viewer.pager.R2LPagerViewer
private val animationSpec = tween<IntOffset>(200)
// SY -->
enum class NavBarType {
VerticalRight,
VerticalLeft,
Bottom,
}
@Composable
fun BoxIgnoreLayoutDirection(modifier: Modifier, content: @Composable BoxScope.() -> Unit) {
val layoutDirection = LocalLayoutDirection.current
CompositionLocalProvider(
LocalLayoutDirection provides LayoutDirection.Ltr
) {
Box(modifier) {
CompositionLocalProvider(
LocalLayoutDirection provides layoutDirection
) {
content()
}
}
}
}
// SY <--
@Composable
fun ReaderAppBars(
visible: Boolean,
viewer: Viewer?,
onNextChapter: () -> Unit,
enabledNext: Boolean,
onPreviousChapter: () -> Unit,
enabledPrevious: Boolean,
currentPage: Int,
totalPages: Int,
onSliderValueChange: (Int) -> Unit,
readingMode: ReadingModeType,
onClickReadingMode: () -> Unit,
orientationMode: OrientationType,
onClickOrientationMode: () -> Unit,
cropEnabled: Boolean,
onClickCropBorder: () -> Unit,
onClickSettings: () -> Unit,
// SY -->
navBarType: NavBarType,
currentPageText: String,
enabledButtons: Set<String>,
isHttpSource: Boolean,
dualPageSplitEnabled: Boolean,
doublePages: Boolean,
onClickChapterList: () -> Unit,
onClickWebView: () -> Unit,
onClickShare: () -> Unit,
onClickPageLayout: () -> Unit,
onClickShiftPage: () -> Unit,
) {
val isRtl = viewer is R2LPagerViewer
// SY -->
BoxIgnoreLayoutDirection(
Modifier.fillMaxWidth()
) {
AnimatedVisibility(
visible = visible && navBarType == NavBarType.VerticalLeft,
enter = slideInHorizontally(
initialOffsetX = { -it },
animationSpec = animationSpec,
),
exit = slideOutHorizontally(
targetOffsetX = { -it },
animationSpec = animationSpec,
),
modifier = Modifier.padding(bottom = 48.dp, top = 120.dp)
.align(Alignment.TopStart)
) {
ChapterNavigator(
isRtl = isRtl,
onNextChapter = onNextChapter,
enabledNext = enabledNext,
onPreviousChapter = onPreviousChapter,
enabledPrevious = enabledPrevious,
currentPage = currentPage,
totalPages = totalPages,
onSliderValueChange = onSliderValueChange,
isVerticalSlider = true,
currentPageText = currentPageText,
)
}
AnimatedVisibility(
visible = visible && navBarType == NavBarType.VerticalRight,
enter = slideInHorizontally(
initialOffsetX = { it },
animationSpec = animationSpec,
),
exit = slideOutHorizontally(
targetOffsetX = { it },
animationSpec = animationSpec,
),
modifier = Modifier.padding(bottom = 48.dp, top = 120.dp)
.align(Alignment.TopEnd)
) {
ChapterNavigator(
isRtl = isRtl,
onNextChapter = onNextChapter,
enabledNext = enabledNext,
onPreviousChapter = onPreviousChapter,
enabledPrevious = enabledPrevious,
currentPage = currentPage,
totalPages = totalPages,
onSliderValueChange = onSliderValueChange,
isVerticalSlider = true,
currentPageText = currentPageText,
)
}
// SY <--
Column(
modifier = Modifier.fillMaxHeight(),
verticalArrangement = Arrangement.SpaceBetween,
) {
Spacer(modifier = Modifier.weight(1f))
AnimatedVisibility(
visible = visible,
enter = slideInVertically(
initialOffsetY = { it },
animationSpec = animationSpec,
),
exit = slideOutVertically(
targetOffsetY = { it },
animationSpec = animationSpec,
),
) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
if (navBarType == NavBarType.Bottom) {
ChapterNavigator(
isRtl = isRtl,
onNextChapter = onNextChapter,
enabledNext = enabledNext,
onPreviousChapter = onPreviousChapter,
enabledPrevious = enabledPrevious,
currentPage = currentPage,
totalPages = totalPages,
onSliderValueChange = onSliderValueChange,
isVerticalSlider = false,
currentPageText = currentPageText,
)
}
BottomReaderBar(
// SY -->
enabledButtons = enabledButtons,
// SY <--
readingMode = readingMode,
onClickReadingMode = onClickReadingMode,
orientationMode = orientationMode,
onClickOrientationMode = onClickOrientationMode,
cropEnabled = cropEnabled,
onClickCropBorder = onClickCropBorder,
onClickSettings = onClickSettings,
// SY -->
isHttpSource = isHttpSource,
dualPageSplitEnabled = dualPageSplitEnabled,
doublePages = doublePages,
onClickChapterList = onClickChapterList,
onClickWebView = onClickWebView,
onClickShare = onClickShare,
onClickPageLayout = onClickPageLayout,
onClickShiftPage = onClickShiftPage
)
}
}
}
}
}

View File

@ -27,21 +27,14 @@ import android.view.WindowManager
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.widget.CompoundButton
import android.widget.RelativeLayout
import android.widget.TextView
import android.widget.Toast
import androidx.activity.viewModels
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
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
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
@ -69,13 +62,13 @@ 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
import eu.kanade.presentation.reader.ReaderPageActionsDialog
import eu.kanade.presentation.reader.ReadingModeSelectDialog
import eu.kanade.presentation.reader.appbars.NavBarType
import eu.kanade.presentation.reader.appbars.ReaderAppBars
import eu.kanade.presentation.reader.settings.ReaderSettingsDialog
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
@ -98,7 +91,6 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressIndicator
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerViewer
import eu.kanade.tachiyomi.ui.reader.viewer.pager.R2LPagerViewer
import eu.kanade.tachiyomi.ui.reader.viewer.pager.VerticalPagerViewer
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
@ -458,9 +450,9 @@ class ReaderActivity : BaseActivity() {
margin(top = true, horizontal = true)
}
}
binding.readerMenuBottom.applyInsetter {
binding.dialogRoot.applyInsetter {
type(navigationBars = true) {
margin(bottom = true, horizontal = true)
margin(vertical = true, horizontal = true)
}
}
@ -501,6 +493,112 @@ class ReaderActivity : BaseActivity() {
)
}
val cropBorderPaged by readerPreferences.cropBorders().collectAsState()
val cropBorderWebtoon by readerPreferences.cropBordersWebtoon().collectAsState()
// SY -->
val readingMode = viewModel.getMangaReadingMode()
val isPagerType = ReadingModeType.isPagerType(readingMode)
val isWebtoon = ReadingModeType.WEBTOON.flagValue == readingMode
val cropBorderContinuousVertical by readerPreferences.cropBordersContinuousVertical().collectAsState()
val cropEnabled = if (isPagerType) {
cropBorderPaged
} else if (isWebtoon) {
cropBorderWebtoon
} else {
cropBorderContinuousVertical
}
val readerBottomButtons by readerPreferences.readerBottomButtons().collectAsState()
val dualPageSplitPaged by readerPreferences.dualPageSplitPaged().collectAsState()
val forceHorizontalSeekbar by readerPreferences.forceHorizontalSeekbar().collectAsState()
val landscapeVerticalSeekbar by readerPreferences.landscapeVerticalSeekbar().collectAsState()
val leftHandedVerticalSeekbar by readerPreferences.leftVerticalSeekbar().collectAsState()
val configuration = LocalConfiguration.current
val verticalSeekbarLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && landscapeVerticalSeekbar
val verticalSeekbarHorizontal = configuration.orientation == Configuration.ORIENTATION_PORTRAIT
val viewerIsVertical = (state.viewer is WebtoonViewer || state.viewer is VerticalPagerViewer)
val showVerticalSeekbar = !forceHorizontalSeekbar && (verticalSeekbarLandscape || verticalSeekbarHorizontal) && viewerIsVertical
val navBarType = when {
!showVerticalSeekbar -> NavBarType.Bottom
leftHandedVerticalSeekbar -> NavBarType.VerticalLeft
else -> NavBarType.VerticalRight
}
// SY <--
ReaderAppBars(
visible = state.menuVisible,
viewer = state.viewer,
onNextChapter = ::loadNextChapter,
enabledNext = state.viewerChapters?.nextChapter != null,
onPreviousChapter = ::loadPreviousChapter,
enabledPrevious = state.viewerChapters?.prevChapter != null,
currentPage = state.currentPage,
totalPages = state.totalPages,
onSliderValueChange = {
isScrollingThroughPages = true
moveToPageIndex(it)
},
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 -->
currentPageText = state.currentPageText,
navBarType = navBarType,
enabledButtons = readerBottomButtons,
isHttpSource = remember {
viewModel.getSource() != null
},
dualPageSplitEnabled = dualPageSplitPaged,
doublePages = state.doublePages,
onClickChapterList = viewModel::openChapterListDialog,
onClickWebView = ::openChapterInWebView,
onClickShare = {
assistUrl?.let {
val intent = it.toUri().toShareIntent(this@ReaderActivity, type = "text/plain")
startActivity(Intent.createChooser(intent, getString(R.string.action_share)))
}
},
onClickPageLayout = {
if (readerPreferences.pageLayout().get() == PagerConfig.PageLayout.AUTOMATIC) {
(viewModel.state.value.viewer as? PagerViewer)?.config?.let { config ->
config.doublePages = !config.doublePages
reloadChapters(config.doublePages, true)
}
} else {
readerPreferences.pageLayout().set(1 - readerPreferences.pageLayout().get())
}
},
onClickShiftPage = ::shiftDoublePages,
// SY <--
)
val onDismissRequest = viewModel::closeDialog
when (state.dialog) {
is ReaderViewModel.Dialog.Loading -> {
@ -589,141 +687,6 @@ 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(
isRtl = isRtl,
// SY -->
isVerticalSlider = isVertical,
// SY <--
onNextChapter = ::loadNextChapter,
enabledNext = state.viewerChapters?.nextChapter != null,
onPreviousChapter = ::loadPreviousChapter,
enabledPrevious = state.viewerChapters?.prevChapter != null,
currentPage = state.currentPage,
// SY -->
currentPageText = state.currentPageText,
// SY <--
totalPages = state.totalPages,
onSliderValueChange = {
isScrollingThroughPages = true
moveToPageIndex(it)
},
)
}
// Init listeners on bottom menu
binding.readerNavVert.setComposeContent {
sliderContent(true)
}
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.flagValue == readingMode
val cropEnabled = if (isPagerType) {
cropBorderPaged
} else if (isWebtoon) {
cropBorderWebtoon
} else {
cropBorderContinuousVertical
}
val readerBottomButtons by readerPreferences.readerBottomButtons().collectAsState()
val dualPageSplitPaged by readerPreferences.dualPageSplitPaged().collectAsState()
AnimatedVisibility(
visible = state.menuVisible,
enter = slideInVertically(
initialOffsetY = { it },
animationSpec = tween(200),
),
exit = slideOutVertically(
targetOffsetY = { it },
animationSpec = tween(200),
),
) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
// SY -->
sliderContent(false)
// SY <--
BottomReaderBar(
// SY -->
enabledButtons = readerBottomButtons,
// SY <--
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 <--
@ -1038,22 +1001,6 @@ class ReaderActivity : BaseActivity() {
showReadingModeToast(viewModel.getMangaReadingMode())
}
// SY -->
binding.readerNavVert.isVisible = !readerPreferences.forceHorizontalSeekbar().get() &&
(
(
resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && readerPreferences.landscapeVerticalSeekbar().get()
) ||
resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
) &&
(viewModel.state.value.viewer is WebtoonViewer || viewModel.state.value.viewer is VerticalPagerViewer)
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
}
// SY <--
supportActionBar?.title = manga.title
loadingIndicator = ReaderProgressIndicator(this)

View File

@ -181,53 +181,6 @@
</com.google.android.material.appbar.AppBarLayout>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/above_guideline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.3"
app:layout_constraintTop_toBottomOf="@id/header"/>
<RelativeLayout
android:id="@+id/seekbar_vert_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal"
app:layout_constraintBottom_toTopOf="@id/below_guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/above_guideline"
tools:ignore="NotSibling">
<androidx.compose.ui.platform.ComposeView
android:id="@+id/reader_nav_vert"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:gravity="center"/>
</RelativeLayout>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/below_guideline"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.85"
app:layout_constraintBottom_toTopOf="@id/reader_menu_bottom"/>
<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:minHeight="52dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.compose.ui.platform.ComposeView