Migrate top reader app bar to Compose
This commit is contained in:
parent
5619a4c0d9
commit
d18022c259
12 changed files with 149 additions and 231 deletions
|
@ -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,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
@ -10,11 +9,10 @@ import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Settings
|
import androidx.compose.material.icons.outlined.Settings
|
||||||
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
|
||||||
|
@ -24,6 +22,7 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BottomReaderBar(
|
fun BottomReaderBar(
|
||||||
|
backgroundColor: Color,
|
||||||
readingMode: ReadingModeType,
|
readingMode: ReadingModeType,
|
||||||
onClickReadingMode: () -> Unit,
|
onClickReadingMode: () -> Unit,
|
||||||
orientationMode: OrientationType,
|
orientationMode: OrientationType,
|
||||||
|
@ -32,11 +31,6 @@ fun BottomReaderBar(
|
||||||
onClickCropBorder: () -> Unit,
|
onClickCropBorder: () -> Unit,
|
||||||
onClickSettings: () -> Unit,
|
onClickSettings: () -> Unit,
|
||||||
) {
|
) {
|
||||||
// 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()
|
||||||
|
|
|
@ -4,14 +4,28 @@ import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.slideInVertically
|
import androidx.compose.animation.slideInVertically
|
||||||
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.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.fillMaxHeight
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.systemBars
|
||||||
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Bookmark
|
||||||
|
import androidx.compose.material.icons.outlined.BookmarkBorder
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.surfaceColorAtElevation
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.IntOffset
|
import androidx.compose.ui.unit.IntOffset
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import eu.kanade.presentation.components.AppBar
|
||||||
|
import eu.kanade.presentation.components.AppBarActions
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
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
|
||||||
|
@ -22,8 +36,18 @@ private val animationSpec = tween<IntOffset>(200)
|
||||||
@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,
|
||||||
|
@ -41,11 +65,64 @@ fun ReaderAppBars(
|
||||||
onClickSettings: () -> Unit,
|
onClickSettings: () -> 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) {
|
||||||
|
Modifier.windowInsetsPadding(WindowInsets.systemBars)
|
||||||
|
} else {
|
||||||
|
Modifier
|
||||||
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
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,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
AppBar(
|
||||||
|
modifier = appBarModifier
|
||||||
|
.clickable(onClick = onClickTopAppBar),
|
||||||
|
backgroundColor = backgroundColor,
|
||||||
|
title = mangaTitle,
|
||||||
|
subtitle = chapterTitle,
|
||||||
|
navigateUp = navigateUp,
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
|
@ -74,6 +151,7 @@ fun ReaderAppBars(
|
||||||
)
|
)
|
||||||
|
|
||||||
BottomReaderBar(
|
BottomReaderBar(
|
||||||
|
backgroundColor = backgroundColor,
|
||||||
readingMode = readingMode,
|
readingMode = readingMode,
|
||||||
onClickReadingMode = onClickReadingMode,
|
onClickReadingMode = onClickReadingMode,
|
||||||
orientationMode = orientationMode,
|
orientationMode = orientationMode,
|
||||||
|
|
|
@ -13,15 +13,10 @@ 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.TextUtils
|
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.view.Menu
|
|
||||||
import android.view.MenuItem
|
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
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.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
|
||||||
|
@ -44,8 +39,7 @@ import androidx.core.view.WindowInsetsControllerCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||||
import com.google.android.material.internal.ToolbarUtils
|
import com.google.android.material.elevation.SurfaceColors
|
||||||
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
|
||||||
|
@ -74,13 +68,11 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
|
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
|
||||||
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressIndicator
|
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressIndicator
|
||||||
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 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
|
||||||
|
@ -265,47 +257,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.
|
||||||
|
@ -348,35 +299,12 @@ class ReaderActivity : BaseActivity() {
|
||||||
* 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.toolbar.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()
|
||||||
|
@ -400,6 +328,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()
|
||||||
val isPagerType = ReadingModeType.isPagerType(viewModel.getMangaReadingMode())
|
val isPagerType = ReadingModeType.isPagerType(viewModel.getMangaReadingMode())
|
||||||
|
@ -407,8 +338,18 @@ class ReaderActivity : BaseActivity() {
|
||||||
|
|
||||||
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,
|
||||||
|
@ -435,15 +376,8 @@ 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,
|
||||||
)
|
)
|
||||||
|
@ -507,13 +441,9 @@ class ReaderActivity : BaseActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
@ -531,35 +461,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)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
binding.toolbar.startAnimation(toolbarAnimation)
|
|
||||||
} 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
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
binding.toolbar.startAnimation(toolbarAnimation)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,14 +500,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
|
||||||
|
@ -610,6 +527,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()
|
||||||
|
@ -629,15 +553,6 @@ class ReaderActivity : BaseActivity() {
|
||||||
binding.readerContainer.removeView(loadingIndicator)
|
binding.readerContainer.removeView(loadingIndicator)
|
||||||
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
|
||||||
|
@ -675,7 +590,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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -298,7 +298,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
|
||||||
|
@ -567,8 +570,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
|
||||||
|
@ -588,9 +591,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(
|
||||||
|
@ -599,6 +604,12 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutableState.update {
|
||||||
|
it.copy(
|
||||||
|
bookmarked = bookmarked,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -873,6 +884,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,
|
||||||
|
|
||||||
|
@ -883,8 +895,11 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||||
val dialog: Dialog? = null,
|
val dialog: Dialog? = null,
|
||||||
val menuVisible: Boolean = false,
|
val menuVisible: Boolean = false,
|
||||||
) {
|
) {
|
||||||
|
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 {
|
||||||
|
|
|
@ -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) {}
|
|
||||||
}
|
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -1,5 +1,4 @@
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
@ -42,22 +41,6 @@
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
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.MaterialToolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:minHeight="?attr/actionBarSize" />
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
<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"
|
||||||
|
|
|
@ -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>
|
|
Reference in a new issue