mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-11-20 19:29:15 +01:00
Merge branch 'master' into sync-part-1
This commit is contained in:
commit
1af1ce924d
13
.github/renovate.json
vendored
13
.github/renovate.json
vendored
@ -1,13 +0,0 @@
|
||||
{
|
||||
"extends": [
|
||||
"config:base"
|
||||
],
|
||||
"schedule": ["every sunday"],
|
||||
"packageRules": [
|
||||
{
|
||||
"managers": ["maven"],
|
||||
"packageNames": ["com.google.guava:guava"],
|
||||
"versionScheme": "docker"
|
||||
}
|
||||
]
|
||||
}
|
22
.github/renovate.json5
vendored
Normal file
22
.github/renovate.json5
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:base"
|
||||
],
|
||||
"schedule": ["every sunday"],
|
||||
"packageRules": [
|
||||
{
|
||||
"managers": ["maven"],
|
||||
"packageNames": ["com.google.guava:guava"],
|
||||
"versionScheme": "docker"
|
||||
},
|
||||
{
|
||||
// Compiler plugins are tightly coupled to Kotlin version
|
||||
"groupName": "Kotlin",
|
||||
"matchPackagePrefixes": [
|
||||
"androidx.compose.compiler",
|
||||
"org.jetbrains.kotlin",
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
@ -22,8 +22,8 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "eu.kanade.tachiyomi"
|
||||
|
||||
versionCode = 103
|
||||
|
||||
versionCode = 104
|
||||
versionName = "0.14.6"
|
||||
|
||||
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||
@ -239,6 +239,7 @@ dependencies {
|
||||
implementation(libs.bundles.voyager)
|
||||
implementation(libs.compose.materialmotion)
|
||||
implementation(libs.compose.simpleicons)
|
||||
implementation(libs.swipe)
|
||||
|
||||
// Logging
|
||||
implementation(libs.logcat)
|
||||
|
@ -16,6 +16,7 @@ import eu.kanade.domain.source.interactor.SetMigrateSorting
|
||||
import eu.kanade.domain.source.interactor.ToggleLanguage
|
||||
import eu.kanade.domain.source.interactor.ToggleSource
|
||||
import eu.kanade.domain.source.interactor.ToggleSourcePin
|
||||
import eu.kanade.domain.track.interactor.TrackChapter
|
||||
import tachiyomi.data.category.CategoryRepositoryImpl
|
||||
import tachiyomi.data.chapter.ChapterRepositoryImpl
|
||||
import tachiyomi.data.history.HistoryRepositoryImpl
|
||||
@ -109,6 +110,7 @@ class DomainModule : InjektModule {
|
||||
addFactory { GetApplicationRelease(get(), get()) }
|
||||
|
||||
addSingletonFactory<TrackRepository> { TrackRepositoryImpl(get()) }
|
||||
addFactory { TrackChapter(get(), get(), get(), get()) }
|
||||
addFactory { DeleteTrack(get()) }
|
||||
addFactory { GetTracksPerManga(get()) }
|
||||
addFactory { GetTracks(get()) }
|
||||
|
@ -7,9 +7,9 @@ import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
|
||||
import tachiyomi.core.metadata.comicinfo.ComicInfo
|
||||
import tachiyomi.core.metadata.comicinfo.ComicInfoPublishingStatus
|
||||
import tachiyomi.core.preference.TriState
|
||||
import tachiyomi.domain.chapter.model.Chapter
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.manga.model.TriStateFilter
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
@ -20,19 +20,19 @@ val Manga.readingModeType: Long
|
||||
val Manga.orientationType: Long
|
||||
get() = viewerFlags and OrientationType.MASK.toLong()
|
||||
|
||||
val Manga.downloadedFilter: TriStateFilter
|
||||
val Manga.downloadedFilter: TriState
|
||||
get() {
|
||||
if (forceDownloaded()) return TriStateFilter.ENABLED_IS
|
||||
if (forceDownloaded()) return TriState.ENABLED_IS
|
||||
return when (downloadedFilterRaw) {
|
||||
Manga.CHAPTER_SHOW_DOWNLOADED -> TriStateFilter.ENABLED_IS
|
||||
Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> TriStateFilter.ENABLED_NOT
|
||||
else -> TriStateFilter.DISABLED
|
||||
Manga.CHAPTER_SHOW_DOWNLOADED -> TriState.ENABLED_IS
|
||||
Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> TriState.ENABLED_NOT
|
||||
else -> TriState.DISABLED
|
||||
}
|
||||
}
|
||||
fun Manga.chaptersFiltered(): Boolean {
|
||||
return unreadFilter != TriStateFilter.DISABLED ||
|
||||
downloadedFilter != TriStateFilter.DISABLED ||
|
||||
bookmarkedFilter != TriStateFilter.DISABLED
|
||||
return unreadFilter != TriState.DISABLED ||
|
||||
downloadedFilter != TriState.DISABLED ||
|
||||
bookmarkedFilter != TriState.DISABLED
|
||||
}
|
||||
fun Manga.forceDownloaded(): Boolean {
|
||||
return favorite && Injekt.get<BasePreferences>().downloadedOnly().get()
|
||||
|
@ -0,0 +1,56 @@
|
||||
package eu.kanade.domain.track.interactor
|
||||
|
||||
import android.content.Context
|
||||
import eu.kanade.domain.track.model.toDbTrack
|
||||
import eu.kanade.domain.track.service.DelayedTrackingUpdateJob
|
||||
import eu.kanade.domain.track.store.DelayedTrackingStore
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.util.lang.launchNonCancellable
|
||||
import tachiyomi.core.util.system.logcat
|
||||
import tachiyomi.domain.track.interactor.GetTracks
|
||||
import tachiyomi.domain.track.interactor.InsertTrack
|
||||
|
||||
class TrackChapter(
|
||||
private val getTracks: GetTracks,
|
||||
private val trackManager: TrackManager,
|
||||
private val insertTrack: InsertTrack,
|
||||
private val delayedTrackingStore: DelayedTrackingStore,
|
||||
) {
|
||||
|
||||
suspend fun await(context: Context, mangaId: Long, chapterNumber: Double) = coroutineScope {
|
||||
launchNonCancellable {
|
||||
val tracks = getTracks.await(mangaId)
|
||||
|
||||
if (tracks.isEmpty()) return@launchNonCancellable
|
||||
|
||||
tracks.mapNotNull { track ->
|
||||
val service = trackManager.getService(track.syncId)
|
||||
if (service != null && service.isLogged && chapterNumber > track.lastChapterRead) {
|
||||
val updatedTrack = track.copy(lastChapterRead = chapterNumber)
|
||||
|
||||
async {
|
||||
runCatching {
|
||||
try {
|
||||
service.update(updatedTrack.toDbTrack(), true)
|
||||
insertTrack.await(updatedTrack)
|
||||
} catch (e: Exception) {
|
||||
delayedTrackingStore.addItem(updatedTrack)
|
||||
DelayedTrackingUpdateJob.setupTask(context)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
.awaitAll()
|
||||
.mapNotNull { it.exceptionOrNull() }
|
||||
.forEach { logcat(LogPriority.INFO, it) }
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package eu.kanade.presentation.browse
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -19,7 +20,6 @@ import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchState
|
||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.presentation.core.components.LazyColumn
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package eu.kanade.presentation.browse
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.State
|
||||
import eu.kanade.presentation.browse.components.GlobalSearchCardRow
|
||||
@ -14,7 +15,6 @@ import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateSearchState
|
||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.presentation.core.components.LazyColumn
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
|
||||
@Composable
|
||||
|
@ -1,6 +1,7 @@
|
||||
package eu.kanade.presentation.browse.components
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
@ -12,7 +13,6 @@ import eu.kanade.presentation.library.components.MangaListItem
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.manga.model.MangaCover
|
||||
import tachiyomi.presentation.core.components.LazyColumn
|
||||
import tachiyomi.presentation.core.util.plus
|
||||
|
||||
@Composable
|
||||
|
@ -3,6 +3,7 @@ package eu.kanade.presentation.category
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
@ -16,7 +17,6 @@ import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryScreenState
|
||||
import tachiyomi.domain.category.model.Category
|
||||
import tachiyomi.presentation.core.components.LazyColumn
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
|
||||
|
@ -10,6 +10,7 @@ import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.TextFieldDefaults
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.ArrowBack
|
||||
import androidx.compose.material.icons.outlined.ArrowForward
|
||||
import androidx.compose.material.icons.outlined.Close
|
||||
import androidx.compose.material.icons.outlined.MoreVert
|
||||
import androidx.compose.material.icons.outlined.Search
|
||||
@ -38,12 +39,14 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import eu.kanade.tachiyomi.R
|
||||
@ -62,7 +65,7 @@ fun AppBar(
|
||||
subtitle: String? = null,
|
||||
// Up button
|
||||
navigateUp: (() -> Unit)? = null,
|
||||
navigationIcon: ImageVector = Icons.Outlined.ArrowBack,
|
||||
navigationIcon: ImageVector? = null,
|
||||
// Menu
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
// Action mode
|
||||
@ -107,7 +110,7 @@ fun AppBar(
|
||||
titleContent: @Composable () -> Unit,
|
||||
// Up button
|
||||
navigateUp: (() -> Unit)? = null,
|
||||
navigationIcon: ImageVector = Icons.Outlined.ArrowBack,
|
||||
navigationIcon: ImageVector? = null,
|
||||
// Menu
|
||||
actions: @Composable RowScope.() -> Unit = {},
|
||||
// Action mode
|
||||
@ -131,10 +134,7 @@ fun AppBar(
|
||||
} else {
|
||||
navigateUp?.let {
|
||||
IconButton(onClick = it) {
|
||||
Icon(
|
||||
imageVector = navigationIcon,
|
||||
contentDescription = stringResource(R.string.abc_action_bar_up_description),
|
||||
)
|
||||
UpIcon(navigationIcon)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -360,6 +360,16 @@ fun SearchToolbar(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UpIcon(navigationIcon: ImageVector? = null) {
|
||||
val icon = navigationIcon
|
||||
?: if (LocalLayoutDirection.current == LayoutDirection.Ltr) Icons.Outlined.ArrowBack else Icons.Outlined.ArrowForward
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = stringResource(R.string.abc_action_bar_up_description),
|
||||
)
|
||||
}
|
||||
|
||||
sealed interface AppBar {
|
||||
sealed interface AppBarAction
|
||||
|
||||
|
@ -1,128 +0,0 @@
|
||||
package eu.kanade.presentation.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
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.ContentAlpha
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.CheckBox
|
||||
import androidx.compose.material.icons.rounded.CheckBoxOutlineBlank
|
||||
import androidx.compose.material.icons.rounded.DisabledByDefault
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||
import androidx.compose.material3.ExposedDropdownMenuDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import tachiyomi.domain.manga.model.TriStateFilter
|
||||
import tachiyomi.presentation.core.components.SettingsItemsPaddings
|
||||
|
||||
@Composable
|
||||
fun TriStateItem(
|
||||
label: String,
|
||||
state: TriStateFilter,
|
||||
enabled: Boolean = true,
|
||||
onClick: ((TriStateFilter) -> Unit)?,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable(
|
||||
enabled = enabled && onClick != null,
|
||||
onClick = {
|
||||
when (state) {
|
||||
TriStateFilter.DISABLED -> onClick?.invoke(TriStateFilter.ENABLED_IS)
|
||||
TriStateFilter.ENABLED_IS -> onClick?.invoke(TriStateFilter.ENABLED_NOT)
|
||||
TriStateFilter.ENABLED_NOT -> onClick?.invoke(TriStateFilter.DISABLED)
|
||||
}
|
||||
},
|
||||
)
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = SettingsItemsPaddings.Horizontal, vertical = SettingsItemsPaddings.Vertical),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(24.dp),
|
||||
) {
|
||||
val stateAlpha = if (enabled && onClick != null) 1f else ContentAlpha.disabled
|
||||
|
||||
Icon(
|
||||
imageVector = when (state) {
|
||||
TriStateFilter.DISABLED -> Icons.Rounded.CheckBoxOutlineBlank
|
||||
TriStateFilter.ENABLED_IS -> Icons.Rounded.CheckBox
|
||||
TriStateFilter.ENABLED_NOT -> Icons.Rounded.DisabledByDefault
|
||||
},
|
||||
contentDescription = null,
|
||||
tint = if (!enabled || state == TriStateFilter.DISABLED) {
|
||||
MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = stateAlpha)
|
||||
} else {
|
||||
when (onClick) {
|
||||
null -> MaterialTheme.colorScheme.onSurface.copy(alpha = ContentAlpha.disabled)
|
||||
else -> MaterialTheme.colorScheme.primary
|
||||
}
|
||||
},
|
||||
)
|
||||
Text(
|
||||
text = label,
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = stateAlpha),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SelectItem(
|
||||
label: String,
|
||||
options: Array<out Any?>,
|
||||
selectedIndex: Int,
|
||||
onSelect: (Int) -> Unit,
|
||||
) {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = expanded,
|
||||
onExpandedChange = { expanded = !expanded },
|
||||
) {
|
||||
OutlinedTextField(
|
||||
modifier = Modifier
|
||||
.menuAnchor()
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = SettingsItemsPaddings.Horizontal, vertical = SettingsItemsPaddings.Vertical),
|
||||
label = { Text(text = label) },
|
||||
value = options[selectedIndex].toString(),
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
singleLine = true,
|
||||
trailingIcon = {
|
||||
ExposedDropdownMenuDefaults.TrailingIcon(
|
||||
expanded = expanded,
|
||||
)
|
||||
},
|
||||
colors = ExposedDropdownMenuDefaults.textFieldColors(),
|
||||
)
|
||||
|
||||
ExposedDropdownMenu(
|
||||
modifier = Modifier.exposedDropdownSize(matchTextFieldWidth = true),
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false },
|
||||
) {
|
||||
options.forEachIndexed { index, text ->
|
||||
DropdownMenuItem(
|
||||
text = { Text(text.toString()) },
|
||||
onClick = {
|
||||
onSelect(index)
|
||||
expanded = false
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.foundation.pager.PagerState
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
@ -42,13 +43,13 @@ fun TabbedDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
tabTitles: List<String>,
|
||||
tabOverflowMenuContent: (@Composable ColumnScope.(() -> Unit) -> Unit)? = null,
|
||||
pagerState: PagerState = rememberPagerState { tabTitles.size },
|
||||
content: @Composable (Int) -> Unit,
|
||||
) {
|
||||
AdaptiveSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val pagerState = rememberPagerState { tabTitles.size }
|
||||
|
||||
Column {
|
||||
Row {
|
||||
|
@ -14,21 +14,21 @@ import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import eu.kanade.presentation.components.TabbedDialog
|
||||
import eu.kanade.presentation.components.TabbedDialogPaddings
|
||||
import eu.kanade.presentation.components.TriStateItem
|
||||
import eu.kanade.presentation.util.collectAsState
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.library.LibrarySettingsScreenModel
|
||||
import tachiyomi.core.preference.TriState
|
||||
import tachiyomi.domain.category.model.Category
|
||||
import tachiyomi.domain.library.model.LibraryDisplayMode
|
||||
import tachiyomi.domain.library.model.LibrarySort
|
||||
import tachiyomi.domain.library.model.sort
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
import tachiyomi.domain.manga.model.TriStateFilter
|
||||
import tachiyomi.presentation.core.components.CheckboxItem
|
||||
import tachiyomi.presentation.core.components.HeadingItem
|
||||
import tachiyomi.presentation.core.components.RadioItem
|
||||
import tachiyomi.presentation.core.components.SliderItem
|
||||
import tachiyomi.presentation.core.components.SortItem
|
||||
import tachiyomi.presentation.core.components.TriStateItem
|
||||
|
||||
@Composable
|
||||
fun LibrarySettingsDialog(
|
||||
@ -74,7 +74,7 @@ private fun ColumnScope.FilterPage(
|
||||
TriStateItem(
|
||||
label = stringResource(R.string.label_downloaded),
|
||||
state = if (downloadedOnly) {
|
||||
TriStateFilter.ENABLED_IS
|
||||
TriState.ENABLED_IS
|
||||
} else {
|
||||
filterDownloaded
|
||||
},
|
||||
|
@ -27,20 +27,20 @@ import eu.kanade.domain.manga.model.downloadedFilter
|
||||
import eu.kanade.domain.manga.model.forceDownloaded
|
||||
import eu.kanade.presentation.components.TabbedDialog
|
||||
import eu.kanade.presentation.components.TabbedDialogPaddings
|
||||
import eu.kanade.presentation.components.TriStateItem
|
||||
import eu.kanade.tachiyomi.R
|
||||
import tachiyomi.core.preference.TriState
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.manga.model.TriStateFilter
|
||||
import tachiyomi.presentation.core.components.RadioItem
|
||||
import tachiyomi.presentation.core.components.SortItem
|
||||
import tachiyomi.presentation.core.components.TriStateItem
|
||||
|
||||
@Composable
|
||||
fun ChapterSettingsDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
manga: Manga? = null,
|
||||
onDownloadFilterChanged: (TriStateFilter) -> Unit,
|
||||
onUnreadFilterChanged: (TriStateFilter) -> Unit,
|
||||
onBookmarkedFilterChanged: (TriStateFilter) -> Unit,
|
||||
onDownloadFilterChanged: (TriState) -> Unit,
|
||||
onUnreadFilterChanged: (TriState) -> Unit,
|
||||
onBookmarkedFilterChanged: (TriState) -> Unit,
|
||||
onSortModeChanged: (Long) -> Unit,
|
||||
onDisplayModeChanged: (Long) -> Unit,
|
||||
onSetAsDefault: (applyToExistingManga: Boolean) -> Unit,
|
||||
@ -78,11 +78,11 @@ fun ChapterSettingsDialog(
|
||||
when (page) {
|
||||
0 -> {
|
||||
FilterPage(
|
||||
downloadFilter = manga?.downloadedFilter ?: TriStateFilter.DISABLED,
|
||||
downloadFilter = manga?.downloadedFilter ?: TriState.DISABLED,
|
||||
onDownloadFilterChanged = onDownloadFilterChanged.takeUnless { manga?.forceDownloaded() == true },
|
||||
unreadFilter = manga?.unreadFilter ?: TriStateFilter.DISABLED,
|
||||
unreadFilter = manga?.unreadFilter ?: TriState.DISABLED,
|
||||
onUnreadFilterChanged = onUnreadFilterChanged,
|
||||
bookmarkedFilter = manga?.bookmarkedFilter ?: TriStateFilter.DISABLED,
|
||||
bookmarkedFilter = manga?.bookmarkedFilter ?: TriState.DISABLED,
|
||||
onBookmarkedFilterChanged = onBookmarkedFilterChanged,
|
||||
)
|
||||
}
|
||||
@ -106,12 +106,12 @@ fun ChapterSettingsDialog(
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.FilterPage(
|
||||
downloadFilter: TriStateFilter,
|
||||
onDownloadFilterChanged: ((TriStateFilter) -> Unit)?,
|
||||
unreadFilter: TriStateFilter,
|
||||
onUnreadFilterChanged: (TriStateFilter) -> Unit,
|
||||
bookmarkedFilter: TriStateFilter,
|
||||
onBookmarkedFilterChanged: (TriStateFilter) -> Unit,
|
||||
downloadFilter: TriState,
|
||||
onDownloadFilterChanged: ((TriState) -> Unit)?,
|
||||
unreadFilter: TriState,
|
||||
onUnreadFilterChanged: (TriState) -> Unit,
|
||||
bookmarkedFilter: TriState,
|
||||
onBookmarkedFilterChanged: (TriState) -> Unit,
|
||||
) {
|
||||
TriStateItem(
|
||||
label = stringResource(R.string.label_downloaded),
|
||||
|
@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.systemBars
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
@ -69,7 +70,6 @@ import tachiyomi.domain.chapter.service.missingChaptersCount
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.source.model.StubSource
|
||||
import tachiyomi.presentation.core.components.LazyColumn
|
||||
import tachiyomi.presentation.core.components.TwoPanelBox
|
||||
import tachiyomi.presentation.core.components.VerticalFastScroller
|
||||
import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton
|
||||
@ -87,8 +87,8 @@ fun MangaScreen(
|
||||
dateRelativeTime: Int,
|
||||
dateFormat: DateFormat,
|
||||
isTabletUi: Boolean,
|
||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||
onBackClicked: () -> Unit,
|
||||
onChapterClicked: (Chapter) -> Unit,
|
||||
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
|
||||
@ -141,8 +141,8 @@ fun MangaScreen(
|
||||
snackbarHostState = snackbarHostState,
|
||||
dateRelativeTime = dateRelativeTime,
|
||||
dateFormat = dateFormat,
|
||||
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||
chapterSwipeStartAction = chapterSwipeStartAction,
|
||||
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||
onBackClicked = onBackClicked,
|
||||
onChapterClicked = onChapterClicked,
|
||||
onDownloadChapter = onDownloadChapter,
|
||||
@ -175,8 +175,8 @@ fun MangaScreen(
|
||||
state = state,
|
||||
snackbarHostState = snackbarHostState,
|
||||
dateRelativeTime = dateRelativeTime,
|
||||
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||
chapterSwipeStartAction = chapterSwipeStartAction,
|
||||
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||
dateFormat = dateFormat,
|
||||
onBackClicked = onBackClicked,
|
||||
onChapterClicked = onChapterClicked,
|
||||
@ -214,8 +214,8 @@ private fun MangaScreenSmallImpl(
|
||||
snackbarHostState: SnackbarHostState,
|
||||
dateRelativeTime: Int,
|
||||
dateFormat: DateFormat,
|
||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||
onBackClicked: () -> Unit,
|
||||
onChapterClicked: (Chapter) -> Unit,
|
||||
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
|
||||
@ -258,7 +258,7 @@ private fun MangaScreenSmallImpl(
|
||||
) {
|
||||
val chapterListState = rememberLazyListState()
|
||||
|
||||
val chapters = remember(state) { state.processedChapters.toList() }
|
||||
val chapters = remember(state) { state.processedChapters }
|
||||
|
||||
val internalOnBackPressed = {
|
||||
if (chapters.fastAny { it.selected }) {
|
||||
@ -320,7 +320,7 @@ private fun MangaScreenSmallImpl(
|
||||
) {
|
||||
ExtendedFloatingActionButton(
|
||||
text = {
|
||||
val id = if (chapters.fastAny { it.chapter.read }) {
|
||||
val id = if (state.chapters.fastAny { it.chapter.read }) {
|
||||
R.string.action_resume
|
||||
} else {
|
||||
R.string.action_start
|
||||
@ -421,8 +421,8 @@ private fun MangaScreenSmallImpl(
|
||||
chapters = chapters,
|
||||
dateRelativeTime = dateRelativeTime,
|
||||
dateFormat = dateFormat,
|
||||
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||
chapterSwipeStartAction = chapterSwipeStartAction,
|
||||
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||
onChapterClicked = onChapterClicked,
|
||||
onDownloadChapter = onDownloadChapter,
|
||||
onChapterSelected = onChapterSelected,
|
||||
@ -440,8 +440,8 @@ fun MangaScreenLargeImpl(
|
||||
snackbarHostState: SnackbarHostState,
|
||||
dateRelativeTime: Int,
|
||||
dateFormat: DateFormat,
|
||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||
onBackClicked: () -> Unit,
|
||||
onChapterClicked: (Chapter) -> Unit,
|
||||
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
|
||||
@ -485,7 +485,7 @@ fun MangaScreenLargeImpl(
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
val density = LocalDensity.current
|
||||
|
||||
val chapters = remember(state) { state.processedChapters.toList() }
|
||||
val chapters = remember(state) { state.processedChapters }
|
||||
|
||||
val insetPadding = WindowInsets.systemBars.only(WindowInsetsSides.Horizontal).asPaddingValues()
|
||||
var topBarHeight by remember { mutableIntStateOf(0) }
|
||||
@ -555,7 +555,7 @@ fun MangaScreenLargeImpl(
|
||||
) {
|
||||
ExtendedFloatingActionButton(
|
||||
text = {
|
||||
val id = if (chapters.fastAny { it.chapter.read }) {
|
||||
val id = if (state.chapters.fastAny { it.chapter.read }) {
|
||||
R.string.action_resume
|
||||
} else {
|
||||
R.string.action_start
|
||||
@ -641,8 +641,8 @@ fun MangaScreenLargeImpl(
|
||||
chapters = chapters,
|
||||
dateRelativeTime = dateRelativeTime,
|
||||
dateFormat = dateFormat,
|
||||
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||
chapterSwipeStartAction = chapterSwipeStartAction,
|
||||
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||
onChapterClicked = onChapterClicked,
|
||||
onDownloadChapter = onDownloadChapter,
|
||||
onChapterSelected = onChapterSelected,
|
||||
@ -703,8 +703,8 @@ private fun LazyListScope.sharedChapterItems(
|
||||
chapters: List<ChapterItem>,
|
||||
dateRelativeTime: Int,
|
||||
dateFormat: DateFormat,
|
||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||
onChapterClicked: (Chapter) -> Unit,
|
||||
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
|
||||
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit,
|
||||
@ -751,8 +751,8 @@ private fun LazyListScope.sharedChapterItems(
|
||||
downloadIndicatorEnabled = chapters.fastAll { !it.selected },
|
||||
downloadStateProvider = { chapterItem.downloadState },
|
||||
downloadProgressProvider = { chapterItem.downloadProgress },
|
||||
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||
chapterSwipeStartAction = chapterSwipeStartAction,
|
||||
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||
onLongClick = {
|
||||
onChapterSelected(chapterItem, !chapterItem.selected, true, true)
|
||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
|
@ -1,20 +1,12 @@
|
||||
package eu.kanade.presentation.manga.components
|
||||
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.material.DismissDirection
|
||||
import androidx.compose.material.DismissValue
|
||||
import androidx.compose.material.SwipeToDismiss
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Bookmark
|
||||
import androidx.compose.material.icons.filled.Circle
|
||||
@ -25,7 +17,6 @@ import androidx.compose.material.icons.outlined.Done
|
||||
import androidx.compose.material.icons.outlined.Download
|
||||
import androidx.compose.material.icons.outlined.FileDownloadOff
|
||||
import androidx.compose.material.icons.outlined.RemoveDone
|
||||
import androidx.compose.material.rememberDismissState
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@ -37,14 +28,18 @@ import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.platform.LocalViewConfiguration
|
||||
import androidx.compose.ui.platform.ViewConfiguration
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@ -53,10 +48,13 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import me.saket.swipe.SwipeableActionsBox
|
||||
import me.saket.swipe.rememberSwipeableActionsState
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
import tachiyomi.presentation.core.components.material.ReadItemAlpha
|
||||
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
|
||||
import tachiyomi.presentation.core.util.selectedBackground
|
||||
import kotlin.math.absoluteValue
|
||||
|
||||
@Composable
|
||||
fun MangaChapterListItem(
|
||||
@ -71,13 +69,19 @@ fun MangaChapterListItem(
|
||||
downloadIndicatorEnabled: Boolean,
|
||||
downloadStateProvider: () -> Download.State,
|
||||
downloadProgressProvider: () -> Int,
|
||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||
onLongClick: () -> Unit,
|
||||
onClick: () -> Unit,
|
||||
onDownloadClick: ((ChapterDownloadAction) -> Unit)?,
|
||||
onChapterSwipe: (LibraryPreferences.ChapterSwipeAction) -> Unit,
|
||||
) {
|
||||
val haptic = LocalHapticFeedback.current
|
||||
val density = LocalDensity.current
|
||||
|
||||
val textAlpha = if (read) ReadItemAlpha else 1f
|
||||
val textSubtitleAlpha = if (read) ReadItemAlpha else SecondaryItemAlpha
|
||||
|
||||
// Increase touch slop of swipe action to reduce accidental trigger
|
||||
val configuration = LocalViewConfiguration.current
|
||||
CompositionLocalProvider(
|
||||
@ -85,247 +89,188 @@ fun MangaChapterListItem(
|
||||
override val touchSlop: Float = configuration.touchSlop * 3f
|
||||
},
|
||||
) {
|
||||
val textAlpha = if (read) ReadItemAlpha else 1f
|
||||
val textSubtitleAlpha = if (read) ReadItemAlpha else SecondaryItemAlpha
|
||||
|
||||
val chapterSwipeStartEnabled = chapterSwipeStartAction != LibraryPreferences.ChapterSwipeAction.Disabled
|
||||
val chapterSwipeEndEnabled = chapterSwipeEndAction != LibraryPreferences.ChapterSwipeAction.Disabled
|
||||
|
||||
val dismissState = rememberDismissState()
|
||||
val dismissDirections = remember { mutableSetOf<DismissDirection>() }
|
||||
var lastDismissDirection: DismissDirection? by remember { mutableStateOf(null) }
|
||||
if (lastDismissDirection == null) {
|
||||
if (chapterSwipeStartEnabled) {
|
||||
dismissDirections.add(DismissDirection.EndToStart)
|
||||
}
|
||||
if (chapterSwipeEndEnabled) {
|
||||
dismissDirections.add(DismissDirection.StartToEnd)
|
||||
}
|
||||
}
|
||||
val animateDismissContentAlpha by animateFloatAsState(
|
||||
label = "animateDismissContentAlpha",
|
||||
targetValue = if (lastDismissDirection != null) 1f else 0f,
|
||||
animationSpec = tween(durationMillis = if (lastDismissDirection != null) 500 else 0),
|
||||
finishedListener = {
|
||||
lastDismissDirection = null
|
||||
},
|
||||
val start = getSwipeAction(
|
||||
action = chapterSwipeStartAction,
|
||||
read = read,
|
||||
bookmark = bookmark,
|
||||
downloadState = downloadStateProvider(),
|
||||
background = MaterialTheme.colorScheme.primaryContainer,
|
||||
onSwipe = { onChapterSwipe(chapterSwipeStartAction) },
|
||||
)
|
||||
val dismissContentAlpha = if (lastDismissDirection != null) animateDismissContentAlpha else 1f
|
||||
val backgroundColor = if (chapterSwipeEndEnabled && (dismissState.dismissDirection == DismissDirection.StartToEnd || lastDismissDirection == DismissDirection.StartToEnd)) {
|
||||
MaterialTheme.colorScheme.primary
|
||||
} else if (chapterSwipeStartEnabled && (dismissState.dismissDirection == DismissDirection.EndToStart || lastDismissDirection == DismissDirection.EndToStart)) {
|
||||
MaterialTheme.colorScheme.primary
|
||||
} else {
|
||||
Color.Unspecified
|
||||
val end = getSwipeAction(
|
||||
action = chapterSwipeEndAction,
|
||||
read = read,
|
||||
bookmark = bookmark,
|
||||
downloadState = downloadStateProvider(),
|
||||
background = MaterialTheme.colorScheme.primaryContainer,
|
||||
onSwipe = { onChapterSwipe(chapterSwipeEndAction) },
|
||||
)
|
||||
|
||||
val swipeableActionsState = rememberSwipeableActionsState()
|
||||
LaunchedEffect(Unit) {
|
||||
// Haptic effect when swipe over threshold
|
||||
val swipeActionThresholdPx = with(density) { swipeActionThreshold.toPx() }
|
||||
snapshotFlow { swipeableActionsState.offset.value.absoluteValue > swipeActionThresholdPx }
|
||||
.collect { if (it) haptic.performHapticFeedback(HapticFeedbackType.LongPress) }
|
||||
}
|
||||
|
||||
LaunchedEffect(dismissState.currentValue) {
|
||||
when (dismissState.currentValue) {
|
||||
DismissValue.DismissedToEnd -> {
|
||||
lastDismissDirection = DismissDirection.StartToEnd
|
||||
val dismissDirectionsCopy = dismissDirections.toSet()
|
||||
dismissDirections.clear()
|
||||
onChapterSwipe(chapterSwipeEndAction)
|
||||
dismissState.snapTo(DismissValue.Default)
|
||||
dismissDirections.addAll(dismissDirectionsCopy)
|
||||
}
|
||||
DismissValue.DismissedToStart -> {
|
||||
lastDismissDirection = DismissDirection.EndToStart
|
||||
val dismissDirectionsCopy = dismissDirections.toSet()
|
||||
dismissDirections.clear()
|
||||
onChapterSwipe(chapterSwipeStartAction)
|
||||
dismissState.snapTo(DismissValue.Default)
|
||||
dismissDirections.addAll(dismissDirectionsCopy)
|
||||
}
|
||||
DismissValue.Default -> { }
|
||||
}
|
||||
}
|
||||
|
||||
SwipeToDismiss(
|
||||
state = dismissState,
|
||||
directions = dismissDirections,
|
||||
background = {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(backgroundColor),
|
||||
SwipeableActionsBox(
|
||||
modifier = Modifier.clipToBounds(),
|
||||
state = swipeableActionsState,
|
||||
startActions = listOfNotNull(start),
|
||||
endActions = listOfNotNull(end),
|
||||
swipeThreshold = swipeActionThreshold,
|
||||
backgroundUntilSwipeThreshold = MaterialTheme.colorScheme.surfaceContainerLowest,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.selectedBackground(selected)
|
||||
.combinedClickable(
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
)
|
||||
.padding(start = 16.dp, top = 12.dp, end = 8.dp, bottom = 12.dp),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
) {
|
||||
if (dismissState.dismissDirection in dismissDirections) {
|
||||
val downloadState = downloadStateProvider()
|
||||
SwipeBackgroundIcon(
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp)
|
||||
.align(Alignment.CenterStart)
|
||||
.alpha(
|
||||
if (dismissState.dismissDirection == DismissDirection.StartToEnd) 1f else 0f,
|
||||
),
|
||||
tint = contentColorFor(backgroundColor),
|
||||
swipeAction = chapterSwipeEndAction,
|
||||
read = read,
|
||||
bookmark = bookmark,
|
||||
downloadState = downloadState,
|
||||
)
|
||||
SwipeBackgroundIcon(
|
||||
modifier = Modifier
|
||||
.padding(end = 16.dp)
|
||||
.align(Alignment.CenterEnd)
|
||||
.alpha(
|
||||
if (dismissState.dismissDirection == DismissDirection.EndToStart) 1f else 0f,
|
||||
),
|
||||
tint = contentColorFor(backgroundColor),
|
||||
swipeAction = chapterSwipeStartAction,
|
||||
read = read,
|
||||
bookmark = bookmark,
|
||||
downloadState = downloadState,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
dismissContent = {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.background(
|
||||
MaterialTheme.colorScheme.background.copy(dismissContentAlpha),
|
||||
)
|
||||
.selectedBackground(selected)
|
||||
.alpha(dismissContentAlpha)
|
||||
.combinedClickable(
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
)
|
||||
.padding(start = 16.dp, top = 12.dp, end = 8.dp, bottom = 12.dp),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
var textHeight by remember { mutableIntStateOf(0) }
|
||||
if (!read) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Circle,
|
||||
contentDescription = stringResource(R.string.unread),
|
||||
modifier = Modifier
|
||||
.height(8.dp)
|
||||
.padding(end = 4.dp),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
if (bookmark) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Bookmark,
|
||||
contentDescription = stringResource(R.string.action_filter_bookmarked),
|
||||
modifier = Modifier
|
||||
.sizeIn(maxHeight = with(LocalDensity.current) { textHeight.toDp() - 2.dp }),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = LocalContentColor.current.copy(alpha = textAlpha),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
onTextLayout = { textHeight = it.size.height },
|
||||
var textHeight by remember { mutableIntStateOf(0) }
|
||||
if (!read) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Circle,
|
||||
contentDescription = stringResource(R.string.unread),
|
||||
modifier = Modifier
|
||||
.height(8.dp)
|
||||
.padding(end = 4.dp),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
if (bookmark) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Bookmark,
|
||||
contentDescription = stringResource(R.string.action_filter_bookmarked),
|
||||
modifier = Modifier
|
||||
.sizeIn(maxHeight = with(LocalDensity.current) { textHeight.toDp() - 2.dp }),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = LocalContentColor.current.copy(alpha = textAlpha),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
onTextLayout = { textHeight = it.size.height },
|
||||
)
|
||||
}
|
||||
|
||||
Row {
|
||||
ProvideTextStyle(
|
||||
value = MaterialTheme.typography.bodyMedium.copy(
|
||||
fontSize = 12.sp,
|
||||
color = LocalContentColor.current.copy(alpha = textSubtitleAlpha),
|
||||
),
|
||||
) {
|
||||
if (date != null) {
|
||||
Text(
|
||||
text = date,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
if (readProgress != null || scanlator != null) DotSeparatorText()
|
||||
}
|
||||
if (readProgress != null) {
|
||||
Text(
|
||||
text = readProgress,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.alpha(ReadItemAlpha),
|
||||
)
|
||||
if (scanlator != null) DotSeparatorText()
|
||||
}
|
||||
if (scanlator != null) {
|
||||
Text(
|
||||
text = scanlator,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
Row {
|
||||
ProvideTextStyle(
|
||||
value = MaterialTheme.typography.bodyMedium.copy(
|
||||
fontSize = 12.sp,
|
||||
color = LocalContentColor.current.copy(alpha = textSubtitleAlpha),
|
||||
),
|
||||
) {
|
||||
if (date != null) {
|
||||
Text(
|
||||
text = date,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
if (readProgress != null || scanlator != null) DotSeparatorText()
|
||||
}
|
||||
if (readProgress != null) {
|
||||
Text(
|
||||
text = readProgress,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.alpha(ReadItemAlpha),
|
||||
)
|
||||
if (scanlator != null) DotSeparatorText()
|
||||
}
|
||||
if (scanlator != null) {
|
||||
Text(
|
||||
text = scanlator,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (onDownloadClick != null) {
|
||||
ChapterDownloadIndicator(
|
||||
enabled = downloadIndicatorEnabled,
|
||||
modifier = Modifier.padding(start = 4.dp),
|
||||
downloadStateProvider = downloadStateProvider,
|
||||
downloadProgressProvider = downloadProgressProvider,
|
||||
onClick = onDownloadClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
if (onDownloadClick != null) {
|
||||
ChapterDownloadIndicator(
|
||||
enabled = downloadIndicatorEnabled,
|
||||
modifier = Modifier.padding(start = 4.dp),
|
||||
downloadStateProvider = downloadStateProvider,
|
||||
downloadProgressProvider = downloadProgressProvider,
|
||||
onClick = onDownloadClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SwipeBackgroundIcon(
|
||||
modifier: Modifier = Modifier,
|
||||
tint: Color,
|
||||
swipeAction: LibraryPreferences.ChapterSwipeAction,
|
||||
private fun getSwipeAction(
|
||||
action: LibraryPreferences.ChapterSwipeAction,
|
||||
read: Boolean,
|
||||
bookmark: Boolean,
|
||||
downloadState: Download.State,
|
||||
) {
|
||||
val imageVector = when (swipeAction) {
|
||||
LibraryPreferences.ChapterSwipeAction.ToggleRead -> {
|
||||
if (!read) {
|
||||
Icons.Outlined.Done
|
||||
} else {
|
||||
Icons.Outlined.RemoveDone
|
||||
}
|
||||
}
|
||||
LibraryPreferences.ChapterSwipeAction.ToggleBookmark -> {
|
||||
if (!bookmark) {
|
||||
Icons.Outlined.BookmarkAdd
|
||||
} else {
|
||||
Icons.Outlined.BookmarkRemove
|
||||
}
|
||||
}
|
||||
LibraryPreferences.ChapterSwipeAction.Download -> {
|
||||
when (downloadState) {
|
||||
Download.State.NOT_DOWNLOADED,
|
||||
Download.State.ERROR,
|
||||
-> { Icons.Outlined.Download }
|
||||
Download.State.QUEUE,
|
||||
Download.State.DOWNLOADING,
|
||||
-> { Icons.Outlined.FileDownloadOff }
|
||||
Download.State.DOWNLOADED -> { Icons.Outlined.Delete }
|
||||
}
|
||||
}
|
||||
background: Color,
|
||||
onSwipe: () -> Unit,
|
||||
): me.saket.swipe.SwipeAction? {
|
||||
return when (action) {
|
||||
LibraryPreferences.ChapterSwipeAction.ToggleRead -> swipeAction(
|
||||
icon = if (!read) Icons.Outlined.Done else Icons.Outlined.RemoveDone,
|
||||
background = background,
|
||||
isUndo = read,
|
||||
onSwipe = onSwipe,
|
||||
)
|
||||
LibraryPreferences.ChapterSwipeAction.ToggleBookmark -> swipeAction(
|
||||
icon = if (!bookmark) Icons.Outlined.BookmarkAdd else Icons.Outlined.BookmarkRemove,
|
||||
background = background,
|
||||
isUndo = bookmark,
|
||||
onSwipe = onSwipe,
|
||||
)
|
||||
LibraryPreferences.ChapterSwipeAction.Download -> swipeAction(
|
||||
icon = when (downloadState) {
|
||||
Download.State.NOT_DOWNLOADED, Download.State.ERROR -> Icons.Outlined.Download
|
||||
Download.State.QUEUE, Download.State.DOWNLOADING -> Icons.Outlined.FileDownloadOff
|
||||
Download.State.DOWNLOADED -> Icons.Outlined.Delete
|
||||
},
|
||||
background = background,
|
||||
onSwipe = onSwipe,
|
||||
)
|
||||
LibraryPreferences.ChapterSwipeAction.Disabled -> null
|
||||
}
|
||||
imageVector?.let {
|
||||
Icon(
|
||||
modifier = modifier,
|
||||
imageVector = imageVector,
|
||||
tint = tint,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun swipeAction(
|
||||
onSwipe: () -> Unit,
|
||||
icon: ImageVector,
|
||||
background: Color,
|
||||
isUndo: Boolean = false,
|
||||
): me.saket.swipe.SwipeAction {
|
||||
return me.saket.swipe.SwipeAction(
|
||||
icon = {
|
||||
Icon(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
imageVector = icon,
|
||||
tint = contentColorFor(background),
|
||||
contentDescription = null,
|
||||
)
|
||||
},
|
||||
background = background,
|
||||
onSwipe = onSwipe,
|
||||
isUndo = isUndo,
|
||||
)
|
||||
}
|
||||
|
||||
private val swipeActionThreshold = 56.dp
|
||||
|
@ -2,13 +2,11 @@ package eu.kanade.presentation.manga.components
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.ArrowBack
|
||||
import androidx.compose.material.icons.outlined.Close
|
||||
import androidx.compose.material.icons.outlined.Download
|
||||
import androidx.compose.material.icons.outlined.FilterList
|
||||
import androidx.compose.material.icons.outlined.FlipToBack
|
||||
import androidx.compose.material.icons.outlined.SelectAll
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@ -29,6 +27,7 @@ import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.components.AppBarActions
|
||||
import eu.kanade.presentation.components.DownloadDropdownMenu
|
||||
import eu.kanade.presentation.components.UpIcon
|
||||
import eu.kanade.presentation.manga.DownloadAction
|
||||
import eu.kanade.tachiyomi.R
|
||||
import tachiyomi.presentation.core.theme.active
|
||||
@ -67,10 +66,7 @@ fun MangaToolbar(
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onBackClicked) {
|
||||
Icon(
|
||||
imageVector = if (isActionMode) Icons.Outlined.Close else Icons.Outlined.ArrowBack,
|
||||
contentDescription = stringResource(R.string.abc_action_bar_up_description),
|
||||
)
|
||||
UpIcon(Icons.Outlined.Close.takeIf { isActionMode })
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
|
@ -2,15 +2,12 @@ package eu.kanade.presentation.more.settings
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.ArrowBack
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.presentation.components.UpIcon
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
|
||||
@Composable
|
||||
@ -27,10 +24,7 @@ fun PreferenceScaffold(
|
||||
navigationIcon = {
|
||||
if (onBackPressed != null) {
|
||||
IconButton(onClick = onBackPressed) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.ArrowBack,
|
||||
contentDescription = stringResource(R.string.abc_action_bar_up_description),
|
||||
)
|
||||
UpIcon()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -28,6 +28,7 @@ import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.presentation.more.settings.Preference
|
||||
import eu.kanade.presentation.more.settings.screen.advanced.ClearDatabaseScreen
|
||||
import eu.kanade.presentation.more.settings.screen.debug.DebugInfoScreen
|
||||
import eu.kanade.presentation.util.collectAsState
|
||||
import eu.kanade.tachiyomi.R
|
||||
|
@ -281,8 +281,8 @@ object SettingsLibraryScreen : SearchableSettings {
|
||||
title = stringResource(R.string.pref_chapter_swipe),
|
||||
preferenceItems = listOf(
|
||||
Preference.PreferenceItem.ListPreference(
|
||||
pref = libraryPreferences.swipeEndAction(),
|
||||
title = stringResource(R.string.pref_chapter_swipe_end),
|
||||
pref = libraryPreferences.swipeToStartAction(),
|
||||
title = stringResource(R.string.pref_chapter_swipe_start),
|
||||
entries = mapOf(
|
||||
LibraryPreferences.ChapterSwipeAction.Disabled to stringResource(R.string.disabled),
|
||||
LibraryPreferences.ChapterSwipeAction.ToggleBookmark to stringResource(R.string.action_bookmark),
|
||||
@ -291,8 +291,8 @@ object SettingsLibraryScreen : SearchableSettings {
|
||||
),
|
||||
),
|
||||
Preference.PreferenceItem.ListPreference(
|
||||
pref = libraryPreferences.swipeStartAction(),
|
||||
title = stringResource(R.string.pref_chapter_swipe_start),
|
||||
pref = libraryPreferences.swipeToEndAction(),
|
||||
title = stringResource(R.string.pref_chapter_swipe_end),
|
||||
entries = mapOf(
|
||||
LibraryPreferences.ChapterSwipeAction.Disabled to stringResource(R.string.disabled),
|
||||
LibraryPreferences.ChapterSwipeAction.ToggleBookmark to stringResource(R.string.action_bookmark),
|
||||
|
@ -4,11 +4,11 @@ import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.ArrowBack
|
||||
import androidx.compose.material.icons.outlined.ChromeReaderMode
|
||||
import androidx.compose.material.icons.outlined.Code
|
||||
import androidx.compose.material.icons.outlined.CollectionsBookmark
|
||||
@ -20,7 +20,6 @@ import androidx.compose.material.icons.outlined.Search
|
||||
import androidx.compose.material.icons.outlined.Security
|
||||
import androidx.compose.material.icons.outlined.SettingsBackupRestore
|
||||
import androidx.compose.material.icons.outlined.Sync
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@ -45,11 +44,12 @@ import cafe.adriel.voyager.navigator.Navigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.components.AppBarActions
|
||||
import eu.kanade.presentation.components.UpIcon
|
||||
import eu.kanade.presentation.more.settings.screen.about.AboutScreen
|
||||
import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget
|
||||
import eu.kanade.presentation.util.LocalBackPress
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.tachiyomi.R
|
||||
import tachiyomi.presentation.core.components.LazyColumn
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import cafe.adriel.voyager.core.screen.Screen as VoyagerScreen
|
||||
|
||||
@ -94,10 +94,7 @@ object SettingsMainScreen : Screen() {
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = backPress::invoke) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.ArrowBack,
|
||||
contentDescription = stringResource(R.string.abc_action_bar_up_description),
|
||||
)
|
||||
UpIcon()
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
|
@ -1,6 +1,5 @@
|
||||
package eu.kanade.presentation.more.settings.screen
|
||||
|
||||
import android.content.res.Resources
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@ -17,7 +16,6 @@ import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.ArrowBack
|
||||
import androidx.compose.material.icons.outlined.Close
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
@ -40,19 +38,21 @@ import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.presentation.components.UpIcon
|
||||
import eu.kanade.presentation.more.settings.Preference
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.system.isLTR
|
||||
import tachiyomi.presentation.core.components.material.Divider
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.screens.EmptyScreen
|
||||
@ -97,11 +97,7 @@ class SettingsSearchScreen : Screen() {
|
||||
val canPop = remember { navigator.canPop }
|
||||
if (canPop) {
|
||||
IconButton(onClick = navigator::pop) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.ArrowBack,
|
||||
contentDescription = stringResource(R.string.abc_action_bar_up_description),
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
UpIcon()
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -169,6 +165,8 @@ private fun SearchResult(
|
||||
) {
|
||||
if (searchKey.isEmpty()) return
|
||||
|
||||
val isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr
|
||||
|
||||
val index = getIndex()
|
||||
val result by produceState<List<SearchResultItem>?>(initialValue = null, searchKey) {
|
||||
value = index.asSequence()
|
||||
@ -204,7 +202,7 @@ private fun SearchResult(
|
||||
SearchResultItem(
|
||||
route = settingsData.route,
|
||||
title = p.title,
|
||||
breadcrumbs = getLocalizedBreadcrumb(path = settingsData.title, node = categoryTitle),
|
||||
breadcrumbs = getLocalizedBreadcrumb(path = settingsData.title, node = categoryTitle, isLtr = isLtr),
|
||||
highlightKey = p.title,
|
||||
)
|
||||
}
|
||||
@ -269,11 +267,11 @@ private fun getIndex() = settingScreens
|
||||
)
|
||||
}
|
||||
|
||||
private fun getLocalizedBreadcrumb(path: String, node: String?): String {
|
||||
private fun getLocalizedBreadcrumb(path: String, node: String?, isLtr: Boolean): String {
|
||||
return if (node == null) {
|
||||
path
|
||||
} else {
|
||||
if (Resources.getSystem().isLTR) {
|
||||
if (isLtr) {
|
||||
// This locale reads left to right.
|
||||
"$path > $node"
|
||||
} else {
|
||||
|
@ -1,4 +1,4 @@
|
||||
package eu.kanade.presentation.more.settings.screen
|
||||
package eu.kanade.presentation.more.settings.screen.about
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.animation.AnimatedVisibility
|
@ -1,4 +1,4 @@
|
||||
package eu.kanade.presentation.more.settings.screen
|
||||
package eu.kanade.presentation.more.settings.screen.about
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
@ -1,4 +1,4 @@
|
||||
package eu.kanade.presentation.more.settings.screen
|
||||
package eu.kanade.presentation.more.settings.screen.about
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.MaterialTheme
|
@ -1,4 +1,4 @@
|
||||
package eu.kanade.presentation.more.settings.screen
|
||||
package eu.kanade.presentation.more.settings.screen.advanced
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
@ -12,7 +12,7 @@ import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.presentation.more.settings.Preference
|
||||
import eu.kanade.presentation.more.settings.PreferenceScaffold
|
||||
import eu.kanade.presentation.more.settings.screen.AboutScreen
|
||||
import eu.kanade.presentation.more.settings.screen.about.AboutScreen
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||
|
@ -4,6 +4,7 @@ import android.content.Context
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
@ -39,7 +40,6 @@ import eu.kanade.tachiyomi.util.system.workManager
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import tachiyomi.presentation.core.components.LazyColumn
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.util.plus
|
||||
|
||||
|
@ -7,6 +7,7 @@ import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
@ -28,7 +29,6 @@ import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.tachiyomi.R
|
||||
import tachiyomi.presentation.core.components.LazyColumn
|
||||
import tachiyomi.presentation.core.components.material.Divider
|
||||
import tachiyomi.presentation.core.util.isScrolledToEnd
|
||||
import tachiyomi.presentation.core.util.isScrolledToStart
|
||||
|
@ -3,6 +3,7 @@ package eu.kanade.presentation.more.stats
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.CollectionsBookmark
|
||||
@ -19,7 +20,6 @@ import eu.kanade.presentation.more.stats.components.StatsSection
|
||||
import eu.kanade.presentation.more.stats.data.StatsData
|
||||
import eu.kanade.presentation.util.toDurationString
|
||||
import eu.kanade.tachiyomi.R
|
||||
import tachiyomi.presentation.core.components.LazyColumn
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import java.util.Locale
|
||||
import kotlin.time.DurationUnit
|
||||
|
@ -0,0 +1,158 @@
|
||||
package eu.kanade.presentation.reader.settings
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.core.graphics.alpha
|
||||
import androidx.core.graphics.blue
|
||||
import androidx.core.graphics.green
|
||||
import androidx.core.graphics.red
|
||||
import eu.kanade.presentation.util.collectAsState
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
|
||||
import tachiyomi.core.preference.getAndSet
|
||||
import tachiyomi.presentation.core.components.CheckboxItem
|
||||
import tachiyomi.presentation.core.components.SelectItem
|
||||
import tachiyomi.presentation.core.components.SliderItem
|
||||
|
||||
@Composable
|
||||
internal fun ColumnScope.ColorFilterPage(screenModel: ReaderSettingsScreenModel) {
|
||||
val colorFilterModes = buildList {
|
||||
addAll(
|
||||
listOf(
|
||||
R.string.label_default,
|
||||
R.string.filter_mode_multiply,
|
||||
R.string.filter_mode_screen,
|
||||
),
|
||||
)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
addAll(
|
||||
listOf(
|
||||
R.string.filter_mode_overlay,
|
||||
R.string.filter_mode_lighten,
|
||||
R.string.filter_mode_darken,
|
||||
),
|
||||
)
|
||||
}
|
||||
}.map { stringResource(it) }
|
||||
|
||||
val customBrightness by screenModel.preferences.customBrightness().collectAsState()
|
||||
CheckboxItem(
|
||||
label = stringResource(R.string.pref_custom_brightness),
|
||||
checked = customBrightness,
|
||||
onClick = {
|
||||
screenModel.togglePreference(ReaderPreferences::customBrightness)
|
||||
},
|
||||
)
|
||||
|
||||
/**
|
||||
* Sets the brightness of the screen. Range is [-75, 100].
|
||||
* From -75 to -1 a semi-transparent black view is shown at the top with the minimum brightness.
|
||||
* From 1 to 100 it sets that value as brightness.
|
||||
* 0 sets system brightness and hides the overlay.
|
||||
*/
|
||||
if (customBrightness) {
|
||||
val customBrightnessValue by screenModel.preferences.customBrightnessValue().collectAsState()
|
||||
SliderItem(
|
||||
label = stringResource(R.string.pref_custom_brightness),
|
||||
min = -75,
|
||||
max = 100,
|
||||
value = customBrightnessValue,
|
||||
valueText = customBrightnessValue.toString(),
|
||||
onChange = { screenModel.preferences.customBrightnessValue().set(it) },
|
||||
)
|
||||
}
|
||||
|
||||
val colorFilter by screenModel.preferences.colorFilter().collectAsState()
|
||||
CheckboxItem(
|
||||
label = stringResource(R.string.pref_custom_color_filter),
|
||||
checked = colorFilter,
|
||||
onClick = {
|
||||
screenModel.togglePreference(ReaderPreferences::colorFilter)
|
||||
},
|
||||
)
|
||||
if (colorFilter) {
|
||||
val colorFilterValue by screenModel.preferences.colorFilterValue().collectAsState()
|
||||
SliderItem(
|
||||
label = stringResource(R.string.color_filter_r_value),
|
||||
max = 255,
|
||||
value = colorFilterValue.red,
|
||||
valueText = colorFilterValue.red.toString(),
|
||||
onChange = { newRValue ->
|
||||
screenModel.preferences.colorFilterValue().getAndSet {
|
||||
getColorValue(it, newRValue, RED_MASK, 16)
|
||||
}
|
||||
},
|
||||
)
|
||||
SliderItem(
|
||||
label = stringResource(R.string.color_filter_g_value),
|
||||
max = 255,
|
||||
value = colorFilterValue.green,
|
||||
valueText = colorFilterValue.green.toString(),
|
||||
onChange = { newGValue ->
|
||||
screenModel.preferences.colorFilterValue().getAndSet {
|
||||
getColorValue(it, newGValue, GREEN_MASK, 8)
|
||||
}
|
||||
},
|
||||
)
|
||||
SliderItem(
|
||||
label = stringResource(R.string.color_filter_b_value),
|
||||
max = 255,
|
||||
value = colorFilterValue.blue,
|
||||
valueText = colorFilterValue.blue.toString(),
|
||||
onChange = { newBValue ->
|
||||
screenModel.preferences.colorFilterValue().getAndSet {
|
||||
getColorValue(it, newBValue, BLUE_MASK, 0)
|
||||
}
|
||||
},
|
||||
)
|
||||
SliderItem(
|
||||
label = stringResource(R.string.color_filter_a_value),
|
||||
max = 255,
|
||||
value = colorFilterValue.alpha,
|
||||
valueText = colorFilterValue.alpha.toString(),
|
||||
onChange = { newAValue ->
|
||||
screenModel.preferences.colorFilterValue().getAndSet {
|
||||
getColorValue(it, newAValue, ALPHA_MASK, 24)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
val colorFilterMode by screenModel.preferences.colorFilterMode().collectAsState()
|
||||
SelectItem(
|
||||
label = stringResource(R.string.pref_color_filter_mode),
|
||||
options = colorFilterModes.toTypedArray(),
|
||||
selectedIndex = colorFilterMode,
|
||||
) {
|
||||
screenModel.preferences.colorFilterMode().set(it)
|
||||
}
|
||||
}
|
||||
|
||||
val grayscale by screenModel.preferences.grayscale().collectAsState()
|
||||
CheckboxItem(
|
||||
label = stringResource(R.string.pref_grayscale),
|
||||
checked = grayscale,
|
||||
onClick = {
|
||||
screenModel.togglePreference(ReaderPreferences::grayscale)
|
||||
},
|
||||
)
|
||||
val invertedColors by screenModel.preferences.invertedColors().collectAsState()
|
||||
CheckboxItem(
|
||||
label = stringResource(R.string.pref_inverted_colors),
|
||||
checked = invertedColors,
|
||||
onClick = {
|
||||
screenModel.togglePreference(ReaderPreferences::invertedColors)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun getColorValue(currentColor: Int, color: Int, mask: Long, bitShift: Int): Int {
|
||||
return (color shl bitShift) or (currentColor and mask.inv().toInt())
|
||||
}
|
||||
private const val ALPHA_MASK: Long = 0xFF000000
|
||||
private const val RED_MASK: Long = 0x00FF0000
|
||||
private const val GREEN_MASK: Long = 0x0000FF00
|
||||
private const val BLUE_MASK: Long = 0x000000FF
|
@ -0,0 +1,96 @@
|
||||
package eu.kanade.presentation.reader.settings
|
||||
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import eu.kanade.presentation.util.collectAsState
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
|
||||
import tachiyomi.presentation.core.components.CheckboxItem
|
||||
import tachiyomi.presentation.core.components.HeadingItem
|
||||
import tachiyomi.presentation.core.components.RadioItem
|
||||
|
||||
@Composable
|
||||
internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) {
|
||||
// TODO: show this in a nicer way
|
||||
HeadingItem(stringResource(R.string.pref_reader_theme))
|
||||
val readerTheme by screenModel.preferences.readerTheme().collectAsState()
|
||||
listOf(
|
||||
R.string.black_background to 1,
|
||||
R.string.gray_background to 2,
|
||||
R.string.white_background to 0,
|
||||
R.string.automatic_background to 3,
|
||||
).map { (titleRes, theme) ->
|
||||
RadioItem(
|
||||
label = stringResource(titleRes),
|
||||
selected = readerTheme == theme,
|
||||
onClick = { screenModel.preferences.readerTheme().set(theme) },
|
||||
)
|
||||
}
|
||||
|
||||
val showPageNumber by screenModel.preferences.showPageNumber().collectAsState()
|
||||
CheckboxItem(
|
||||
label = stringResource(R.string.pref_show_page_number),
|
||||
checked = showPageNumber,
|
||||
onClick = {
|
||||
screenModel.togglePreference(ReaderPreferences::showPageNumber)
|
||||
},
|
||||
)
|
||||
|
||||
val fullscreen by screenModel.preferences.fullscreen().collectAsState()
|
||||
CheckboxItem(
|
||||
label = stringResource(R.string.pref_fullscreen),
|
||||
checked = fullscreen,
|
||||
onClick = {
|
||||
screenModel.togglePreference(ReaderPreferences::fullscreen)
|
||||
},
|
||||
)
|
||||
|
||||
// TODO: hide if there's no cutout
|
||||
val cutoutShort by screenModel.preferences.cutoutShort().collectAsState()
|
||||
CheckboxItem(
|
||||
label = stringResource(R.string.pref_cutout_short),
|
||||
checked = cutoutShort,
|
||||
onClick = {
|
||||
screenModel.togglePreference(ReaderPreferences::cutoutShort)
|
||||
},
|
||||
)
|
||||
|
||||
val keepScreenOn by screenModel.preferences.keepScreenOn().collectAsState()
|
||||
CheckboxItem(
|
||||
label = stringResource(R.string.pref_keep_screen_on),
|
||||
checked = keepScreenOn,
|
||||
onClick = {
|
||||
screenModel.togglePreference(ReaderPreferences::keepScreenOn)
|
||||
},
|
||||
)
|
||||
|
||||
val readWithLongTap by screenModel.preferences.readWithLongTap().collectAsState()
|
||||
CheckboxItem(
|
||||
label = stringResource(R.string.pref_read_with_long_tap),
|
||||
checked = readWithLongTap,
|
||||
onClick = {
|
||||
screenModel.togglePreference(ReaderPreferences::readWithLongTap)
|
||||
},
|
||||
)
|
||||
|
||||
val alwaysShowChapterTransition by screenModel.preferences.alwaysShowChapterTransition().collectAsState()
|
||||
CheckboxItem(
|
||||
label = stringResource(R.string.pref_always_show_chapter_transition),
|
||||
checked = alwaysShowChapterTransition,
|
||||
onClick = {
|
||||
screenModel.togglePreference(ReaderPreferences::alwaysShowChapterTransition)
|
||||
},
|
||||
)
|
||||
|
||||
val pageTransitions by screenModel.preferences.pageTransitions().collectAsState()
|
||||
CheckboxItem(
|
||||
label = stringResource(R.string.pref_page_transitions),
|
||||
checked = pageTransitions,
|
||||
onClick = {
|
||||
screenModel.togglePreference(ReaderPreferences::pageTransitions)
|
||||
},
|
||||
)
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package eu.kanade.presentation.reader.settings
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.window.DialogWindowProvider
|
||||
import eu.kanade.presentation.components.TabbedDialog
|
||||
import eu.kanade.presentation.components.TabbedDialogPaddings
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
|
||||
|
||||
@Composable
|
||||
fun ReaderSettingsDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
onShowMenus: () -> Unit,
|
||||
onHideMenus: () -> Unit,
|
||||
screenModel: ReaderSettingsScreenModel,
|
||||
) {
|
||||
// TODO: undimming doesn't seem to work
|
||||
val window = (LocalView.current.parent as? DialogWindowProvider)?.window
|
||||
|
||||
val tabTitles = listOf(
|
||||
stringResource(R.string.pref_category_reading_mode),
|
||||
stringResource(R.string.pref_category_general),
|
||||
stringResource(R.string.custom_filter),
|
||||
)
|
||||
val pagerState = rememberPagerState { tabTitles.size }
|
||||
|
||||
LaunchedEffect(pagerState.currentPage) {
|
||||
if (pagerState.currentPage == 2) {
|
||||
window?.setDimAmount(0f)
|
||||
onHideMenus()
|
||||
} else {
|
||||
window?.setDimAmount(0.75f)
|
||||
onShowMenus()
|
||||
}
|
||||
}
|
||||
|
||||
TabbedDialog(
|
||||
onDismissRequest = {
|
||||
onDismissRequest()
|
||||
onShowMenus()
|
||||
},
|
||||
tabTitles = tabTitles,
|
||||
pagerState = pagerState,
|
||||
) { page ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(vertical = TabbedDialogPaddings.Vertical)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
when (page) {
|
||||
0 -> ReadingModePage(screenModel)
|
||||
1 -> GeneralPage(screenModel)
|
||||
2 -> ColorFilterPage(screenModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
package eu.kanade.presentation.reader.settings
|
||||
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.runtime.Composable
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
|
||||
|
||||
@Composable
|
||||
internal fun ColumnScope.ReadingModePage(screenModel: ReaderSettingsScreenModel) {
|
||||
// TODO
|
||||
}
|
@ -28,7 +28,6 @@ import eu.kanade.domain.ui.UiPreferences
|
||||
import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode
|
||||
import eu.kanade.tachiyomi.crash.CrashActivity
|
||||
import eu.kanade.tachiyomi.crash.GlobalExceptionHandler
|
||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
||||
import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher
|
||||
import eu.kanade.tachiyomi.data.coil.MangaCoverKeyer
|
||||
import eu.kanade.tachiyomi.data.coil.MangaKeyer
|
||||
@ -54,7 +53,6 @@ import org.acra.ktx.initAcra
|
||||
import org.acra.sender.HttpSender
|
||||
import org.conscrypt.Conscrypt
|
||||
import tachiyomi.core.util.system.logcat
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
import tachiyomi.presentation.widget.TachiyomiWidgetManager
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
@ -64,11 +62,9 @@ import java.security.Security
|
||||
class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
|
||||
|
||||
private val basePreferences: BasePreferences by injectLazy()
|
||||
private val libraryPreferences: LibraryPreferences by injectLazy()
|
||||
private val networkPreferences: NetworkPreferences by injectLazy()
|
||||
|
||||
private val disableIncognitoReceiver = DisableIncognitoReceiver()
|
||||
private val chapterCache: ChapterCache by injectLazy()
|
||||
|
||||
@SuppressLint("LaunchActivityFromNotification")
|
||||
override fun onCreate() {
|
||||
@ -172,10 +168,6 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
|
||||
|
||||
override fun onStop(owner: LifecycleOwner) {
|
||||
SecureActivityDelegate.onApplicationStopped()
|
||||
|
||||
if (libraryPreferences.autoClearChapterCache().get()) {
|
||||
chapterCache.clear()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPackageName(): String {
|
||||
|
@ -21,12 +21,11 @@ import eu.kanade.tachiyomi.util.system.isReleaseBuildType
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.util.system.workManager
|
||||
import tachiyomi.core.preference.PreferenceStore
|
||||
import tachiyomi.core.preference.TriState
|
||||
import tachiyomi.core.preference.getEnum
|
||||
import tachiyomi.domain.backup.service.BackupPreferences
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_NON_COMPLETED
|
||||
import tachiyomi.domain.manga.model.TriStateFilter
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.io.File
|
||||
|
||||
object Migrations {
|
||||
@ -350,12 +349,12 @@ object Migrations {
|
||||
remove(key)
|
||||
|
||||
val newValue = when (pref.get()) {
|
||||
1 -> TriStateFilter.ENABLED_IS
|
||||
2 -> TriStateFilter.ENABLED_NOT
|
||||
else -> TriStateFilter.DISABLED
|
||||
1 -> TriState.ENABLED_IS
|
||||
2 -> TriState.ENABLED_NOT
|
||||
else -> TriState.DISABLED
|
||||
}
|
||||
|
||||
preferenceStore.getEnum("${key}_v2", TriStateFilter.DISABLED).set(newValue)
|
||||
preferenceStore.getEnum("${key}_v2", TriState.DISABLED).set(newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ data class BackupChapter(
|
||||
// chapterNumber is called number is 1.x
|
||||
@ProtoNumber(9) var chapterNumber: Float = 0F,
|
||||
@ProtoNumber(10) var sourceOrder: Long = 0,
|
||||
@ProtoNumber(11) var lastModifiedAt: Long? = null,
|
||||
@ProtoNumber(11) var lastModifiedAt: Long = 0,
|
||||
) {
|
||||
fun toChapterImpl(): Chapter {
|
||||
return Chapter.create().copy(
|
||||
@ -39,7 +39,7 @@ data class BackupChapter(
|
||||
}
|
||||
}
|
||||
|
||||
val backupChapterMapper = { _: Long, _: Long, url: String, name: String, scanlator: String?, read: Boolean, bookmark: Boolean, lastPageRead: Long, chapterNumber: Float, source_order: Long, dateFetch: Long, dateUpload: Long, lastModifiedAt: Long? ->
|
||||
val backupChapterMapper = { _: Long, _: Long, url: String, name: String, scanlator: String?, read: Boolean, bookmark: Boolean, lastPageRead: Long, chapterNumber: Float, source_order: Long, dateFetch: Long, dateUpload: Long, lastModifiedAt: Long ->
|
||||
BackupChapter(
|
||||
url = url,
|
||||
name = name,
|
||||
|
@ -39,7 +39,8 @@ data class BackupManga(
|
||||
@ProtoNumber(103) var viewer_flags: Int? = null,
|
||||
@ProtoNumber(104) var history: List<BackupHistory> = emptyList(),
|
||||
@ProtoNumber(105) var updateStrategy: UpdateStrategy = UpdateStrategy.ALWAYS_UPDATE,
|
||||
@ProtoNumber(106) var lastModifiedAt: Long? = 0,
|
||||
@ProtoNumber(106) var lastModifiedAt: Long = 0,
|
||||
@ProtoNumber(107) var favoriteModifiedAt: Long? = null,
|
||||
) {
|
||||
fun getMangaImpl(): Manga {
|
||||
return Manga.create().copy(
|
||||
@ -58,6 +59,7 @@ data class BackupManga(
|
||||
chapterFlags = this@BackupManga.chapterFlags.toLong(),
|
||||
updateStrategy = this@BackupManga.updateStrategy,
|
||||
lastModifiedAt = this@BackupManga.lastModifiedAt,
|
||||
favoriteModifiedAt = this@BackupManga.favoriteModifiedAt,
|
||||
)
|
||||
}
|
||||
|
||||
@ -92,6 +94,7 @@ data class BackupManga(
|
||||
chapterFlags = manga.chapterFlags.toInt(),
|
||||
updateStrategy = manga.updateStrategy,
|
||||
lastModifiedAt = manga.lastModifiedAt,
|
||||
favoriteModifiedAt = manga.favoriteModifiedAt,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ interface Chapter : SChapter, Serializable {
|
||||
|
||||
var source_order: Int
|
||||
|
||||
var last_modified: Long?
|
||||
var last_modified: Long
|
||||
}
|
||||
|
||||
fun Chapter.toDomainChapter(): DomainChapter? {
|
||||
|
@ -26,7 +26,7 @@ class ChapterImpl : Chapter {
|
||||
|
||||
override var source_order: Int = 0
|
||||
|
||||
override var last_modified: Long? = null
|
||||
override var last_modified: Long = 0
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
|
@ -7,9 +7,6 @@ import android.view.View
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.ArrowBack
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
@ -21,7 +18,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
@ -38,6 +34,7 @@ import androidx.preference.forEach
|
||||
import androidx.preference.getOnBindEditTextListener
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.presentation.components.UpIcon
|
||||
import eu.kanade.presentation.util.Screen
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.SharedPreferencesDataStore
|
||||
@ -62,10 +59,7 @@ class SourcePreferencesScreen(val sourceId: Long) : Screen() {
|
||||
title = { Text(text = Injekt.get<SourceManager>().getOrStub(sourceId).toString()) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = navigator::pop) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.ArrowBack,
|
||||
contentDescription = stringResource(R.string.abc_action_bar_up_description),
|
||||
)
|
||||
UpIcon()
|
||||
}
|
||||
},
|
||||
scrollBehavior = it,
|
||||
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.browse.migration
|
||||
import eu.kanade.domain.manga.model.hasCustomCover
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||
import eu.kanade.tachiyomi.data.download.DownloadCache
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.track.interactor.GetTracks
|
||||
@ -12,15 +13,18 @@ import uy.kohesive.injekt.injectLazy
|
||||
|
||||
object MigrationFlags {
|
||||
|
||||
private const val CHAPTERS = 0b0001
|
||||
private const val CATEGORIES = 0b0010
|
||||
private const val TRACK = 0b0100
|
||||
private const val CUSTOM_COVER = 0b1000
|
||||
private const val CHAPTERS = 0b00001
|
||||
private const val CATEGORIES = 0b00010
|
||||
private const val TRACK = 0b00100
|
||||
private const val CUSTOM_COVER = 0b01000
|
||||
private const val DELETE_DOWNLOADED = 0b10000
|
||||
|
||||
private val coverCache: CoverCache by injectLazy()
|
||||
private val getTracks: GetTracks = Injekt.get()
|
||||
private val downloadCache: DownloadCache by injectLazy()
|
||||
|
||||
val flags get() = arrayOf(CHAPTERS, CATEGORIES, TRACK, CUSTOM_COVER)
|
||||
val flags get() = arrayOf(CHAPTERS, CATEGORIES, TRACK, CUSTOM_COVER, DELETE_DOWNLOADED)
|
||||
private var enableFlags = emptyList<Int>().toMutableList()
|
||||
|
||||
fun hasChapters(value: Int): Boolean {
|
||||
return value and CHAPTERS != 0
|
||||
@ -38,23 +42,36 @@ object MigrationFlags {
|
||||
return value and CUSTOM_COVER != 0
|
||||
}
|
||||
|
||||
fun hasDeleteDownloaded(value: Int): Boolean {
|
||||
return value and DELETE_DOWNLOADED != 0
|
||||
}
|
||||
|
||||
fun getEnabledFlagsPositions(value: Int): List<Int> {
|
||||
return flags.mapIndexedNotNull { index, flag -> if (value and flag != 0) index else null }
|
||||
}
|
||||
|
||||
fun getFlagsFromPositions(positions: Array<Int>): Int {
|
||||
return positions.fold(0) { accumulated, position -> accumulated or (1 shl position) }
|
||||
val fold = positions.fold(0) { accumulated, position -> accumulated or enableFlags[position] }
|
||||
enableFlags.clear()
|
||||
return fold
|
||||
}
|
||||
|
||||
fun titles(manga: Manga?): Array<Int> {
|
||||
enableFlags.add(CHAPTERS)
|
||||
enableFlags.add(CATEGORIES)
|
||||
val titles = arrayOf(R.string.chapters, R.string.categories).toMutableList()
|
||||
if (manga != null) {
|
||||
if (runBlocking { getTracks.await(manga.id) }.isNotEmpty()) {
|
||||
titles.add(R.string.track)
|
||||
enableFlags.add(TRACK)
|
||||
}
|
||||
|
||||
if (manga.hasCustomCover(coverCache)) {
|
||||
titles.add(R.string.custom_cover)
|
||||
enableFlags.add(CUSTOM_COVER)
|
||||
}
|
||||
if (downloadCache.getDownloadCount(manga) > 0) {
|
||||
titles.add(R.string.delete_downloaded)
|
||||
enableFlags.add(DELETE_DOWNLOADED)
|
||||
}
|
||||
}
|
||||
return titles.toTypedArray()
|
||||
|
@ -34,6 +34,7 @@ import eu.kanade.domain.manga.model.hasCustomCover
|
||||
import eu.kanade.domain.manga.model.toSManga
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.data.track.EnhancedTrackService
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
@ -161,6 +162,7 @@ internal fun MigrateDialog(
|
||||
|
||||
internal class MigrateDialogScreenModel(
|
||||
private val sourceManager: SourceManager = Injekt.get(),
|
||||
private val downloadManager: DownloadManager = Injekt.get(),
|
||||
private val updateManga: UpdateManga = Injekt.get(),
|
||||
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
|
||||
private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get(),
|
||||
@ -219,6 +221,7 @@ internal class MigrateDialogScreenModel(
|
||||
val migrateCategories = MigrationFlags.hasCategories(flags)
|
||||
val migrateTracks = MigrationFlags.hasTracks(flags)
|
||||
val migrateCustomCover = MigrationFlags.hasCustomCover(flags)
|
||||
val deleteDownloaded = MigrationFlags.hasDeleteDownloaded(flags)
|
||||
|
||||
try {
|
||||
syncChaptersWithSource.await(sourceChapters, newManga, newSource)
|
||||
@ -283,6 +286,13 @@ internal class MigrateDialogScreenModel(
|
||||
insertTrack.awaitAll(tracks)
|
||||
}
|
||||
|
||||
// Delete downloaded
|
||||
if (deleteDownloaded) {
|
||||
if (oldSource != null) {
|
||||
downloadManager.deleteManga(oldManga, oldSource)
|
||||
}
|
||||
}
|
||||
|
||||
if (replace) {
|
||||
updateManga.await(MangaUpdate(oldManga.id, favorite = false, dateAdded = 0))
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@ -15,18 +16,17 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.presentation.components.AdaptiveSheet
|
||||
import eu.kanade.presentation.components.SelectItem
|
||||
import eu.kanade.presentation.components.TriStateItem
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.source.model.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import tachiyomi.domain.manga.model.TriStateFilter
|
||||
import tachiyomi.core.preference.TriState
|
||||
import tachiyomi.presentation.core.components.CheckboxItem
|
||||
import tachiyomi.presentation.core.components.CollapsibleBox
|
||||
import tachiyomi.presentation.core.components.HeadingItem
|
||||
import tachiyomi.presentation.core.components.LazyColumn
|
||||
import tachiyomi.presentation.core.components.SelectItem
|
||||
import tachiyomi.presentation.core.components.SortItem
|
||||
import tachiyomi.presentation.core.components.TextItem
|
||||
import tachiyomi.presentation.core.components.TriStateItem
|
||||
import tachiyomi.presentation.core.components.material.Button
|
||||
import tachiyomi.presentation.core.components.material.Divider
|
||||
|
||||
@ -164,19 +164,19 @@ private fun FilterItem(filter: Filter<*>, onUpdate: () -> Unit) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun Int.toTriStateFilter(): TriStateFilter {
|
||||
private fun Int.toTriStateFilter(): TriState {
|
||||
return when (this) {
|
||||
Filter.TriState.STATE_IGNORE -> TriStateFilter.DISABLED
|
||||
Filter.TriState.STATE_INCLUDE -> TriStateFilter.ENABLED_IS
|
||||
Filter.TriState.STATE_EXCLUDE -> TriStateFilter.ENABLED_NOT
|
||||
Filter.TriState.STATE_IGNORE -> TriState.DISABLED
|
||||
Filter.TriState.STATE_INCLUDE -> TriState.ENABLED_IS
|
||||
Filter.TriState.STATE_EXCLUDE -> TriState.ENABLED_NOT
|
||||
else -> throw IllegalStateException("Unknown TriState state: $this")
|
||||
}
|
||||
}
|
||||
|
||||
private fun TriStateFilter.toTriStateInt(): Int {
|
||||
private fun TriState.toTriStateInt(): Int {
|
||||
return when (this) {
|
||||
TriStateFilter.DISABLED -> Filter.TriState.STATE_IGNORE
|
||||
TriStateFilter.ENABLED_IS -> Filter.TriState.STATE_INCLUDE
|
||||
TriStateFilter.ENABLED_NOT -> Filter.TriState.STATE_EXCLUDE
|
||||
TriState.DISABLED -> Filter.TriState.STATE_IGNORE
|
||||
TriState.ENABLED_IS -> Filter.TriState.STATE_INCLUDE
|
||||
TriState.ENABLED_NOT -> Filter.TriState.STATE_EXCLUDE
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.update
|
||||
import tachiyomi.core.preference.CheckboxState
|
||||
import tachiyomi.core.preference.TriState
|
||||
import tachiyomi.core.util.lang.launchIO
|
||||
import tachiyomi.core.util.lang.launchNonCancellable
|
||||
import tachiyomi.core.util.lang.withIOContext
|
||||
@ -57,7 +58,6 @@ import tachiyomi.domain.library.service.LibraryPreferences
|
||||
import tachiyomi.domain.manga.interactor.GetLibraryManga
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.manga.model.MangaUpdate
|
||||
import tachiyomi.domain.manga.model.TriStateFilter
|
||||
import tachiyomi.domain.manga.model.applyFilter
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
import tachiyomi.domain.track.interactor.GetTracksPerManga
|
||||
@ -153,7 +153,7 @@ class LibraryScreenModel(
|
||||
prefs.filterBookmarked,
|
||||
prefs.filterCompleted,
|
||||
) + trackFilter.values
|
||||
).any { it != TriStateFilter.DISABLED }
|
||||
).any { it != TriState.DISABLED }
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.onEach {
|
||||
@ -169,12 +169,12 @@ class LibraryScreenModel(
|
||||
*/
|
||||
private suspend fun LibraryMap.applyFilters(
|
||||
trackMap: Map<Long, List<Long>>,
|
||||
loggedInTrackServices: Map<Long, TriStateFilter>,
|
||||
loggedInTrackServices: Map<Long, TriState>,
|
||||
): LibraryMap {
|
||||
val prefs = getLibraryItemPreferencesFlow().first()
|
||||
val downloadedOnly = prefs.globalFilterDownloaded
|
||||
val filterDownloaded =
|
||||
if (downloadedOnly) TriStateFilter.ENABLED_IS else prefs.filterDownloaded
|
||||
if (downloadedOnly) TriState.ENABLED_IS else prefs.filterDownloaded
|
||||
val filterUnread = prefs.filterUnread
|
||||
val filterStarted = prefs.filterStarted
|
||||
val filterBookmarked = prefs.filterBookmarked
|
||||
@ -182,8 +182,8 @@ class LibraryScreenModel(
|
||||
|
||||
val isNotLoggedInAnyTrack = loggedInTrackServices.isEmpty()
|
||||
|
||||
val excludedTracks = loggedInTrackServices.mapNotNull { if (it.value == TriStateFilter.ENABLED_NOT) it.key else null }
|
||||
val includedTracks = loggedInTrackServices.mapNotNull { if (it.value == TriStateFilter.ENABLED_IS) it.key else null }
|
||||
val excludedTracks = loggedInTrackServices.mapNotNull { if (it.value == TriState.ENABLED_NOT) it.key else null }
|
||||
val includedTracks = loggedInTrackServices.mapNotNull { if (it.value == TriState.ENABLED_IS) it.key else null }
|
||||
val trackFiltersIsIgnored = includedTracks.isEmpty() && excludedTracks.isEmpty()
|
||||
|
||||
val filterFnDownloaded: (LibraryItem) -> Boolean = {
|
||||
@ -308,11 +308,11 @@ class LibraryScreenModel(
|
||||
localBadge = it[1] as Boolean,
|
||||
languageBadge = it[2] as Boolean,
|
||||
globalFilterDownloaded = it[3] as Boolean,
|
||||
filterDownloaded = it[4] as TriStateFilter,
|
||||
filterUnread = it[5] as TriStateFilter,
|
||||
filterStarted = it[6] as TriStateFilter,
|
||||
filterBookmarked = it[7] as TriStateFilter,
|
||||
filterCompleted = it[8] as TriStateFilter,
|
||||
filterDownloaded = it[4] as TriState,
|
||||
filterUnread = it[5] as TriState,
|
||||
filterStarted = it[6] as TriState,
|
||||
filterBookmarked = it[7] as TriState,
|
||||
filterCompleted = it[8] as TriState,
|
||||
)
|
||||
},
|
||||
)
|
||||
@ -365,7 +365,7 @@ class LibraryScreenModel(
|
||||
*
|
||||
* @return map of track id with the filter value
|
||||
*/
|
||||
private fun getTrackingFilterFlow(): Flow<Map<Long, TriStateFilter>> {
|
||||
private fun getTrackingFilterFlow(): Flow<Map<Long, TriState>> {
|
||||
val loggedServices = trackManager.services.filter { it.isLogged }
|
||||
return if (loggedServices.isNotEmpty()) {
|
||||
val prefFlows = loggedServices
|
||||
@ -670,11 +670,11 @@ class LibraryScreenModel(
|
||||
val languageBadge: Boolean,
|
||||
|
||||
val globalFilterDownloaded: Boolean,
|
||||
val filterDownloaded: TriStateFilter,
|
||||
val filterUnread: TriStateFilter,
|
||||
val filterStarted: TriStateFilter,
|
||||
val filterBookmarked: TriStateFilter,
|
||||
val filterCompleted: TriStateFilter,
|
||||
val filterDownloaded: TriState,
|
||||
val filterUnread: TriState,
|
||||
val filterStarted: TriState,
|
||||
val filterBookmarked: TriState,
|
||||
val filterCompleted: TriState,
|
||||
)
|
||||
|
||||
@Immutable
|
||||
|
@ -6,6 +6,7 @@ import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.util.preference.toggle
|
||||
import tachiyomi.core.preference.Preference
|
||||
import tachiyomi.core.preference.TriState
|
||||
import tachiyomi.core.preference.getAndSet
|
||||
import tachiyomi.core.util.lang.launchIO
|
||||
import tachiyomi.domain.category.interactor.SetDisplayMode
|
||||
@ -14,7 +15,6 @@ import tachiyomi.domain.category.model.Category
|
||||
import tachiyomi.domain.library.model.LibraryDisplayMode
|
||||
import tachiyomi.domain.library.model.LibrarySort
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
import tachiyomi.domain.manga.model.TriStateFilter
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
@ -33,7 +33,7 @@ class LibrarySettingsScreenModel(
|
||||
preference(libraryPreferences).toggle()
|
||||
}
|
||||
|
||||
fun toggleFilter(preference: (LibraryPreferences) -> Preference<TriStateFilter>) {
|
||||
fun toggleFilter(preference: (LibraryPreferences) -> Preference<TriState>) {
|
||||
preference(libraryPreferences).getAndSet {
|
||||
it.next()
|
||||
}
|
||||
|
@ -63,6 +63,7 @@ import eu.kanade.presentation.util.collectAsState
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import eu.kanade.tachiyomi.Migrations
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
||||
import eu.kanade.tachiyomi.data.download.DownloadCache
|
||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
||||
import eu.kanade.tachiyomi.data.updater.AppUpdateChecker
|
||||
@ -105,6 +106,7 @@ class MainActivity : BaseActivity() {
|
||||
private val preferences: BasePreferences by injectLazy()
|
||||
|
||||
private val downloadCache: DownloadCache by injectLazy()
|
||||
private val chapterCache: ChapterCache by injectLazy()
|
||||
|
||||
// To be checked by splash screen. If true then splash screen will be removed.
|
||||
var ready = false
|
||||
@ -112,12 +114,14 @@ class MainActivity : BaseActivity() {
|
||||
private var navigator: Navigator? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
val isLaunch = savedInstanceState == null
|
||||
|
||||
// Prevent splash screen showing up on configuration changes
|
||||
val splashScreen = if (savedInstanceState == null) installSplashScreen() else null
|
||||
val splashScreen = if (isLaunch) installSplashScreen() else null
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val didMigration = if (savedInstanceState == null) {
|
||||
val didMigration = if (isLaunch) {
|
||||
Migrations.upgrade(
|
||||
context = applicationContext,
|
||||
basePreferences = preferences,
|
||||
@ -149,7 +153,7 @@ class MainActivity : BaseActivity() {
|
||||
val downloadOnly by preferences.downloadedOnly().collectAsState()
|
||||
val indexing by downloadCache.isInitializing.collectAsState()
|
||||
|
||||
// Set statusbar color considering the top app state banner
|
||||
// Set status bar color considering the top app state banner
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val isSystemInDarkTheme = isSystemInDarkTheme()
|
||||
val statusBarBackgroundColor = when {
|
||||
@ -189,7 +193,7 @@ class MainActivity : BaseActivity() {
|
||||
LaunchedEffect(navigator) {
|
||||
this@MainActivity.navigator = navigator
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
if (isLaunch) {
|
||||
// Set start screen
|
||||
handleIntentAction(intent, navigator)
|
||||
|
||||
@ -267,6 +271,10 @@ class MainActivity : BaseActivity() {
|
||||
elapsed <= SPLASH_MIN_DURATION || (!ready && elapsed <= SPLASH_MAX_DURATION)
|
||||
}
|
||||
setSplashScreenExitAnimation(splashScreen)
|
||||
|
||||
if (isLaunch && libraryPreferences.autoClearChapterCache().get()) {
|
||||
chapterCache.clear()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onProvideAssistContent(outContent: AssistContent) {
|
||||
@ -279,7 +287,7 @@ class MainActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HandleOnNewIntent(context: Context, navigator: Navigator) {
|
||||
private fun HandleOnNewIntent(context: Context, navigator: Navigator) {
|
||||
LaunchedEffect(Unit) {
|
||||
callbackFlow<Intent> {
|
||||
val componentActivity = context as ComponentActivity
|
||||
|
@ -101,8 +101,8 @@ class MangaScreen(
|
||||
dateRelativeTime = screenModel.relativeTime,
|
||||
dateFormat = screenModel.dateFormat,
|
||||
isTabletUi = isTabletUi(),
|
||||
chapterSwipeEndAction = screenModel.chapterSwipeEndAction,
|
||||
chapterSwipeStartAction = screenModel.chapterSwipeStartAction,
|
||||
chapterSwipeEndAction = screenModel.chapterSwipeEndAction,
|
||||
onBackClicked = navigator::pop,
|
||||
onChapterClicked = { openChapter(context, it) },
|
||||
onDownloadChapter = screenModel::runChapterDownloadActions.takeIf { !successState.source.isLocalOrStub() },
|
||||
|
@ -46,6 +46,7 @@ import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.preference.CheckboxState
|
||||
import tachiyomi.core.preference.TriState
|
||||
import tachiyomi.core.preference.mapAsCheckboxState
|
||||
import tachiyomi.core.util.lang.launchIO
|
||||
import tachiyomi.core.util.lang.launchNonCancellable
|
||||
@ -67,7 +68,6 @@ import tachiyomi.domain.manga.interactor.GetDuplicateLibraryManga
|
||||
import tachiyomi.domain.manga.interactor.GetMangaWithChapters
|
||||
import tachiyomi.domain.manga.interactor.SetMangaChapterFlags
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.manga.model.TriStateFilter
|
||||
import tachiyomi.domain.manga.model.applyFilter
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
import tachiyomi.domain.track.interactor.GetTracks
|
||||
@ -119,11 +119,11 @@ class MangaInfoScreenModel(
|
||||
private val allChapters: List<ChapterItem>?
|
||||
get() = successState?.chapters
|
||||
|
||||
private val filteredChapters: Sequence<ChapterItem>?
|
||||
private val filteredChapters: List<ChapterItem>?
|
||||
get() = successState?.processedChapters
|
||||
|
||||
val chapterSwipeEndAction = libraryPreferences.swipeEndAction().get()
|
||||
val chapterSwipeStartAction = libraryPreferences.swipeStartAction().get()
|
||||
val chapterSwipeStartAction = libraryPreferences.swipeToEndAction().get()
|
||||
val chapterSwipeEndAction = libraryPreferences.swipeToStartAction().get()
|
||||
|
||||
val relativeTime by uiPreferences.relativeTime().asState(coroutineScope)
|
||||
val dateFormat by mutableStateOf(UiPreferences.dateFormat(uiPreferences.dateFormat().get()))
|
||||
@ -576,7 +576,7 @@ class MangaInfoScreenModel(
|
||||
}
|
||||
|
||||
private fun getUnreadChapters(): List<Chapter> {
|
||||
val chapterItems = if (skipFiltered) filteredChapters.orEmpty().toList() else allChapters.orEmpty()
|
||||
val chapterItems = if (skipFiltered) filteredChapters.orEmpty() else allChapters.orEmpty()
|
||||
return chapterItems
|
||||
.filter { (chapter, dlStatus) -> !chapter.read && dlStatus == Download.State.NOT_DOWNLOADED }
|
||||
.map { it.chapter }
|
||||
@ -664,7 +664,7 @@ class MangaInfoScreenModel(
|
||||
|
||||
fun markPreviousChapterRead(pointer: Chapter) {
|
||||
val successState = successState ?: return
|
||||
val chapters = filteredChapters.orEmpty().map { it.chapter }.toList()
|
||||
val chapters = filteredChapters.orEmpty().map { it.chapter }
|
||||
val prevChapters = if (successState.manga.sortDescending()) chapters.asReversed() else chapters
|
||||
val pointerPos = prevChapters.indexOf(pointer)
|
||||
if (pointerPos != -1) markChaptersRead(prevChapters.take(pointerPos), true)
|
||||
@ -743,13 +743,13 @@ class MangaInfoScreenModel(
|
||||
* Sets the read filter and requests an UI update.
|
||||
* @param state whether to display only unread chapters or all chapters.
|
||||
*/
|
||||
fun setUnreadFilter(state: TriStateFilter) {
|
||||
fun setUnreadFilter(state: TriState) {
|
||||
val manga = successState?.manga ?: return
|
||||
|
||||
val flag = when (state) {
|
||||
TriStateFilter.DISABLED -> Manga.SHOW_ALL
|
||||
TriStateFilter.ENABLED_IS -> Manga.CHAPTER_SHOW_UNREAD
|
||||
TriStateFilter.ENABLED_NOT -> Manga.CHAPTER_SHOW_READ
|
||||
TriState.DISABLED -> Manga.SHOW_ALL
|
||||
TriState.ENABLED_IS -> Manga.CHAPTER_SHOW_UNREAD
|
||||
TriState.ENABLED_NOT -> Manga.CHAPTER_SHOW_READ
|
||||
}
|
||||
coroutineScope.launchNonCancellable {
|
||||
setMangaChapterFlags.awaitSetUnreadFilter(manga, flag)
|
||||
@ -760,13 +760,13 @@ class MangaInfoScreenModel(
|
||||
* Sets the download filter and requests an UI update.
|
||||
* @param state whether to display only downloaded chapters or all chapters.
|
||||
*/
|
||||
fun setDownloadedFilter(state: TriStateFilter) {
|
||||
fun setDownloadedFilter(state: TriState) {
|
||||
val manga = successState?.manga ?: return
|
||||
|
||||
val flag = when (state) {
|
||||
TriStateFilter.DISABLED -> Manga.SHOW_ALL
|
||||
TriStateFilter.ENABLED_IS -> Manga.CHAPTER_SHOW_DOWNLOADED
|
||||
TriStateFilter.ENABLED_NOT -> Manga.CHAPTER_SHOW_NOT_DOWNLOADED
|
||||
TriState.DISABLED -> Manga.SHOW_ALL
|
||||
TriState.ENABLED_IS -> Manga.CHAPTER_SHOW_DOWNLOADED
|
||||
TriState.ENABLED_NOT -> Manga.CHAPTER_SHOW_NOT_DOWNLOADED
|
||||
}
|
||||
|
||||
coroutineScope.launchNonCancellable {
|
||||
@ -778,13 +778,13 @@ class MangaInfoScreenModel(
|
||||
* Sets the bookmark filter and requests an UI update.
|
||||
* @param state whether to display only bookmarked chapters or all chapters.
|
||||
*/
|
||||
fun setBookmarkedFilter(state: TriStateFilter) {
|
||||
fun setBookmarkedFilter(state: TriState) {
|
||||
val manga = successState?.manga ?: return
|
||||
|
||||
val flag = when (state) {
|
||||
TriStateFilter.DISABLED -> Manga.SHOW_ALL
|
||||
TriStateFilter.ENABLED_IS -> Manga.CHAPTER_SHOW_BOOKMARKED
|
||||
TriStateFilter.ENABLED_NOT -> Manga.CHAPTER_SHOW_NOT_BOOKMARKED
|
||||
TriState.DISABLED -> Manga.SHOW_ALL
|
||||
TriState.ENABLED_IS -> Manga.CHAPTER_SHOW_BOOKMARKED
|
||||
TriState.ENABLED_NOT -> Manga.CHAPTER_SHOW_NOT_BOOKMARKED
|
||||
}
|
||||
|
||||
coroutineScope.launchNonCancellable {
|
||||
@ -987,8 +987,9 @@ sealed class MangaScreenState {
|
||||
val hasPromptedToAddBefore: Boolean = false,
|
||||
) : MangaScreenState() {
|
||||
|
||||
val processedChapters: Sequence<ChapterItem>
|
||||
get() = chapters.applyFilters(manga)
|
||||
val processedChapters by lazy {
|
||||
chapters.applyFilters(manga).toList()
|
||||
}
|
||||
|
||||
val trackingAvailable: Boolean
|
||||
get() = trackItems.isNotEmpty()
|
||||
|
@ -31,6 +31,7 @@ import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
@ -51,6 +52,7 @@ import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.domain.manga.model.orientationType
|
||||
import eu.kanade.presentation.reader.ChapterNavigator
|
||||
import eu.kanade.presentation.reader.PageIndicatorText
|
||||
import eu.kanade.presentation.reader.settings.ReaderSettingsDialog
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
|
||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||
@ -65,8 +67,8 @@ 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.ReaderColorFilterDialog
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsSheet
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressIndicator
|
||||
@ -235,13 +237,18 @@ class ReaderActivity : BaseActivity() {
|
||||
readingModeToast?.cancel()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
viewModel.flushReadTimer()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set menu visibility again on activity resume to apply immersive mode again if needed.
|
||||
* Helps with rotations.
|
||||
*/
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
viewModel.setReadStartTime()
|
||||
viewModel.restartReadTimer()
|
||||
setMenuVisibility(viewModel.state.value.menuVisible, animate = false)
|
||||
}
|
||||
|
||||
@ -384,6 +391,8 @@ class ReaderActivity : BaseActivity() {
|
||||
|
||||
binding.dialogRoot.setComposeContent {
|
||||
val state by viewModel.state.collectAsState()
|
||||
val settingsScreenModel = remember { ReaderSettingsScreenModel() }
|
||||
|
||||
val onDismissRequest = viewModel::closeDialog
|
||||
when (state.dialog) {
|
||||
is ReaderViewModel.Dialog.Loading -> {
|
||||
@ -401,14 +410,12 @@ class ReaderActivity : BaseActivity() {
|
||||
},
|
||||
)
|
||||
}
|
||||
is ReaderViewModel.Dialog.ColorFilter -> {
|
||||
setMenuVisibility(false)
|
||||
ReaderColorFilterDialog(
|
||||
onDismissRequest = {
|
||||
onDismissRequest()
|
||||
setMenuVisibility(true)
|
||||
},
|
||||
readerPreferences = viewModel.readerPreferences,
|
||||
is ReaderViewModel.Dialog.Settings -> {
|
||||
ReaderSettingsDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
onShowMenus = { setMenuVisibility(true) },
|
||||
onHideMenus = { setMenuVisibility(false) },
|
||||
screenModel = settingsScreenModel,
|
||||
)
|
||||
}
|
||||
is ReaderViewModel.Dialog.PageActions -> {
|
||||
@ -541,7 +548,7 @@ class ReaderActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
// Settings sheet
|
||||
with(binding.actionSettings) {
|
||||
with(binding.actionSettingsLegacy) {
|
||||
setTooltip(R.string.action_settings)
|
||||
|
||||
var readerSettingSheet: ReaderSettingsSheet? = null
|
||||
@ -551,13 +558,11 @@ class ReaderActivity : BaseActivity() {
|
||||
readerSettingSheet = ReaderSettingsSheet(this@ReaderActivity).apply { show() }
|
||||
}
|
||||
}
|
||||
|
||||
// Color filter sheet
|
||||
with(binding.actionColorSettings) {
|
||||
setTooltip(R.string.custom_filter)
|
||||
with(binding.actionSettings) {
|
||||
setTooltip(R.string.action_settings)
|
||||
|
||||
setOnClickListener {
|
||||
viewModel.openColorFilterDialog()
|
||||
viewModel.openSettingsDialog()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -588,7 +593,7 @@ class ReaderActivity : BaseActivity() {
|
||||
* Sets the visibility of the menu according to [visible] and with an optional parameter to
|
||||
* [animate] the views.
|
||||
*/
|
||||
fun setMenuVisibility(visible: Boolean, animate: Boolean = true) {
|
||||
private fun setMenuVisibility(visible: Boolean, animate: Boolean = true) {
|
||||
viewModel.showMenus(visible)
|
||||
if (visible) {
|
||||
windowInsetsController.show(WindowInsetsCompat.Type.systemBars())
|
||||
@ -793,7 +798,6 @@ class ReaderActivity : BaseActivity() {
|
||||
* Called from the viewer whenever a [page] is marked as active. It updates the values of the
|
||||
* bottom menu and delegates the change to the presenter.
|
||||
*/
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun onPageSelected(page: ReaderPage) {
|
||||
viewModel.onPageSelected(page)
|
||||
}
|
||||
@ -811,7 +815,7 @@ class ReaderActivity : BaseActivity() {
|
||||
* the viewer is reaching the beginning or end of a chapter or the transition page is active.
|
||||
*/
|
||||
fun requestPreloadChapter(chapter: ReaderChapter) {
|
||||
lifecycleScope.launchIO { viewModel.preloadChapter(chapter) }
|
||||
lifecycleScope.launchIO { viewModel.preload(chapter) }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -898,7 +902,7 @@ class ReaderActivity : BaseActivity() {
|
||||
/**
|
||||
* Updates viewer inset depending on fullscreen reader preferences.
|
||||
*/
|
||||
fun updateViewerInset(fullscreen: Boolean) {
|
||||
private fun updateViewerInset(fullscreen: Boolean) {
|
||||
viewModel.state.value.viewer?.getView()?.applyInsetter {
|
||||
if (!fullscreen) {
|
||||
type(navigationBars = true, statusBars = true) {
|
||||
|
@ -10,10 +10,8 @@ import eu.kanade.domain.chapter.model.toDbChapter
|
||||
import eu.kanade.domain.manga.interactor.SetMangaViewerFlags
|
||||
import eu.kanade.domain.manga.model.orientationType
|
||||
import eu.kanade.domain.manga.model.readingModeType
|
||||
import eu.kanade.domain.track.model.toDbTrack
|
||||
import eu.kanade.domain.track.service.DelayedTrackingUpdateJob
|
||||
import eu.kanade.domain.track.interactor.TrackChapter
|
||||
import eu.kanade.domain.track.service.TrackPreferences
|
||||
import eu.kanade.domain.track.store.DelayedTrackingStore
|
||||
import eu.kanade.tachiyomi.data.database.models.toDomainChapter
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.data.download.DownloadProvider
|
||||
@ -21,10 +19,10 @@ import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.data.saver.Image
|
||||
import eu.kanade.tachiyomi.data.saver.ImageSaver
|
||||
import eu.kanade.tachiyomi.data.saver.Location
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.ui.reader.loader.ChapterLoader
|
||||
import eu.kanade.tachiyomi.ui.reader.loader.DownloadPageLoader
|
||||
import eu.kanade.tachiyomi.ui.reader.model.InsertPage
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
||||
@ -41,11 +39,8 @@ import eu.kanade.tachiyomi.util.lang.byteSize
|
||||
import eu.kanade.tachiyomi.util.lang.takeBytes
|
||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||
import eu.kanade.tachiyomi.util.storage.cacheImageDir
|
||||
import eu.kanade.tachiyomi.util.system.isOnline
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
@ -56,7 +51,6 @@ import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.util.lang.launchIO
|
||||
@ -75,8 +69,6 @@ import tachiyomi.domain.history.model.HistoryUpdate
|
||||
import tachiyomi.domain.manga.interactor.GetManga
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
import tachiyomi.domain.track.interactor.GetTracks
|
||||
import tachiyomi.domain.track.interactor.InsertTrack
|
||||
import tachiyomi.source.local.isLocal
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
@ -96,12 +88,10 @@ class ReaderViewModel(
|
||||
private val basePreferences: BasePreferences = Injekt.get(),
|
||||
private val downloadPreferences: DownloadPreferences = Injekt.get(),
|
||||
private val trackPreferences: TrackPreferences = Injekt.get(),
|
||||
private val delayedTrackingStore: DelayedTrackingStore = Injekt.get(),
|
||||
private val trackChapter: TrackChapter = Injekt.get(),
|
||||
private val getManga: GetManga = Injekt.get(),
|
||||
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
|
||||
private val getNextChapters: GetNextChapters = Injekt.get(),
|
||||
private val getTracks: GetTracks = Injekt.get(),
|
||||
private val insertTrack: InsertTrack = Injekt.get(),
|
||||
private val upsertHistory: UpsertHistory = Injekt.get(),
|
||||
private val updateChapter: UpdateChapter = Injekt.get(),
|
||||
private val setMangaViewerFlags: SetMangaViewerFlags = Injekt.get(),
|
||||
@ -197,13 +187,8 @@ class ReaderViewModel(
|
||||
.map(::ReaderChapter)
|
||||
}
|
||||
|
||||
private var hasTrackers: Boolean = false
|
||||
private val checkTrackers: (Manga) -> Unit = { manga ->
|
||||
val tracks = runBlocking { getTracks.await(manga.id) }
|
||||
hasTrackers = tracks.isNotEmpty()
|
||||
}
|
||||
|
||||
private val incognitoMode = preferences.incognitoMode().get()
|
||||
private val downloadAheadAmount = downloadPreferences.autoDownloadWhileReading().get()
|
||||
|
||||
init {
|
||||
// To save state
|
||||
@ -257,8 +242,6 @@ class ReaderViewModel(
|
||||
mutableState.update { it.copy(manga = manga) }
|
||||
if (chapterId == -1L) chapterId = initialChapterId
|
||||
|
||||
checkTrackers(manga)
|
||||
|
||||
val context = Injekt.get<Application>()
|
||||
val source = sourceManager.getOrStub(manga.source)
|
||||
loader = ChapterLoader(context, downloadManager, downloadProvider, manga, source)
|
||||
@ -312,12 +295,15 @@ class ReaderViewModel(
|
||||
* Called when the user changed to the given [chapter] when changing pages from the viewer.
|
||||
* It's used only to set this chapter as active.
|
||||
*/
|
||||
private suspend fun loadNewChapter(chapter: ReaderChapter) {
|
||||
private fun loadNewChapter(chapter: ReaderChapter) {
|
||||
val loader = loader ?: return
|
||||
|
||||
logcat { "Loading ${chapter.chapter.url}" }
|
||||
viewModelScope.launchIO {
|
||||
logcat { "Loading ${chapter.chapter.url}" }
|
||||
|
||||
flushReadTimer()
|
||||
restartReadTimer()
|
||||
|
||||
withIOContext {
|
||||
try {
|
||||
loadChapter(loader, chapter)
|
||||
} catch (e: Throwable) {
|
||||
@ -356,7 +342,7 @@ class ReaderViewModel(
|
||||
* Called when the viewers decide it's a good time to preload a [chapter] and improve the UX so
|
||||
* that the user doesn't have to wait too long to continue reading.
|
||||
*/
|
||||
private suspend fun preload(chapter: ReaderChapter) {
|
||||
suspend fun preload(chapter: ReaderChapter) {
|
||||
if (chapter.state is ReaderChapter.State.Loaded || chapter.state == ReaderChapter.State.Loading) {
|
||||
return
|
||||
}
|
||||
@ -395,9 +381,7 @@ class ReaderViewModel(
|
||||
|
||||
fun onViewerLoaded(viewer: Viewer?) {
|
||||
mutableState.update {
|
||||
it.copy(
|
||||
viewer = viewer,
|
||||
)
|
||||
it.copy(viewer = viewer)
|
||||
}
|
||||
}
|
||||
|
||||
@ -412,31 +396,19 @@ class ReaderViewModel(
|
||||
return
|
||||
}
|
||||
|
||||
val currentChapters = state.value.viewerChapters ?: return
|
||||
val pages = page.chapter.pages ?: return
|
||||
val selectedChapter = page.chapter
|
||||
val pages = selectedChapter.pages ?: return
|
||||
|
||||
// Save last page read and mark as read if needed
|
||||
saveReadingProgress()
|
||||
mutableState.update {
|
||||
it.copy(
|
||||
currentPage = page.index + 1,
|
||||
)
|
||||
}
|
||||
if (!incognitoMode) {
|
||||
selectedChapter.chapter.last_page_read = page.index
|
||||
if (selectedChapter.pages?.lastIndex == page.index) {
|
||||
selectedChapter.chapter.read = true
|
||||
updateTrackChapterRead(selectedChapter)
|
||||
deleteChapterIfNeeded(selectedChapter)
|
||||
}
|
||||
viewModelScope.launchNonCancellable {
|
||||
updateChapterProgress(selectedChapter, page.index)
|
||||
}
|
||||
|
||||
if (selectedChapter != currentChapters.currChapter) {
|
||||
if (selectedChapter != getCurrentChapter()) {
|
||||
logcat { "Setting ${selectedChapter.chapter.url} as active" }
|
||||
setReadStartTime()
|
||||
viewModelScope.launch { loadNewChapter(selectedChapter) }
|
||||
loadNewChapter(selectedChapter)
|
||||
}
|
||||
|
||||
val inDownloadRange = page.number.toDouble() / pages.size > 0.25
|
||||
if (inDownloadRange) {
|
||||
downloadNextChapters()
|
||||
@ -444,12 +416,11 @@ class ReaderViewModel(
|
||||
}
|
||||
|
||||
private fun downloadNextChapters() {
|
||||
if (downloadAheadAmount == 0) return
|
||||
val manga = manga ?: return
|
||||
val amount = downloadPreferences.autoDownloadWhileReading().get()
|
||||
if (amount == 0 || !manga.favorite) return
|
||||
|
||||
// Only download ahead if current + next chapter is already downloaded too to avoid jank
|
||||
if (getCurrentChapter()?.pageLoader?.isLocal == true) return
|
||||
if (getCurrentChapter()?.pageLoader !is DownloadPageLoader) return
|
||||
val nextChapter = state.value.viewerChapters?.nextChapter?.chapter ?: return
|
||||
|
||||
viewModelScope.launchIO {
|
||||
@ -466,7 +437,7 @@ class ReaderViewModel(
|
||||
} else {
|
||||
this
|
||||
}
|
||||
}.take(amount)
|
||||
}.take(downloadAheadAmount)
|
||||
|
||||
downloadManager.downloadChapters(
|
||||
manga,
|
||||
@ -507,40 +478,51 @@ class ReaderViewModel(
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when reader chapter is changed in reader or when activity is paused.
|
||||
* Saves the chapter progress (last read page and whether it's read)
|
||||
* if incognito mode isn't on.
|
||||
*/
|
||||
private fun saveReadingProgress() {
|
||||
private suspend fun updateChapterProgress(readerChapter: ReaderChapter, pageIndex: Int) {
|
||||
mutableState.update {
|
||||
it.copy(currentPage = pageIndex + 1)
|
||||
}
|
||||
|
||||
if (!incognitoMode) {
|
||||
readerChapter.requestedPage = pageIndex
|
||||
readerChapter.chapter.last_page_read = pageIndex
|
||||
|
||||
if (readerChapter.pages?.lastIndex == pageIndex) {
|
||||
readerChapter.chapter.read = true
|
||||
updateTrackChapterRead(readerChapter)
|
||||
deleteChapterIfNeeded(readerChapter)
|
||||
}
|
||||
|
||||
updateChapter.await(
|
||||
ChapterUpdate(
|
||||
id = readerChapter.chapter.id!!,
|
||||
read = readerChapter.chapter.read,
|
||||
bookmark = readerChapter.chapter.bookmark,
|
||||
lastPageRead = readerChapter.chapter.last_page_read.toLong(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun restartReadTimer() {
|
||||
chapterReadStartTime = Date().time
|
||||
}
|
||||
|
||||
fun flushReadTimer() {
|
||||
getCurrentChapter()?.let {
|
||||
viewModelScope.launchNonCancellable {
|
||||
saveChapterProgress(it)
|
||||
saveChapterHistory(it)
|
||||
updateHistory(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves this [readerChapter] progress (last read page and whether it's read)
|
||||
* if incognito mode isn't on.
|
||||
* Saves the chapter last read history if incognito mode isn't on.
|
||||
*/
|
||||
private suspend fun saveChapterProgress(readerChapter: ReaderChapter) {
|
||||
if (incognitoMode) return
|
||||
|
||||
val chapter = readerChapter.chapter
|
||||
readerChapter.requestedPage = chapter.last_page_read
|
||||
updateChapter.await(
|
||||
ChapterUpdate(
|
||||
id = chapter.id!!,
|
||||
read = chapter.read,
|
||||
bookmark = chapter.bookmark,
|
||||
lastPageRead = chapter.last_page_read.toLong(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves this [readerChapter] last read history if incognito mode isn't on.
|
||||
*/
|
||||
private suspend fun saveChapterHistory(readerChapter: ReaderChapter) {
|
||||
private suspend fun updateHistory(readerChapter: ReaderChapter) {
|
||||
if (incognitoMode) return
|
||||
|
||||
val chapterId = readerChapter.chapter.id!!
|
||||
@ -551,17 +533,6 @@ class ReaderViewModel(
|
||||
chapterReadStartTime = null
|
||||
}
|
||||
|
||||
fun setReadStartTime() {
|
||||
chapterReadStartTime = Date().time
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the activity to preload the given [chapter].
|
||||
*/
|
||||
suspend fun preloadChapter(chapter: ReaderChapter) {
|
||||
preload(chapter)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the activity to load and set the next chapter as active.
|
||||
*/
|
||||
@ -714,8 +685,8 @@ class ReaderViewModel(
|
||||
mutableState.update { it.copy(dialog = Dialog.PageActions(page)) }
|
||||
}
|
||||
|
||||
fun openColorFilterDialog() {
|
||||
mutableState.update { it.copy(dialog = Dialog.ColorFilter) }
|
||||
fun openSettingsDialog() {
|
||||
mutableState.update { it.copy(dialog = Dialog.Settings) }
|
||||
}
|
||||
|
||||
fun closeDialog() {
|
||||
@ -839,44 +810,14 @@ class ReaderViewModel(
|
||||
* will run in a background thread and errors are ignored.
|
||||
*/
|
||||
private fun updateTrackChapterRead(readerChapter: ReaderChapter) {
|
||||
if (incognitoMode || !hasTrackers) return
|
||||
if (incognitoMode) return
|
||||
if (!trackPreferences.autoUpdateTrack().get()) return
|
||||
|
||||
val manga = manga ?: return
|
||||
val chapterRead = readerChapter.chapter.chapter_number.toDouble()
|
||||
|
||||
val trackManager = Injekt.get<TrackManager>()
|
||||
val context = Injekt.get<Application>()
|
||||
|
||||
viewModelScope.launchNonCancellable {
|
||||
getTracks.await(manga.id)
|
||||
.mapNotNull { track ->
|
||||
val service = trackManager.getService(track.syncId)
|
||||
if (service != null && service.isLogged && chapterRead > track.lastChapterRead) {
|
||||
val updatedTrack = track.copy(lastChapterRead = chapterRead)
|
||||
|
||||
// We want these to execute even if the presenter is destroyed and leaks
|
||||
// for a while. The view can still be garbage collected.
|
||||
async {
|
||||
runCatching {
|
||||
try {
|
||||
if (!context.isOnline()) error("Couldn't update tracker as device is offline")
|
||||
service.update(updatedTrack.toDbTrack(), true)
|
||||
insertTrack.await(updatedTrack)
|
||||
} catch (e: Exception) {
|
||||
delayedTrackingStore.addItem(updatedTrack)
|
||||
DelayedTrackingUpdateJob.setupTask(context)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
.awaitAll()
|
||||
.mapNotNull { it.exceptionOrNull() }
|
||||
.forEach { logcat(LogPriority.INFO, it) }
|
||||
trackChapter.await(context, manga.id, readerChapter.chapter.chapter_number.toDouble())
|
||||
}
|
||||
}
|
||||
|
||||
@ -922,7 +863,7 @@ class ReaderViewModel(
|
||||
|
||||
sealed class Dialog {
|
||||
object Loading : Dialog()
|
||||
object ColorFilter : Dialog()
|
||||
object Settings : Dialog()
|
||||
data class PageActions(val page: ReaderPage) : Dialog()
|
||||
}
|
||||
|
||||
|
@ -1,164 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.setting
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.DialogWindowProvider
|
||||
import androidx.core.graphics.alpha
|
||||
import androidx.core.graphics.blue
|
||||
import androidx.core.graphics.green
|
||||
import androidx.core.graphics.red
|
||||
import eu.kanade.presentation.components.AdaptiveSheet
|
||||
import eu.kanade.presentation.more.settings.LocalPreferenceMinHeight
|
||||
import eu.kanade.presentation.more.settings.Preference
|
||||
import eu.kanade.presentation.more.settings.PreferenceScreen
|
||||
import eu.kanade.presentation.util.collectAsState
|
||||
import eu.kanade.tachiyomi.R
|
||||
import tachiyomi.core.preference.getAndSet
|
||||
|
||||
@Composable
|
||||
fun ReaderColorFilterDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
readerPreferences: ReaderPreferences,
|
||||
) {
|
||||
val colorFilterModes = buildList {
|
||||
addAll(
|
||||
listOf(
|
||||
R.string.label_default,
|
||||
R.string.filter_mode_multiply,
|
||||
R.string.filter_mode_screen,
|
||||
),
|
||||
)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
addAll(
|
||||
listOf(
|
||||
R.string.filter_mode_overlay,
|
||||
R.string.filter_mode_lighten,
|
||||
R.string.filter_mode_darken,
|
||||
),
|
||||
)
|
||||
}
|
||||
}.map { stringResource(it) }
|
||||
|
||||
val customBrightness by readerPreferences.customBrightness().collectAsState()
|
||||
val customBrightnessValue by readerPreferences.customBrightnessValue().collectAsState()
|
||||
val colorFilter by readerPreferences.colorFilter().collectAsState()
|
||||
val colorFilterValue by readerPreferences.colorFilterValue().collectAsState()
|
||||
val colorFilterMode by readerPreferences.colorFilterMode().collectAsState()
|
||||
|
||||
AdaptiveSheet(
|
||||
onDismissRequest = onDismissRequest,
|
||||
) {
|
||||
(LocalView.current.parent as? DialogWindowProvider)?.window?.setDimAmount(0f)
|
||||
|
||||
CompositionLocalProvider(
|
||||
LocalPreferenceMinHeight provides 48.dp,
|
||||
) {
|
||||
PreferenceScreen(
|
||||
items = listOfNotNull(
|
||||
Preference.PreferenceItem.SwitchPreference(
|
||||
pref = readerPreferences.customBrightness(),
|
||||
title = stringResource(R.string.pref_custom_brightness),
|
||||
),
|
||||
/**
|
||||
* Sets the brightness of the screen. Range is [-75, 100].
|
||||
* From -75 to -1 a semi-transparent black view is shown at the top with the minimum brightness.
|
||||
* From 1 to 100 it sets that value as brightness.
|
||||
* 0 sets system brightness and hides the overlay.
|
||||
*/
|
||||
Preference.PreferenceItem.SliderPreference(
|
||||
value = customBrightnessValue,
|
||||
title = stringResource(R.string.pref_custom_brightness),
|
||||
min = -75,
|
||||
max = 100,
|
||||
onValueChanged = {
|
||||
readerPreferences.customBrightnessValue().set(it)
|
||||
true
|
||||
},
|
||||
).takeIf { customBrightness },
|
||||
|
||||
Preference.PreferenceItem.SwitchPreference(
|
||||
pref = readerPreferences.colorFilter(),
|
||||
title = stringResource(R.string.pref_custom_color_filter),
|
||||
),
|
||||
Preference.PreferenceItem.SliderPreference(
|
||||
value = colorFilterValue.red,
|
||||
title = stringResource(R.string.color_filter_r_value),
|
||||
max = 255,
|
||||
onValueChanged = { newRValue ->
|
||||
readerPreferences.colorFilterValue().getAndSet {
|
||||
getColorValue(it, newRValue, RED_MASK, 16)
|
||||
}
|
||||
true
|
||||
},
|
||||
).takeIf { colorFilter },
|
||||
Preference.PreferenceItem.SliderPreference(
|
||||
value = colorFilterValue.green,
|
||||
title = stringResource(R.string.color_filter_g_value),
|
||||
max = 255,
|
||||
onValueChanged = { newRValue ->
|
||||
readerPreferences.colorFilterValue().getAndSet {
|
||||
getColorValue(it, newRValue, GREEN_MASK, 8)
|
||||
}
|
||||
true
|
||||
},
|
||||
).takeIf { colorFilter },
|
||||
Preference.PreferenceItem.SliderPreference(
|
||||
value = colorFilterValue.blue,
|
||||
title = stringResource(R.string.color_filter_b_value),
|
||||
max = 255,
|
||||
onValueChanged = { newRValue ->
|
||||
readerPreferences.colorFilterValue().getAndSet {
|
||||
getColorValue(it, newRValue, BLUE_MASK, 0)
|
||||
}
|
||||
true
|
||||
},
|
||||
).takeIf { colorFilter },
|
||||
Preference.PreferenceItem.SliderPreference(
|
||||
value = colorFilterValue.alpha,
|
||||
title = stringResource(R.string.color_filter_a_value),
|
||||
max = 255,
|
||||
onValueChanged = { newRValue ->
|
||||
readerPreferences.colorFilterValue().getAndSet {
|
||||
getColorValue(it, newRValue, ALPHA_MASK, 24)
|
||||
}
|
||||
true
|
||||
},
|
||||
).takeIf { colorFilter },
|
||||
Preference.PreferenceItem.BasicListPreference(
|
||||
value = colorFilterMode.toString(),
|
||||
title = stringResource(R.string.pref_color_filter_mode),
|
||||
entries = colorFilterModes
|
||||
.mapIndexed { index, mode -> index.toString() to mode }
|
||||
.toMap(),
|
||||
onValueChanged = { newValue ->
|
||||
readerPreferences.colorFilterMode().set(newValue.toInt())
|
||||
true
|
||||
},
|
||||
).takeIf { colorFilter },
|
||||
|
||||
Preference.PreferenceItem.SwitchPreference(
|
||||
pref = readerPreferences.grayscale(),
|
||||
title = stringResource(R.string.pref_grayscale),
|
||||
),
|
||||
Preference.PreferenceItem.SwitchPreference(
|
||||
pref = readerPreferences.invertedColors(),
|
||||
title = stringResource(R.string.pref_inverted_colors),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getColorValue(currentColor: Int, color: Int, mask: Long, bitShift: Int): Int {
|
||||
return (color shl bitShift) or (currentColor and mask.inv().toInt())
|
||||
}
|
||||
private const val ALPHA_MASK: Long = 0xFF000000
|
||||
private const val RED_MASK: Long = 0x00FF0000
|
||||
private const val GREEN_MASK: Long = 0x0000FF00
|
||||
private const val BLUE_MASK: Long = 0x000000FF
|
@ -1,53 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.setting
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.databinding.ReaderGeneralSettingsBinding
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.util.preference.bindToPreference
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
/**
|
||||
* Sheet to show reader and viewer preferences.
|
||||
*/
|
||||
class ReaderGeneralSettings @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
NestedScrollView(context, attrs) {
|
||||
|
||||
private val readerPreferences: ReaderPreferences by injectLazy()
|
||||
|
||||
private val binding = ReaderGeneralSettingsBinding.inflate(LayoutInflater.from(context), this, false)
|
||||
|
||||
init {
|
||||
addView(binding.root)
|
||||
|
||||
initGeneralPreferences()
|
||||
}
|
||||
|
||||
/**
|
||||
* Init general reader preferences.
|
||||
*/
|
||||
private fun initGeneralPreferences() {
|
||||
binding.backgroundColor.bindToIntPreference(readerPreferences.readerTheme(), R.array.reader_themes_values)
|
||||
binding.showPageNumber.bindToPreference(readerPreferences.showPageNumber())
|
||||
binding.fullscreen.bindToPreference(readerPreferences.fullscreen())
|
||||
readerPreferences.fullscreen().changes()
|
||||
.onEach {
|
||||
// If the preference is explicitly disabled, that means the setting was configured since there is a cutout
|
||||
binding.cutoutShort.isVisible = it && ((context as ReaderActivity).hasCutout || !readerPreferences.cutoutShort().get())
|
||||
binding.cutoutShort.bindToPreference(readerPreferences.cutoutShort())
|
||||
}
|
||||
.launchIn((context as ReaderActivity).lifecycleScope)
|
||||
|
||||
binding.keepscreen.bindToPreference(readerPreferences.keepScreenOn())
|
||||
binding.longTap.bindToPreference(readerPreferences.readWithLongTap())
|
||||
binding.alwaysShowChapterTransition.bindToPreference(readerPreferences.alwaysShowChapterTransition())
|
||||
binding.pageTransitions.bindToPreference(readerPreferences.pageTransitions())
|
||||
}
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.setting
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import eu.kanade.domain.manga.model.orientationType
|
||||
import eu.kanade.domain.manga.model.readingModeType
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.databinding.ReaderReadingModeSettingsBinding
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerViewer
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
|
||||
import eu.kanade.tachiyomi.util.preference.bindToPreference
|
||||
import eu.kanade.tachiyomi.util.system.isReleaseBuildType
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
/**
|
||||
* Sheet to show reader and viewer preferences.
|
||||
*/
|
||||
class ReaderReadingModeSettings @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
NestedScrollView(context, attrs) {
|
||||
|
||||
private val readerPreferences: ReaderPreferences by injectLazy()
|
||||
|
||||
private val binding = ReaderReadingModeSettingsBinding.inflate(LayoutInflater.from(context), this, false)
|
||||
|
||||
init {
|
||||
addView(binding.root)
|
||||
|
||||
initGeneralPreferences()
|
||||
|
||||
when ((context as ReaderActivity).viewModel.state.value.viewer) {
|
||||
is PagerViewer -> initPagerPreferences()
|
||||
is WebtoonViewer -> initWebtoonPreferences()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init general reader preferences.
|
||||
*/
|
||||
private fun initGeneralPreferences() {
|
||||
binding.viewer.onItemSelectedListener = { position ->
|
||||
val readingModeType = ReadingModeType.fromSpinner(position)
|
||||
(context as ReaderActivity).viewModel.setMangaReadingMode(readingModeType.flagValue)
|
||||
|
||||
val mangaViewer = (context as ReaderActivity).viewModel.getMangaReadingMode()
|
||||
if (mangaViewer == ReadingModeType.WEBTOON.flagValue || mangaViewer == ReadingModeType.CONTINUOUS_VERTICAL.flagValue) {
|
||||
initWebtoonPreferences()
|
||||
} else {
|
||||
initPagerPreferences()
|
||||
}
|
||||
}
|
||||
binding.viewer.setSelection((context as ReaderActivity).viewModel.manga?.readingModeType?.let { ReadingModeType.fromPreference(it.toInt()).prefValue } ?: ReadingModeType.DEFAULT.prefValue)
|
||||
|
||||
binding.rotationMode.onItemSelectedListener = { position ->
|
||||
val rotationType = OrientationType.fromSpinner(position)
|
||||
(context as ReaderActivity).viewModel.setMangaOrientationType(rotationType.flagValue)
|
||||
}
|
||||
binding.rotationMode.setSelection((context as ReaderActivity).viewModel.manga?.orientationType?.let { OrientationType.fromPreference(it.toInt()).prefValue } ?: OrientationType.DEFAULT.prefValue)
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the preferences for the pager reader.
|
||||
*/
|
||||
private fun initPagerPreferences() {
|
||||
binding.webtoonPrefsGroup.root.isVisible = false
|
||||
binding.pagerPrefsGroup.root.isVisible = true
|
||||
|
||||
binding.pagerPrefsGroup.tappingInverted.bindToPreference(readerPreferences.pagerNavInverted(), ReaderPreferences.TappingInvertMode::class.java)
|
||||
binding.pagerPrefsGroup.navigatePan.bindToPreference(readerPreferences.navigateToPan())
|
||||
|
||||
binding.pagerPrefsGroup.pagerNav.bindToPreference(readerPreferences.navigationModePager())
|
||||
readerPreferences.navigationModePager().changes()
|
||||
.onEach {
|
||||
val isTappingEnabled = it != 5
|
||||
binding.pagerPrefsGroup.tappingInverted.isVisible = isTappingEnabled
|
||||
binding.pagerPrefsGroup.navigatePan.isVisible = isTappingEnabled
|
||||
}
|
||||
.launchIn((context as ReaderActivity).lifecycleScope)
|
||||
// Makes so that landscape zoom gets hidden away when image scale type is not fit screen
|
||||
binding.pagerPrefsGroup.scaleType.bindToPreference(readerPreferences.imageScaleType(), 1)
|
||||
readerPreferences.imageScaleType().changes()
|
||||
.onEach { binding.pagerPrefsGroup.landscapeZoom.isVisible = it == 1 }
|
||||
.launchIn((context as ReaderActivity).lifecycleScope)
|
||||
binding.pagerPrefsGroup.landscapeZoom.bindToPreference(readerPreferences.landscapeZoom())
|
||||
|
||||
binding.pagerPrefsGroup.zoomStart.bindToPreference(readerPreferences.zoomStart(), 1)
|
||||
binding.pagerPrefsGroup.cropBorders.bindToPreference(readerPreferences.cropBorders())
|
||||
|
||||
binding.pagerPrefsGroup.dualPageSplit.bindToPreference(readerPreferences.dualPageSplitPaged())
|
||||
readerPreferences.dualPageSplitPaged().changes()
|
||||
.onEach {
|
||||
binding.pagerPrefsGroup.dualPageInvert.isVisible = it
|
||||
if (it) {
|
||||
binding.pagerPrefsGroup.dualPageRotateToFit.isChecked = false
|
||||
}
|
||||
}
|
||||
.launchIn((context as ReaderActivity).lifecycleScope)
|
||||
binding.pagerPrefsGroup.dualPageInvert.bindToPreference(readerPreferences.dualPageInvertPaged())
|
||||
|
||||
binding.pagerPrefsGroup.dualPageRotateToFit.bindToPreference(readerPreferences.dualPageRotateToFit())
|
||||
readerPreferences.dualPageRotateToFit().changes()
|
||||
.onEach {
|
||||
binding.pagerPrefsGroup.dualPageRotateToFitInvert.isVisible = it
|
||||
if (it) {
|
||||
binding.pagerPrefsGroup.dualPageSplit.isChecked = false
|
||||
}
|
||||
}
|
||||
.launchIn((context as ReaderActivity).lifecycleScope)
|
||||
binding.pagerPrefsGroup.dualPageRotateToFitInvert.bindToPreference(readerPreferences.dualPageRotateToFitInvert())
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the preferences for the webtoon reader.
|
||||
*/
|
||||
private fun initWebtoonPreferences() {
|
||||
binding.pagerPrefsGroup.root.isVisible = false
|
||||
binding.webtoonPrefsGroup.root.isVisible = true
|
||||
|
||||
binding.webtoonPrefsGroup.tappingInverted.bindToPreference(readerPreferences.webtoonNavInverted(), ReaderPreferences.TappingInvertMode::class.java)
|
||||
|
||||
binding.webtoonPrefsGroup.webtoonNav.bindToPreference(readerPreferences.navigationModeWebtoon())
|
||||
readerPreferences.navigationModeWebtoon().changes()
|
||||
.onEach { binding.webtoonPrefsGroup.tappingInverted.isVisible = it != 5 }
|
||||
.launchIn((context as ReaderActivity).lifecycleScope)
|
||||
binding.webtoonPrefsGroup.cropBordersWebtoon.bindToPreference(readerPreferences.cropBordersWebtoon())
|
||||
binding.webtoonPrefsGroup.webtoonSidePadding.bindToIntPreference(readerPreferences.webtoonSidePadding(), R.array.webtoon_side_padding_values)
|
||||
|
||||
binding.webtoonPrefsGroup.dualPageSplit.bindToPreference(readerPreferences.dualPageSplitWebtoon())
|
||||
// Makes it so that dual page invert gets hidden away when dual page split is turned off
|
||||
readerPreferences.dualPageSplitWebtoon().changes()
|
||||
.onEach { binding.webtoonPrefsGroup.dualPageInvert.isVisible = it }
|
||||
.launchIn((context as ReaderActivity).lifecycleScope)
|
||||
binding.webtoonPrefsGroup.dualPageInvert.bindToPreference(readerPreferences.dualPageInvertWebtoon())
|
||||
|
||||
binding.webtoonPrefsGroup.longStripSplit.isVisible = !isReleaseBuildType
|
||||
binding.webtoonPrefsGroup.longStripSplit.bindToPreference(readerPreferences.longStripSplitWebtoon())
|
||||
|
||||
binding.webtoonPrefsGroup.doubleTapZoom.bindToPreference(readerPreferences.webtoonDoubleTapZoomEnabled())
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.setting
|
||||
|
||||
import cafe.adriel.voyager.core.model.ScreenModel
|
||||
import eu.kanade.tachiyomi.util.preference.toggle
|
||||
import tachiyomi.core.preference.Preference
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class ReaderSettingsScreenModel(
|
||||
val preferences: ReaderPreferences = Injekt.get(),
|
||||
) : ScreenModel {
|
||||
|
||||
fun togglePreference(preference: (ReaderPreferences) -> Preference<Boolean>) {
|
||||
preference(preferences).toggle()
|
||||
}
|
||||
}
|
@ -1,55 +1,136 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.setting
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import eu.kanade.domain.manga.model.orientationType
|
||||
import eu.kanade.domain.manga.model.readingModeType
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.databinding.CommonTabbedSheetBinding
|
||||
import eu.kanade.tachiyomi.databinding.ReaderReadingModeSettingsBinding
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.widget.ViewPagerAdapter
|
||||
import eu.kanade.tachiyomi.widget.sheet.BaseBottomSheetDialog
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerViewer
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
|
||||
import eu.kanade.tachiyomi.util.preference.bindToPreference
|
||||
import eu.kanade.tachiyomi.util.system.isReleaseBuildType
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class ReaderSettingsSheet(
|
||||
private val activity: ReaderActivity,
|
||||
) : BaseBottomSheetDialog(activity) {
|
||||
) : BottomSheetDialog(activity) {
|
||||
|
||||
private val tabs = listOf(
|
||||
ReaderReadingModeSettings(activity) to R.string.pref_category_reading_mode,
|
||||
ReaderGeneralSettings(activity) to R.string.pref_category_general,
|
||||
)
|
||||
private val readerPreferences: ReaderPreferences by injectLazy()
|
||||
|
||||
private lateinit var binding: CommonTabbedSheetBinding
|
||||
|
||||
override fun createView(inflater: LayoutInflater): View {
|
||||
binding = CommonTabbedSheetBinding.inflate(activity.layoutInflater)
|
||||
|
||||
val adapter = Adapter()
|
||||
binding.pager.adapter = adapter
|
||||
binding.tabs.setupWithViewPager(binding.pager)
|
||||
|
||||
return binding.root
|
||||
}
|
||||
private lateinit var binding: ReaderReadingModeSettingsBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
behavior.isFitToContents = false
|
||||
behavior.halfExpandedRatio = 0.25f
|
||||
binding = ReaderReadingModeSettingsBinding.inflate(activity.layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
initGeneralPreferences()
|
||||
|
||||
when (activity.viewModel.state.value.viewer) {
|
||||
is PagerViewer -> initPagerPreferences()
|
||||
is WebtoonViewer -> initWebtoonPreferences()
|
||||
}
|
||||
}
|
||||
|
||||
private inner class Adapter : ViewPagerAdapter() {
|
||||
private fun initGeneralPreferences() {
|
||||
binding.viewer.onItemSelectedListener = { position ->
|
||||
val readingModeType = ReadingModeType.fromSpinner(position)
|
||||
activity.viewModel.setMangaReadingMode(readingModeType.flagValue)
|
||||
|
||||
override fun createView(container: ViewGroup, position: Int): View {
|
||||
return tabs[position].first
|
||||
val mangaViewer = activity.viewModel.getMangaReadingMode()
|
||||
if (mangaViewer == ReadingModeType.WEBTOON.flagValue || mangaViewer == ReadingModeType.CONTINUOUS_VERTICAL.flagValue) {
|
||||
initWebtoonPreferences()
|
||||
} else {
|
||||
initPagerPreferences()
|
||||
}
|
||||
}
|
||||
binding.viewer.setSelection(activity.viewModel.manga?.readingModeType?.let { ReadingModeType.fromPreference(it.toInt()).prefValue } ?: ReadingModeType.DEFAULT.prefValue)
|
||||
|
||||
override fun getCount(): Int {
|
||||
return tabs.size
|
||||
binding.rotationMode.onItemSelectedListener = { position ->
|
||||
val rotationType = OrientationType.fromSpinner(position)
|
||||
activity.viewModel.setMangaOrientationType(rotationType.flagValue)
|
||||
}
|
||||
binding.rotationMode.setSelection(activity.viewModel.manga?.orientationType?.let { OrientationType.fromPreference(it.toInt()).prefValue } ?: OrientationType.DEFAULT.prefValue)
|
||||
}
|
||||
|
||||
override fun getPageTitle(position: Int): CharSequence {
|
||||
return activity.resources!!.getString(tabs[position].second)
|
||||
}
|
||||
private fun initPagerPreferences() {
|
||||
binding.webtoonPrefsGroup.root.isVisible = false
|
||||
binding.pagerPrefsGroup.root.isVisible = true
|
||||
|
||||
binding.pagerPrefsGroup.tappingInverted.bindToPreference(readerPreferences.pagerNavInverted(), ReaderPreferences.TappingInvertMode::class.java)
|
||||
binding.pagerPrefsGroup.navigatePan.bindToPreference(readerPreferences.navigateToPan())
|
||||
|
||||
binding.pagerPrefsGroup.pagerNav.bindToPreference(readerPreferences.navigationModePager())
|
||||
readerPreferences.navigationModePager().changes()
|
||||
.onEach {
|
||||
val isTappingEnabled = it != 5
|
||||
binding.pagerPrefsGroup.tappingInverted.isVisible = isTappingEnabled
|
||||
binding.pagerPrefsGroup.navigatePan.isVisible = isTappingEnabled
|
||||
}
|
||||
.launchIn(activity.lifecycleScope)
|
||||
// Makes so that landscape zoom gets hidden away when image scale type is not fit screen
|
||||
binding.pagerPrefsGroup.scaleType.bindToPreference(readerPreferences.imageScaleType(), 1)
|
||||
readerPreferences.imageScaleType().changes()
|
||||
.onEach { binding.pagerPrefsGroup.landscapeZoom.isVisible = it == 1 }
|
||||
.launchIn(activity.lifecycleScope)
|
||||
binding.pagerPrefsGroup.landscapeZoom.bindToPreference(readerPreferences.landscapeZoom())
|
||||
|
||||
binding.pagerPrefsGroup.zoomStart.bindToPreference(readerPreferences.zoomStart(), 1)
|
||||
binding.pagerPrefsGroup.cropBorders.bindToPreference(readerPreferences.cropBorders())
|
||||
|
||||
binding.pagerPrefsGroup.dualPageSplit.bindToPreference(readerPreferences.dualPageSplitPaged())
|
||||
readerPreferences.dualPageSplitPaged().changes()
|
||||
.onEach {
|
||||
binding.pagerPrefsGroup.dualPageInvert.isVisible = it
|
||||
if (it) {
|
||||
binding.pagerPrefsGroup.dualPageRotateToFit.isChecked = false
|
||||
}
|
||||
}
|
||||
.launchIn(activity.lifecycleScope)
|
||||
binding.pagerPrefsGroup.dualPageInvert.bindToPreference(readerPreferences.dualPageInvertPaged())
|
||||
|
||||
binding.pagerPrefsGroup.dualPageRotateToFit.bindToPreference(readerPreferences.dualPageRotateToFit())
|
||||
readerPreferences.dualPageRotateToFit().changes()
|
||||
.onEach {
|
||||
binding.pagerPrefsGroup.dualPageRotateToFitInvert.isVisible = it
|
||||
if (it) {
|
||||
binding.pagerPrefsGroup.dualPageSplit.isChecked = false
|
||||
}
|
||||
}
|
||||
.launchIn(activity.lifecycleScope)
|
||||
binding.pagerPrefsGroup.dualPageRotateToFitInvert.bindToPreference(readerPreferences.dualPageRotateToFitInvert())
|
||||
}
|
||||
|
||||
private fun initWebtoonPreferences() {
|
||||
binding.pagerPrefsGroup.root.isVisible = false
|
||||
binding.webtoonPrefsGroup.root.isVisible = true
|
||||
|
||||
binding.webtoonPrefsGroup.tappingInverted.bindToPreference(readerPreferences.webtoonNavInverted(), ReaderPreferences.TappingInvertMode::class.java)
|
||||
|
||||
binding.webtoonPrefsGroup.webtoonNav.bindToPreference(readerPreferences.navigationModeWebtoon())
|
||||
readerPreferences.navigationModeWebtoon().changes()
|
||||
.onEach { binding.webtoonPrefsGroup.tappingInverted.isVisible = it != 5 }
|
||||
.launchIn(activity.lifecycleScope)
|
||||
binding.webtoonPrefsGroup.cropBordersWebtoon.bindToPreference(readerPreferences.cropBordersWebtoon())
|
||||
binding.webtoonPrefsGroup.webtoonSidePadding.bindToIntPreference(readerPreferences.webtoonSidePadding(), R.array.webtoon_side_padding_values)
|
||||
|
||||
binding.webtoonPrefsGroup.dualPageSplit.bindToPreference(readerPreferences.dualPageSplitWebtoon())
|
||||
// Makes it so that dual page invert gets hidden away when dual page split is turned off
|
||||
readerPreferences.dualPageSplitWebtoon().changes()
|
||||
.onEach { binding.webtoonPrefsGroup.dualPageInvert.isVisible = it }
|
||||
.launchIn(activity.lifecycleScope)
|
||||
binding.webtoonPrefsGroup.dualPageInvert.bindToPreference(readerPreferences.dualPageInvertWebtoon())
|
||||
|
||||
binding.webtoonPrefsGroup.longStripSplit.isVisible = !isReleaseBuildType
|
||||
binding.webtoonPrefsGroup.longStripSplit.bindToPreference(readerPreferences.longStripSplitWebtoon())
|
||||
|
||||
binding.webtoonPrefsGroup.doubleTapZoom.bindToPreference(readerPreferences.webtoonDoubleTapZoomEnabled())
|
||||
}
|
||||
}
|
||||
|
@ -12,10 +12,10 @@ import androidx.compose.ui.Modifier
|
||||
import cafe.adriel.voyager.navigator.LocalNavigator
|
||||
import cafe.adriel.voyager.navigator.Navigator
|
||||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import eu.kanade.presentation.more.settings.screen.AboutScreen
|
||||
import eu.kanade.presentation.more.settings.screen.SettingsAppearanceScreen
|
||||
import eu.kanade.presentation.more.settings.screen.SettingsBackupAndSyncScreen
|
||||
import eu.kanade.presentation.more.settings.screen.SettingsMainScreen
|
||||
import eu.kanade.presentation.more.settings.screen.about.AboutScreen
|
||||
import eu.kanade.presentation.util.DefaultNavigatorScreenTransition
|
||||
import eu.kanade.presentation.util.LocalBackPress
|
||||
import eu.kanade.presentation.util.Screen
|
||||
|
@ -1,55 +0,0 @@
|
||||
package eu.kanade.tachiyomi.widget.sheet
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.google.android.material.bottomsheet.getElevation
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.system.displayCompat
|
||||
import eu.kanade.tachiyomi.util.system.isNightMode
|
||||
import eu.kanade.tachiyomi.util.view.setNavigationBarTransparentCompat
|
||||
|
||||
abstract class BaseBottomSheetDialog(context: Context) : BottomSheetDialog(context) {
|
||||
|
||||
abstract fun createView(inflater: LayoutInflater): View
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val rootView = createView(layoutInflater)
|
||||
setContentView(rootView)
|
||||
|
||||
// Enforce max width for tablets
|
||||
val width = context.resources.getDimensionPixelSize(R.dimen.bottom_sheet_width)
|
||||
if (width > 0) {
|
||||
behavior.maxWidth = width
|
||||
}
|
||||
|
||||
// Set peek height to 50% display height
|
||||
context.displayCompat?.let {
|
||||
val metrics = DisplayMetrics()
|
||||
it.getRealMetrics(metrics)
|
||||
behavior.peekHeight = metrics.heightPixels / 2
|
||||
}
|
||||
|
||||
// Set navbar color to transparent for edge-to-edge bottom sheet if we can use light navigation bar
|
||||
// TODO Replace deprecated systemUiVisibility when material-components uses new API to modify status bar icons
|
||||
@Suppress("DEPRECATION")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
window?.setNavigationBarTransparentCompat(context, behavior.getElevation())
|
||||
val bottomSheet = rootView.parent as ViewGroup
|
||||
var flags = bottomSheet.systemUiVisibility
|
||||
flags = if (context.isNightMode()) {
|
||||
flags and View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv()
|
||||
} else {
|
||||
flags or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
|
||||
}
|
||||
bottomSheet.systemUiVisibility = flags
|
||||
}
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
package eu.kanade.tachiyomi.widget.sheet
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import java.lang.reflect.Field
|
||||
|
||||
/**
|
||||
* From https://github.com/kafumi/android-bottomsheet-viewpager
|
||||
*/
|
||||
class BottomSheetViewPager @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
) : ViewPager(context, attrs) {
|
||||
|
||||
private val positionField: Field = LayoutParams::class.java.getDeclaredField("position").also {
|
||||
it.isAccessible = true
|
||||
}
|
||||
|
||||
override fun getChildAt(index: Int): View {
|
||||
val currentView = getCurrentView() ?: return super.getChildAt(index)
|
||||
return if (index == 0) {
|
||||
currentView
|
||||
} else {
|
||||
var view = super.getChildAt(index)
|
||||
if (view == currentView) {
|
||||
view = super.getChildAt(0)
|
||||
}
|
||||
return view
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCurrentView(): View? {
|
||||
for (i in 0 until childCount) {
|
||||
val child = super.getChildAt(i)
|
||||
val lp = child.layoutParams as? LayoutParams
|
||||
if (lp != null) {
|
||||
val position = positionField.getInt(lp)
|
||||
if (!lp.isDecor && currentItem == position) {
|
||||
return child
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
init {
|
||||
addOnPageChangeListener(
|
||||
object : SimpleOnPageChangeListener() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
requestLayout()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
@ -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="@android:color/black"
|
||||
android:pathData="M20,15.31L23.31,12 20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69zM12,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6z" />
|
||||
</vector>
|
@ -1,50 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/transparent_tabs_background">
|
||||
|
||||
<!-- Remove background color so rounded sheet corners work -->
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/menu"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:tabGravity="fill"
|
||||
app:tabMode="fixed" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/menu"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/action_menu"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_overflow_24dp"
|
||||
app:tint="?attr/colorOnSurface"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<eu.kanade.tachiyomi.widget.sheet.BottomSheetViewPager
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
@ -119,29 +119,29 @@
|
||||
app:tint="?attr/colorOnSurface" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/action_settings"
|
||||
android:id="@+id/action_settings_legacy"
|
||||
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"
|
||||
app:layout_constraintEnd_toStartOf="@+id/action_color_settings"
|
||||
app:layout_constraintEnd_toStartOf="@+id/action_settings"
|
||||
app:layout_constraintStart_toEndOf="@id/action_rotation"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_settings_24dp"
|
||||
app:tint="?attr/colorOnSurface" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/action_color_settings"
|
||||
android:id="@+id/action_settings"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/custom_filter"
|
||||
android:contentDescription="@string/action_settings"
|
||||
android:padding="@dimen/screen_edge_margin"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/action_settings"
|
||||
app:layout_constraintStart_toEndOf="@id/action_settings_legacy"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_brightness_5_24dp"
|
||||
app:srcCompat="@drawable/ic_settings_24dp"
|
||||
app:tint="?attr/colorOnSurface" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
@ -1,87 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<eu.kanade.tachiyomi.widget.MaterialSpinnerView
|
||||
android:id="@+id/background_color"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:entries="@array/reader_themes"
|
||||
app:title="@string/pref_reader_theme" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/show_page_number"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp"
|
||||
android:text="@string/pref_show_page_number"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/fullscreen"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp"
|
||||
android:text="@string/pref_fullscreen"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/cutout_short"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp"
|
||||
android:text="@string/pref_cutout_short"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/keepscreen"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp"
|
||||
android:text="@string/pref_keep_screen_on"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/long_tap"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp"
|
||||
android:text="@string/pref_read_with_long_tap"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/always_show_chapter_transition"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp"
|
||||
android:text="@string/pref_always_show_chapter_transition"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/page_transitions"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp"
|
||||
android:text="@string/pref_page_transitions"
|
||||
android:textColor="?android:attr/textColorSecondary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
@ -1,5 +1,3 @@
|
||||
<resources>
|
||||
<dimen name="bottom_sheet_width">480dp</dimen>
|
||||
|
||||
<dimen name="screen_edge_margin">24dp</dimen>
|
||||
</resources>
|
||||
|
@ -9,20 +9,6 @@
|
||||
<item>@string/vertical_plus_viewer</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="reader_themes">
|
||||
<item>@string/black_background</item>
|
||||
<item>@string/gray_background</item>
|
||||
<item>@string/white_background</item>
|
||||
<item>@string/automatic_background</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="reader_themes_values">
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>0</item>
|
||||
<item>3</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="image_scale_type">
|
||||
<item>@string/scale_type_fit_screen</item>
|
||||
<item>@string/scale_type_stretch</item>
|
||||
|
@ -1,6 +1,4 @@
|
||||
<resources>
|
||||
<dimen name="bottom_sheet_width">0dp</dimen>
|
||||
|
||||
<dimen name="dialog_radius">8dp</dimen>
|
||||
|
||||
<dimen name="screen_edge_margin">16dp</dimen>
|
||||
|
16
core/src/main/java/tachiyomi/core/preference/TriState.kt
Normal file
16
core/src/main/java/tachiyomi/core/preference/TriState.kt
Normal file
@ -0,0 +1,16 @@
|
||||
package tachiyomi.core.preference
|
||||
|
||||
enum class TriState {
|
||||
DISABLED, // Disable filter
|
||||
ENABLED_IS, // Enabled with "is" filter
|
||||
ENABLED_NOT, // Enabled with "not" filter
|
||||
;
|
||||
|
||||
fun next(): TriState {
|
||||
return when (this) {
|
||||
DISABLED -> ENABLED_IS
|
||||
ENABLED_IS -> ENABLED_NOT
|
||||
ENABLED_NOT -> DISABLED
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ package tachiyomi.data.chapter
|
||||
|
||||
import tachiyomi.domain.chapter.model.Chapter
|
||||
|
||||
val chapterMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long, Float, Long, Long, Long, Long?) -> Chapter =
|
||||
val chapterMapper: (Long, Long, String, String, String?, Boolean, Boolean, Long, Float, Long, Long, Long, Long) -> Chapter =
|
||||
{ id, mangaId, url, name, scanlator, read, bookmark, lastPageRead, chapterNumber, sourceOrder, dateFetch, dateUpload, lastModifiedAt ->
|
||||
Chapter(
|
||||
id = id,
|
||||
|
@ -4,8 +4,8 @@ import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
||||
import tachiyomi.domain.library.model.LibraryManga
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
|
||||
val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long?) -> Manga =
|
||||
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt ->
|
||||
val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long?) -> Manga =
|
||||
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt ->
|
||||
Manga(
|
||||
id = id,
|
||||
source = source,
|
||||
@ -28,11 +28,12 @@ val mangaMapper: (Long, Long, String, String?, String?, String?, List<String>?,
|
||||
updateStrategy = updateStrategy,
|
||||
initialized = initialized,
|
||||
lastModifiedAt = lastModifiedAt,
|
||||
favoriteModifiedAt = favoriteModifiedAt,
|
||||
)
|
||||
}
|
||||
|
||||
val libraryManga: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long?, Long, Long, Long, Long, Long, Long, Long) -> LibraryManga =
|
||||
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, totalCount, readCount, latestUpload, chapterFetchedAt, lastRead, bookmarkCount, category ->
|
||||
val libraryManga: (Long, Long, String, String?, String?, String?, List<String>?, String, Long, String?, Boolean, Long?, Long?, Boolean, Long, Long, Long, Long, UpdateStrategy, Long, Long, Long?, Long, Long, Long, Long, Long, Long, Long) -> LibraryManga =
|
||||
{ id, source, url, artist, author, description, genre, title, status, thumbnailUrl, favorite, lastUpdate, nextUpdate, initialized, viewerFlags, chapterFlags, coverLastModified, dateAdded, updateStrategy, calculateInterval, lastModifiedAt, favoriteModifiedAt, totalCount, readCount, latestUpload, chapterFetchedAt, lastRead, bookmarkCount, category ->
|
||||
LibraryManga(
|
||||
manga = mangaMapper(
|
||||
id,
|
||||
@ -56,6 +57,7 @@ val libraryManga: (Long, Long, String, String?, String?, String?, List<String>?,
|
||||
updateStrategy,
|
||||
calculateInterval,
|
||||
lastModifiedAt,
|
||||
favoriteModifiedAt,
|
||||
),
|
||||
category = category,
|
||||
totalChapters = totalCount,
|
||||
|
@ -11,7 +11,7 @@ CREATE TABLE chapters(
|
||||
source_order INTEGER NOT NULL,
|
||||
date_fetch INTEGER AS Long NOT NULL,
|
||||
date_upload INTEGER AS Long NOT NULL,
|
||||
last_modified_at INTEGER AS Long,
|
||||
last_modified_at INTEGER AS Long NOT NULL,
|
||||
FOREIGN KEY(manga_id) REFERENCES mangas (_id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
@ -60,8 +60,8 @@ DELETE FROM chapters
|
||||
WHERE _id IN :chapterIds;
|
||||
|
||||
insert:
|
||||
INSERT INTO chapters(manga_id,url,name,scanlator,read,bookmark,last_page_read,chapter_number,source_order,date_fetch,date_upload)
|
||||
VALUES (:mangaId,:url,:name,:scanlator,:read,:bookmark,:lastPageRead,:chapterNumber,:sourceOrder,:dateFetch,:dateUpload);
|
||||
INSERT INTO chapters(manga_id, url, name, scanlator, read, bookmark, last_page_read, chapter_number, source_order, date_fetch, date_upload, last_modified_at)
|
||||
VALUES (:mangaId, :url, :name, :scanlator, :read, :bookmark, :lastPageRead, :chapterNumber, :sourceOrder, :dateFetch, :dateUpload, strftime('%s', 'now'));
|
||||
|
||||
update:
|
||||
UPDATE chapters
|
||||
|
@ -23,23 +23,23 @@ CREATE TABLE mangas(
|
||||
date_added INTEGER AS Long NOT NULL,
|
||||
update_strategy INTEGER AS UpdateStrategy NOT NULL DEFAULT 0,
|
||||
calculate_interval INTEGER DEFAULT 0 NOT NULL,
|
||||
last_modified_at INTEGER AS Long
|
||||
last_modified_at INTEGER AS Long NOT NULL,
|
||||
favorite_modified_at INTEGER AS Long
|
||||
);
|
||||
|
||||
CREATE INDEX library_favorite_index ON mangas(favorite) WHERE favorite = 1;
|
||||
CREATE INDEX mangas_url_index ON mangas(url);
|
||||
|
||||
CREATE TRIGGER update_last_modified_at_mangas
|
||||
AFTER UPDATE ON mangas
|
||||
FOR EACH ROW
|
||||
CREATE TRIGGER update_favorite_modified_at_mangas
|
||||
AFTER UPDATE OF favorite ON mangas
|
||||
BEGIN
|
||||
UPDATE mangas
|
||||
SET last_modified_at = strftime('%s', 'now')
|
||||
SET favorite_modified_at = strftime('%s', 'now')
|
||||
WHERE _id = new._id;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER insert_last_modified_at_mangas
|
||||
AFTER INSERT ON mangas
|
||||
CREATE TRIGGER update_last_modified_at_mangas
|
||||
AFTER UPDATE ON mangas
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE mangas
|
||||
@ -68,6 +68,11 @@ getAllManga:
|
||||
SELECT *
|
||||
FROM mangas;
|
||||
|
||||
getMangasWithFavoriteTimestamp:
|
||||
SELECT *
|
||||
FROM mangas
|
||||
WHERE favorite_modified_at IS NOT NULL;
|
||||
|
||||
getSourceIdWithFavoriteCount:
|
||||
SELECT
|
||||
source,
|
||||
@ -104,8 +109,8 @@ DELETE FROM mangas
|
||||
WHERE favorite = 0 AND source IN :sourceIds;
|
||||
|
||||
insert:
|
||||
INSERT INTO mangas(source,url,artist,author,description,genre,title,status,thumbnail_url,favorite,last_update,next_update,initialized,viewer,chapter_flags,cover_last_modified,date_added,update_strategy,calculate_interval)
|
||||
VALUES (:source,:url,:artist,:author,:description,:genre,:title,:status,:thumbnailUrl,:favorite,:lastUpdate,:nextUpdate,:initialized,:viewerFlags,:chapterFlags,:coverLastModified,:dateAdded,:updateStrategy,:calculateInterval);
|
||||
INSERT INTO mangas(source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added, update_strategy, calculate_interval, last_modified_at)
|
||||
VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :dateAdded, :updateStrategy, :calculateInterval, strftime('%s', 'now'));
|
||||
|
||||
update:
|
||||
UPDATE mangas SET
|
||||
|
@ -2,7 +2,7 @@ CREATE TABLE mangas_categories(
|
||||
_id INTEGER NOT NULL PRIMARY KEY,
|
||||
manga_id INTEGER NOT NULL,
|
||||
category_id INTEGER NOT NULL,
|
||||
last_modified_at INTEGER AS Long,
|
||||
last_modified_at INTEGER AS Long NOT NULL,
|
||||
FOREIGN KEY(category_id) REFERENCES categories (_id)
|
||||
ON DELETE CASCADE,
|
||||
FOREIGN KEY(manga_id) REFERENCES mangas (_id)
|
||||
@ -18,18 +18,9 @@ BEGIN
|
||||
WHERE _id = new._id;
|
||||
END;
|
||||
|
||||
CREATE TRIGGER insert_last_modified_at_mangas_categories
|
||||
AFTER INSERT ON mangas_categories
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE mangas_categories
|
||||
SET last_modified_at = strftime('%s', 'now')
|
||||
WHERE _id = new._id;
|
||||
END;
|
||||
|
||||
insert:
|
||||
INSERT INTO mangas_categories(manga_id, category_id)
|
||||
VALUES (:mangaId, :categoryId);
|
||||
INSERT INTO mangas_categories(manga_id, category_id, last_modified_at)
|
||||
VALUES (:mangaId, :categoryId, strftime('%s', 'now'));
|
||||
|
||||
deleteMangaCategoryByMangaId:
|
||||
DELETE FROM mangas_categories
|
||||
|
@ -1,94 +1,12 @@
|
||||
import kotlin.collections.List;
|
||||
import eu.kanade.tachiyomi.source.model.UpdateStrategy;
|
||||
|
||||
-- Drop indices
|
||||
DROP INDEX IF EXISTS library_favorite_index;
|
||||
DROP INDEX IF EXISTS mangas_url_index;
|
||||
DROP INDEX IF EXISTS chapters_manga_id_index;
|
||||
DROP INDEX IF EXISTS chapters_unread_by_manga_index;
|
||||
|
||||
-- Rename existing tables to temporary tables
|
||||
ALTER TABLE mangas RENAME TO mangas_temp;
|
||||
ALTER TABLE chapters RENAME TO chapters_temp;
|
||||
ALTER TABLE mangas_categories RENAME TO mangas_categories_temp;
|
||||
|
||||
-- Create new tables with updated schema
|
||||
CREATE TABLE mangas(
|
||||
_id INTEGER NOT NULL PRIMARY KEY,
|
||||
source INTEGER NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
artist TEXT,
|
||||
author TEXT,
|
||||
description TEXT,
|
||||
genre TEXT AS List<String>,
|
||||
title TEXT NOT NULL,
|
||||
status INTEGER NOT NULL,
|
||||
thumbnail_url TEXT,
|
||||
favorite INTEGER AS Boolean NOT NULL,
|
||||
last_update INTEGER AS Long,
|
||||
next_update INTEGER AS Long,
|
||||
initialized INTEGER AS Boolean NOT NULL,
|
||||
viewer INTEGER NOT NULL,
|
||||
chapter_flags INTEGER NOT NULL,
|
||||
cover_last_modified INTEGER AS Long NOT NULL,
|
||||
date_added INTEGER AS Long NOT NULL,
|
||||
update_strategy INTEGER AS UpdateStrategy NOT NULL DEFAULT 0,
|
||||
last_modified_at INTEGER AS Long
|
||||
);
|
||||
|
||||
CREATE TABLE mangas_categories(
|
||||
_id INTEGER NOT NULL PRIMARY KEY,
|
||||
manga_id INTEGER NOT NULL,
|
||||
category_id INTEGER NOT NULL,
|
||||
last_modified_at INTEGER AS Long,
|
||||
FOREIGN KEY(category_id) REFERENCES categories (_id)
|
||||
ON DELETE CASCADE,
|
||||
FOREIGN KEY(manga_id) REFERENCES mangas (_id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE chapters(
|
||||
_id INTEGER NOT NULL PRIMARY KEY,
|
||||
manga_id INTEGER NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
scanlator TEXT,
|
||||
read INTEGER AS Boolean NOT NULL,
|
||||
bookmark INTEGER AS Boolean NOT NULL,
|
||||
last_page_read INTEGER NOT NULL,
|
||||
chapter_number REAL AS Float NOT NULL,
|
||||
source_order INTEGER NOT NULL,
|
||||
date_fetch INTEGER AS Long NOT NULL,
|
||||
date_upload INTEGER AS Long NOT NULL,
|
||||
last_modified_at INTEGER AS Long,
|
||||
FOREIGN KEY(manga_id) REFERENCES mangas (_id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Copy data from temporary tables to new tables
|
||||
INSERT INTO mangas
|
||||
SELECT _id, source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added, update_strategy, NULL
|
||||
FROM mangas_temp;
|
||||
|
||||
INSERT INTO chapters
|
||||
SELECT _id, manga_id, url, name, scanlator, read, bookmark, last_page_read, chapter_number, source_order, date_fetch, date_upload, NULL
|
||||
FROM chapters_temp;
|
||||
|
||||
INSERT INTO mangas_categories
|
||||
SELECT _id, manga_id, category_id, NULL
|
||||
FROM mangas_categories_temp;
|
||||
|
||||
-- Create indices
|
||||
CREATE INDEX library_favorite_index ON mangas(favorite) WHERE favorite = 1;
|
||||
CREATE INDEX mangas_url_index ON mangas(url);
|
||||
CREATE INDEX chapters_manga_id_index ON chapters(manga_id);
|
||||
CREATE INDEX chapters_unread_by_manga_index ON chapters(manga_id, read) WHERE read = 0;
|
||||
|
||||
-- Drop temporary tables
|
||||
DROP TABLE IF EXISTS mangas_temp;
|
||||
DROP TABLE IF EXISTS chapters_temp;
|
||||
DROP TABLE IF EXISTS mangas_categories_temp;
|
||||
ALTER TABLE mangas ADD COLUMN last_modified_at INTEGER AS Long NOT NULL DEFAULT 0;
|
||||
ALTER TABLE mangas ADD COLUMN favorite_modified_at INTEGER AS Long;
|
||||
ALTER TABLE mangas_categories ADD COLUMN last_modified_at INTEGER AS Long NOT NULL DEFAULT 0;
|
||||
ALTER TABLE chapters ADD COLUMN last_modified_at INTEGER AS Long NOT NULL DEFAULT 0;
|
||||
|
||||
UPDATE mangas SET last_modified_at = strftime('%s', 'now');
|
||||
UPDATE mangas SET favorite_modified_at = strftime('%s', 'now') WHERE favorite = 1;
|
||||
UPDATE mangas_categories SET last_modified_at = strftime('%s', 'now');
|
||||
UPDATE chapters SET last_modified_at = strftime('%s', 'now');
|
||||
|
||||
-- Create triggers
|
||||
DROP TRIGGER IF EXISTS update_last_modified_at_mangas;
|
||||
@ -101,13 +19,12 @@ BEGIN
|
||||
WHERE _id = new._id;
|
||||
END;
|
||||
|
||||
DROP TRIGGER IF EXISTS insert_last_modified_at_mangas;
|
||||
CREATE TRIGGER insert_last_modified_at_mangas
|
||||
AFTER INSERT ON mangas
|
||||
FOR EACH ROW
|
||||
DROP TRIGGER IF EXISTS update_favorite_modified_at_mangas;
|
||||
CREATE TRIGGER update_last_favorited_at_mangas
|
||||
AFTER UPDATE OF favorite ON mangas
|
||||
BEGIN
|
||||
UPDATE mangas
|
||||
SET last_modified_at = strftime('%s', 'now')
|
||||
SET favorite_modified_at = strftime('%s', 'now')
|
||||
WHERE _id = new._id;
|
||||
END;
|
||||
|
||||
@ -129,14 +46,4 @@ BEGIN
|
||||
UPDATE mangas_categories
|
||||
SET last_modified_at = strftime('%s', 'now')
|
||||
WHERE _id = new._id;
|
||||
END;
|
||||
|
||||
DROP TRIGGER IF EXISTS insert_last_modified_at_mangas_categories;
|
||||
CREATE TRIGGER insert_last_modified_at_mangas_categories
|
||||
AFTER INSERT ON mangas_categories
|
||||
FOR EACH ROW
|
||||
BEGIN
|
||||
UPDATE mangas_categories
|
||||
SET last_modified_at = strftime('%s', 'now')
|
||||
WHERE _id = new._id;
|
||||
END;
|
||||
END;
|
@ -13,7 +13,7 @@ data class Chapter(
|
||||
val dateUpload: Long,
|
||||
val chapterNumber: Float,
|
||||
val scanlator: String?,
|
||||
val lastModifiedAt: Long?,
|
||||
val lastModifiedAt: Long,
|
||||
) {
|
||||
val isRecognizedNumber: Boolean
|
||||
get() = chapterNumber >= 0f
|
||||
@ -32,7 +32,7 @@ data class Chapter(
|
||||
dateUpload = -1,
|
||||
chapterNumber = -1f,
|
||||
scanlator = null,
|
||||
lastModifiedAt = null,
|
||||
lastModifiedAt = 0,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
package tachiyomi.domain.library.service
|
||||
|
||||
import tachiyomi.core.preference.PreferenceStore
|
||||
import tachiyomi.core.preference.TriState
|
||||
import tachiyomi.core.preference.getEnum
|
||||
import tachiyomi.domain.library.model.LibraryDisplayMode
|
||||
import tachiyomi.domain.library.model.LibrarySort
|
||||
import tachiyomi.domain.manga.model.Manga
|
||||
import tachiyomi.domain.manga.model.TriStateFilter
|
||||
|
||||
class LibraryPreferences(
|
||||
private val preferenceStore: PreferenceStore,
|
||||
@ -49,27 +49,27 @@ class LibraryPreferences(
|
||||
|
||||
// region Filter
|
||||
|
||||
fun filterDownloaded() = preferenceStore.getEnum("pref_filter_library_downloaded_v2", TriStateFilter.DISABLED)
|
||||
fun filterDownloaded() = preferenceStore.getEnum("pref_filter_library_downloaded_v2", TriState.DISABLED)
|
||||
|
||||
fun filterUnread() = preferenceStore.getEnum("pref_filter_library_unread_v2", TriStateFilter.DISABLED)
|
||||
fun filterUnread() = preferenceStore.getEnum("pref_filter_library_unread_v2", TriState.DISABLED)
|
||||
|
||||
fun filterStarted() = preferenceStore.getEnum("pref_filter_library_started_v2", TriStateFilter.DISABLED)
|
||||
fun filterStarted() = preferenceStore.getEnum("pref_filter_library_started_v2", TriState.DISABLED)
|
||||
|
||||
fun filterBookmarked() = preferenceStore.getEnum("pref_filter_library_bookmarked_v2", TriStateFilter.DISABLED)
|
||||
fun filterBookmarked() = preferenceStore.getEnum("pref_filter_library_bookmarked_v2", TriState.DISABLED)
|
||||
|
||||
fun filterCompleted() = preferenceStore.getEnum("pref_filter_library_completed_v2", TriStateFilter.DISABLED)
|
||||
fun filterCompleted() = preferenceStore.getEnum("pref_filter_library_completed_v2", TriState.DISABLED)
|
||||
|
||||
fun filterIntervalCustom() = preferenceStore.getEnum("pref_filter_library_interval_custom", TriStateFilter.DISABLED)
|
||||
fun filterIntervalCustom() = preferenceStore.getEnum("pref_filter_library_interval_custom", TriState.DISABLED)
|
||||
|
||||
fun filterIntervalLong() = preferenceStore.getEnum("pref_filter_library_interval_long", TriStateFilter.DISABLED)
|
||||
fun filterIntervalLong() = preferenceStore.getEnum("pref_filter_library_interval_long", TriState.DISABLED)
|
||||
|
||||
fun filterIntervalLate() = preferenceStore.getEnum("pref_filter_library_interval_late", TriStateFilter.DISABLED)
|
||||
fun filterIntervalLate() = preferenceStore.getEnum("pref_filter_library_interval_late", TriState.DISABLED)
|
||||
|
||||
fun filterIntervalDropped() = preferenceStore.getEnum("pref_filter_library_interval_dropped", TriStateFilter.DISABLED)
|
||||
fun filterIntervalDropped() = preferenceStore.getEnum("pref_filter_library_interval_dropped", TriState.DISABLED)
|
||||
|
||||
fun filterIntervalPassed() = preferenceStore.getEnum("pref_filter_library_interval_passed", TriStateFilter.DISABLED)
|
||||
fun filterIntervalPassed() = preferenceStore.getEnum("pref_filter_library_interval_passed", TriState.DISABLED)
|
||||
|
||||
fun filterTracking(id: Int) = preferenceStore.getEnum("pref_filter_library_tracked_${id}_v2", TriStateFilter.DISABLED)
|
||||
fun filterTracking(id: Int) = preferenceStore.getEnum("pref_filter_library_tracked_${id}_v2", TriState.DISABLED)
|
||||
|
||||
// endregion
|
||||
|
||||
@ -134,9 +134,9 @@ class LibraryPreferences(
|
||||
|
||||
// region Swipe Actions
|
||||
|
||||
fun swipeEndAction() = preferenceStore.getEnum("pref_chapter_swipe_end_action", ChapterSwipeAction.ToggleBookmark)
|
||||
fun swipeToStartAction() = preferenceStore.getEnum("pref_chapter_swipe_end_action", ChapterSwipeAction.ToggleBookmark)
|
||||
|
||||
fun swipeStartAction() = preferenceStore.getEnum("pref_chapter_swipe_start_action", ChapterSwipeAction.ToggleRead)
|
||||
fun swipeToEndAction() = preferenceStore.getEnum("pref_chapter_swipe_start_action", ChapterSwipeAction.ToggleRead)
|
||||
|
||||
// endregion
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package tachiyomi.domain.manga.model
|
||||
|
||||
import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
||||
import tachiyomi.core.preference.TriState
|
||||
import java.io.Serializable
|
||||
|
||||
data class Manga(
|
||||
@ -24,7 +25,8 @@ data class Manga(
|
||||
val thumbnailUrl: String?,
|
||||
val updateStrategy: UpdateStrategy,
|
||||
val initialized: Boolean,
|
||||
val lastModifiedAt: Long?,
|
||||
val lastModifiedAt: Long,
|
||||
val favoriteModifiedAt: Long?,
|
||||
) : Serializable {
|
||||
|
||||
val sorting: Long
|
||||
@ -42,18 +44,18 @@ data class Manga(
|
||||
val bookmarkedFilterRaw: Long
|
||||
get() = chapterFlags and CHAPTER_BOOKMARKED_MASK
|
||||
|
||||
val unreadFilter: TriStateFilter
|
||||
val unreadFilter: TriState
|
||||
get() = when (unreadFilterRaw) {
|
||||
CHAPTER_SHOW_UNREAD -> TriStateFilter.ENABLED_IS
|
||||
CHAPTER_SHOW_READ -> TriStateFilter.ENABLED_NOT
|
||||
else -> TriStateFilter.DISABLED
|
||||
CHAPTER_SHOW_UNREAD -> TriState.ENABLED_IS
|
||||
CHAPTER_SHOW_READ -> TriState.ENABLED_NOT
|
||||
else -> TriState.DISABLED
|
||||
}
|
||||
|
||||
val bookmarkedFilter: TriStateFilter
|
||||
val bookmarkedFilter: TriState
|
||||
get() = when (bookmarkedFilterRaw) {
|
||||
CHAPTER_SHOW_BOOKMARKED -> TriStateFilter.ENABLED_IS
|
||||
CHAPTER_SHOW_NOT_BOOKMARKED -> TriStateFilter.ENABLED_NOT
|
||||
else -> TriStateFilter.DISABLED
|
||||
CHAPTER_SHOW_BOOKMARKED -> TriState.ENABLED_IS
|
||||
CHAPTER_SHOW_NOT_BOOKMARKED -> TriState.ENABLED_NOT
|
||||
else -> TriState.DISABLED
|
||||
}
|
||||
|
||||
fun sortDescending(): Boolean {
|
||||
@ -111,6 +113,7 @@ data class Manga(
|
||||
updateStrategy = UpdateStrategy.ALWAYS_UPDATE,
|
||||
initialized = false,
|
||||
lastModifiedAt = 0L,
|
||||
favoriteModifiedAt = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
package tachiyomi.domain.manga.model
|
||||
|
||||
import tachiyomi.core.preference.TriState
|
||||
|
||||
inline fun applyFilter(filter: TriState, predicate: () -> Boolean): Boolean = when (filter) {
|
||||
TriState.DISABLED -> true
|
||||
TriState.ENABLED_IS -> predicate()
|
||||
TriState.ENABLED_NOT -> !predicate()
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package tachiyomi.domain.manga.model
|
||||
|
||||
enum class TriStateFilter {
|
||||
DISABLED, // Disable filter
|
||||
ENABLED_IS, // Enabled with "is" filter
|
||||
ENABLED_NOT, // Enabled with "not" filter
|
||||
;
|
||||
|
||||
fun next(): TriStateFilter {
|
||||
return when (this) {
|
||||
DISABLED -> ENABLED_IS
|
||||
ENABLED_IS -> ENABLED_NOT
|
||||
ENABLED_NOT -> DISABLED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun applyFilter(filter: TriStateFilter, predicate: () -> Boolean): Boolean = when (filter) {
|
||||
TriStateFilter.DISABLED -> true
|
||||
TriStateFilter.ENABLED_IS -> predicate()
|
||||
TriStateFilter.ENABLED_NOT -> !predicate()
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
[versions]
|
||||
compiler = "1.4.8"
|
||||
compose-bom = "2023.06.00-alpha01"
|
||||
accompanist = "0.31.4-beta"
|
||||
compose-bom = "2023.07.00-alpha01"
|
||||
accompanist = "0.31.5-beta"
|
||||
|
||||
[libraries]
|
||||
activity = "androidx.activity:activity-compose:1.7.2"
|
||||
|
@ -1,5 +1,5 @@
|
||||
[versions]
|
||||
aboutlib_version = "10.8.0"
|
||||
aboutlib_version = "10.8.2"
|
||||
okhttp_version = "5.0.0-alpha.11"
|
||||
shizuku_version = "12.2.0"
|
||||
sqlite = "2.3.1"
|
||||
@ -20,7 +20,7 @@ flowreactivenetwork = "ru.beryukhov:flowreactivenetwork:1.0.4"
|
||||
okhttp-core = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp_version" }
|
||||
okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp_version" }
|
||||
okhttp-dnsoverhttps = { module = "com.squareup.okhttp3:okhttp-dnsoverhttps", version.ref = "okhttp_version" }
|
||||
okio = "com.squareup.okio:okio:3.3.0"
|
||||
okio = "com.squareup.okio:okio:3.4.0"
|
||||
|
||||
conscrypt-android = "org.conscrypt:conscrypt-android:2.5.2"
|
||||
|
||||
@ -58,9 +58,11 @@ flexible-adapter-core = "com.github.arkon.FlexibleAdapter:flexible-adapter:c8013
|
||||
photoview = "com.github.chrisbanes:PhotoView:2.3.0"
|
||||
directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0"
|
||||
insetter = "dev.chrisbanes.insetter:insetter:0.6.1"
|
||||
compose-materialmotion = "io.github.fornewid:material-motion-compose-core:1.0.3"
|
||||
compose-materialmotion = "io.github.fornewid:material-motion-compose-core:1.0.4"
|
||||
compose-simpleicons = "br.com.devsrsouza.compose.icons.android:simple-icons:1.0.0"
|
||||
|
||||
swipe = "me.saket.swipe:swipe:1.2.0"
|
||||
|
||||
logcat = "com.squareup.logcat:logcat:0.1"
|
||||
|
||||
acra-http = "ch.acra:acra-http:5.10.1"
|
||||
|
@ -831,4 +831,5 @@
|
||||
<string name="skipped_reason_not_in_release_period">S’ha omès perquè no se n’espera cap publicació avui</string>
|
||||
<string name="action_filter_interval_passed">Període de comprovació superat</string>
|
||||
<string name="pref_update_release_grace_period_info">Es recomana un període de gràcia baix per a minimitzar la sobrecàrrega de les fonts. Com més comprovacions fallides es produeixin, més interval hi haurà entre comprovacions, fins a un màxim de 28 dies.</string>
|
||||
<string name="delete_downloaded">Suprimeix els baixats</string>
|
||||
</resources>
|
@ -639,7 +639,7 @@
|
||||
<string name="pref_verbose_logging_summary">Vypisovat podrobné informace do systémového protokolu (sníží výkon aplikace)</string>
|
||||
<string name="channel_app_updates">Aktualizace aplikace</string>
|
||||
<string name="notification_size_warning">Varování: velké aktualizace poškozují zdroje a můžou vést k pomalejším aktualizacím a zvýšenému využití baterie. Klepnutím se dozvíte více.</string>
|
||||
<string name="pref_auto_clear_chapter_cache">Vymazat mezipaměť kapitol při zavření aplikace</string>
|
||||
<string name="pref_auto_clear_chapter_cache">Vymazat mezipaměť kapitol při spuštění aplikace</string>
|
||||
<string name="extension_api_error">Chyba v získání seznamu rozšíření</string>
|
||||
<string name="privacy_policy">Zásady ochrany osobních údajů</string>
|
||||
<string name="clear_database_source_item_count">%1$d neknihovní záznamy v databázi</string>
|
||||
@ -849,4 +849,5 @@
|
||||
<string name="track_delete_remote_text">Odebrat také z %s</string>
|
||||
<string name="action_ok">OK</string>
|
||||
<string name="track_delete_title">Odebrat sledování %s\?</string>
|
||||
<string name="delete_downloaded">Odstranit stažené</string>
|
||||
</resources>
|
@ -565,7 +565,7 @@
|
||||
<string name="pref_appearance_summary">Темӑ, кун тата вӑхӑт хармачӗ</string>
|
||||
<string name="pref_library_summary">Пухмӑшсем, пӗтӗмӗшле ҫӗнетӳ</string>
|
||||
<string name="pref_reader_summary">Вулав тытӑмӗ, кӑтартӑнни, куҫӑм</string>
|
||||
<string name="label_default">Пӳрӳлле</string>
|
||||
<string name="label_default">Йаланхилле</string>
|
||||
<string name="action_open_random_manga">Ӑнсӑрт ҫырав уҫ</string>
|
||||
<string name="theme_strawberrydaiquiri">Ҫӗр ҫырли тайккирийӗ</string>
|
||||
<string name="theme_midnightdusk">Ҫур ҫӗр ӗнтрӗкӗ</string>
|
||||
@ -630,4 +630,5 @@
|
||||
<string name="ext_info_version">Версси</string>
|
||||
<string name="pref_library_update_refresh_trackers">Йӗрлеве хӑй-хальлӗн ҫӗнетни</string>
|
||||
<string name="ext_update_all">Пурне те ҫӗнет</string>
|
||||
<string name="delete_downloaded">Тийесе илнисене катерт</string>
|
||||
</resources>
|
@ -386,7 +386,7 @@
|
||||
<string name="add_to_library">Zur Bibliothek hinzufügen</string>
|
||||
<string name="confirm_exit">Zum Beenden nochmal die Zurück-Taste drücken</string>
|
||||
<string name="information_webview_required">WebView ist für Tachiyomi erforderlich</string>
|
||||
<string name="licenses">Quelloffene Lizenzen</string>
|
||||
<string name="licenses">Open-Source-Lizenzen</string>
|
||||
<string name="website">Webseite</string>
|
||||
<string name="label_downloaded_only">Nur Heruntergeladenes</string>
|
||||
<string name="recent_manga_time">Kap. %1$s - %2$s</string>
|
||||
@ -628,7 +628,7 @@
|
||||
<string name="download_queue_size_warning">Achtung: Große Downloads könnten dazu führen, dass Quellen langsamer werden und/oder Tachiyomi blockieren. Tippe, um mehr zu erfahren.</string>
|
||||
<string name="ext_update_all">Alle aktualisieren</string>
|
||||
<string name="channel_app_updates">Anwendungsaktualisierungen</string>
|
||||
<string name="pref_auto_clear_chapter_cache">Kapitel-Zwischenspeicher beim Schließen der App löschen</string>
|
||||
<string name="pref_auto_clear_chapter_cache">Kapitel-Zwischenspeicher beim Öffnen der App löschen</string>
|
||||
<string name="clear_database_source_item_count">%1$d Nicht-Bibliothekseinträge in der Datenbank</string>
|
||||
<string name="database_clean">Nichts zu bereinigen</string>
|
||||
<string name="extension_api_error">Herunterladen der Erweiterungsliste ist fehlgeschlagen</string>
|
||||
@ -831,4 +831,5 @@
|
||||
<string name="track_delete_remote_text">Auch aus %s entfernen</string>
|
||||
<string name="action_ok">OK</string>
|
||||
<string name="track_delete_title">Tracking von %s entfernen\?</string>
|
||||
<string name="delete_downloaded">Heruntergeladenes löschen</string>
|
||||
</resources>
|
@ -628,7 +628,7 @@
|
||||
<string name="download_queue_size_warning">Προειδοποίηση: οι μαζικές λήψεις ενδέχεται να οδηγήσουν σε επιβράδυνση των πηγών ή/και αποκλεισμό του Tachiyomi. Πατήστε για να μάθετε περισσότερα.</string>
|
||||
<string name="ext_update_all">Ενημέρωση όλων</string>
|
||||
<string name="channel_app_updates">Ενημερώσεις εφαρμογής</string>
|
||||
<string name="pref_auto_clear_chapter_cache">Εκκαθάριση της προσωρινής μνήμης κεφαλαίων στο κλείσιμο της εφαρμογής</string>
|
||||
<string name="pref_auto_clear_chapter_cache">Εκκαθάριση της προσωρινής μνήμης κεφαλαίων κατά την εκκίνηση της εφαρμογής</string>
|
||||
<string name="clear_database_source_item_count">%1$d καταχωρήσεις εκτός βιβλιοθήκης στη βάση δεδομένων</string>
|
||||
<string name="database_clean">Τίποτα προς εκκαθάριση</string>
|
||||
<string name="extension_api_error">Απέτυχε η λήψη λίστας επεκτάσεων</string>
|
||||
@ -831,4 +831,5 @@
|
||||
<string name="track_delete_title">Κατάργηση παρακολούθησης %s;</string>
|
||||
<string name="track_delete_remote_text">Επίσης, αφαιρέστε από %s</string>
|
||||
<string name="track_delete_text">Αυτό θα καταργήσει την παρακολούθηση τοπικά.</string>
|
||||
<string name="delete_downloaded">Διαγραφή ληφθέντων</string>
|
||||
</resources>
|
@ -671,7 +671,7 @@
|
||||
<string name="download_queue_size_warning">Advertencia: Las descargas grandes pueden llevar a que las fuentes se vuelvan cada vez más lentas y en casos extremos que los servidores limiten o impidan el acceso a Tachiyomi. Toca aquí para más información.</string>
|
||||
<string name="ext_update_all">Actualizar todas</string>
|
||||
<string name="channel_app_updates">Actualizaciones de la aplicación</string>
|
||||
<string name="pref_auto_clear_chapter_cache">Borrar la caché de capítulos al cerrar la aplicación</string>
|
||||
<string name="pref_auto_clear_chapter_cache">Borrar la caché de capítulos al abrir la aplicación</string>
|
||||
<string name="database_clean">Base de datos limpia</string>
|
||||
<string name="clear_database_source_item_count">Hay %1$d elementos en la base de datos que no están en la biblioteca</string>
|
||||
<string name="extension_api_error">No se pudo descargar el listado de extensiones</string>
|
||||
@ -880,4 +880,5 @@
|
||||
<string name="track_delete_title">¿Quitar el rastreo de %s\?</string>
|
||||
<string name="track_delete_text">Esto eliminará el seguimiento localmente.</string>
|
||||
<string name="track_delete_remote_text">Quitar también de %s</string>
|
||||
<string name="delete_downloaded">Borrar los ya descargados</string>
|
||||
</resources>
|
@ -64,7 +64,7 @@
|
||||
<string name="action_sort_total">Dami ng kabanata</string>
|
||||
<string name="action_sort_alpha">Pa-alpabeto</string>
|
||||
<string name="action_filter_empty">Tanggalin ang pansala</string>
|
||||
<string name="action_filter_unread">Babasahin</string>
|
||||
<string name="action_filter_unread">Hindi Nabasa</string>
|
||||
<string name="action_filter_bookmarked">Tinandaan</string>
|
||||
<string name="action_filter">Pansala</string>
|
||||
<string name="action_menu">Menu</string>
|
||||
@ -801,10 +801,10 @@
|
||||
<string name="pref_chapter_swipe_end">Mag-swipe ng pakanang pagkilos</string>
|
||||
<string name="action_set_interval">Itakda ang pagitan</string>
|
||||
<string name="action_filter_interval_custom">Sinadyang takdang pagkuha</string>
|
||||
<string name="action_filter_interval_late">Nahuling 10+ pagsusuri</string>
|
||||
<string name="action_filter_interval_late">Huling 10+ pagsusuri</string>
|
||||
<string name="action_sort_next_updated">Susunod na inaasahang update</string>
|
||||
<string name="action_filter_interval_dropped">Nawala\? or Nahulog\? (depending on the context, \"Nahulog\" means dropped or dropped something, and \"Nawala\" means Gone/Vanished) Nahuling 20+ at 2 buwan</string>
|
||||
<string name="action_filter_interval_passed">Lumipas ang check period</string>
|
||||
<string name="action_filter_interval_passed">Lumipas na panahon ng pagsuri</string>
|
||||
<string name="action_filter_interval_long">Kunin kada buwan (kada ika-28 na araw)</string>
|
||||
<plurals name="pref_update_release_following_days">
|
||||
<item quantity="one">pagkatapos ng %d araw</item>
|
||||
|
@ -314,7 +314,7 @@
|
||||
<string name="theme_system">सिस्टम का पालन करें</string>
|
||||
<string name="pref_manage_notifications">सूचनाओं का प्रबंधन</string>
|
||||
<string name="pref_category_security">सुरक्षा और गोपनीयता</string>
|
||||
<string name="lock_with_biometrics">अनलॉक की आवश्यकता है</string>
|
||||
<string name="lock_with_biometrics">खोलने की आवश्यकता है</string>
|
||||
<string name="lock_when_idle">निष्क्रिय होने पर लॉक करें</string>
|
||||
<string name="lock_always">हमेशा</string>
|
||||
<string name="lock_never">कभी नहीँ</string>
|
||||
@ -739,4 +739,9 @@
|
||||
<string name="pref_page_rotate">फिट होने के लिए चौड़े पृष्ठों को घुमाएं</string>
|
||||
<string name="pref_backup_summary">मैनुअल और स्वचालित बैकअप</string>
|
||||
<string name="pref_security_summary">ऐप लॉक, सुरक्षित स्क्रीन</string>
|
||||
<string name="action_set_interval">अंतराल निर्धारित करें</string>
|
||||
<string name="action_filter_interval_custom">अनुकूलित लाने का अंतराल</string>
|
||||
<string name="action_filter_interval_long">मासिक प्राप्त करें (28 दिन)</string>
|
||||
<string name="action_filter_interval_late">देर से 10+ की जाँच</string>
|
||||
<string name="action_filter_interval_dropped">छोड़ा हुआ\? देर से 20+ और 2 महीने</string>
|
||||
</resources>
|
@ -673,7 +673,7 @@
|
||||
<string name="download_queue_size_warning">Attenzione: grossi download di massa possono rallentare le fonti e/o bloccare tachiyomi. Tocca per saperne di più.</string>
|
||||
<string name="ext_update_all">Aggiorna tutto</string>
|
||||
<string name="channel_app_updates">Aggiornamenti dell\'applicazione</string>
|
||||
<string name="pref_auto_clear_chapter_cache">Cancella la cache capitoli alla chiusura dell\'app</string>
|
||||
<string name="pref_auto_clear_chapter_cache">Cancella la cache capitoli all\'avvio dell\'app</string>
|
||||
<string name="clear_database_source_item_count">%1$d voci non presenti in libreria nel database</string>
|
||||
<string name="database_clean">Niente da Pulire</string>
|
||||
<string name="extension_api_error">Impossibile ottenere l\'elenco estensioni</string>
|
||||
@ -882,4 +882,5 @@
|
||||
<string name="track_delete_remote_text">Rimuovi anche da %s</string>
|
||||
<string name="action_ok">OK</string>
|
||||
<string name="track_delete_text">Questo rimuoverà il tracciamento locale.</string>
|
||||
<string name="delete_downloaded">Cancella scaricati</string>
|
||||
</resources>
|
@ -616,7 +616,7 @@
|
||||
<string name="download_queue_size_warning">Amaran: muat turun secara pukal besar boleh menyebabkan sumber menjadi lebih perlahan dan/atau menyekat Tachiyomi. Ketik untuk ketahui selebihnya.</string>
|
||||
<string name="ext_update_all">Kemas kini semua</string>
|
||||
<string name="channel_app_updates">Kemas kini aplikasi</string>
|
||||
<string name="pref_auto_clear_chapter_cache">Hapus cache bab apabila menutup aplikasi</string>
|
||||
<string name="pref_auto_clear_chapter_cache">Hapus cache bab apabila membuka aplikasi</string>
|
||||
<string name="database_clean">Tiada apa untuk dibersihkan</string>
|
||||
<string name="clear_database_source_item_count">%1$d entri bukan pustaka dalam pangkalan data</string>
|
||||
<string name="extension_api_error">Gagal mendapatkan senarai sambungan</string>
|
||||
@ -813,4 +813,5 @@
|
||||
<string name="track_delete_text">Ini akan membuang penjejakan secara lokal.</string>
|
||||
<string name="action_ok">OK</string>
|
||||
<string name="track_delete_remote_text">Juga buang daripada %s</string>
|
||||
<string name="delete_downloaded">Padam dimuat turun</string>
|
||||
</resources>
|
@ -421,7 +421,7 @@
|
||||
<string name="notification_chapters_single">अध्याय %1$s</string>
|
||||
<string name="scale_type_original_size">मूल आकार</string>
|
||||
<string name="pref_zoom_start">जूम सुरु स्थिति</string>
|
||||
<string name="pref_auto_clear_chapter_cache">एप बन्दमा अध्याय क्यास खाली गर्नुहोस्</string>
|
||||
<string name="pref_auto_clear_chapter_cache">एप खोलेमा अध्याय क्यास खाली गर्नुहोस्</string>
|
||||
<string name="information_webview_outdated">राम्रो संगतताको लागि कृपया WebView एप अपडेट गर्नुहोस्</string>
|
||||
<string name="pref_library_update_manga_restriction">इन्ट्री अपडेट गर्न छोड्नुहोस्</string>
|
||||
<string name="pref_update_only_completely_read">नपढेको अध्याय(हरू) सँग</string>
|
||||
@ -696,7 +696,7 @@
|
||||
<string name="confirm_add_duplicate_manga">तपाईको पुस्तकालयमा एउटै नामको इन्ट्री छ।
|
||||
\n
|
||||
\nके तपाईं अझै जारी राख्न चाहनुहुन्छ\?</string>
|
||||
<string name="reading_list">पढ्ने सूची</string>
|
||||
<string name="reading_list">पढिरहेको सूची</string>
|
||||
<string name="updates_last_update_info">पुस्तकालय पछिल्लो पटक अपडेट गरिएको: %s</string>
|
||||
<string name="crash_screen_description">%s एक अप्रत्याशित त्रुटिमा पर्यो। समर्थन को लागि हामी तपाईंलाई हाम्रो Discord को #support च्यानलमा क्र्यास लगहरू साझेदारी गर्न सुझाव दिन्छौं।</string>
|
||||
<string name="update_check_open">GitHub मा खोल्नुहोस्</string>
|
||||
@ -831,4 +831,5 @@
|
||||
<string name="track_delete_remote_text">%s बाट पनि हटाउनुहोस्</string>
|
||||
<string name="track_delete_text">यसले लोकल रूपमा ट्र्याकिङ हटाउनेछ।</string>
|
||||
<string name="action_ok">ठीक छ</string>
|
||||
<string name="delete_downloaded">डाउनलोड गरिएको मेट्नुहोस्</string>
|
||||
</resources>
|
@ -640,7 +640,7 @@
|
||||
<string name="download_queue_size_warning">Aviso: grandes downloads em massa podem levar as fontes a ficarem lentas e/ou começarem a bloquear o Tachiyomi. Toque para saber mais.</string>
|
||||
<string name="ext_update_all">Atualizar tudo</string>
|
||||
<string name="channel_app_updates">Atualizações do aplicativo</string>
|
||||
<string name="pref_auto_clear_chapter_cache">Limpar o cache de capítulos ao fechar o aplicativo</string>
|
||||
<string name="pref_auto_clear_chapter_cache">Limpar o cache de capítulos ao abrir o aplicativo</string>
|
||||
<string name="clear_database_source_item_count">%1$d itens que não estão na biblioteca no banco de dados</string>
|
||||
<string name="database_clean">Nada a ser limpo</string>
|
||||
<string name="extension_api_error">Erro ao obter a lista de extensões</string>
|
||||
@ -841,4 +841,5 @@
|
||||
<string name="track_delete_title">Remover o monitoramento do %s\?</string>
|
||||
<string name="track_delete_text">Isso irá remover o monitoramento localmente.</string>
|
||||
<string name="track_delete_remote_text">Também remover do %s</string>
|
||||
<string name="delete_downloaded">Deletar os disponíveis offline</string>
|
||||
</resources>
|
@ -652,7 +652,7 @@
|
||||
<string name="download_queue_size_warning">Предупреждение: Большое количество загрузок может привести к замедлению работы источников и/или блокировке Tachiyomi. Нажмите для подробностей.</string>
|
||||
<string name="ext_update_all">Обновить все</string>
|
||||
<string name="channel_app_updates">Обновления приложения</string>
|
||||
<string name="pref_auto_clear_chapter_cache">Очищать кэш глав при закрытии приложения</string>
|
||||
<string name="pref_auto_clear_chapter_cache">Очищать кэш глав при запуске приложения</string>
|
||||
<string name="clear_database_source_item_count">%1$d не библиотечных серий в базе данных</string>
|
||||
<string name="database_clean">Нечего очищать</string>
|
||||
<string name="extension_api_error">Не удалось получить список расширений</string>
|
||||
@ -867,4 +867,5 @@
|
||||
<string name="track_delete_text">Это удалит отслеживание локально.</string>
|
||||
<string name="track_delete_remote_text">Также удалить из %s</string>
|
||||
<string name="action_ok">ОК</string>
|
||||
<string name="delete_downloaded">Удалить загруженное</string>
|
||||
</resources>
|
@ -567,7 +567,7 @@
|
||||
<string name="cover_saved">Cobertedda sarvada</string>
|
||||
<string name="manga_cover">Cobertedda</string>
|
||||
<string name="tracking_guide">Ghia pro s\'arrastamentu</string>
|
||||
<string name="categorized_display_settings">Impostatziones de ordinamentu e visualizatzione pro categoria</string>
|
||||
<string name="categorized_display_settings">Impostatziones de ordinamentu pro categoria</string>
|
||||
<string name="information_empty_category_dialog">Non tenes galu peruna categoria.</string>
|
||||
<string name="action_start_downloading_now">Incumintza a iscarrigare como</string>
|
||||
<string name="theme_tako">Tako</string>
|
||||
@ -643,7 +643,7 @@
|
||||
<string name="cancelled">Annullada</string>
|
||||
<string name="webtoon_side_padding_5">5%</string>
|
||||
<string name="action_show_manga">Ammustra s\'elementu</string>
|
||||
<string name="pref_landscape_zoom">Ismànnia s\'immàgine in orizontale</string>
|
||||
<string name="pref_landscape_zoom">Ismànnia in automàticu sas immàgines largas</string>
|
||||
<string name="action_display_cover_only_grid">Grìllia cun coberteddas ebbia</string>
|
||||
<string name="pref_update_only_started">No incumintzadas</string>
|
||||
<string name="pref_navigate_pan">Iscurre sas pàginas largas</string>
|
||||
@ -799,4 +799,36 @@
|
||||
<string name="pref_chapter_swipe">Iscurrimentu de capìtulu</string>
|
||||
<string name="pref_chapter_swipe_start">Atzione de iscurrimentu a manca</string>
|
||||
<string name="pref_debug_info">Informatziones de depuratzione de còdighe</string>
|
||||
<string name="manga_display_interval_title">Càrcula cada</string>
|
||||
<string name="action_filter_interval_custom">Intervallu de recùperu personalizadu</string>
|
||||
<string name="action_filter_interval_long">Recùpera cada mese (28 dies)</string>
|
||||
<string name="action_filter_interval_dropped">Abbandonadu\? In ritardu de 20+ dies e 2 meses</string>
|
||||
<string name="action_sort_next_updated">Agiornamentu imbente prevìdidu</string>
|
||||
<string name="pref_update_only_in_release_period">Foras de su perìodu de publicatzione prevìdidu</string>
|
||||
<string name="intervals_header">Intervallos</string>
|
||||
<string name="manga_display_modified_interval_title">Imposta s\'agiornamentu pro cada</string>
|
||||
<string name="manga_modify_interval_title">Modìfica s\'intervallu</string>
|
||||
<string name="skipped_reason_not_in_release_period">Brincadu ca non bi fiat peruna publicatzione prevìdida oe</string>
|
||||
<string name="action_set_interval">Imposta s\'intervallu</string>
|
||||
<string name="action_filter_interval_late">Verìfica in ritardu de 10+ dies</string>
|
||||
<string name="action_filter_interval_passed">Perìodu de controllu coladu</string>
|
||||
<string name="manga_modify_calculated_interval_title">Personaliza s\'intervallu</string>
|
||||
<plurals name="pref_update_release_leading_days">
|
||||
<item quantity="one">%d die in antis</item>
|
||||
<item quantity="other">%d dies in antis</item>
|
||||
</plurals>
|
||||
<plurals name="pref_update_release_following_days">
|
||||
<item quantity="one">%d die a pustis</item>
|
||||
<item quantity="other">%d dies a pustis</item>
|
||||
</plurals>
|
||||
<string name="pref_update_release_grace_period_info">Unu perìodu de gràtzia bassu est cussigiadu pro minimare sa pressione subra sas fontes. Prus controllos pro un\'elementu si perdent, prus longu at a èssere s\'intervallu intre sos controllos cun unu màssimu de 28 dies.</string>
|
||||
<string name="action_ok">AB</string>
|
||||
<string name="pref_update_release_grace_period">Perìodu de gràtzia prevìdidu pro sa publicatzione</string>
|
||||
<string name="track_delete_title">Bogare s\'arrastadore de %s\?</string>
|
||||
<string name="track_delete_text">Custu at a bogare s\'arrastamentu locale.</string>
|
||||
<string name="track_delete_remote_text">Boga fintzas dae %s</string>
|
||||
<plurals name="day">
|
||||
<item quantity="one">1 die</item>
|
||||
<item quantity="other">%d dies</item>
|
||||
</plurals>
|
||||
</resources>
|
@ -830,4 +830,5 @@
|
||||
<string name="skipped_reason_not_in_release_period">Atlandı çünkü bugün bir yayın beklenmiyordu</string>
|
||||
<string name="track_delete_text">Bu, izlemeyi yerel olarak kaldıracak.</string>
|
||||
<string name="track_delete_remote_text">Ayrıca şuradan da kaldır: %s</string>
|
||||
<string name="manga_display_modified_interval_title">Güncelleme aralığını ayarla</string>
|
||||
</resources>
|
@ -616,7 +616,7 @@
|
||||
<string name="download_queue_size_warning">警告:批量下载可能导致图源变慢,甚至会使得它们屏蔽 Tachiyomi。点击了解详情。</string>
|
||||
<string name="ext_update_all">全部更新</string>
|
||||
<string name="channel_app_updates">应用更新</string>
|
||||
<string name="pref_auto_clear_chapter_cache">关闭应用时清除章节缓存</string>
|
||||
<string name="pref_auto_clear_chapter_cache">启动时清除章节缓存</string>
|
||||
<string name="database_clean">无需清理</string>
|
||||
<string name="clear_database_source_item_count">数据库中有 %1$d 部作品未添加到书架</string>
|
||||
<string name="extension_api_error">无法获取插件列表</string>
|
||||
@ -797,4 +797,5 @@
|
||||
<string name="track_delete_title">要删除 %s 的记录吗?</string>
|
||||
<string name="track_delete_remote_text">同时删除 %s 上的数据</string>
|
||||
<string name="track_delete_text">将会在本地删除进度记录的关联。</string>
|
||||
<string name="delete_downloaded">删除已下载章节</string>
|
||||
</resources>
|
@ -616,7 +616,7 @@
|
||||
<string name="download_queue_size_warning">警告:大量批次下載可能壅塞來源並 (或) 使其封鎖 Tachiyomi。輕觸以瞭解詳情。</string>
|
||||
<string name="ext_update_all">全部更新</string>
|
||||
<string name="channel_app_updates">應用程式更新</string>
|
||||
<string name="pref_auto_clear_chapter_cache">結束應用程式時清除章節快取</string>
|
||||
<string name="pref_auto_clear_chapter_cache">啟動應用程式時清除章節快取</string>
|
||||
<string name="clear_database_source_item_count">資料庫中有 %1$d 部作品不屬於藏書</string>
|
||||
<string name="database_clean">無須清理</string>
|
||||
<string name="extension_api_error">擴充套件清單取得失敗</string>
|
||||
@ -631,7 +631,7 @@
|
||||
<string name="action_faq_and_guides">常見問題與指南</string>
|
||||
<string name="webtoon_side_padding_5">5%</string>
|
||||
<string name="action_show_manga">顯示作品</string>
|
||||
<string name="pref_landscape_zoom">縮放橫向圖片</string>
|
||||
<string name="pref_landscape_zoom">自動縮放寬頁</string>
|
||||
<string name="pref_navigate_pan">導覽寬頁時先平移後翻頁</string>
|
||||
<string name="action_display_cover_only_grid">純封面格狀</string>
|
||||
<string name="pref_update_only_started">無已讀的章節</string>
|
||||
@ -785,4 +785,8 @@
|
||||
<string name="pref_chapter_swipe">目錄滑動動作</string>
|
||||
<string name="pref_library_columns_per_row">每列 %d 欄</string>
|
||||
<string name="action_ok">確定</string>
|
||||
<string name="delete_downloaded">刪除舊有下載章節</string>
|
||||
<string name="track_delete_title">移除「%s」歷程平台?</string>
|
||||
<string name="track_delete_text">這將在本機上解除登錄該歷程平台。</string>
|
||||
<string name="track_delete_remote_text">同時移除「%s」上的資料</string>
|
||||
</resources>
|
@ -12,6 +12,7 @@
|
||||
<string name="manga">Library entries</string>
|
||||
<string name="chapters">Chapters</string>
|
||||
<string name="track">Tracking</string>
|
||||
<string name="delete_downloaded">Delete downloaded</string>
|
||||
<string name="history">History</string>
|
||||
|
||||
<!-- Screen titles -->
|
||||
@ -380,7 +381,7 @@
|
||||
<string name="tapping_inverted_vertical">Vertical</string>
|
||||
<string name="tapping_inverted_both">Both</string>
|
||||
<string name="pref_reader_actions">Actions</string>
|
||||
<string name="pref_read_with_long_tap">Show on long tap</string>
|
||||
<string name="pref_read_with_long_tap">Show actions on long tap</string>
|
||||
<string name="pref_create_folder_per_manga">Save pages into separate folders</string>
|
||||
<string name="pref_create_folder_per_manga_summary">Creates folders according to entries\' title</string>
|
||||
<string name="pref_reader_theme">Background color</string>
|
||||
@ -474,7 +475,7 @@
|
||||
<item quantity="one">Next unread chapter</item>
|
||||
<item quantity="other">Next %d unread chapters</item>
|
||||
</plurals>
|
||||
<string name="download_ahead_info">Only works on entries in library and if the current chapter plus the next one are already downloaded</string>
|
||||
<string name="download_ahead_info">Only works if the current chapter + the next one are already downloaded.</string>
|
||||
<string name="save_chapter_as_cbz">Save as CBZ archive</string>
|
||||
<string name="split_tall_images">Split tall images</string>
|
||||
<string name="split_tall_images_summary">Improves reader performance</string>
|
||||
@ -570,7 +571,7 @@
|
||||
<string name="used_cache">Used: %1$s</string>
|
||||
<string name="cache_deleted">Cache cleared. %1$d files have been deleted</string>
|
||||
<string name="cache_delete_error">Error occurred while clearing</string>
|
||||
<string name="pref_auto_clear_chapter_cache">Clear chapter cache on app close</string>
|
||||
<string name="pref_auto_clear_chapter_cache">Clear chapter cache on app launch</string>
|
||||
<string name="pref_invalidate_download_cache">Invalidate downloads index</string>
|
||||
<string name="pref_invalidate_download_cache_summary">Force app to recheck downloaded chapters</string>
|
||||
<string name="pref_clear_database">Clear database</string>
|
||||
|
@ -21,6 +21,8 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":core"))
|
||||
|
||||
// Compose
|
||||
implementation(platform(compose.bom))
|
||||
implementation(compose.activity)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user