Restrict line length with ktlint
(cherry picked from commit 1d144e67678a99ec7198e5efcb1410b5da4bc42e) # Conflicts: # .editorconfig # app/src/main/java/eu/kanade/domain/chapter/model/ChapterFilter.kt # app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderAppBars.kt # app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt # source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt
This commit is contained in:
parent
0a9f15d74c
commit
d9a6a7be50
@ -1,15 +1,16 @@
|
|||||||
[*.{kt,kts}]
|
[*.{kt,kts}]
|
||||||
indent_size=4
|
max_line_length = 120
|
||||||
insert_final_newline=true
|
indent_size = 4
|
||||||
ij_kotlin_allow_trailing_comma=true
|
insert_final_newline = true
|
||||||
ij_kotlin_allow_trailing_comma_on_call_site=true
|
ij_kotlin_allow_trailing_comma = true
|
||||||
ij_kotlin_name_count_to_use_star_import=2147483647
|
ij_kotlin_allow_trailing_comma_on_call_site = true
|
||||||
ij_kotlin_name_count_to_use_star_import_for_members=2147483647
|
ij_kotlin_name_count_to_use_star_import = 2147483647
|
||||||
|
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
|
||||||
|
|
||||||
ktlint_standard_filename=disabled
|
ktlint_standard_filename = disabled
|
||||||
ktlint_standard_argument-list-wrapping=disabled
|
ktlint_standard_argument-list-wrapping = disabled
|
||||||
ktlint_standard_function-naming=disabled
|
ktlint_standard_function-naming = disabled
|
||||||
ktlint_standard_property-naming=disabled
|
ktlint_standard_property-naming = disabled
|
||||||
ktlint_standard_multiline-expression-wrapping=disabled
|
ktlint_standard_multiline-expression-wrapping = disabled
|
||||||
ktlint_standard_string-template-indent=disabled
|
ktlint_standard_string-template-indent = disabled
|
||||||
ktlint_standard_comment-wrapping=disabled
|
ktlint_standard_comment-wrapping = disabled
|
||||||
|
@ -100,7 +100,11 @@ class SyncChaptersWithSource(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Recognize chapter number for the chapter.
|
// Recognize chapter number for the chapter.
|
||||||
val chapterNumber = ChapterRecognition.parseChapterNumber(manga.title, chapter.name, chapter.chapterNumber)
|
val chapterNumber = ChapterRecognition.parseChapterNumber(
|
||||||
|
manga.title,
|
||||||
|
chapter.name,
|
||||||
|
chapter.chapterNumber,
|
||||||
|
)
|
||||||
chapter = chapter.copy(chapterNumber = chapterNumber)
|
chapter = chapter.copy(chapterNumber = chapterNumber)
|
||||||
|
|
||||||
val dbChapter = dbChapters.find { it.url == chapter.url }
|
val dbChapter = dbChapters.find { it.url == chapter.url }
|
||||||
@ -117,7 +121,12 @@ class SyncChaptersWithSource(
|
|||||||
} else {
|
} else {
|
||||||
if (shouldUpdateDbChapter.await(dbChapter, chapter)) {
|
if (shouldUpdateDbChapter.await(dbChapter, chapter)) {
|
||||||
val shouldRenameChapter = downloadProvider.isChapterDirNameChanged(dbChapter, chapter) &&
|
val shouldRenameChapter = downloadProvider.isChapterDirNameChanged(dbChapter, chapter) &&
|
||||||
downloadManager.isChapterDownloaded(dbChapter.name, dbChapter.scanlator, /* SY --> */ manga.ogTitle /* SY <-- */, manga.source)
|
downloadManager.isChapterDownloaded(
|
||||||
|
dbChapter.name,
|
||||||
|
dbChapter.scanlator,
|
||||||
|
/* SY --> */ manga.ogTitle /* SY <-- */,
|
||||||
|
manga.source,
|
||||||
|
)
|
||||||
|
|
||||||
if (shouldRenameChapter) {
|
if (shouldRenameChapter) {
|
||||||
downloadManager.renameChapter(source, manga, dbChapter, chapter)
|
downloadManager.renameChapter(source, manga, dbChapter, chapter)
|
||||||
|
@ -14,7 +14,11 @@ import tachiyomi.source.local.isLocal
|
|||||||
* Applies the view filters to the list of chapters obtained from the database.
|
* Applies the view filters to the list of chapters obtained from the database.
|
||||||
* @return an observable of the list of chapters filtered and sorted.
|
* @return an observable of the list of chapters filtered and sorted.
|
||||||
*/
|
*/
|
||||||
fun List<Chapter>.applyFilters(manga: Manga, downloadManager: DownloadManager/* SY --> */, mergedManga: Map<Long, Manga>/* SY <-- */): List<Chapter> {
|
fun List<Chapter>.applyFilters(
|
||||||
|
manga: Manga,
|
||||||
|
downloadManager: DownloadManager/* SY --> */,
|
||||||
|
mergedManga: Map<Long, Manga>, /* SY <-- */
|
||||||
|
): List<Chapter> {
|
||||||
val isLocalManga = manga.isLocal()
|
val isLocalManga = manga.isLocal()
|
||||||
val unreadFilter = manga.unreadFilter
|
val unreadFilter = manga.unreadFilter
|
||||||
val downloadedFilter = manga.downloadedFilter
|
val downloadedFilter = manga.downloadedFilter
|
||||||
@ -28,13 +32,21 @@ fun List<Chapter>.applyFilters(manga: Manga, downloadManager: DownloadManager/*
|
|||||||
val manga = mergedManga.getOrElse(chapter.mangaId) { manga }
|
val manga = mergedManga.getOrElse(chapter.mangaId) { manga }
|
||||||
// SY <--
|
// SY <--
|
||||||
applyFilter(downloadedFilter) {
|
applyFilter(downloadedFilter) {
|
||||||
val downloaded = downloadManager.isChapterDownloaded(chapter.name, chapter.scanlator, /* SY --> */ manga.ogTitle /* SY <-- */, manga.source)
|
val downloaded = downloadManager.isChapterDownloaded(
|
||||||
|
chapter.name,
|
||||||
|
chapter.scanlator,
|
||||||
|
/* SY --> */ manga.ogTitle /* SY <-- */,
|
||||||
|
manga.source,
|
||||||
|
)
|
||||||
downloaded || isLocalManga
|
downloaded || isLocalManga
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// SY -->
|
// SY -->
|
||||||
.filter { chapter ->
|
.filter { chapter ->
|
||||||
manga.filteredScanlators.isNullOrEmpty() || MdUtil.getScanlators(chapter.scanlator).any { group -> manga.filteredScanlators!!.contains(group) }
|
manga.filteredScanlators.isNullOrEmpty() ||
|
||||||
|
MdUtil.getScanlators(chapter.scanlator).any { group ->
|
||||||
|
manga.filteredScanlators!!.contains(group)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
.sortedWith(getChapterSort(manga))
|
.sortedWith(getChapterSort(manga))
|
||||||
@ -55,7 +67,10 @@ fun List<ChapterList.Item>.applyFilters(manga: Manga): Sequence<ChapterList.Item
|
|||||||
.filter { applyFilter(downloadedFilter) { it.isDownloaded || isLocalManga } }
|
.filter { applyFilter(downloadedFilter) { it.isDownloaded || isLocalManga } }
|
||||||
// SY -->
|
// SY -->
|
||||||
.filter { chapter ->
|
.filter { chapter ->
|
||||||
manga.filteredScanlators.isNullOrEmpty() || MdUtil.getScanlators(chapter.chapter.scanlator).any { group -> manga.filteredScanlators!!.contains(group) }
|
manga.filteredScanlators.isNullOrEmpty() ||
|
||||||
|
MdUtil.getScanlators(chapter.chapter.scanlator).any { group ->
|
||||||
|
manga.filteredScanlators!!.contains(group)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
.sortedWith { (chapter1), (chapter2) -> getChapterSort(manga).invoke(chapter1, chapter2) }
|
.sortedWith { (chapter1), (chapter2) -> getChapterSort(manga).invoke(chapter1, chapter2) }
|
||||||
|
@ -24,8 +24,9 @@ class GetExtensionsByType(
|
|||||||
val (updates, installed) = _installed
|
val (updates, installed) = _installed
|
||||||
.filter { (showNsfwSources || it.isNsfw.not()) }
|
.filter { (showNsfwSources || it.isNsfw.not()) }
|
||||||
.sortedWith(
|
.sortedWith(
|
||||||
compareBy<Extension.Installed> { it.isObsolete.not() /* SY --> */ && it.isRedundant.not() /* SY <-- */ }
|
compareBy<Extension.Installed> {
|
||||||
.thenBy(String.CASE_INSENSITIVE_ORDER) { it.name },
|
it.isObsolete.not() /* SY --> */ && it.isRedundant.not() /* SY <-- */
|
||||||
|
}.thenBy(String.CASE_INSENSITIVE_ORDER) { it.name },
|
||||||
)
|
)
|
||||||
.partition { it.hasUpdate }
|
.partition { it.hasUpdate }
|
||||||
|
|
||||||
|
@ -11,7 +11,12 @@ class SourcePreferences(
|
|||||||
private val preferenceStore: PreferenceStore,
|
private val preferenceStore: PreferenceStore,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun sourceDisplayMode() = preferenceStore.getObject("pref_display_mode_catalogue", LibraryDisplayMode.default, LibraryDisplayMode.Serializer::serialize, LibraryDisplayMode.Serializer::deserialize)
|
fun sourceDisplayMode() = preferenceStore.getObject(
|
||||||
|
"pref_display_mode_catalogue",
|
||||||
|
LibraryDisplayMode.default,
|
||||||
|
LibraryDisplayMode.Serializer::serialize,
|
||||||
|
LibraryDisplayMode.Serializer::deserialize,
|
||||||
|
)
|
||||||
|
|
||||||
fun enabledLanguages() = preferenceStore.getStringSet("source_languages", LocaleHelper.getDefaultEnabledLanguages())
|
fun enabledLanguages() = preferenceStore.getStringSet("source_languages", LocaleHelper.getDefaultEnabledLanguages())
|
||||||
|
|
||||||
@ -28,7 +33,10 @@ class SourcePreferences(
|
|||||||
|
|
||||||
fun migrationSortingMode() = preferenceStore.getEnum("pref_migration_sorting", SetMigrateSorting.Mode.ALPHABETICAL)
|
fun migrationSortingMode() = preferenceStore.getEnum("pref_migration_sorting", SetMigrateSorting.Mode.ALPHABETICAL)
|
||||||
|
|
||||||
fun migrationSortingDirection() = preferenceStore.getEnum("pref_migration_direction", SetMigrateSorting.Direction.ASCENDING)
|
fun migrationSortingDirection() = preferenceStore.getEnum(
|
||||||
|
"pref_migration_direction",
|
||||||
|
SetMigrateSorting.Direction.ASCENDING,
|
||||||
|
)
|
||||||
|
|
||||||
fun extensionUpdatesCount() = preferenceStore.getInt("ext_updates_count", 0)
|
fun extensionUpdatesCount() = preferenceStore.getInt("ext_updates_count", 0)
|
||||||
|
|
||||||
|
@ -61,7 +61,10 @@ class AddTracks(
|
|||||||
?.readAt
|
?.readAt
|
||||||
|
|
||||||
firstReadChapterDate?.let {
|
firstReadChapterDate?.let {
|
||||||
val startDate = firstReadChapterDate.time.convertEpochMillisZone(ZoneOffset.systemDefault(), ZoneOffset.UTC)
|
val startDate = firstReadChapterDate.time.convertEpochMillisZone(
|
||||||
|
ZoneOffset.systemDefault(),
|
||||||
|
ZoneOffset.UTC,
|
||||||
|
)
|
||||||
track = track.copy(
|
track = track.copy(
|
||||||
startDate = startDate,
|
startDate = startDate,
|
||||||
)
|
)
|
||||||
|
@ -43,7 +43,9 @@ class DelayedTrackingUpdateJob(private val context: Context, workerParams: Worke
|
|||||||
track?.copy(lastChapterRead = it.lastChapterRead.toDouble())
|
track?.copy(lastChapterRead = it.lastChapterRead.toDouble())
|
||||||
}
|
}
|
||||||
.forEach { track ->
|
.forEach { track ->
|
||||||
logcat(LogPriority.DEBUG) { "Updating delayed track item: ${track.mangaId}, last chapter read: ${track.lastChapterRead}" }
|
logcat(LogPriority.DEBUG) {
|
||||||
|
"Updating delayed track item: ${track.mangaId}, last chapter read: ${track.lastChapterRead}"
|
||||||
|
}
|
||||||
trackChapter.await(context, track.mangaId, track.lastChapterRead)
|
trackChapter.await(context, track.mangaId, track.lastChapterRead)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,14 +113,26 @@ private fun MigrateSourceList(
|
|||||||
|
|
||||||
IconButton(onClick = onToggleSortingMode) {
|
IconButton(onClick = onToggleSortingMode) {
|
||||||
when (sortingMode) {
|
when (sortingMode) {
|
||||||
SetMigrateSorting.Mode.ALPHABETICAL -> Icon(Icons.Outlined.SortByAlpha, contentDescription = stringResource(R.string.action_sort_alpha))
|
SetMigrateSorting.Mode.ALPHABETICAL -> Icon(
|
||||||
SetMigrateSorting.Mode.TOTAL -> Icon(Icons.Outlined.Numbers, contentDescription = stringResource(R.string.action_sort_count))
|
Icons.Outlined.SortByAlpha,
|
||||||
|
contentDescription = stringResource(R.string.action_sort_alpha),
|
||||||
|
)
|
||||||
|
SetMigrateSorting.Mode.TOTAL -> Icon(
|
||||||
|
Icons.Outlined.Numbers,
|
||||||
|
contentDescription = stringResource(R.string.action_sort_count),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IconButton(onClick = onToggleSortingDirection) {
|
IconButton(onClick = onToggleSortingDirection) {
|
||||||
when (sortingDirection) {
|
when (sortingDirection) {
|
||||||
SetMigrateSorting.Direction.ASCENDING -> Icon(Icons.Outlined.ArrowUpward, contentDescription = stringResource(R.string.action_asc))
|
SetMigrateSorting.Direction.ASCENDING -> Icon(
|
||||||
SetMigrateSorting.Direction.DESCENDING -> Icon(Icons.Outlined.ArrowDownward, contentDescription = stringResource(R.string.action_desc))
|
Icons.Outlined.ArrowUpward,
|
||||||
|
contentDescription = stringResource(R.string.action_asc),
|
||||||
|
)
|
||||||
|
SetMigrateSorting.Direction.DESCENDING -> Icon(
|
||||||
|
Icons.Outlined.ArrowDownward,
|
||||||
|
contentDescription = stringResource(R.string.action_desc),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,7 +173,13 @@ private fun SourcePinButton(
|
|||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val icon = if (isPinned) Icons.Filled.PushPin else Icons.Outlined.PushPin
|
val icon = if (isPinned) Icons.Filled.PushPin else Icons.Outlined.PushPin
|
||||||
val tint = if (isPinned) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onBackground.copy(alpha = SecondaryItemAlpha)
|
val tint = if (isPinned) {
|
||||||
|
MaterialTheme.colorScheme.primary
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.onBackground.copy(
|
||||||
|
alpha = SecondaryItemAlpha,
|
||||||
|
)
|
||||||
|
}
|
||||||
val description = if (isPinned) R.string.action_unpin else R.string.action_pin
|
val description = if (isPinned) R.string.action_unpin else R.string.action_pin
|
||||||
IconButton(onClick = onClick) {
|
IconButton(onClick = onClick) {
|
||||||
Icon(
|
Icon(
|
||||||
|
@ -25,7 +25,9 @@ fun BaseSourceItem(
|
|||||||
action: @Composable RowScope.(Source) -> Unit = {},
|
action: @Composable RowScope.(Source) -> Unit = {},
|
||||||
content: @Composable RowScope.(Source, String?) -> Unit = defaultContent,
|
content: @Composable RowScope.(Source, String?) -> Unit = defaultContent,
|
||||||
) {
|
) {
|
||||||
val sourceLangString = LocaleHelper.getSourceDisplayName(source.lang, LocalContext.current).takeIf { showLanguageInContent }
|
val sourceLangString = LocaleHelper.getSourceDisplayName(source.lang, LocalContext.current).takeIf {
|
||||||
|
showLanguageInContent
|
||||||
|
}
|
||||||
BaseBrowseItem(
|
BaseBrowseItem(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
onClickItem = onClickItem,
|
onClickItem = onClickItem,
|
||||||
|
@ -71,7 +71,10 @@ fun CategoryListItem(
|
|||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
IconButton(onClick = onRename) {
|
IconButton(onClick = onRename) {
|
||||||
Icon(imageVector = Icons.Outlined.Edit, contentDescription = stringResource(R.string.action_rename_category))
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Edit,
|
||||||
|
contentDescription = stringResource(R.string.action_rename_category),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
IconButton(onClick = onDelete) {
|
IconButton(onClick = onDelete) {
|
||||||
Icon(imageVector = Icons.Outlined.Delete, contentDescription = stringResource(R.string.action_delete))
|
Icon(imageVector = Icons.Outlined.Delete, contentDescription = stringResource(R.string.action_delete))
|
||||||
|
@ -764,7 +764,9 @@ fun MangaScreenLargeImpl(
|
|||||||
val isReading = remember(state.chapters) {
|
val isReading = remember(state.chapters) {
|
||||||
state.chapters.fastAny { it.chapter.read }
|
state.chapters.fastAny { it.chapter.read }
|
||||||
}
|
}
|
||||||
Text(text = stringResource(if (isReading) R.string.action_resume else R.string.action_start))
|
Text(
|
||||||
|
text = stringResource(if (isReading) R.string.action_resume else R.string.action_start),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
|
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
|
||||||
onClick = onContinueReading,
|
onClick = onContinueReading,
|
||||||
@ -1030,7 +1032,9 @@ private fun LazyListScope.sharedChapterItems(
|
|||||||
it + 1,
|
it + 1,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
scanlator = item.chapter.scanlator.takeIf { !it.isNullOrBlank() /* SY --> */ && item.showScanlator /* SY <-- */ },
|
scanlator = item.chapter.scanlator.takeIf {
|
||||||
|
!it.isNullOrBlank() /* SY --> */ && item.showScanlator /* SY <-- */
|
||||||
|
},
|
||||||
// SY -->
|
// SY -->
|
||||||
sourceName = item.sourceName,
|
sourceName = item.sourceName,
|
||||||
// SY <--
|
// SY <--
|
||||||
@ -1082,7 +1086,11 @@ private fun onChapterItemClick(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
typealias MetadataDescriptionComposable = @Composable (state: MangaScreenModel.State.Success, openMetadataViewer: () -> Unit, search: (String) -> Unit) -> Unit
|
typealias MetadataDescriptionComposable = @Composable (
|
||||||
|
state: MangaScreenModel.State.Success,
|
||||||
|
openMetadataViewer: () -> Unit,
|
||||||
|
search: (String) -> Unit,
|
||||||
|
) -> Unit
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun metadataDescription(source: Source): MetadataDescriptionComposable? {
|
fun metadataDescription(source: Source): MetadataDescriptionComposable? {
|
||||||
|
@ -248,7 +248,8 @@ fun LibraryBottomActionMenu(
|
|||||||
tonalElevation = 3.dp,
|
tonalElevation = 3.dp,
|
||||||
) {
|
) {
|
||||||
val haptic = LocalHapticFeedback.current
|
val haptic = LocalHapticFeedback.current
|
||||||
val confirm = remember { mutableStateListOf(false, false, false, false, false /* SY --> */, false /* SY <-- */) }
|
val confirm =
|
||||||
|
remember { mutableStateListOf(false, false, false, false, false /* SY --> */, false /* SY <-- */) }
|
||||||
var resetJob: Job? = remember { null }
|
var resetJob: Job? = remember { null }
|
||||||
val onLongClickItem: (Int) -> Unit = { toConfirmIndex ->
|
val onLongClickItem: (Int) -> Unit = { toConfirmIndex ->
|
||||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||||
|
@ -42,7 +42,6 @@ import androidx.compose.material.icons.outlined.Sync
|
|||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.LocalContentColor
|
import androidx.compose.material3.LocalContentColor
|
||||||
import androidx.compose.material3.LocalMinimumInteractiveComponentEnforcement
|
|
||||||
import androidx.compose.material3.LocalTextStyle
|
import androidx.compose.material3.LocalTextStyle
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ProvideTextStyle
|
import androidx.compose.material3.ProvideTextStyle
|
||||||
@ -192,7 +191,11 @@ fun MangaActionRow(
|
|||||||
)
|
)
|
||||||
if (onEditIntervalClicked != null && fetchInterval != null) {
|
if (onEditIntervalClicked != null && fetchInterval != null) {
|
||||||
MangaActionButton(
|
MangaActionButton(
|
||||||
title = pluralStringResource(id = R.plurals.day, count = fetchInterval.absoluteValue, fetchInterval.absoluteValue),
|
title = pluralStringResource(
|
||||||
|
id = R.plurals.day,
|
||||||
|
count = fetchInterval.absoluteValue,
|
||||||
|
fetchInterval.absoluteValue,
|
||||||
|
),
|
||||||
icon = Icons.Default.HourglassEmpty,
|
icon = Icons.Default.HourglassEmpty,
|
||||||
color = if (isUserIntervalMode) MaterialTheme.colorScheme.primary else defaultActionButtonColor,
|
color = if (isUserIntervalMode) MaterialTheme.colorScheme.primary else defaultActionButtonColor,
|
||||||
onClick = onEditIntervalClicked,
|
onClick = onEditIntervalClicked,
|
||||||
@ -625,7 +628,9 @@ private fun MangaSummary(
|
|||||||
val image = AnimatedImageVector.animatedVectorResource(R.drawable.anim_caret_down)
|
val image = AnimatedImageVector.animatedVectorResource(R.drawable.anim_caret_down)
|
||||||
Icon(
|
Icon(
|
||||||
painter = rememberAnimatedVectorPainter(image, !expanded),
|
painter = rememberAnimatedVectorPainter(image, !expanded),
|
||||||
contentDescription = stringResource(if (expanded) R.string.manga_info_collapse else R.string.manga_info_expand),
|
contentDescription = stringResource(
|
||||||
|
if (expanded) R.string.manga_info_collapse else R.string.manga_info_expand,
|
||||||
|
),
|
||||||
tint = MaterialTheme.colorScheme.onBackground,
|
tint = MaterialTheme.colorScheme.onBackground,
|
||||||
modifier = Modifier.background(Brush.radialGradient(colors = colors.asReversed())),
|
modifier = Modifier.background(Brush.radialGradient(colors = colors.asReversed())),
|
||||||
)
|
)
|
||||||
|
@ -72,7 +72,9 @@ fun MoreScreen(
|
|||||||
WarningBanner(
|
WarningBanner(
|
||||||
textRes = R.string.fdroid_warning,
|
textRes = R.string.fdroid_warning,
|
||||||
modifier = Modifier.clickable {
|
modifier = Modifier.clickable {
|
||||||
uriHandler.openUri("https://tachiyomi.org/docs/faq/general#how-do-i-update-from-the-f-droid-builds")
|
uriHandler.openUri(
|
||||||
|
"https://tachiyomi.org/docs/faq/general#how-do-i-update-from-the-f-droid-builds",
|
||||||
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,8 @@ fun getCategoriesLabel(
|
|||||||
|
|
||||||
val includedItemsText = when {
|
val includedItemsText = when {
|
||||||
// Some selected, but not all
|
// Some selected, but not all
|
||||||
includedCategories.isNotEmpty() && includedCategories.size != allCategories.size -> includedCategories.joinToString { it.visualName(context) }
|
includedCategories.isNotEmpty() && includedCategories.size != allCategories.size ->
|
||||||
|
includedCategories.joinToString { it.visualName(context) }
|
||||||
// All explicitly selected
|
// All explicitly selected
|
||||||
includedCategories.size == allCategories.size -> stringResource(R.string.all)
|
includedCategories.size == allCategories.size -> stringResource(R.string.all)
|
||||||
allExcluded -> stringResource(R.string.none)
|
allExcluded -> stringResource(R.string.none)
|
||||||
|
@ -124,7 +124,9 @@ object SettingsAppearanceScreen : SearchableSettings {
|
|||||||
uiPreferences: UiPreferences,
|
uiPreferences: UiPreferences,
|
||||||
): Preference.PreferenceGroup {
|
): Preference.PreferenceGroup {
|
||||||
val langs = remember { getLangs(context) }
|
val langs = remember { getLangs(context) }
|
||||||
var currentLanguage by remember { mutableStateOf(AppCompatDelegate.getApplicationLocales().get(0)?.toLanguageTag() ?: "") }
|
var currentLanguage by remember {
|
||||||
|
mutableStateOf(AppCompatDelegate.getApplicationLocales().get(0)?.toLanguageTag() ?: "")
|
||||||
|
}
|
||||||
val now = remember { Date().time }
|
val now = remember { Date().time }
|
||||||
|
|
||||||
val dateFormat by uiPreferences.dateFormat().collectAsState()
|
val dateFormat by uiPreferences.dateFormat().collectAsState()
|
||||||
|
@ -181,7 +181,8 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
unsortedPreferences: UnsortedPreferences,
|
unsortedPreferences: UnsortedPreferences,
|
||||||
openWarnConfigureDialogController: () -> Unit,
|
openWarnConfigureDialogController: () -> Unit,
|
||||||
): Preference.PreferenceItem.SwitchPreference {
|
): Preference.PreferenceItem.SwitchPreference {
|
||||||
val activityResultContract = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
val activityResultContract =
|
||||||
|
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||||
if (it.resultCode == Activity.RESULT_OK) {
|
if (it.resultCode == Activity.RESULT_OK) {
|
||||||
// Upload settings
|
// Upload settings
|
||||||
openWarnConfigureDialogController()
|
openWarnConfigureDialogController()
|
||||||
@ -932,7 +933,9 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun updateCheckerFrequency(unsortedPreferences: UnsortedPreferences): Preference.PreferenceItem.ListPreference<Int> {
|
fun updateCheckerFrequency(
|
||||||
|
unsortedPreferences: UnsortedPreferences,
|
||||||
|
): Preference.PreferenceItem.ListPreference<Int> {
|
||||||
val value by unsortedPreferences.exhAutoUpdateFrequency().collectAsState()
|
val value by unsortedPreferences.exhAutoUpdateFrequency().collectAsState()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
return Preference.PreferenceItem.ListPreference(
|
return Preference.PreferenceItem.ListPreference(
|
||||||
@ -941,7 +944,12 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
subtitle = if (value == 0) {
|
subtitle = if (value == 0) {
|
||||||
stringResource(R.string.time_between_batches_summary_1, stringResource(R.string.app_name))
|
stringResource(R.string.time_between_batches_summary_1, stringResource(R.string.app_name))
|
||||||
} else {
|
} else {
|
||||||
stringResource(R.string.time_between_batches_summary_2, stringResource(R.string.app_name), value, EHentaiUpdateWorkerConstants.UPDATES_PER_ITERATION)
|
stringResource(
|
||||||
|
R.string.time_between_batches_summary_2,
|
||||||
|
stringResource(R.string.app_name),
|
||||||
|
value,
|
||||||
|
EHentaiUpdateWorkerConstants.UPDATES_PER_ITERATION,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
entries = mapOf(
|
entries = mapOf(
|
||||||
0 to stringResource(R.string.time_between_batches_never),
|
0 to stringResource(R.string.time_between_batches_never),
|
||||||
@ -961,7 +969,9 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun autoUpdateRequirements(unsortedPreferences: UnsortedPreferences): Preference.PreferenceItem.MultiSelectListPreference {
|
fun autoUpdateRequirements(
|
||||||
|
unsortedPreferences: UnsortedPreferences,
|
||||||
|
): Preference.PreferenceItem.MultiSelectListPreference {
|
||||||
val value by unsortedPreferences.exhAutoUpdateRequirements().collectAsState()
|
val value by unsortedPreferences.exhAutoUpdateRequirements().collectAsState()
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
return Preference.PreferenceItem.MultiSelectListPreference(
|
return Preference.PreferenceItem.MultiSelectListPreference(
|
||||||
@ -1100,12 +1110,18 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
|
|
||||||
private fun getRelativeTimeString(relativeTime: RelativeTime, context: Context): String {
|
private fun getRelativeTimeString(relativeTime: RelativeTime, context: Context): String {
|
||||||
return relativeTime.years?.let { context.resources.getQuantityString(R.plurals.humanize_year, it.toInt(), it) }
|
return relativeTime.years?.let { context.resources.getQuantityString(R.plurals.humanize_year, it.toInt(), it) }
|
||||||
?: relativeTime.months?.let { context.resources.getQuantityString(R.plurals.humanize_month, it.toInt(), it) }
|
?: relativeTime.months?.let {
|
||||||
|
context.resources.getQuantityString(R.plurals.humanize_month, it.toInt(), it)
|
||||||
|
}
|
||||||
?: relativeTime.weeks?.let { context.resources.getQuantityString(R.plurals.humanize_week, it.toInt(), it) }
|
?: relativeTime.weeks?.let { context.resources.getQuantityString(R.plurals.humanize_week, it.toInt(), it) }
|
||||||
?: relativeTime.days?.let { context.resources.getQuantityString(R.plurals.humanize_day, it.toInt(), it) }
|
?: relativeTime.days?.let { context.resources.getQuantityString(R.plurals.humanize_day, it.toInt(), it) }
|
||||||
?: relativeTime.hours?.let { context.resources.getQuantityString(R.plurals.humanize_hour, it.toInt(), it) }
|
?: relativeTime.hours?.let { context.resources.getQuantityString(R.plurals.humanize_hour, it.toInt(), it) }
|
||||||
?: relativeTime.minutes?.let { context.resources.getQuantityString(R.plurals.humanize_minute, it.toInt(), it) }
|
?: relativeTime.minutes?.let {
|
||||||
?: relativeTime.seconds?.let { context.resources.getQuantityString(R.plurals.humanize_second, it.toInt(), it) }
|
context.resources.getQuantityString(R.plurals.humanize_minute, it.toInt(), it)
|
||||||
|
}
|
||||||
|
?: relativeTime.seconds?.let {
|
||||||
|
context.resources.getQuantityString(R.plurals.humanize_second, it.toInt(), it)
|
||||||
|
}
|
||||||
?: context.getString(R.string.humanize_fallback)
|
?: context.getString(R.string.humanize_fallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1138,7 +1154,12 @@ object SettingsEhScreen : SearchableSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val statsText = if (stats != null) {
|
val statsText = if (stats != null) {
|
||||||
context.getString(R.string.gallery_updater_stats_text, getRelativeTimeString(getRelativeTimeFromNow(stats.startTime.milliseconds), context), stats.updateCount, stats.possibleUpdates)
|
context.getString(
|
||||||
|
R.string.gallery_updater_stats_text,
|
||||||
|
getRelativeTimeString(getRelativeTimeFromNow(stats.startTime.milliseconds), context),
|
||||||
|
stats.updateCount,
|
||||||
|
stats.possibleUpdates,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
context.getString(R.string.gallery_updater_not_ran_yet)
|
context.getString(R.string.gallery_updater_not_ran_yet)
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,10 @@ object SettingsMangadexScreen : SearchableSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun loginPreference(mdex: MangaDex, trackPreferences: TrackPreferences): Preference.PreferenceItem.CustomPreference {
|
fun loginPreference(
|
||||||
|
mdex: MangaDex,
|
||||||
|
trackPreferences: TrackPreferences,
|
||||||
|
): Preference.PreferenceItem.CustomPreference {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val loggedIn by remember { trackPreferences.trackToken(mdex.mdList) }.collectAsState()
|
val loggedIn by remember { trackPreferences.trackToken(mdex.mdList) }.collectAsState()
|
||||||
|
@ -115,7 +115,9 @@ class WorkerInfoScreen : Screen() {
|
|||||||
private val workManager = context.workManager
|
private val workManager = context.workManager
|
||||||
|
|
||||||
val finished = workManager
|
val finished = workManager
|
||||||
.getWorkInfosLiveData(WorkQuery.fromStates(WorkInfo.State.SUCCEEDED, WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
|
.getWorkInfosLiveData(
|
||||||
|
WorkQuery.fromStates(WorkInfo.State.SUCCEEDED, WorkInfo.State.FAILED, WorkInfo.State.CANCELLED),
|
||||||
|
)
|
||||||
.asFlow()
|
.asFlow()
|
||||||
.map(::constructString)
|
.map(::constructString)
|
||||||
.stateIn(ioCoroutineScope, SharingStarted.WhileSubscribed(), "")
|
.stateIn(ioCoroutineScope, SharingStarted.WhileSubscribed(), "")
|
||||||
|
@ -115,8 +115,16 @@ fun <T> TriStateListDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!listState.isScrolledToStart()) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter))
|
if (!listState.isScrolledToStart()) {
|
||||||
if (!listState.isScrolledToEnd()) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter))
|
HorizontalDivider(
|
||||||
|
modifier = Modifier.align(Alignment.TopCenter),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (!listState.isScrolledToEnd()) {
|
||||||
|
HorizontalDivider(
|
||||||
|
modifier = Modifier.align(Alignment.BottomCenter),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -53,6 +53,6 @@ fun PageIndicatorText(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun PageIndicatorTextPreview() {
|
private fun PageIndicatorTextPreview() {
|
||||||
TachiyomiTheme {
|
TachiyomiTheme {
|
||||||
PageIndicatorText(currentPage = 10, totalPages = 69)
|
PageIndicatorText(currentPage = "10", totalPages = 69)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,9 @@ fun ChapterNavigator(
|
|||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Outlined.SkipPrevious,
|
imageVector = Icons.Outlined.SkipPrevious,
|
||||||
contentDescription = stringResource(if (isRtl) R.string.action_next_chapter else R.string.action_previous_chapter),
|
contentDescription = stringResource(
|
||||||
|
if (isRtl) R.string.action_next_chapter else R.string.action_previous_chapter,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,7 +157,9 @@ fun ChapterNavigator(
|
|||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Outlined.SkipNext,
|
imageVector = Icons.Outlined.SkipNext,
|
||||||
contentDescription = stringResource(if (isRtl) R.string.action_previous_chapter else R.string.action_next_chapter),
|
contentDescription = stringResource(
|
||||||
|
if (isRtl) R.string.action_previous_chapter else R.string.action_next_chapter,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ fun ExhUtils(
|
|||||||
Column(
|
Column(
|
||||||
modifier
|
modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.background(backgroundColor)
|
.background(backgroundColor),
|
||||||
) {
|
) {
|
||||||
AnimatedVisibility(visible = isVisible) {
|
AnimatedVisibility(visible = isVisible) {
|
||||||
Column {
|
Column {
|
||||||
@ -83,12 +83,12 @@ fun ExhUtils(
|
|||||||
fontSize = 13.sp,
|
fontSize = 13.sp,
|
||||||
fontFamily = FontFamily.SansSerif,
|
fontFamily = FontFamily.SansSerif,
|
||||||
style = MaterialTheme.typography.labelLarge,
|
style = MaterialTheme.typography.labelLarge,
|
||||||
modifier = Modifier.padding(start = 24.dp)
|
modifier = Modifier.padding(start = 24.dp),
|
||||||
)
|
)
|
||||||
Switch(
|
Switch(
|
||||||
checked = isAutoScroll,
|
checked = isAutoScroll,
|
||||||
onCheckedChange = null,
|
onCheckedChange = null,
|
||||||
enabled = isAutoScrollEnabled
|
enabled = isAutoScrollEnabled,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Row(
|
Row(
|
||||||
@ -112,18 +112,18 @@ fun ExhUtils(
|
|||||||
focusedContainerColor = Color.Transparent,
|
focusedContainerColor = Color.Transparent,
|
||||||
unfocusedContainerColor = Color.Transparent,
|
unfocusedContainerColor = Color.Transparent,
|
||||||
focusedTextColor = MaterialTheme.colorScheme.onSurface,
|
focusedTextColor = MaterialTheme.colorScheme.onSurface,
|
||||||
unfocusedTextColor = MaterialTheme.colorScheme.onSurface
|
unfocusedTextColor = MaterialTheme.colorScheme.onSurface,
|
||||||
),
|
),
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
keyboardOptions = KeyboardOptions(
|
keyboardOptions = KeyboardOptions(
|
||||||
keyboardType = KeyboardType.Decimal
|
keyboardType = KeyboardType.Decimal,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
AnimatedVisibility(!isAutoScrollEnabled) {
|
AnimatedVisibility(!isAutoScrollEnabled) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.eh_autoscroll_freq_invalid),
|
text = stringResource(R.string.eh_autoscroll_freq_invalid),
|
||||||
color = MaterialTheme.colorScheme.error,
|
color = MaterialTheme.colorScheme.error,
|
||||||
style = MaterialTheme.typography.labelSmall
|
style = MaterialTheme.typography.labelSmall,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,7 +147,7 @@ fun ExhUtils(
|
|||||||
Row(
|
Row(
|
||||||
Modifier.fillMaxWidth(0.5f),
|
Modifier.fillMaxWidth(0.5f),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = onClickRetryAll,
|
onClick = onClickRetryAll,
|
||||||
@ -157,7 +157,7 @@ fun ExhUtils(
|
|||||||
text = stringResource(R.string.eh_retry_all),
|
text = stringResource(R.string.eh_retry_all),
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
fontSize = 13.sp,
|
fontSize = 13.sp,
|
||||||
fontFamily = FontFamily.SansSerif
|
fontFamily = FontFamily.SansSerif,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
TextButton(
|
TextButton(
|
||||||
@ -175,7 +175,7 @@ fun ExhUtils(
|
|||||||
Row(
|
Row(
|
||||||
Modifier.fillMaxWidth(),
|
Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = onClickBoostPage,
|
onClick = onClickBoostPage,
|
||||||
@ -185,7 +185,7 @@ fun ExhUtils(
|
|||||||
text = stringResource(R.string.eh_boost_page),
|
text = stringResource(R.string.eh_boost_page),
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
fontSize = 13.sp,
|
fontSize = 13.sp,
|
||||||
fontFamily = FontFamily.SansSerif
|
fontFamily = FontFamily.SansSerif,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
TextButton(
|
TextButton(
|
||||||
@ -206,7 +206,7 @@ fun ExhUtils(
|
|||||||
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = { onSetExhUtilsVisibility(!isVisible) },
|
onClick = { onSetExhUtilsVisibility(!isVisible) },
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth(),
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = if (isVisible) {
|
imageVector = if (isVisible) {
|
||||||
@ -237,7 +237,7 @@ private fun ExhUtilsPreview() {
|
|||||||
onClickBoostPage = {},
|
onClickBoostPage = {},
|
||||||
onClickBoostPageHelp = {},
|
onClickBoostPageHelp = {},
|
||||||
onClickRetryAll = {},
|
onClickRetryAll = {},
|
||||||
onClickRetryAllHelp = {}
|
onClickRetryAllHelp = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,11 +46,11 @@ enum class NavBarType {
|
|||||||
fun BoxIgnoreLayoutDirection(modifier: Modifier, content: @Composable BoxScope.() -> Unit) {
|
fun BoxIgnoreLayoutDirection(modifier: Modifier, content: @Composable BoxScope.() -> Unit) {
|
||||||
val layoutDirection = LocalLayoutDirection.current
|
val layoutDirection = LocalLayoutDirection.current
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalLayoutDirection provides LayoutDirection.Ltr
|
LocalLayoutDirection provides LayoutDirection.Ltr,
|
||||||
) {
|
) {
|
||||||
Box(modifier) {
|
Box(modifier) {
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalLayoutDirection provides layoutDirection
|
LocalLayoutDirection provides layoutDirection,
|
||||||
) {
|
) {
|
||||||
content()
|
content()
|
||||||
}
|
}
|
||||||
@ -125,7 +125,7 @@ fun ReaderAppBars(
|
|||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
BoxIgnoreLayoutDirection(
|
BoxIgnoreLayoutDirection(
|
||||||
Modifier.fillMaxWidth()
|
Modifier.fillMaxWidth(),
|
||||||
) {
|
) {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
visible = visible && navBarType == NavBarType.VerticalLeft,
|
visible = visible && navBarType == NavBarType.VerticalLeft,
|
||||||
@ -139,7 +139,7 @@ fun ReaderAppBars(
|
|||||||
),
|
),
|
||||||
modifier = modifierWithInsetsPadding
|
modifier = modifierWithInsetsPadding
|
||||||
.padding(bottom = 48.dp, top = 120.dp)
|
.padding(bottom = 48.dp, top = 120.dp)
|
||||||
.align(Alignment.TopStart)
|
.align(Alignment.TopStart),
|
||||||
) {
|
) {
|
||||||
ChapterNavigator(
|
ChapterNavigator(
|
||||||
isRtl = isRtl,
|
isRtl = isRtl,
|
||||||
@ -167,7 +167,7 @@ fun ReaderAppBars(
|
|||||||
),
|
),
|
||||||
modifier = modifierWithInsetsPadding
|
modifier = modifierWithInsetsPadding
|
||||||
.padding(bottom = 48.dp, top = 120.dp)
|
.padding(bottom = 48.dp, top = 120.dp)
|
||||||
.align(Alignment.TopEnd)
|
.align(Alignment.TopEnd),
|
||||||
) {
|
) {
|
||||||
ChapterNavigator(
|
ChapterNavigator(
|
||||||
isRtl = isRtl,
|
isRtl = isRtl,
|
||||||
@ -246,13 +246,12 @@ fun ReaderAppBars(
|
|||||||
onClickRetryAll = onClickRetryAll,
|
onClickRetryAll = onClickRetryAll,
|
||||||
onClickRetryAllHelp = onClickRetryAllHelp,
|
onClickRetryAllHelp = onClickRetryAllHelp,
|
||||||
onClickBoostPage = onClickBoostPage,
|
onClickBoostPage = onClickBoostPage,
|
||||||
onClickBoostPageHelp = onClickBoostPageHelp
|
onClickBoostPageHelp = onClickBoostPageHelp,
|
||||||
)
|
)
|
||||||
// SY <--
|
// SY <--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
@ -304,7 +303,7 @@ fun ReaderAppBars(
|
|||||||
onClickWebView = onOpenInWebView,
|
onClickWebView = onOpenInWebView,
|
||||||
onClickShare = onShare,
|
onClickShare = onShare,
|
||||||
onClickPageLayout = onClickPageLayout,
|
onClickPageLayout = onClickPageLayout,
|
||||||
onClickShiftPage = onClickShiftPage
|
onClickShiftPage = onClickShiftPage,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,11 @@ internal fun LazyListScope.updatesUiItems(
|
|||||||
update = updatesItem.update,
|
update = updatesItem.update,
|
||||||
selected = updatesItem.selected,
|
selected = updatesItem.selected,
|
||||||
readProgress = updatesItem.update.lastPageRead
|
readProgress = updatesItem.update.lastPageRead
|
||||||
.takeIf { /* SY --> */(!updatesItem.update.read || (preserveReadingPosition && updatesItem.isEhBasedUpdate()))/* SY <-- */ && it > 0L }
|
.takeIf {
|
||||||
|
/* SY --> */(
|
||||||
|
!updatesItem.update.read || (preserveReadingPosition && updatesItem.isEhBasedUpdate())
|
||||||
|
)/* SY <-- */ && it > 0L
|
||||||
|
}
|
||||||
?.let {
|
?.let {
|
||||||
stringResource(
|
stringResource(
|
||||||
R.string.chapter_progress,
|
R.string.chapter_progress,
|
||||||
|
@ -173,7 +173,9 @@ fun WebViewScreenContent(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clip(MaterialTheme.shapes.small)
|
.clip(MaterialTheme.shapes.small)
|
||||||
.clickable {
|
.clickable {
|
||||||
uriHandler.openUri("https://tachiyomi.org/docs/guides/troubleshooting/#cloudflare")
|
uriHandler.openUri(
|
||||||
|
"https://tachiyomi.org/docs/guides/troubleshooting/#cloudflare",
|
||||||
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -304,7 +304,12 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
|
|||||||
|
|
||||||
fun register() {
|
fun register() {
|
||||||
if (!registered) {
|
if (!registered) {
|
||||||
ContextCompat.registerReceiver(this@App, this, IntentFilter(ACTION_DISABLE_INCOGNITO_MODE), ContextCompat.RECEIVER_NOT_EXPORTED)
|
ContextCompat.registerReceiver(
|
||||||
|
this@App,
|
||||||
|
this,
|
||||||
|
IntentFilter(ACTION_DISABLE_INCOGNITO_MODE),
|
||||||
|
ContextCompat.RECEIVER_NOT_EXPORTED,
|
||||||
|
)
|
||||||
registered = true
|
registered = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,13 +123,22 @@ object Migrations {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
prefs.edit {
|
prefs.edit {
|
||||||
putInt(libraryPreferences.filterDownloaded().key(), convertBooleanPrefToTriState("pref_filter_downloaded_key"))
|
putInt(
|
||||||
|
libraryPreferences.filterDownloaded().key(),
|
||||||
|
convertBooleanPrefToTriState("pref_filter_downloaded_key"),
|
||||||
|
)
|
||||||
remove("pref_filter_downloaded_key")
|
remove("pref_filter_downloaded_key")
|
||||||
|
|
||||||
putInt(libraryPreferences.filterUnread().key(), convertBooleanPrefToTriState("pref_filter_unread_key"))
|
putInt(
|
||||||
|
libraryPreferences.filterUnread().key(),
|
||||||
|
convertBooleanPrefToTriState("pref_filter_unread_key"),
|
||||||
|
)
|
||||||
remove("pref_filter_unread_key")
|
remove("pref_filter_unread_key")
|
||||||
|
|
||||||
putInt(libraryPreferences.filterCompleted().key(), convertBooleanPrefToTriState("pref_filter_completed_key"))
|
putInt(
|
||||||
|
libraryPreferences.filterCompleted().key(),
|
||||||
|
convertBooleanPrefToTriState("pref_filter_completed_key"),
|
||||||
|
)
|
||||||
remove("pref_filter_completed_key")
|
remove("pref_filter_completed_key")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -245,7 +254,10 @@ object Migrations {
|
|||||||
if (oldSecureScreen) {
|
if (oldSecureScreen) {
|
||||||
securityPreferences.secureScreen().set(SecurityPreferences.SecureScreenMode.ALWAYS)
|
securityPreferences.secureScreen().set(SecurityPreferences.SecureScreenMode.ALWAYS)
|
||||||
}
|
}
|
||||||
if (DeviceUtil.isMiui && basePreferences.extensionInstaller().get() == BasePreferences.ExtensionInstaller.PACKAGEINSTALLER) {
|
if (
|
||||||
|
DeviceUtil.isMiui &&
|
||||||
|
basePreferences.extensionInstaller().get() == BasePreferences.ExtensionInstaller.PACKAGEINSTALLER
|
||||||
|
) {
|
||||||
basePreferences.extensionInstaller().set(BasePreferences.ExtensionInstaller.LEGACY)
|
basePreferences.extensionInstaller().set(BasePreferences.ExtensionInstaller.LEGACY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -262,7 +274,12 @@ object Migrations {
|
|||||||
if (oldVersion < 81) {
|
if (oldVersion < 81) {
|
||||||
// Handle renamed enum values
|
// Handle renamed enum values
|
||||||
prefs.edit {
|
prefs.edit {
|
||||||
val newSortingMode = when (val oldSortingMode = prefs.getString(libraryPreferences.sortingMode().key(), "ALPHABETICAL")) {
|
val newSortingMode = when (
|
||||||
|
val oldSortingMode = prefs.getString(
|
||||||
|
libraryPreferences.sortingMode().key(),
|
||||||
|
"ALPHABETICAL",
|
||||||
|
)
|
||||||
|
) {
|
||||||
"LAST_CHECKED" -> "LAST_MANGA_UPDATE"
|
"LAST_CHECKED" -> "LAST_MANGA_UPDATE"
|
||||||
"UNREAD" -> "UNREAD_COUNT"
|
"UNREAD" -> "UNREAD_COUNT"
|
||||||
"DATE_FETCHED" -> "CHAPTER_FETCH_DATE"
|
"DATE_FETCHED" -> "CHAPTER_FETCH_DATE"
|
||||||
|
@ -104,7 +104,8 @@ class BackupCreator(
|
|||||||
throw IllegalStateException(context.getString(R.string.missing_storage_permission))
|
throw IllegalStateException(context.getString(R.string.missing_storage_permission))
|
||||||
}
|
}
|
||||||
|
|
||||||
val databaseManga = getFavorites.await() /* SY --> */ + if (flags and BACKUP_READ_MANGA_MASK == BACKUP_READ_MANGA) {
|
val databaseManga = getFavorites.await() /* SY --> */ +
|
||||||
|
if (flags and BACKUP_READ_MANGA_MASK == BACKUP_READ_MANGA) {
|
||||||
handler.awaitList { mangasQueries.getReadMangaNotInLibrary(MangaMapper::mapManga) }
|
handler.awaitList { mangasQueries.getReadMangaNotInLibrary(MangaMapper::mapManga) }
|
||||||
} else {
|
} else {
|
||||||
emptyList()
|
emptyList()
|
||||||
@ -226,12 +227,18 @@ class BackupCreator(
|
|||||||
val mangaObject = BackupManga.copyFrom(
|
val mangaObject = BackupManga.copyFrom(
|
||||||
manga,
|
manga,
|
||||||
// SY -->
|
// SY -->
|
||||||
if (options and BACKUP_CUSTOM_INFO_MASK == BACKUP_CUSTOM_INFO) getCustomMangaInfo.get(manga.id) else null, /* SY <-- */
|
if (options and BACKUP_CUSTOM_INFO_MASK == BACKUP_CUSTOM_INFO) {
|
||||||
|
getCustomMangaInfo.get(manga.id)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}, /* SY <-- */
|
||||||
)
|
)
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
if (manga.source == MERGED_SOURCE_ID) {
|
if (manga.source == MERGED_SOURCE_ID) {
|
||||||
mangaObject.mergedMangaReferences = handler.awaitList { mergedQueries.selectByMergeId(manga.id, backupMergedMangaReferenceMapper) }
|
mangaObject.mergedMangaReferences = handler.awaitList {
|
||||||
|
mergedQueries.selectByMergeId(manga.id, backupMergedMangaReferenceMapper)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val source = sourceManager.get(manga.source)?.getMainSource<MetadataSource<*, *>>()
|
val source = sourceManager.get(manga.source)?.getMainSource<MetadataSource<*, *>>()
|
||||||
|
@ -20,7 +20,9 @@ class BackupNotifier(private val context: Context) {
|
|||||||
|
|
||||||
private val preferences: SecurityPreferences by injectLazy()
|
private val preferences: SecurityPreferences by injectLazy()
|
||||||
|
|
||||||
private val progressNotificationBuilder = context.notificationBuilder(Notifications.CHANNEL_BACKUP_RESTORE_PROGRESS) {
|
private val progressNotificationBuilder = context.notificationBuilder(
|
||||||
|
Notifications.CHANNEL_BACKUP_RESTORE_PROGRESS,
|
||||||
|
) {
|
||||||
setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher))
|
setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher))
|
||||||
setSmallIcon(R.drawable.ic_tachi)
|
setSmallIcon(R.drawable.ic_tachi)
|
||||||
setAutoCancel(false)
|
setAutoCancel(false)
|
||||||
@ -28,7 +30,9 @@ class BackupNotifier(private val context: Context) {
|
|||||||
setOnlyAlertOnce(true)
|
setOnlyAlertOnce(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val completeNotificationBuilder = context.notificationBuilder(Notifications.CHANNEL_BACKUP_RESTORE_COMPLETE) {
|
private val completeNotificationBuilder = context.notificationBuilder(
|
||||||
|
Notifications.CHANNEL_BACKUP_RESTORE_COMPLETE,
|
||||||
|
) {
|
||||||
setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher))
|
setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher))
|
||||||
setSmallIcon(R.drawable.ic_tachi)
|
setSmallIcon(R.drawable.ic_tachi)
|
||||||
setAutoCancel(false)
|
setAutoCancel(false)
|
||||||
@ -72,14 +76,25 @@ class BackupNotifier(private val context: Context) {
|
|||||||
addAction(
|
addAction(
|
||||||
R.drawable.ic_share_24dp,
|
R.drawable.ic_share_24dp,
|
||||||
context.getString(R.string.action_share),
|
context.getString(R.string.action_share),
|
||||||
NotificationReceiver.shareBackupPendingBroadcast(context, unifile.uri, Notifications.ID_BACKUP_COMPLETE),
|
NotificationReceiver.shareBackupPendingBroadcast(
|
||||||
|
context,
|
||||||
|
unifile.uri,
|
||||||
|
Notifications.ID_BACKUP_COMPLETE,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
show(Notifications.ID_BACKUP_COMPLETE)
|
show(Notifications.ID_BACKUP_COMPLETE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showRestoreProgress(content: String = "", contentTitle: String = context.getString(R.string.restoring_backup), progress: Int = 0, maxAmount: Int = 100): NotificationCompat.Builder {
|
fun showRestoreProgress(
|
||||||
|
content: String = "",
|
||||||
|
contentTitle: String = context.getString(
|
||||||
|
R.string.restoring_backup,
|
||||||
|
),
|
||||||
|
progress: Int = 0,
|
||||||
|
maxAmount: Int = 100,
|
||||||
|
): NotificationCompat.Builder {
|
||||||
val builder = with(progressNotificationBuilder) {
|
val builder = with(progressNotificationBuilder) {
|
||||||
setContentTitle(contentTitle)
|
setContentTitle(contentTitle)
|
||||||
|
|
||||||
@ -114,7 +129,15 @@ class BackupNotifier(private val context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showRestoreComplete(time: Long, errorCount: Int, path: String?, file: String?, contentTitle: String = context.getString(R.string.restore_completed)) {
|
fun showRestoreComplete(
|
||||||
|
time: Long,
|
||||||
|
errorCount: Int,
|
||||||
|
path: String?,
|
||||||
|
file: String?,
|
||||||
|
contentTitle: String = context.getString(
|
||||||
|
R.string.restore_completed,
|
||||||
|
),
|
||||||
|
) {
|
||||||
context.cancelNotification(Notifications.ID_RESTORE_PROGRESS)
|
context.cancelNotification(Notifications.ID_RESTORE_PROGRESS)
|
||||||
|
|
||||||
val timeString = context.getString(
|
val timeString = context.getString(
|
||||||
@ -127,7 +150,14 @@ class BackupNotifier(private val context: Context) {
|
|||||||
|
|
||||||
with(completeNotificationBuilder) {
|
with(completeNotificationBuilder) {
|
||||||
setContentTitle(contentTitle)
|
setContentTitle(contentTitle)
|
||||||
setContentText(context.resources.getQuantityString(R.plurals.restore_completed_message, errorCount, timeString, errorCount))
|
setContentText(
|
||||||
|
context.resources.getQuantityString(
|
||||||
|
R.plurals.restore_completed_message,
|
||||||
|
errorCount,
|
||||||
|
timeString,
|
||||||
|
errorCount,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
clearActions()
|
clearActions()
|
||||||
if (errorCount > 0 && !path.isNullOrEmpty() && !file.isNullOrEmpty()) {
|
if (errorCount > 0 && !path.isNullOrEmpty() && !file.isNullOrEmpty()) {
|
||||||
|
@ -108,7 +108,13 @@ class BackupRestorer(
|
|||||||
val logFile = writeErrorLog()
|
val logFile = writeErrorLog()
|
||||||
|
|
||||||
if (sync) {
|
if (sync) {
|
||||||
notifier.showRestoreComplete(time, errors.size, logFile.parent, logFile.name, contentTitle = context.getString(R.string.library_sync_complete))
|
notifier.showRestoreComplete(
|
||||||
|
time,
|
||||||
|
errors.size,
|
||||||
|
logFile.parent,
|
||||||
|
logFile.name,
|
||||||
|
contentTitle = context.getString(R.string.library_sync_complete),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
notifier.showRestoreComplete(time, errors.size, logFile.parent, logFile.name)
|
notifier.showRestoreComplete(time, errors.size, logFile.parent, logFile.name)
|
||||||
}
|
}
|
||||||
@ -210,7 +216,12 @@ class BackupRestorer(
|
|||||||
)
|
)
|
||||||
|
|
||||||
restoreProgress += 1
|
restoreProgress += 1
|
||||||
showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.categories), context.getString(R.string.restoring_backup))
|
showRestoreProgress(
|
||||||
|
restoreProgress,
|
||||||
|
restoreAmount,
|
||||||
|
context.getString(R.string.categories),
|
||||||
|
context.getString(R.string.restoring_backup),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
@ -234,7 +245,12 @@ class BackupRestorer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
restoreProgress += 1
|
restoreProgress += 1
|
||||||
showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.saved_searches), context.getString(R.string.restoring_backup))
|
showRestoreProgress(
|
||||||
|
restoreProgress,
|
||||||
|
restoreAmount,
|
||||||
|
context.getString(R.string.saved_searches),
|
||||||
|
context.getString(R.string.restoring_backup),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
@ -259,13 +275,33 @@ class BackupRestorer(
|
|||||||
val dbManga = getMangaFromDatabase(manga.url, manga.source)
|
val dbManga = getMangaFromDatabase(manga.url, manga.source)
|
||||||
val restoredManga = if (dbManga == null) {
|
val restoredManga = if (dbManga == null) {
|
||||||
// Manga not in database
|
// Manga not in database
|
||||||
restoreExistingManga(manga, chapters, categories, history, tracks, backupCategories/* SY --> */, mergedMangaReferences, flatMetadata, customManga/* SY <-- */)
|
restoreExistingManga(
|
||||||
|
manga = manga,
|
||||||
|
chapters = chapters,
|
||||||
|
categories = categories,
|
||||||
|
history = history,
|
||||||
|
tracks = tracks,
|
||||||
|
backupCategories = backupCategories/* SY --> */,
|
||||||
|
mergedMangaReferences = mergedMangaReferences,
|
||||||
|
flatMetadata = flatMetadata,
|
||||||
|
customManga = customManga, /* SY <-- */
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// Manga in database
|
// Manga in database
|
||||||
// Copy information from manga already in database
|
// Copy information from manga already in database
|
||||||
val updatedManga = restoreExistingManga(manga, dbManga)
|
val updatedManga = restoreExistingManga(manga, dbManga)
|
||||||
// Fetch rest of manga information
|
// Fetch rest of manga information
|
||||||
restoreNewManga(updatedManga, chapters, categories, history, tracks, backupCategories/* SY --> */, mergedMangaReferences, flatMetadata, customManga/* SY <-- */)
|
restoreNewManga(
|
||||||
|
backupManga = updatedManga,
|
||||||
|
chapters = chapters,
|
||||||
|
categories = categories,
|
||||||
|
history = history,
|
||||||
|
tracks = tracks,
|
||||||
|
backupCategories = backupCategories/* SY --> */,
|
||||||
|
mergedMangaReferences = mergedMangaReferences,
|
||||||
|
flatMetadata = flatMetadata,
|
||||||
|
customManga = customManga, /* SY <-- */
|
||||||
|
)
|
||||||
}
|
}
|
||||||
updateManga.awaitUpdateFetchInterval(restoredManga, now, currentFetchWindow)
|
updateManga.awaitUpdateFetchInterval(restoredManga, now, currentFetchWindow)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@ -275,9 +311,19 @@ class BackupRestorer(
|
|||||||
|
|
||||||
restoreProgress += 1
|
restoreProgress += 1
|
||||||
if (sync) {
|
if (sync) {
|
||||||
showRestoreProgress(restoreProgress, restoreAmount, manga.title, context.getString(R.string.syncing_library))
|
showRestoreProgress(
|
||||||
|
restoreProgress,
|
||||||
|
restoreAmount,
|
||||||
|
manga.title,
|
||||||
|
context.getString(R.string.syncing_library),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
showRestoreProgress(restoreProgress, restoreAmount, manga.title, context.getString(R.string.restoring_backup))
|
showRestoreProgress(
|
||||||
|
restoreProgress,
|
||||||
|
restoreAmount,
|
||||||
|
manga.title,
|
||||||
|
context.getString(R.string.restoring_backup),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,7 +396,16 @@ class BackupRestorer(
|
|||||||
): Manga {
|
): Manga {
|
||||||
val fetchedManga = restoreNewManga(manga)
|
val fetchedManga = restoreNewManga(manga)
|
||||||
restoreChapters(fetchedManga, chapters)
|
restoreChapters(fetchedManga, chapters)
|
||||||
restoreExtras(fetchedManga, categories, history, tracks, backupCategories/* SY --> */, mergedMangaReferences, flatMetadata, customManga/* SY <-- */)
|
restoreExtras(
|
||||||
|
manga = fetchedManga,
|
||||||
|
categories = categories,
|
||||||
|
history = history,
|
||||||
|
tracks = tracks,
|
||||||
|
backupCategories = backupCategories/* SY --> */,
|
||||||
|
mergedMangaReferences = mergedMangaReferences,
|
||||||
|
flatMetadata = flatMetadata,
|
||||||
|
customManga = customManga, /* SY <-- */
|
||||||
|
)
|
||||||
return fetchedManga
|
return fetchedManga
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -498,7 +553,16 @@ class BackupRestorer(
|
|||||||
// SY <--
|
// SY <--
|
||||||
): Manga {
|
): Manga {
|
||||||
restoreChapters(backupManga, chapters)
|
restoreChapters(backupManga, chapters)
|
||||||
restoreExtras(backupManga, categories, history, tracks, backupCategories, mergedMangaReferences, flatMetadata, customManga)
|
restoreExtras(
|
||||||
|
manga = backupManga,
|
||||||
|
categories = categories,
|
||||||
|
history = history,
|
||||||
|
tracks = tracks,
|
||||||
|
backupCategories = backupCategories,
|
||||||
|
mergedMangaReferences = mergedMangaReferences,
|
||||||
|
flatMetadata = flatMetadata,
|
||||||
|
customManga = customManga,
|
||||||
|
)
|
||||||
return backupManga
|
return backupManga
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -696,17 +760,33 @@ class BackupRestorer(
|
|||||||
* @param manga the merge manga for the references
|
* @param manga the merge manga for the references
|
||||||
* @param backupMergedMangaReferences the list of backup manga references for the merged manga
|
* @param backupMergedMangaReferences the list of backup manga references for the merged manga
|
||||||
*/
|
*/
|
||||||
internal suspend fun restoreMergedMangaReferencesForManga(mergeMangaId: Long, backupMergedMangaReferences: List<BackupMergedMangaReference>) {
|
internal suspend fun restoreMergedMangaReferencesForManga(
|
||||||
|
mergeMangaId: Long,
|
||||||
|
backupMergedMangaReferences: List<BackupMergedMangaReference>,
|
||||||
|
) {
|
||||||
// Get merged manga references from file and from db
|
// Get merged manga references from file and from db
|
||||||
val dbMergedMangaReferences = handler.awaitList { mergedQueries.selectAll(MergedMangaMapper::map) }
|
val dbMergedMangaReferences = handler.awaitList {
|
||||||
|
mergedQueries.selectAll(MergedMangaMapper::map)
|
||||||
|
}
|
||||||
|
|
||||||
// Iterate over them
|
// Iterate over them
|
||||||
backupMergedMangaReferences.forEach { backupMergedMangaReference ->
|
backupMergedMangaReferences.forEach { backupMergedMangaReference ->
|
||||||
// If the backupMergedMangaReference isn't in the db, remove the id and insert a new backupMergedMangaReference
|
// If the backupMergedMangaReference isn't in the db,
|
||||||
|
// remove the id and insert a new backupMergedMangaReference
|
||||||
// Store the inserted id in the backupMergedMangaReference
|
// Store the inserted id in the backupMergedMangaReference
|
||||||
if (dbMergedMangaReferences.none { backupMergedMangaReference.mergeUrl == it.mergeUrl && backupMergedMangaReference.mangaUrl == it.mangaUrl }) {
|
if (dbMergedMangaReferences.none {
|
||||||
|
backupMergedMangaReference.mergeUrl == it.mergeUrl &&
|
||||||
|
backupMergedMangaReference.mangaUrl == it.mangaUrl
|
||||||
|
}
|
||||||
|
) {
|
||||||
// Let the db assign the id
|
// Let the db assign the id
|
||||||
val mergedManga = handler.awaitOneOrNull { mangasQueries.getMangaByUrlAndSource(backupMergedMangaReference.mangaUrl, backupMergedMangaReference.mangaSourceId, MangaMapper::mapManga) } ?: return@forEach
|
val mergedManga = handler.awaitOneOrNull {
|
||||||
|
mangasQueries.getMangaByUrlAndSource(
|
||||||
|
backupMergedMangaReference.mangaUrl,
|
||||||
|
backupMergedMangaReference.mangaSourceId,
|
||||||
|
MangaMapper::mapManga,
|
||||||
|
)
|
||||||
|
} ?: return@forEach
|
||||||
backupMergedMangaReference.getMergedMangaReference().run {
|
backupMergedMangaReference.getMergedMangaReference().run {
|
||||||
handler.await {
|
handler.await {
|
||||||
mergedQueries.insert(
|
mergedQueries.insert(
|
||||||
@ -746,7 +826,12 @@ class BackupRestorer(
|
|||||||
BackupCreateJob.setupTask(context)
|
BackupCreateJob.setupTask(context)
|
||||||
|
|
||||||
restoreProgress += 1
|
restoreProgress += 1
|
||||||
showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.app_settings), context.getString(R.string.restoring_backup))
|
showRestoreProgress(
|
||||||
|
restoreProgress,
|
||||||
|
restoreAmount,
|
||||||
|
context.getString(R.string.app_settings),
|
||||||
|
context.getString(R.string.restoring_backup),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun restoreSourcePreferences(preferences: List<BackupSourcePreferences>) {
|
private fun restoreSourcePreferences(preferences: List<BackupSourcePreferences>) {
|
||||||
@ -756,7 +841,12 @@ class BackupRestorer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
restoreProgress += 1
|
restoreProgress += 1
|
||||||
showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.source_settings), context.getString(R.string.restoring_backup))
|
showRestoreProgress(
|
||||||
|
restoreProgress,
|
||||||
|
restoreAmount,
|
||||||
|
context.getString(R.string.source_settings),
|
||||||
|
context.getString(R.string.restoring_backup),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun restorePreferences(
|
private fun restorePreferences(
|
||||||
|
@ -29,7 +29,9 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti
|
|||||||
if (resources.sourceOrNull()?.peek()?.use { CbzCrypto.detectCoverImageArchive(it.inputStream()) } == true) {
|
if (resources.sourceOrNull()?.peek()?.use { CbzCrypto.detectCoverImageArchive(it.inputStream()) } == true) {
|
||||||
if (resources.source().peek().use { ImageUtil.findImageType(it.inputStream()) == null }) {
|
if (resources.source().peek().use { ImageUtil.findImageType(it.inputStream()) == null }) {
|
||||||
zip4j = ZipFile(resources.file().toFile().absolutePath)
|
zip4j = ZipFile(resources.file().toFile().absolutePath)
|
||||||
entry = zip4j.fileHeaders.firstOrNull { it.fileName.equals(CbzCrypto.DEFAULT_COVER_NAME, ignoreCase = true) }
|
entry = zip4j.fileHeaders.firstOrNull {
|
||||||
|
it.fileName.equals(CbzCrypto.DEFAULT_COVER_NAME, ignoreCase = true)
|
||||||
|
}
|
||||||
|
|
||||||
if (zip4j.isEncrypted) zip4j.setPassword(CbzCrypto.getDecryptedPasswordCbz())
|
if (zip4j.isEncrypted) zip4j.setPassword(CbzCrypto.getDecryptedPasswordCbz())
|
||||||
}
|
}
|
||||||
|
@ -148,7 +148,10 @@ class DownloadCache(
|
|||||||
if (sourceDir != null) {
|
if (sourceDir != null) {
|
||||||
val mangaDir = sourceDir.mangaDirs[provider.getMangaDirName(mangaTitle)]
|
val mangaDir = sourceDir.mangaDirs[provider.getMangaDirName(mangaTitle)]
|
||||||
if (mangaDir != null) {
|
if (mangaDir != null) {
|
||||||
return provider.getValidChapterDirNames(chapterName, chapterScanlator).any { it in mangaDir.chapterDirs }
|
return provider.getValidChapterDirNames(
|
||||||
|
chapterName,
|
||||||
|
chapterScanlator,
|
||||||
|
).any { it in mangaDir.chapterDirs }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -156,7 +156,12 @@ class DownloadManager(
|
|||||||
* @return the list of pages from the chapter.
|
* @return the list of pages from the chapter.
|
||||||
*/
|
*/
|
||||||
fun buildPageList(source: Source, manga: Manga, chapter: Chapter): List<Page> {
|
fun buildPageList(source: Source, manga: Manga, chapter: Chapter): List<Page> {
|
||||||
val chapterDir = provider.findChapterDir(chapter.name, chapter.scanlator, /* SY --> */ manga.ogTitle /* SY <-- */, source)
|
val chapterDir = provider.findChapterDir(
|
||||||
|
chapter.name,
|
||||||
|
chapter.scanlator,
|
||||||
|
/* SY --> */ manga.ogTitle /* SY <-- */,
|
||||||
|
source,
|
||||||
|
)
|
||||||
val files = chapterDir?.listFiles().orEmpty()
|
val files = chapterDir?.listFiles().orEmpty()
|
||||||
.filter { "image" in it.type.orEmpty() }
|
.filter { "image" in it.type.orEmpty() }
|
||||||
|
|
||||||
@ -292,7 +297,13 @@ class DownloadManager(
|
|||||||
* @param manga the manga of the chapters.
|
* @param manga the manga of the chapters.
|
||||||
* @param source the source of the chapters.
|
* @param source the source of the chapters.
|
||||||
*/
|
*/
|
||||||
suspend fun cleanupChapters(allChapters: List<Chapter>, manga: Manga, source: Source, removeRead: Boolean, removeNonFavorite: Boolean): Int {
|
suspend fun cleanupChapters(
|
||||||
|
allChapters: List<Chapter>,
|
||||||
|
manga: Manga,
|
||||||
|
source: Source,
|
||||||
|
removeRead: Boolean,
|
||||||
|
removeNonFavorite: Boolean,
|
||||||
|
): Int {
|
||||||
var cleaned = 0
|
var cleaned = 0
|
||||||
|
|
||||||
if (removeNonFavorite && !manga.favorite) {
|
if (removeNonFavorite && !manga.favorite) {
|
||||||
|
@ -95,7 +95,10 @@ internal class DownloadNotifier(private val context: Context) {
|
|||||||
} else {
|
} else {
|
||||||
val title = download.manga.title.chop(15)
|
val title = download.manga.title.chop(15)
|
||||||
val quotedTitle = Pattern.quote(title)
|
val quotedTitle = Pattern.quote(title)
|
||||||
val chapter = download.chapter.name.replaceFirst("$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE), "")
|
val chapter = download.chapter.name.replaceFirst(
|
||||||
|
"$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE),
|
||||||
|
"",
|
||||||
|
)
|
||||||
setContentTitle("$title - $chapter".chop(30))
|
setContentTitle("$title - $chapter".chop(30))
|
||||||
setContentText(downloadingProgressText)
|
setContentText(downloadingProgressText)
|
||||||
}
|
}
|
||||||
|
@ -286,7 +286,9 @@ class Downloader(
|
|||||||
val wasEmpty = queueState.value.isEmpty()
|
val wasEmpty = queueState.value.isEmpty()
|
||||||
val chaptersWithoutDir = chapters
|
val chaptersWithoutDir = chapters
|
||||||
// Filter out those already downloaded.
|
// Filter out those already downloaded.
|
||||||
.filter { provider.findChapterDir(it.name, it.scanlator, /* SY --> */ manga.ogTitle /* SY <-- */, source) == null }
|
.filter {
|
||||||
|
provider.findChapterDir(it.name, it.scanlator, /* SY --> */ manga.ogTitle /* SY <-- */, source) == null
|
||||||
|
}
|
||||||
// Add chapters to queue from the start.
|
// Add chapters to queue from the start.
|
||||||
.sortedByDescending { it.sourceOrder }
|
.sortedByDescending { it.sourceOrder }
|
||||||
|
|
||||||
@ -335,7 +337,11 @@ class Downloader(
|
|||||||
val availSpace = DiskUtil.getAvailableStorageSpace(mangaDir)
|
val availSpace = DiskUtil.getAvailableStorageSpace(mangaDir)
|
||||||
if (availSpace != -1L && availSpace < MIN_DISK_SPACE) {
|
if (availSpace != -1L && availSpace < MIN_DISK_SPACE) {
|
||||||
download.status = Download.State.ERROR
|
download.status = Download.State.ERROR
|
||||||
notifier.onError(context.getString(R.string.download_insufficient_space), download.chapter.name, download.manga.title)
|
notifier.onError(
|
||||||
|
context.getString(R.string.download_insufficient_space),
|
||||||
|
download.chapter.name,
|
||||||
|
download.manga.title,
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -449,13 +455,16 @@ class Downloader(
|
|||||||
tmpFile?.delete()
|
tmpFile?.delete()
|
||||||
|
|
||||||
// Try to find the image file
|
// Try to find the image file
|
||||||
val imageFile = tmpDir.listFiles()?.firstOrNull { it.name!!.startsWith("$filename.") || it.name!!.startsWith("${filename}__001") }
|
val imageFile = tmpDir.listFiles()?.firstOrNull {
|
||||||
|
it.name!!.startsWith("$filename.") || it.name!!.startsWith("${filename}__001")
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// If the image is already downloaded, do nothing. Otherwise download from network
|
// If the image is already downloaded, do nothing. Otherwise download from network
|
||||||
val file = when {
|
val file = when {
|
||||||
imageFile != null -> imageFile
|
imageFile != null -> imageFile
|
||||||
chapterCache.isImageInCache(page.imageUrl!!) -> copyImageFromCache(chapterCache.getImageFile(page.imageUrl!!), tmpDir, filename)
|
chapterCache.isImageInCache(page.imageUrl!!) ->
|
||||||
|
copyImageFromCache(chapterCache.getImageFile(page.imageUrl!!), tmpDir, filename)
|
||||||
else -> downloadImage(page, download.source, tmpDir, filename, dataSaver)
|
else -> downloadImage(page, download.source, tmpDir, filename, dataSaver)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -482,7 +491,13 @@ class Downloader(
|
|||||||
* @param tmpDir the temporary directory of the download.
|
* @param tmpDir the temporary directory of the download.
|
||||||
* @param filename the filename of the image.
|
* @param filename the filename of the image.
|
||||||
*/
|
*/
|
||||||
private suspend fun downloadImage(page: Page, source: HttpSource, tmpDir: UniFile, filename: String, dataSaver: DataSaver): UniFile {
|
private suspend fun downloadImage(
|
||||||
|
page: Page,
|
||||||
|
source: HttpSource,
|
||||||
|
tmpDir: UniFile,
|
||||||
|
filename: String,
|
||||||
|
dataSaver: DataSaver,
|
||||||
|
): UniFile {
|
||||||
page.status = Page.State.DOWNLOAD_IMAGE
|
page.status = Page.State.DOWNLOAD_IMAGE
|
||||||
page.progress = 0
|
page.progress = 0
|
||||||
return flow {
|
return flow {
|
||||||
|
@ -402,7 +402,9 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
|||||||
val errorMessage = when (e) {
|
val errorMessage = when (e) {
|
||||||
is NoChaptersException -> context.getString(R.string.no_chapters_error)
|
is NoChaptersException -> context.getString(R.string.no_chapters_error)
|
||||||
// failedUpdates will already have the source, don't need to copy it into the message
|
// failedUpdates will already have the source, don't need to copy it into the message
|
||||||
is SourceNotInstalledException -> context.getString(R.string.loader_not_implemented_error)
|
is SourceNotInstalledException -> context.getString(
|
||||||
|
R.string.loader_not_implemented_error,
|
||||||
|
)
|
||||||
else -> e.message
|
else -> e.message
|
||||||
}
|
}
|
||||||
failedUpdates.add(manga to errorMessage)
|
failedUpdates.add(manga to errorMessage)
|
||||||
@ -722,7 +724,9 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
|||||||
if (interval > 0) {
|
if (interval > 0) {
|
||||||
val restrictions = preferences.autoUpdateDeviceRestrictions().get()
|
val restrictions = preferences.autoUpdateDeviceRestrictions().get()
|
||||||
val constraints = Constraints(
|
val constraints = Constraints(
|
||||||
requiredNetworkType = if (DEVICE_NETWORK_NOT_METERED in restrictions) { NetworkType.UNMETERED } else { NetworkType.CONNECTED },
|
requiredNetworkType = if (DEVICE_NETWORK_NOT_METERED in restrictions) {
|
||||||
|
NetworkType.UNMETERED
|
||||||
|
} else { NetworkType.CONNECTED },
|
||||||
requiresCharging = DEVICE_CHARGING in restrictions,
|
requiresCharging = DEVICE_CHARGING in restrictions,
|
||||||
requiresBatteryNotLow = true,
|
requiresBatteryNotLow = true,
|
||||||
)
|
)
|
||||||
@ -739,7 +743,11 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
|||||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.MINUTES)
|
.setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.MINUTES)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
context.workManager.enqueueUniquePeriodicWork(WORK_NAME_AUTO, ExistingPeriodicWorkPolicy.UPDATE, request)
|
context.workManager.enqueueUniquePeriodicWork(
|
||||||
|
WORK_NAME_AUTO,
|
||||||
|
ExistingPeriodicWorkPolicy.UPDATE,
|
||||||
|
request,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
context.workManager.cancelUniqueWork(WORK_NAME_AUTO)
|
context.workManager.cancelUniqueWork(WORK_NAME_AUTO)
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,12 @@ class LibraryUpdateNotifier(private val context: Context) {
|
|||||||
} else {
|
} else {
|
||||||
val updatingText = manga.joinToString("\n") { it.title.chop(40) }
|
val updatingText = manga.joinToString("\n") { it.title.chop(40) }
|
||||||
progressNotificationBuilder
|
progressNotificationBuilder
|
||||||
.setContentTitle(context.getString(R.string.notification_updating_progress, percentFormatter.format(current.toFloat() / total)))
|
.setContentTitle(
|
||||||
|
context.getString(
|
||||||
|
R.string.notification_updating_progress,
|
||||||
|
percentFormatter.format(current.toFloat() / total),
|
||||||
|
),
|
||||||
|
)
|
||||||
.setStyle(NotificationCompat.BigTextStyle().bigText(updatingText))
|
.setStyle(NotificationCompat.BigTextStyle().bigText(updatingText))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +171,13 @@ class LibraryUpdateNotifier(private val context: Context) {
|
|||||||
if (updates.size == 1 && !preferences.hideNotificationContent().get()) {
|
if (updates.size == 1 && !preferences.hideNotificationContent().get()) {
|
||||||
setContentText(updates.first().first.title.chop(NOTIF_TITLE_MAX_LEN))
|
setContentText(updates.first().first.title.chop(NOTIF_TITLE_MAX_LEN))
|
||||||
} else {
|
} else {
|
||||||
setContentText(context.resources.getQuantityString(R.plurals.notification_new_chapters_summary, updates.size, updates.size))
|
setContentText(
|
||||||
|
context.resources.getQuantityString(
|
||||||
|
R.plurals.notification_new_chapters_summary,
|
||||||
|
updates.size,
|
||||||
|
updates.size,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
if (!preferences.hideNotificationContent().get()) {
|
if (!preferences.hideNotificationContent().get()) {
|
||||||
setStyle(
|
setStyle(
|
||||||
@ -196,7 +207,10 @@ class LibraryUpdateNotifier(private val context: Context) {
|
|||||||
launchUI {
|
launchUI {
|
||||||
context.notify(
|
context.notify(
|
||||||
updates.map { (manga, chapters) ->
|
updates.map { (manga, chapters) ->
|
||||||
NotificationManagerCompat.NotificationWithIdAndTag(manga.id.hashCode(), createNewChaptersNotification(manga, chapters))
|
NotificationManagerCompat.NotificationWithIdAndTag(
|
||||||
|
manga.id.hashCode(),
|
||||||
|
createNewChaptersNotification(manga, chapters),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -292,17 +306,28 @@ class LibraryUpdateNotifier(private val context: Context) {
|
|||||||
// No sensible chapter numbers to show (i.e. no chapters have parsed chapter number)
|
// No sensible chapter numbers to show (i.e. no chapters have parsed chapter number)
|
||||||
0 -> {
|
0 -> {
|
||||||
// "1 new chapter" or "5 new chapters"
|
// "1 new chapter" or "5 new chapters"
|
||||||
context.resources.getQuantityString(R.plurals.notification_chapters_generic, chapters.size, chapters.size)
|
context.resources.getQuantityString(
|
||||||
|
R.plurals.notification_chapters_generic,
|
||||||
|
chapters.size,
|
||||||
|
chapters.size,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
// Only 1 chapter has a parsed chapter number
|
// Only 1 chapter has a parsed chapter number
|
||||||
1 -> {
|
1 -> {
|
||||||
val remaining = chapters.size - displayableChapterNumbers.size
|
val remaining = chapters.size - displayableChapterNumbers.size
|
||||||
if (remaining == 0) {
|
if (remaining == 0) {
|
||||||
// "Chapter 2.5"
|
// "Chapter 2.5"
|
||||||
context.resources.getString(R.string.notification_chapters_single, displayableChapterNumbers.first())
|
context.resources.getString(
|
||||||
|
R.string.notification_chapters_single,
|
||||||
|
displayableChapterNumbers.first(),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// "Chapter 2.5 and 10 more"
|
// "Chapter 2.5 and 10 more"
|
||||||
context.resources.getString(R.string.notification_chapters_single_and_more, displayableChapterNumbers.first(), remaining)
|
context.resources.getString(
|
||||||
|
R.string.notification_chapters_single_and_more,
|
||||||
|
displayableChapterNumbers.first(),
|
||||||
|
remaining,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Everything else (i.e. multiple parsed chapter numbers)
|
// Everything else (i.e. multiple parsed chapter numbers)
|
||||||
@ -312,10 +337,18 @@ class LibraryUpdateNotifier(private val context: Context) {
|
|||||||
// "Chapters 1, 2.5, 3, 4, 5 and 10 more"
|
// "Chapters 1, 2.5, 3, 4, 5 and 10 more"
|
||||||
val remaining = displayableChapterNumbers.size - NOTIF_MAX_CHAPTERS
|
val remaining = displayableChapterNumbers.size - NOTIF_MAX_CHAPTERS
|
||||||
val joinedChapterNumbers = displayableChapterNumbers.take(NOTIF_MAX_CHAPTERS).joinToString(", ")
|
val joinedChapterNumbers = displayableChapterNumbers.take(NOTIF_MAX_CHAPTERS).joinToString(", ")
|
||||||
context.resources.getQuantityString(R.plurals.notification_chapters_multiple_and_more, remaining, joinedChapterNumbers, remaining)
|
context.resources.getQuantityString(
|
||||||
|
R.plurals.notification_chapters_multiple_and_more,
|
||||||
|
remaining,
|
||||||
|
joinedChapterNumbers,
|
||||||
|
remaining,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// "Chapters 1, 2.5, 3"
|
// "Chapters 1, 2.5, 3"
|
||||||
context.resources.getString(R.string.notification_chapters_multiple, displayableChapterNumbers.joinToString(", "))
|
context.resources.getString(
|
||||||
|
R.string.notification_chapters_multiple,
|
||||||
|
displayableChapterNumbers.joinToString(", "),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -329,7 +362,12 @@ class LibraryUpdateNotifier(private val context: Context) {
|
|||||||
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||||
action = Constants.SHORTCUT_UPDATES
|
action = Constants.SHORTCUT_UPDATES
|
||||||
}
|
}
|
||||||
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -23,7 +23,12 @@ object NotificationHandler {
|
|||||||
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||||
action = Constants.SHORTCUT_DOWNLOADS
|
action = Constants.SHORTCUT_DOWNLOADS
|
||||||
}
|
}
|
||||||
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,7 +42,12 @@ object NotificationHandler {
|
|||||||
setDataAndType(uri, "image/*")
|
setDataAndType(uri, "image/*")
|
||||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
}
|
}
|
||||||
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -306,7 +306,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
||||||
action = ACTION_RESUME_DOWNLOADS
|
action = ACTION_RESUME_DOWNLOADS
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -319,7 +324,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
||||||
action = ACTION_PAUSE_DOWNLOADS
|
action = ACTION_PAUSE_DOWNLOADS
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -332,7 +342,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
||||||
action = ACTION_CLEAR_DOWNLOADS
|
action = ACTION_CLEAR_DOWNLOADS
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -347,7 +362,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
action = ACTION_DISMISS_NOTIFICATION
|
action = ACTION_DISMISS_NOTIFICATION
|
||||||
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -402,7 +422,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
putExtra(EXTRA_FILE_LOCATION, path)
|
putExtra(EXTRA_FILE_LOCATION, path)
|
||||||
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -419,7 +444,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
putExtra(EXTRA_FILE_LOCATION, path)
|
putExtra(EXTRA_FILE_LOCATION, path)
|
||||||
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -431,7 +461,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
*/
|
*/
|
||||||
internal fun openChapterPendingActivity(context: Context, manga: Manga, chapter: Chapter): PendingIntent {
|
internal fun openChapterPendingActivity(context: Context, manga: Manga, chapter: Chapter): PendingIntent {
|
||||||
val newIntent = ReaderActivity.newIntent(context, manga.id, chapter.id)
|
val newIntent = ReaderActivity.newIntent(context, manga.id, chapter.id)
|
||||||
return PendingIntent.getActivity(context, manga.id.hashCode(), newIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
manga.id.hashCode(),
|
||||||
|
newIntent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -447,7 +482,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
.putExtra(Constants.MANGA_EXTRA, manga.id)
|
.putExtra(Constants.MANGA_EXTRA, manga.id)
|
||||||
.putExtra("notificationId", manga.id.hashCode())
|
.putExtra("notificationId", manga.id.hashCode())
|
||||||
.putExtra("groupId", groupId)
|
.putExtra("groupId", groupId)
|
||||||
return PendingIntent.getActivity(context, manga.id.hashCode(), newIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
manga.id.hashCode(),
|
||||||
|
newIntent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -469,7 +509,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
putExtra(EXTRA_NOTIFICATION_ID, manga.id.hashCode())
|
putExtra(EXTRA_NOTIFICATION_ID, manga.id.hashCode())
|
||||||
putExtra(EXTRA_GROUP_ID, groupId)
|
putExtra(EXTRA_GROUP_ID, groupId)
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, manga.id.hashCode(), newIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
manga.id.hashCode(),
|
||||||
|
newIntent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -491,7 +536,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
putExtra(EXTRA_NOTIFICATION_ID, manga.id.hashCode())
|
putExtra(EXTRA_NOTIFICATION_ID, manga.id.hashCode())
|
||||||
putExtra(EXTRA_GROUP_ID, groupId)
|
putExtra(EXTRA_GROUP_ID, groupId)
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, manga.id.hashCode(), newIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
manga.id.hashCode(),
|
||||||
|
newIntent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -504,7 +554,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
||||||
action = ACTION_CANCEL_LIBRARY_UPDATE
|
action = ACTION_CANCEL_LIBRARY_UPDATE
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -513,12 +568,21 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
* @param context context of application
|
* @param context context of application
|
||||||
* @return [PendingIntent]
|
* @return [PendingIntent]
|
||||||
*/
|
*/
|
||||||
internal fun downloadAppUpdatePendingBroadcast(context: Context, url: String, title: String? = null): PendingIntent {
|
internal fun downloadAppUpdatePendingBroadcast(
|
||||||
|
context: Context,
|
||||||
|
url: String,
|
||||||
|
title: String? = null,
|
||||||
|
): PendingIntent {
|
||||||
return Intent(context, NotificationReceiver::class.java).run {
|
return Intent(context, NotificationReceiver::class.java).run {
|
||||||
action = ACTION_START_APP_UPDATE
|
action = ACTION_START_APP_UPDATE
|
||||||
putExtra(AppUpdateDownloadJob.EXTRA_DOWNLOAD_URL, url)
|
putExtra(AppUpdateDownloadJob.EXTRA_DOWNLOAD_URL, url)
|
||||||
title?.let { putExtra(AppUpdateDownloadJob.EXTRA_DOWNLOAD_TITLE, it) }
|
title?.let { putExtra(AppUpdateDownloadJob.EXTRA_DOWNLOAD_TITLE, it) }
|
||||||
PendingIntent.getBroadcast(context, 0, this, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
this,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -529,7 +593,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
||||||
action = ACTION_CANCEL_APP_UPDATE_DOWNLOAD
|
action = ACTION_CANCEL_APP_UPDATE_DOWNLOAD
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -543,7 +612,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
action = Constants.SHORTCUT_EXTENSIONS
|
action = Constants.SHORTCUT_EXTENSIONS
|
||||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||||
}
|
}
|
||||||
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -560,7 +634,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
putExtra(EXTRA_URI, uri)
|
putExtra(EXTRA_URI, uri)
|
||||||
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -591,7 +670,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
action = ACTION_CANCEL_RESTORE
|
action = ACTION_CANCEL_RESTORE
|
||||||
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,8 @@ class TrackerManager {
|
|||||||
val kavita = Kavita(KAVITA)
|
val kavita = Kavita(KAVITA)
|
||||||
val suwayomi = Suwayomi(9L)
|
val suwayomi = Suwayomi(9L)
|
||||||
|
|
||||||
val trackers = listOf(mdList, myAnimeList, aniList, kitsu, shikimori, bangumi, komga, mangaUpdates, kavita, suwayomi)
|
val trackers =
|
||||||
|
listOf(mdList, myAnimeList, aniList, kitsu, shikimori, bangumi, komga, mangaUpdates, kavita, suwayomi)
|
||||||
|
|
||||||
fun loggedInTrackers() = trackers.filter { it.isLoggedIn }
|
fun loggedInTrackers() = trackers.filter { it.isLoggedIn }
|
||||||
|
|
||||||
|
@ -48,11 +48,15 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
|
|||||||
when (it.code) {
|
when (it.code) {
|
||||||
200 -> return it.parseAs<AuthenticationDto>().token
|
200 -> return it.parseAs<AuthenticationDto>().token
|
||||||
401 -> {
|
401 -> {
|
||||||
logcat(LogPriority.WARN) { "Unauthorized / api key not valid: Cleaned api URL: $apiUrl, Api key is empty: ${apiKey.isEmpty()}" }
|
logcat(LogPriority.WARN) {
|
||||||
|
"Unauthorized / API key not valid: API URL: $apiUrl, empty API key: ${apiKey.isEmpty()}"
|
||||||
|
}
|
||||||
throw IOException("Unauthorized / api key not valid")
|
throw IOException("Unauthorized / api key not valid")
|
||||||
}
|
}
|
||||||
500 -> {
|
500 -> {
|
||||||
logcat(LogPriority.WARN) { "Error fetching JWT token. Cleaned api URL: $apiUrl, Api key is empty: ${apiKey.isEmpty()}" }
|
logcat(
|
||||||
|
LogPriority.WARN,
|
||||||
|
) { "Error fetching JWT token. API URL: $apiUrl, empty API key: ${apiKey.isEmpty()}" }
|
||||||
throw IOException("Error fetching JWT token")
|
throw IOException("Error fetching JWT token")
|
||||||
}
|
}
|
||||||
else -> {}
|
else -> {}
|
||||||
@ -62,12 +66,12 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
|
|||||||
// Not sure which one to catch
|
// Not sure which one to catch
|
||||||
} catch (e: SocketTimeoutException) {
|
} catch (e: SocketTimeoutException) {
|
||||||
logcat(LogPriority.WARN) {
|
logcat(LogPriority.WARN) {
|
||||||
"Could not fetch JWT token. Probably due to connectivity issue or the url '$apiUrl' is not available, skipping"
|
"Could not fetch JWT token. Probably due to connectivity issue or URL '$apiUrl' not available, skipping"
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logcat(LogPriority.ERROR) {
|
logcat(LogPriority.ERROR) {
|
||||||
"Unhandled exception fetching JWT token for url: '$apiUrl'"
|
"Unhandled exception fetching JWT token for URL: '$apiUrl'"
|
||||||
}
|
}
|
||||||
throw IOException(e)
|
throw IOException(e)
|
||||||
}
|
}
|
||||||
@ -129,7 +133,10 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logcat(LogPriority.WARN, e) { "Exception getting latest chapter read. Could not get itemRequest: $requestUrl" }
|
logcat(
|
||||||
|
LogPriority.WARN,
|
||||||
|
e,
|
||||||
|
) { "Exception getting latest chapter read. Could not get itemRequest: $requestUrl" }
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
return 0F
|
return 0F
|
||||||
@ -164,8 +171,14 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateProgress(track: Track): Track {
|
suspend fun updateProgress(track: Track): Track {
|
||||||
val requestUrl = "${getApiFromUrl(track.tracking_url)}/Tachiyomi/mark-chapter-until-as-read?seriesId=${getIdFromUrl(track.tracking_url)}&chapterNumber=${track.last_chapter_read}"
|
val requestUrl = "${getApiFromUrl(
|
||||||
authClient.newCall(POST(requestUrl, body = "{}".toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())))
|
track.tracking_url,
|
||||||
|
)}/Tachiyomi/mark-chapter-until-as-read?seriesId=${getIdFromUrl(
|
||||||
|
track.tracking_url,
|
||||||
|
)}&chapterNumber=${track.last_chapter_read}"
|
||||||
|
authClient.newCall(
|
||||||
|
POST(requestUrl, body = "{}".toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())),
|
||||||
|
)
|
||||||
.awaitSuccess()
|
.awaitSuccess()
|
||||||
return getTrackSearch(track.tracking_url)
|
return getTrackSearch(track.tracking_url)
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,10 @@ class MyAnimeListApi(
|
|||||||
return withIOContext {
|
return withIOContext {
|
||||||
val url = "$baseApiUrl/manga".toUri().buildUpon()
|
val url = "$baseApiUrl/manga".toUri().buildUpon()
|
||||||
.appendPath(id.toString())
|
.appendPath(id.toString())
|
||||||
.appendQueryParameter("fields", "id,title,synopsis,num_chapters,main_picture,status,media_type,start_date")
|
.appendQueryParameter(
|
||||||
|
"fields",
|
||||||
|
"id,title,synopsis,num_chapters,main_picture,status,media_type,start_date",
|
||||||
|
)
|
||||||
.build()
|
.build()
|
||||||
with(json) {
|
with(json) {
|
||||||
authClient.newCall(GET(url.toString()))
|
authClient.newCall(GET(url.toString()))
|
||||||
|
@ -91,7 +91,9 @@ class Suwayomi(id: Long) : BaseTracker(id, "Suwayomi"), EnhancedTracker {
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isTrackFrom(track: DomainTrack, manga: DomainManga, source: Source?): Boolean = source?.let { accept(it) } == true
|
override fun isTrackFrom(track: DomainTrack, manga: DomainManga, source: Source?): Boolean = source?.let {
|
||||||
|
accept(it)
|
||||||
|
} == true
|
||||||
|
|
||||||
override fun migrateTrack(track: DomainTrack, manga: DomainManga, newSource: Source): DomainTrack? =
|
override fun migrateTrack(track: DomainTrack, manga: DomainManga, newSource: Source): DomainTrack? =
|
||||||
if (accept(newSource)) {
|
if (accept(newSource)) {
|
||||||
|
@ -38,7 +38,9 @@ class AppUpdateChecker {
|
|||||||
|
|
||||||
when (result) {
|
when (result) {
|
||||||
is GetApplicationRelease.Result.NewUpdate -> AppUpdateNotifier(context).promptUpdate(result.release)
|
is GetApplicationRelease.Result.NewUpdate -> AppUpdateNotifier(context).promptUpdate(result.release)
|
||||||
is GetApplicationRelease.Result.ThirdPartyInstallation -> AppUpdateNotifier(context).promptFdroidUpdate()
|
is GetApplicationRelease.Result.ThirdPartyInstallation -> AppUpdateNotifier(
|
||||||
|
context,
|
||||||
|
).promptFdroidUpdate()
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,12 @@ internal class AppUpdateNotifier(private val context: Context) {
|
|||||||
|
|
||||||
val releaseIntent = Intent(Intent.ACTION_VIEW, release.releaseLink.toUri()).run {
|
val releaseIntent = Intent(Intent.ACTION_VIEW, release.releaseLink.toUri()).run {
|
||||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||||
PendingIntent.getActivity(context, release.hashCode(), this, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
release.hashCode(),
|
||||||
|
this,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
with(notificationBuilder) {
|
with(notificationBuilder) {
|
||||||
@ -143,7 +148,12 @@ internal class AppUpdateNotifier(private val context: Context) {
|
|||||||
setContentTitle(context.getString(R.string.update_check_notification_update_available))
|
setContentTitle(context.getString(R.string.update_check_notification_update_available))
|
||||||
setContentText(context.getString(R.string.update_check_fdroid_migration_info))
|
setContentText(context.getString(R.string.update_check_fdroid_migration_info))
|
||||||
setSmallIcon(R.drawable.ic_tachi)
|
setSmallIcon(R.drawable.ic_tachi)
|
||||||
setContentIntent(NotificationHandler.openUrl(context, "https://tachiyomi.org/docs/faq/general#how-do-i-update-from-the-f-droid-builds"))
|
setContentIntent(
|
||||||
|
NotificationHandler.openUrl(
|
||||||
|
context,
|
||||||
|
"https://tachiyomi.org/docs/faq/general#how-do-i-update-from-the-f-droid-builds",
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
notificationBuilder.show(Notifications.ID_APP_UPDATE_PROMPT)
|
notificationBuilder.show(Notifications.ID_APP_UPDATE_PROMPT)
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,12 @@ class PackageInstallerInstaller(private val service: Service) : Installer(servic
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
ContextCompat.registerReceiver(service, packageActionReceiver, IntentFilter(INSTALL_ACTION), ContextCompat.RECEIVER_EXPORTED)
|
ContextCompat.registerReceiver(
|
||||||
|
service,
|
||||||
|
packageActionReceiver,
|
||||||
|
IntentFilter(INSTALL_ACTION),
|
||||||
|
ContextCompat.RECEIVER_EXPORTED,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +108,9 @@ internal class ExtensionInstallReceiver(private val listener: Listener) :
|
|||||||
logcat(LogPriority.WARN) { "Package name not found" }
|
logcat(LogPriority.WARN) { "Package name not found" }
|
||||||
return LoadResult.Error
|
return LoadResult.Error
|
||||||
}
|
}
|
||||||
return GlobalScope.async(Dispatchers.Default, CoroutineStart.DEFAULT) { ExtensionLoader.loadExtensionFromPkgName(context, pkgName) }.await()
|
return GlobalScope.async(Dispatchers.Default, CoroutineStart.DEFAULT) {
|
||||||
|
ExtensionLoader.loadExtensionFromPkgName(context, pkgName)
|
||||||
|
}.await()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,7 +59,9 @@ class AndroidSourceManager(
|
|||||||
|
|
||||||
private val stubSourcesMap = ConcurrentHashMap<Long, StubSource>()
|
private val stubSourcesMap = ConcurrentHashMap<Long, StubSource>()
|
||||||
|
|
||||||
override val catalogueSources: Flow<List<CatalogueSource>> = sourcesMapFlow.map { it.values.filterIsInstance<CatalogueSource>() }
|
override val catalogueSources: Flow<List<CatalogueSource>> = sourcesMapFlow.map {
|
||||||
|
it.values.filterIsInstance<CatalogueSource>()
|
||||||
|
}
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
private val preferences: UnsortedPreferences by injectLazy()
|
private val preferences: UnsortedPreferences by injectLazy()
|
||||||
@ -118,7 +120,9 @@ class AndroidSourceManager(
|
|||||||
private fun Source.toInternalSource(): Source? {
|
private fun Source.toInternalSource(): Source? {
|
||||||
// EXH -->
|
// EXH -->
|
||||||
val sourceQName = this::class.qualifiedName
|
val sourceQName = this::class.qualifiedName
|
||||||
val factories = DELEGATED_SOURCES.entries.filter { it.value.factory }.map { it.value.originalSourceQualifiedClassName }
|
val factories = DELEGATED_SOURCES.entries
|
||||||
|
.filter { it.value.factory }
|
||||||
|
.map { it.value.originalSourceQualifiedClassName }
|
||||||
val delegate = if (sourceQName != null) {
|
val delegate = if (sourceQName != null) {
|
||||||
val matched = factories.find { sourceQName.startsWith(it) }
|
val matched = factories.find { sourceQName.startsWith(it) }
|
||||||
if (matched != null) {
|
if (matched != null) {
|
||||||
@ -149,7 +153,12 @@ class AndroidSourceManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return if (id in BlacklistedSources.BLACKLISTED_EXT_SOURCES) {
|
return if (id in BlacklistedSources.BLACKLISTED_EXT_SOURCES) {
|
||||||
xLogD("Removing blacklisted source: (id: %s, name: %s, lang: %s)!", id, name, (this as? CatalogueSource)?.lang)
|
xLogD(
|
||||||
|
"Removing blacklisted source: (id: %s, name: %s, lang: %s)!",
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
(this as? CatalogueSource)?.lang,
|
||||||
|
)
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
newSource
|
newSource
|
||||||
@ -177,15 +186,21 @@ class AndroidSourceManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
override fun getVisibleOnlineSources() = sourcesMapFlow.value.values.filterIsInstance<HttpSource>().filter {
|
override fun getVisibleOnlineSources() = sourcesMapFlow.value.values
|
||||||
|
.filterIsInstance<HttpSource>()
|
||||||
|
.filter {
|
||||||
it.id !in BlacklistedSources.HIDDEN_SOURCES
|
it.id !in BlacklistedSources.HIDDEN_SOURCES
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getVisibleCatalogueSources() = sourcesMapFlow.value.values.filterIsInstance<CatalogueSource>().filter {
|
override fun getVisibleCatalogueSources() = sourcesMapFlow.value.values
|
||||||
|
.filterIsInstance<CatalogueSource>()
|
||||||
|
.filter {
|
||||||
it.id !in BlacklistedSources.HIDDEN_SOURCES
|
it.id !in BlacklistedSources.HIDDEN_SOURCES
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDelegatedCatalogueSources() = sourcesMapFlow.value.values.filterIsInstance<EnhancedHttpSource>().mapNotNull { enhancedHttpSource ->
|
fun getDelegatedCatalogueSources() = sourcesMapFlow.value.values
|
||||||
|
.filterIsInstance<EnhancedHttpSource>()
|
||||||
|
.mapNotNull { enhancedHttpSource ->
|
||||||
enhancedHttpSource.enhancedSource as? DelegatedHttpSource
|
enhancedHttpSource.enhancedSource as? DelegatedHttpSource
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
@ -256,7 +271,8 @@ class AndroidSourceManager(
|
|||||||
),
|
),
|
||||||
).associateBy { it.originalSourceQualifiedClassName }
|
).associateBy { it.originalSourceQualifiedClassName }
|
||||||
|
|
||||||
val currentDelegatedSources: MutableMap<Long, DelegatedSource> = ListenMutableMap(mutableMapOf(), ::handleSourceLibrary)
|
val currentDelegatedSources: MutableMap<Long, DelegatedSource> =
|
||||||
|
ListenMutableMap(mutableMapOf(), ::handleSourceLibrary)
|
||||||
|
|
||||||
data class DelegatedSource(
|
data class DelegatedSource(
|
||||||
val sourceName: String,
|
val sourceName: String,
|
||||||
|
@ -281,7 +281,9 @@ class EHentai(
|
|||||||
private fun getRating(element: Element?): Double? {
|
private fun getRating(element: Element?): Double? {
|
||||||
val ratingStyle = element?.attr("style")?.nullIfBlank()
|
val ratingStyle = element?.attr("style")?.nullIfBlank()
|
||||||
return if (ratingStyle != null) {
|
return if (ratingStyle != null) {
|
||||||
val matches = RATING_REGEX.findAll(ratingStyle).mapNotNull { it.groupValues.getOrNull(1)?.toIntOrNull() }.toList()
|
val matches = RATING_REGEX.findAll(ratingStyle)
|
||||||
|
.mapNotNull { it.groupValues.getOrNull(1)?.toIntOrNull() }
|
||||||
|
.toList()
|
||||||
if (matches.size == 2) {
|
if (matches.size == 2) {
|
||||||
var rate = 5 - matches[0] / 16
|
var rate = 5 - matches[0] / 16
|
||||||
if (matches[1] == 21) {
|
if (matches[1] == 21) {
|
||||||
@ -314,7 +316,9 @@ class EHentai(
|
|||||||
/**
|
/**
|
||||||
* Parse a list of galleries
|
* Parse a list of galleries
|
||||||
*/
|
*/
|
||||||
private fun genericMangaParse(response: Response) = extendedGenericMangaParse(response.asJsoup()).let { (parsedManga, nextPage) ->
|
private fun genericMangaParse(
|
||||||
|
response: Response,
|
||||||
|
) = extendedGenericMangaParse(response.asJsoup()).let { (parsedManga, nextPage) ->
|
||||||
MetadataMangasPage(
|
MetadataMangasPage(
|
||||||
parsedManga.map { it.manga },
|
parsedManga.map { it.manga },
|
||||||
nextPage != null,
|
nextPage != null,
|
||||||
@ -409,7 +413,9 @@ class EHentai(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("Use the 1.x API instead", replaceWith = ReplaceWith("getPageList"))
|
@Deprecated("Use the 1.x API instead", replaceWith = ReplaceWith("getPageList"))
|
||||||
override fun fetchPageList(chapter: SChapter): Observable<List<Page>> = fetchChapterPage(chapter, baseUrl + chapter.url)
|
override fun fetchPageList(
|
||||||
|
chapter: SChapter,
|
||||||
|
): Observable<List<Page>> = fetchChapterPage(chapter, baseUrl + chapter.url)
|
||||||
.map {
|
.map {
|
||||||
it.mapIndexed { i, s ->
|
it.mapIndexed { i, s ->
|
||||||
Page(i, s)
|
Page(i, s)
|
||||||
@ -461,7 +467,9 @@ class EHentai(
|
|||||||
|
|
||||||
private fun <T : MangasPage> T.checkValid(): MangasPage =
|
private fun <T : MangasPage> T.checkValid(): MangasPage =
|
||||||
if (exh && mangas.isEmpty() && preferences.igneousVal().get().equals("mystery", true)) {
|
if (exh && mangas.isEmpty() && preferences.igneousVal().get().equals("mystery", true)) {
|
||||||
throw Exception("Invalid igneous cookie, try re-logging or finding a correct one to input in the login menu")
|
throw Exception(
|
||||||
|
"Invalid igneous cookie, try re-logging or finding a correct one to input in the login menu",
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
@ -521,7 +529,11 @@ class EHentai(
|
|||||||
if (jumpSeekValue != null && page == 1) {
|
if (jumpSeekValue != null && page == 1) {
|
||||||
if (
|
if (
|
||||||
MATCH_SEEK_REGEX.matches(jumpSeekValue) ||
|
MATCH_SEEK_REGEX.matches(jumpSeekValue) ||
|
||||||
(MATCH_YEAR_REGEX.matches(jumpSeekValue) && jumpSeekValue.toIntOrNull()?.let { it in 2007..2099 } == true)
|
(
|
||||||
|
MATCH_YEAR_REGEX.matches(jumpSeekValue) && jumpSeekValue.toIntOrNull()?.let {
|
||||||
|
it in 2007..2099
|
||||||
|
} == true
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
uri.appendQueryParameter("seek", jumpSeekValue)
|
uri.appendQueryParameter("seek", jumpSeekValue)
|
||||||
} else if (MATCH_JUMP_REGEX.matches(jumpSeekValue)) {
|
} else if (MATCH_JUMP_REGEX.matches(jumpSeekValue)) {
|
||||||
@ -590,7 +602,9 @@ class EHentai(
|
|||||||
// Pull to most recent
|
// Pull to most recent
|
||||||
val doc = response.asJsoup()
|
val doc = response.asJsoup()
|
||||||
val newerGallery = doc.select("#gnd a").lastOrNull()
|
val newerGallery = doc.select("#gnd a").lastOrNull()
|
||||||
val pre = if (newerGallery != null && DebugToggles.PULL_TO_ROOT_WHEN_LOADING_EXH_MANGA_DETAILS.enabled) {
|
val pre = if (
|
||||||
|
newerGallery != null && DebugToggles.PULL_TO_ROOT_WHEN_LOADING_EXH_MANGA_DETAILS.enabled
|
||||||
|
) {
|
||||||
manga.url = EHentaiSearchMetadata.normalizeUrl(newerGallery.attr("href"))
|
manga.url = EHentaiSearchMetadata.normalizeUrl(newerGallery.attr("href"))
|
||||||
client.newCall(mangaDetailsRequest(manga))
|
client.newCall(mangaDetailsRequest(manga))
|
||||||
.asObservableSuccess().map { it.asJsoup() }
|
.asObservableSuccess().map { it.asJsoup() }
|
||||||
@ -627,7 +641,9 @@ class EHentai(
|
|||||||
// Pull to most recent
|
// Pull to most recent
|
||||||
val doc = response.asJsoup()
|
val doc = response.asJsoup()
|
||||||
val newerGallery = doc.select("#gnd a").lastOrNull()
|
val newerGallery = doc.select("#gnd a").lastOrNull()
|
||||||
val pre = if (newerGallery != null && DebugToggles.PULL_TO_ROOT_WHEN_LOADING_EXH_MANGA_DETAILS.enabled) {
|
val pre = if (
|
||||||
|
newerGallery != null && DebugToggles.PULL_TO_ROOT_WHEN_LOADING_EXH_MANGA_DETAILS.enabled
|
||||||
|
) {
|
||||||
val sManga = manga.copy(
|
val sManga = manga.copy(
|
||||||
url = EHentaiSearchMetadata.normalizeUrl(newerGallery.attr("href")),
|
url = EHentaiSearchMetadata.normalizeUrl(newerGallery.attr("href")),
|
||||||
)
|
)
|
||||||
@ -756,16 +772,24 @@ class EHentai(
|
|||||||
tags += RaisedTag(EH_UPLOADER_NAMESPACE, it, TAG_TYPE_VIRTUAL)
|
tags += RaisedTag(EH_UPLOADER_NAMESPACE, it, TAG_TYPE_VIRTUAL)
|
||||||
}
|
}
|
||||||
visible?.let {
|
visible?.let {
|
||||||
tags += RaisedTag(EH_VISIBILITY_NAMESPACE, it.substringAfter('(').substringBeforeLast(')'), TAG_TYPE_VIRTUAL)
|
tags += RaisedTag(
|
||||||
|
EH_VISIBILITY_NAMESPACE,
|
||||||
|
it.substringAfter('(').substringBeforeLast(')'),
|
||||||
|
TAG_TYPE_VIRTUAL,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun chapterListParse(response: Response) = throw UnsupportedOperationException("Unused method was called somehow!")
|
override fun chapterListParse(response: Response) =
|
||||||
override fun chapterPageParse(response: Response) = throw UnsupportedOperationException("Unused method was called somehow!")
|
throw UnsupportedOperationException("Unused method was called somehow!")
|
||||||
|
override fun chapterPageParse(
|
||||||
|
response: Response,
|
||||||
|
) = throw UnsupportedOperationException("Unused method was called somehow!")
|
||||||
|
|
||||||
override fun pageListParse(response: Response) = throw UnsupportedOperationException("Unused method was called somehow!")
|
override fun pageListParse(response: Response) =
|
||||||
|
throw UnsupportedOperationException("Unused method was called somehow!")
|
||||||
|
|
||||||
override fun fetchImageUrl(page: Page): Observable<String> {
|
override fun fetchImageUrl(page: Page): Observable<String> {
|
||||||
return client.newCall(imageUrlRequest(page))
|
return client.newCall(imageUrlRequest(page))
|
||||||
@ -959,7 +983,11 @@ class EHentai(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AdvancedOption(name: String, val param: String, defValue: Boolean = false) : Filter.CheckBox(name, defValue), UriFilter {
|
class AdvancedOption(
|
||||||
|
name: String,
|
||||||
|
val param: String,
|
||||||
|
defValue: Boolean = false,
|
||||||
|
) : Filter.CheckBox(name, defValue), UriFilter {
|
||||||
override fun addToUri(builder: Uri.Builder) {
|
override fun addToUri(builder: Uri.Builder) {
|
||||||
if (state) {
|
if (state) {
|
||||||
builder.appendQueryParameter(param, "on")
|
builder.appendQueryParameter(param, "on")
|
||||||
@ -1140,7 +1168,9 @@ class EHentai(
|
|||||||
)
|
)
|
||||||
|
|
||||||
val obj = outJson["tokenlist"]!!.jsonArray.first().jsonObject
|
val obj = outJson["tokenlist"]!!.jsonArray.first().jsonObject
|
||||||
return "${uri.scheme}://${uri.host}/g/${obj["gid"]!!.jsonPrimitive.int}/${obj["token"]!!.jsonPrimitive.content}/"
|
return "${uri.scheme}://${uri.host}/g/${obj["gid"]!!.jsonPrimitive.int}/${
|
||||||
|
obj["token"]!!.jsonPrimitive.content
|
||||||
|
}/"
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getPagePreviewList(
|
override suspend fun getPagePreviewList(
|
||||||
@ -1190,7 +1220,8 @@ class EHentai(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun fetchPreviewImage(page: PagePreviewInfo, cacheControl: CacheControl?): Response {
|
override suspend fun fetchPreviewImage(page: PagePreviewInfo, cacheControl: CacheControl?): Response {
|
||||||
return client.newCachelessCallWithProgress(exGet(page.imageUrl, cacheControl = cacheControl), page).awaitSuccess()
|
return client.newCachelessCallWithProgress(exGet(page.imageUrl, cacheControl = cacheControl), page)
|
||||||
|
.awaitSuccess()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,7 +49,11 @@ class MergedSource : HttpSource() {
|
|||||||
|
|
||||||
override fun popularMangaRequest(page: Int) = throw UnsupportedOperationException()
|
override fun popularMangaRequest(page: Int) = throw UnsupportedOperationException()
|
||||||
override fun popularMangaParse(response: Response) = throw UnsupportedOperationException()
|
override fun popularMangaParse(response: Response) = throw UnsupportedOperationException()
|
||||||
override fun searchMangaRequest(page: Int, query: String, filters: FilterList) = throw UnsupportedOperationException()
|
override fun searchMangaRequest(
|
||||||
|
page: Int,
|
||||||
|
query: String,
|
||||||
|
filters: FilterList,
|
||||||
|
) = throw UnsupportedOperationException()
|
||||||
override fun searchMangaParse(response: Response) = throw UnsupportedOperationException()
|
override fun searchMangaParse(response: Response) = throw UnsupportedOperationException()
|
||||||
override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException()
|
override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException()
|
||||||
override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException()
|
override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException()
|
||||||
@ -63,6 +67,7 @@ class MergedSource : HttpSource() {
|
|||||||
override fun fetchChapterList(manga: SManga) = throw UnsupportedOperationException()
|
override fun fetchChapterList(manga: SManga) = throw UnsupportedOperationException()
|
||||||
override suspend fun getChapterList(manga: SManga) = throw UnsupportedOperationException()
|
override suspend fun getChapterList(manga: SManga) = throw UnsupportedOperationException()
|
||||||
override suspend fun getImage(page: Page): Response = throw UnsupportedOperationException()
|
override suspend fun getImage(page: Page): Response = throw UnsupportedOperationException()
|
||||||
|
|
||||||
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getImageUrl"))
|
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getImageUrl"))
|
||||||
override fun fetchImageUrl(page: Page) = throw UnsupportedOperationException()
|
override fun fetchImageUrl(page: Page) = throw UnsupportedOperationException()
|
||||||
override suspend fun getImageUrl(page: Page) = throw UnsupportedOperationException()
|
override suspend fun getImageUrl(page: Page) = throw UnsupportedOperationException()
|
||||||
@ -70,9 +75,11 @@ class MergedSource : HttpSource() {
|
|||||||
@Deprecated("Use the 1.x API instead", replaceWith = ReplaceWith("getPageList"))
|
@Deprecated("Use the 1.x API instead", replaceWith = ReplaceWith("getPageList"))
|
||||||
override fun fetchPageList(chapter: SChapter) = throw UnsupportedOperationException()
|
override fun fetchPageList(chapter: SChapter) = throw UnsupportedOperationException()
|
||||||
override suspend fun getPageList(chapter: SChapter) = throw UnsupportedOperationException()
|
override suspend fun getPageList(chapter: SChapter) = throw UnsupportedOperationException()
|
||||||
|
|
||||||
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getLatestUpdates"))
|
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getLatestUpdates"))
|
||||||
override fun fetchLatestUpdates(page: Int) = throw UnsupportedOperationException()
|
override fun fetchLatestUpdates(page: Int) = throw UnsupportedOperationException()
|
||||||
override suspend fun getLatestUpdates(page: Int) = throw UnsupportedOperationException()
|
override suspend fun getLatestUpdates(page: Int) = throw UnsupportedOperationException()
|
||||||
|
|
||||||
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getPopularManga"))
|
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getPopularManga"))
|
||||||
override fun fetchPopularManga(page: Int) = throw UnsupportedOperationException()
|
override fun fetchPopularManga(page: Int) = throw UnsupportedOperationException()
|
||||||
override suspend fun getPopularManga(page: Int) = throw UnsupportedOperationException()
|
override suspend fun getPopularManga(page: Int) = throw UnsupportedOperationException()
|
||||||
@ -99,7 +106,12 @@ class MergedSource : HttpSource() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun fetchChaptersForMergedManga(manga: Manga, downloadChapters: Boolean = true, editScanlators: Boolean = false, dedupe: Boolean = true) {
|
suspend fun fetchChaptersForMergedManga(
|
||||||
|
manga: Manga,
|
||||||
|
downloadChapters: Boolean = true,
|
||||||
|
editScanlators: Boolean = false,
|
||||||
|
dedupe: Boolean = true,
|
||||||
|
) {
|
||||||
fetchChaptersAndSync(manga, downloadChapters)
|
fetchChaptersAndSync(manga, downloadChapters)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +121,12 @@ class MergedSource : HttpSource() {
|
|||||||
"Manga references are empty, chapters unavailable, merge is likely corrupted"
|
"Manga references are empty, chapters unavailable, merge is likely corrupted"
|
||||||
}
|
}
|
||||||
|
|
||||||
val ifDownloadNewChapters = downloadChapters && manga.shouldDownloadNewChapters(getCategories.await(manga.id).map { it.id }, downloadPreferences)
|
val ifDownloadNewChapters = downloadChapters && manga.shouldDownloadNewChapters(
|
||||||
|
getCategories.await(manga.id).map {
|
||||||
|
it.id
|
||||||
|
},
|
||||||
|
downloadPreferences,
|
||||||
|
)
|
||||||
val semaphore = Semaphore(5)
|
val semaphore = Semaphore(5)
|
||||||
var exception: Exception? = null
|
var exception: Exception? = null
|
||||||
return supervisorScope {
|
return supervisorScope {
|
||||||
|
@ -58,7 +58,7 @@ class NHentai(delegate: HttpSource, val context: Context) :
|
|||||||
|
|
||||||
override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage {
|
override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage {
|
||||||
return urlImportFetchSearchMangaSuspend(context, query) {
|
return urlImportFetchSearchMangaSuspend(context, query) {
|
||||||
super<DelegatedHttpSource>.getSearchManga(page, query, filters,)
|
super<DelegatedHttpSource>.getSearchManga(page, query, filters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -195,7 +195,8 @@ class NHentai(delegate: HttpSource, val context: Context) :
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun thumbnailUrlFromType(mediaId: String, page: Int, t: String) = NHentaiSearchMetadata.typeToExtension(t)?.let {
|
private fun thumbnailUrlFromType(mediaId: String, page: Int, t: String) =
|
||||||
|
NHentaiSearchMetadata.typeToExtension(t)?.let {
|
||||||
"https://t3.nhentai.net/galleries/$mediaId/${page}t.$it"
|
"https://t3.nhentai.net/galleries/$mediaId/${page}t.$it"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ class EightMuses(delegate: HttpSource, val context: Context) :
|
|||||||
|
|
||||||
override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage {
|
override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage {
|
||||||
return urlImportFetchSearchMangaSuspend(context, query) {
|
return urlImportFetchSearchMangaSuspend(context, query) {
|
||||||
super<DelegatedHttpSource>.getSearchManga(page, query, filters,)
|
super<DelegatedHttpSource>.getSearchManga(page, query, filters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ class HBrowse(delegate: HttpSource, val context: Context) :
|
|||||||
|
|
||||||
override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage {
|
override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage {
|
||||||
return urlImportFetchSearchMangaSuspend(context, query) {
|
return urlImportFetchSearchMangaSuspend(context, query) {
|
||||||
super<DelegatedHttpSource>.getSearchManga(page, query, filters,)
|
super<DelegatedHttpSource>.getSearchManga(page, query, filters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ class Pururin(delegate: HttpSource, val context: Context) :
|
|||||||
query
|
query
|
||||||
}
|
}
|
||||||
return urlImportFetchSearchMangaSuspend(context, newQuery) {
|
return urlImportFetchSearchMangaSuspend(context, newQuery) {
|
||||||
super<DelegatedHttpSource>.getSearchManga(page, query, filters,)
|
super<DelegatedHttpSource>.getSearchManga(page, query, filters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +111,11 @@ class Pururin(delegate: HttpSource, val context: Context) :
|
|||||||
tags += RaisedTag(
|
tags += RaisedTag(
|
||||||
namespace,
|
namespace,
|
||||||
searchUrl.lastPathSegment!!.substringBefore("."),
|
searchUrl.lastPathSegment!!.substringBefore("."),
|
||||||
if (namespace != PururinSearchMetadata.TAG_NAMESPACE_CATEGORY) PururinSearchMetadata.TAG_TYPE_DEFAULT else RaisedSearchMetadata.TAG_TYPE_VIRTUAL,
|
if (namespace != PururinSearchMetadata.TAG_NAMESPACE_CATEGORY) {
|
||||||
|
PururinSearchMetadata.TAG_TYPE_DEFAULT
|
||||||
|
} else {
|
||||||
|
RaisedSearchMetadata.TAG_TYPE_VIRTUAL
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ class Tsumino(delegate: HttpSource, val context: Context) :
|
|||||||
|
|
||||||
override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage {
|
override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage {
|
||||||
return urlImportFetchSearchMangaSuspend(context, query) {
|
return urlImportFetchSearchMangaSuspend(context, query) {
|
||||||
super<DelegatedHttpSource>.getSearchManga(page, query, filters,)
|
super<DelegatedHttpSource>.getSearchManga(page, query, filters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,8 @@ class MigrateSearchScreen(private val mangaId: Long, private val validSources: L
|
|||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
override fun Content() {
|
||||||
val navigator = LocalNavigator.currentOrThrow
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
val screenModel = rememberScreenModel { MigrateSearchScreenModel(mangaId = mangaId, validSources = validSources) }
|
val screenModel =
|
||||||
|
rememberScreenModel { MigrateSearchScreenModel(mangaId = mangaId, validSources = validSources) }
|
||||||
val state by screenModel.state.collectAsState()
|
val state by screenModel.state.collectAsState()
|
||||||
|
|
||||||
val dialogScreenModel = rememberScreenModel { MigrateSearchScreenDialogScreenModel(mangaId = mangaId) }
|
val dialogScreenModel = rememberScreenModel { MigrateSearchScreenDialogScreenModel(mangaId = mangaId) }
|
||||||
|
@ -19,7 +19,6 @@ import eu.kanade.domain.manga.interactor.UpdateManga
|
|||||||
import eu.kanade.domain.manga.model.toDomainManga
|
import eu.kanade.domain.manga.model.toDomainManga
|
||||||
import eu.kanade.domain.source.interactor.GetExhSavedSearch
|
import eu.kanade.domain.source.interactor.GetExhSavedSearch
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
import eu.kanade.domain.track.model.toDomainTrack
|
|
||||||
import eu.kanade.domain.track.interactor.AddTracks
|
import eu.kanade.domain.track.interactor.AddTracks
|
||||||
import eu.kanade.domain.ui.UiPreferences
|
import eu.kanade.domain.ui.UiPreferences
|
||||||
import eu.kanade.presentation.util.ioCoroutineScope
|
import eu.kanade.presentation.util.ioCoroutineScope
|
||||||
@ -52,13 +51,11 @@ import kotlinx.serialization.decodeFromString
|
|||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonArray
|
import kotlinx.serialization.json.JsonArray
|
||||||
import logcat.LogPriority
|
|
||||||
import tachiyomi.core.preference.CheckboxState
|
import tachiyomi.core.preference.CheckboxState
|
||||||
import tachiyomi.core.preference.mapAsCheckboxState
|
import tachiyomi.core.preference.mapAsCheckboxState
|
||||||
import tachiyomi.core.util.lang.launchIO
|
import tachiyomi.core.util.lang.launchIO
|
||||||
import tachiyomi.core.util.lang.launchNonCancellable
|
import tachiyomi.core.util.lang.launchNonCancellable
|
||||||
import tachiyomi.core.util.lang.withUIContext
|
import tachiyomi.core.util.lang.withUIContext
|
||||||
import tachiyomi.core.util.system.logcat
|
|
||||||
import tachiyomi.domain.UnsortedPreferences
|
import tachiyomi.domain.UnsortedPreferences
|
||||||
import tachiyomi.domain.category.interactor.GetCategories
|
import tachiyomi.domain.category.interactor.GetCategories
|
||||||
import tachiyomi.domain.category.interactor.SetMangaCategories
|
import tachiyomi.domain.category.interactor.SetMangaCategories
|
||||||
@ -370,7 +367,9 @@ open class BrowseSourceScreenModel(
|
|||||||
// Choose a category
|
// Choose a category
|
||||||
else -> {
|
else -> {
|
||||||
val preselectedIds = getCategories.await(manga.id).map { it.id }
|
val preselectedIds = getCategories.await(manga.id).map { it.id }
|
||||||
setDialog(Dialog.ChangeMangaCategory(manga, categories.mapAsCheckboxState { it.id in preselectedIds }))
|
setDialog(
|
||||||
|
Dialog.ChangeMangaCategory(manga, categories.mapAsCheckboxState { it.id in preselectedIds }),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -426,7 +425,10 @@ open class BrowseSourceScreenModel(
|
|||||||
sealed class Listing(open val query: String?, open val filters: FilterList) {
|
sealed class Listing(open val query: String?, open val filters: FilterList) {
|
||||||
data object Popular : Listing(query = GetRemoteManga.QUERY_POPULAR, filters = FilterList())
|
data object Popular : Listing(query = GetRemoteManga.QUERY_POPULAR, filters = FilterList())
|
||||||
data object Latest : Listing(query = GetRemoteManga.QUERY_LATEST, filters = FilterList())
|
data object Latest : Listing(query = GetRemoteManga.QUERY_LATEST, filters = FilterList())
|
||||||
data class Search(override val query: String?, override val filters: FilterList) : Listing(query = query, filters = filters)
|
data class Search(
|
||||||
|
override val query: String?,
|
||||||
|
override val filters: FilterList,
|
||||||
|
) : Listing(query = query, filters = filters)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun valueOf(query: String?): Listing {
|
fun valueOf(query: String?): Listing {
|
||||||
@ -530,7 +532,9 @@ open class BrowseSourceScreenModel(
|
|||||||
source = source.id,
|
source = source.id,
|
||||||
name = name.trim(),
|
name = name.trim(),
|
||||||
query = query,
|
query = query,
|
||||||
filtersJson = runCatching { filterSerializer.serialize(filterList).ifEmpty { null }?.let { Json.encodeToString(it) } }.getOrNull(),
|
filtersJson = runCatching {
|
||||||
|
filterSerializer.serialize(filterList).ifEmpty { null }?.let { Json.encodeToString(it) }
|
||||||
|
}.getOrNull(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -29,10 +29,8 @@ import kotlinx.coroutines.flow.onEach
|
|||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.decodeFromString
|
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import tachiyomi.core.util.lang.awaitSingle
|
|
||||||
import tachiyomi.core.util.lang.launchIO
|
import tachiyomi.core.util.lang.launchIO
|
||||||
import tachiyomi.core.util.lang.launchNonCancellable
|
import tachiyomi.core.util.lang.launchNonCancellable
|
||||||
import tachiyomi.core.util.lang.withIOContext
|
import tachiyomi.core.util.lang.withIOContext
|
||||||
@ -175,7 +173,9 @@ open class SourceFeedScreenModel(
|
|||||||
|
|
||||||
mutableState.update { state ->
|
mutableState.update { state ->
|
||||||
state.copy(
|
state.copy(
|
||||||
items = state.items.map { item -> if (item.id == sourceFeed.id) sourceFeed.withResults(titles) else item },
|
items = state.items.map { item ->
|
||||||
|
if (item.id == sourceFeed.id) sourceFeed.withResults(titles) else item
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,10 @@ object HomeScreen : Screen() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RowScope.NavigationBarItem(tab: eu.kanade.presentation.util.Tab/* SY --> */, alwaysShowLabel: Boolean/* SY <-- */) {
|
private fun RowScope.NavigationBarItem(
|
||||||
|
tab: eu.kanade.presentation.util.Tab/* SY --> */,
|
||||||
|
alwaysShowLabel: Boolean, /* SY <-- */
|
||||||
|
) {
|
||||||
val tabNavigator = LocalTabNavigator.current
|
val tabNavigator = LocalTabNavigator.current
|
||||||
val navigator = LocalNavigator.currentOrThrow
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
@ -352,7 +352,8 @@ class LibraryScreenModel(
|
|||||||
private fun LibraryMap.applySort(
|
private fun LibraryMap.applySort(
|
||||||
// Map<MangaId, List<Track>>
|
// Map<MangaId, List<Track>>
|
||||||
trackMap: Map<Long, List<Track>>,
|
trackMap: Map<Long, List<Track>>,
|
||||||
/* SY --> */ groupSort: LibrarySort? = null, /* SY <-- */
|
/* SY --> */
|
||||||
|
groupSort: LibrarySort? = null, /* SY <-- */
|
||||||
): LibraryMap {
|
): LibraryMap {
|
||||||
// SY -->
|
// SY -->
|
||||||
val listOfTags by lazy {
|
val listOfTags by lazy {
|
||||||
@ -813,7 +814,8 @@ class LibraryScreenModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getColumnsPreferenceForCurrentOrientation(isLandscape: Boolean): PreferenceMutableState<Int> {
|
fun getColumnsPreferenceForCurrentOrientation(isLandscape: Boolean): PreferenceMutableState<Int> {
|
||||||
return (if (isLandscape) libraryPreferences.landscapeColumns() else libraryPreferences.portraitColumns()).asState(screenModelScope)
|
return (if (isLandscape) libraryPreferences.landscapeColumns() else libraryPreferences.portraitColumns())
|
||||||
|
.asState(screenModelScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getRandomLibraryItemForCurrentCategory(): LibraryItem? {
|
suspend fun getRandomLibraryItemForCurrentCategory(): LibraryItem? {
|
||||||
@ -1163,7 +1165,9 @@ class LibraryScreenModel(
|
|||||||
.find { it.int == id }
|
.find { it.int == id }
|
||||||
.let { it ?: TrackStatus.OTHER }
|
.let { it ?: TrackStatus.OTHER }
|
||||||
.let { context.getString(it.res) },
|
.let { context.getString(it.res) },
|
||||||
order = TrackStatus.values().indexOfFirst { it.int == id }.takeUnless { it == -1 }?.toLong() ?: TrackStatus.OTHER.ordinal.toLong(),
|
order = TrackStatus.values().indexOfFirst {
|
||||||
|
it.int == id
|
||||||
|
}.takeUnless { it == -1 }?.toLong() ?: TrackStatus.OTHER.ordinal.toLong(),
|
||||||
flags = 0,
|
flags = 0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,9 @@ object LibraryTab : Tab {
|
|||||||
onClickSelectAll = { screenModel.selectAll(screenModel.activeCategoryIndex) },
|
onClickSelectAll = { screenModel.selectAll(screenModel.activeCategoryIndex) },
|
||||||
onClickInvertSelection = { screenModel.invertSelection(screenModel.activeCategoryIndex) },
|
onClickInvertSelection = { screenModel.invertSelection(screenModel.activeCategoryIndex) },
|
||||||
onClickFilter = screenModel::showSettingsDialog,
|
onClickFilter = screenModel::showSettingsDialog,
|
||||||
onClickRefresh = { onClickRefresh(state.categories[screenModel.activeCategoryIndex.coerceAtMost(state.categories.lastIndex)]) },
|
onClickRefresh = {
|
||||||
|
onClickRefresh(state.categories[screenModel.activeCategoryIndex.coerceAtMost(state.categories.lastIndex)])
|
||||||
|
},
|
||||||
onClickGlobalUpdate = { onClickRefresh(null) },
|
onClickGlobalUpdate = { onClickRefresh(null) },
|
||||||
onClickOpenRandomManga = {
|
onClickOpenRandomManga = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
@ -224,7 +226,9 @@ object LibraryTab : Tab {
|
|||||||
scope.launchIO {
|
scope.launchIO {
|
||||||
val chapter = screenModel.getNextUnreadChapter(it.manga)
|
val chapter = screenModel.getNextUnreadChapter(it.manga)
|
||||||
if (chapter != null) {
|
if (chapter != null) {
|
||||||
context.startActivity(ReaderActivity.newIntent(context, chapter.mangaId, chapter.id))
|
context.startActivity(
|
||||||
|
ReaderActivity.newIntent(context, chapter.mangaId, chapter.id),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
snackbarHostState.showSnackbar(context.getString(R.string.no_next_chapter))
|
snackbarHostState.showSnackbar(context.getString(R.string.no_next_chapter))
|
||||||
}
|
}
|
||||||
|
@ -453,7 +453,11 @@ class MainActivity : BaseActivity() {
|
|||||||
private fun handleIntentAction(intent: Intent, navigator: Navigator): Boolean {
|
private fun handleIntentAction(intent: Intent, navigator: Navigator): Boolean {
|
||||||
val notificationId = intent.getIntExtra("notificationId", -1)
|
val notificationId = intent.getIntExtra("notificationId", -1)
|
||||||
if (notificationId > -1) {
|
if (notificationId > -1) {
|
||||||
NotificationReceiver.dismissNotification(applicationContext, notificationId, intent.getIntExtra("groupId", 0))
|
NotificationReceiver.dismissNotification(
|
||||||
|
applicationContext,
|
||||||
|
notificationId,
|
||||||
|
intent.getIntExtra("groupId", 0),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val tabToOpen = when (intent.action) {
|
val tabToOpen = when (intent.action) {
|
||||||
|
@ -99,7 +99,8 @@ class MangaScreen(
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val haptic = LocalHapticFeedback.current
|
val haptic = LocalHapticFeedback.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val screenModel = rememberScreenModel { MangaScreenModel(context, mangaId, fromSource, smartSearchConfig != null) }
|
val screenModel =
|
||||||
|
rememberScreenModel { MangaScreenModel(context, mangaId, fromSource, smartSearchConfig != null) }
|
||||||
|
|
||||||
val state by screenModel.state.collectAsState()
|
val state by screenModel.state.collectAsState()
|
||||||
|
|
||||||
@ -172,7 +173,9 @@ class MangaScreen(
|
|||||||
onShareClicked = { shareManga(context, screenModel.manga, screenModel.source) }.takeIf { isHttpSource },
|
onShareClicked = { shareManga(context, screenModel.manga, screenModel.source) }.takeIf { isHttpSource },
|
||||||
onDownloadActionClicked = screenModel::runDownloadAction.takeIf { !successState.source.isLocalOrStub() },
|
onDownloadActionClicked = screenModel::runDownloadAction.takeIf { !successState.source.isLocalOrStub() },
|
||||||
onEditCategoryClicked = screenModel::showChangeCategoryDialog.takeIf { successState.manga.favorite },
|
onEditCategoryClicked = screenModel::showChangeCategoryDialog.takeIf { successState.manga.favorite },
|
||||||
onEditFetchIntervalClicked = screenModel::showSetFetchIntervalDialog.takeIf { screenModel.isUpdateIntervalEnabled && successState.manga.favorite },
|
onEditFetchIntervalClicked = screenModel::showSetFetchIntervalDialog.takeIf {
|
||||||
|
screenModel.isUpdateIntervalEnabled && successState.manga.favorite
|
||||||
|
},
|
||||||
// SY -->
|
// SY -->
|
||||||
onMigrateClicked = { migrateManga(navigator, screenModel.manga!!) }.takeIf { successState.manga.favorite },
|
onMigrateClicked = { migrateManga(navigator, screenModel.manga!!) }.takeIf { successState.manga.favorite },
|
||||||
onMetadataViewerClicked = { openMetadataViewer(navigator, successState.manga) },
|
onMetadataViewerClicked = { openMetadataViewer(navigator, successState.manga) },
|
||||||
@ -181,7 +184,9 @@ class MangaScreen(
|
|||||||
onMergedSettingsClicked = screenModel::showEditMergedSettingsDialog,
|
onMergedSettingsClicked = screenModel::showEditMergedSettingsDialog,
|
||||||
onMergeClicked = { openSmartSearch(navigator, successState.manga) },
|
onMergeClicked = { openSmartSearch(navigator, successState.manga) },
|
||||||
onMergeWithAnotherClicked = { mergeWithAnother(navigator, context, successState.manga, screenModel::smartSearchMerge) },
|
onMergeWithAnotherClicked = { mergeWithAnother(navigator, context, successState.manga, screenModel::smartSearchMerge) },
|
||||||
onOpenPagePreview = { openPagePreview(context, successState.chapters.minByOrNull { it.chapter.sourceOrder }?.chapter, it) },
|
onOpenPagePreview = {
|
||||||
|
openPagePreview(context, successState.chapters.minByOrNull { it.chapter.sourceOrder }?.chapter, it)
|
||||||
|
},
|
||||||
onMorePreviewsClicked = { openMorePagePreviews(navigator, successState.manga) },
|
onMorePreviewsClicked = { openMorePagePreviews(navigator, successState.manga) },
|
||||||
// SY <--
|
// SY <--
|
||||||
onMultiBookmarkClicked = screenModel::bookmarkChapters,
|
onMultiBookmarkClicked = screenModel::bookmarkChapters,
|
||||||
|
@ -869,7 +869,9 @@ class MangaScreenModel(
|
|||||||
// SY <--
|
// SY <--
|
||||||
screenModelScope.launchIO {
|
screenModelScope.launchIO {
|
||||||
downloadManager.statusFlow()
|
downloadManager.statusFlow()
|
||||||
.filter { /* SY --> */ if (isMergedSource) it.manga.id in mergedIds else /* SY <-- */ it.manga.id == successState?.manga?.id }
|
.filter {
|
||||||
|
/* SY --> */ if (isMergedSource) it.manga.id in mergedIds else /* SY <-- */ it.manga.id == successState?.manga?.id
|
||||||
|
}
|
||||||
.catch { error -> logcat(LogPriority.ERROR, error) }
|
.catch { error -> logcat(LogPriority.ERROR, error) }
|
||||||
.collect {
|
.collect {
|
||||||
withUIContext {
|
withUIContext {
|
||||||
@ -880,7 +882,9 @@ class MangaScreenModel(
|
|||||||
|
|
||||||
screenModelScope.launchIO {
|
screenModelScope.launchIO {
|
||||||
downloadManager.progressFlow()
|
downloadManager.progressFlow()
|
||||||
.filter { /* SY --> */ if (isMergedSource) it.manga.id in mergedIds else /* SY <-- */ it.manga.id == successState?.manga?.id }
|
.filter {
|
||||||
|
/* SY --> */ if (isMergedSource) it.manga.id in mergedIds else /* SY <-- */ it.manga.id == successState?.manga?.id
|
||||||
|
}
|
||||||
.catch { error -> logcat(LogPriority.ERROR, error) }
|
.catch { error -> logcat(LogPriority.ERROR, error) }
|
||||||
.collect {
|
.collect {
|
||||||
withUIContext {
|
withUIContext {
|
||||||
@ -1454,7 +1458,10 @@ class MangaScreenModel(
|
|||||||
}
|
}
|
||||||
// SY -->
|
// SY -->
|
||||||
.map { trackItems ->
|
.map { trackItems ->
|
||||||
if (manga.source in mangaDexSourceIds || state.mergedData?.manga?.values.orEmpty().any { it.source in mangaDexSourceIds }) {
|
if (manga.source in mangaDexSourceIds || state.mergedData?.manga?.values.orEmpty().any {
|
||||||
|
it.source in mangaDexSourceIds
|
||||||
|
}
|
||||||
|
) {
|
||||||
val mdTrack = trackItems.firstOrNull { it.tracker is MdList }
|
val mdTrack = trackItems.firstOrNull { it.tracker is MdList }
|
||||||
when {
|
when {
|
||||||
mdTrack == null -> {
|
mdTrack == null -> {
|
||||||
@ -1614,7 +1621,9 @@ class MangaScreenModel(
|
|||||||
get() = trackItems.isNotEmpty()
|
get() = trackItems.isNotEmpty()
|
||||||
|
|
||||||
val trackingCount: Int
|
val trackingCount: Int
|
||||||
get() = trackItems.count { it.track != null && ((it.tracker is MdList && it.track.status != FollowStatus.UNFOLLOWED.int.toLong()) || it.tracker !is MdList ) }
|
get() = trackItems.count {
|
||||||
|
it.track != null && ((it.tracker is MdList && it.track.status != FollowStatus.UNFOLLOWED.int.toLong()) || it.tracker !is MdList)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies the view filters to the list of chapters obtained from the database.
|
* Applies the view filters to the list of chapters obtained from the database.
|
||||||
|
@ -53,7 +53,9 @@ class EditMergedSettingsState(
|
|||||||
context.toast(R.string.merged_references_invalid)
|
context.toast(R.string.merged_references_invalid)
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
}
|
}
|
||||||
mergedMangas += mergedReferences.filter { it.mangaSourceId != MERGED_SOURCE_ID }.map { reference -> mergedManga.firstOrNull { it.id == reference.mangaId } to reference }
|
mergedMangas += mergedReferences.filter {
|
||||||
|
it.mangaSourceId != MERGED_SOURCE_ID
|
||||||
|
}.map { reference -> mergedManga.firstOrNull { it.id == reference.mangaId } to reference }
|
||||||
mergeReference = mergedReferences.firstOrNull { it.mangaSourceId == MERGED_SOURCE_ID }
|
mergeReference = mergedReferences.firstOrNull { it.mangaSourceId == MERGED_SOURCE_ID }
|
||||||
|
|
||||||
val isPriorityOrder = mergeReference?.let { it.chapterSortMode == MergedMangaReference.CHAPTER_SORT_PRIORITY } ?: false
|
val isPriorityOrder = mergeReference?.let { it.chapterSortMode == MergedMangaReference.CHAPTER_SORT_PRIORITY } ?: false
|
||||||
@ -66,7 +68,11 @@ class EditMergedSettingsState(
|
|||||||
|
|
||||||
mergedMangaAdapter?.isHandleDragEnabled = isPriorityOrder
|
mergedMangaAdapter?.isHandleDragEnabled = isPriorityOrder
|
||||||
|
|
||||||
mergedMangaAdapter?.updateDataSet(mergedMangas.map { it.toModel() }.sortedBy { it.mergedMangaReference.chapterPriority })
|
mergedMangaAdapter?.updateDataSet(
|
||||||
|
mergedMangas.map {
|
||||||
|
it.toModel()
|
||||||
|
}.sortedBy { it.mergedMangaReference.chapterPriority },
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onItemReleased(position: Int) {
|
override fun onItemReleased(position: Int) {
|
||||||
@ -113,7 +119,9 @@ class EditMergedSettingsState(
|
|||||||
val (manga, reference) = pair
|
val (manga, reference) = pair
|
||||||
if (reference.id != adapterReference.id) return@map pair
|
if (reference.id != adapterReference.id) return@map pair
|
||||||
|
|
||||||
mergedMangaAdapter?.allBoundViewHolders?.firstOrNull { it is EditMergedMangaHolder && it.reference.id == reference.id }?.let {
|
mergedMangaAdapter?.allBoundViewHolders?.firstOrNull {
|
||||||
|
it is EditMergedMangaHolder && it.reference.id == reference.id
|
||||||
|
}?.let {
|
||||||
if (it is EditMergedMangaHolder) {
|
if (it is EditMergedMangaHolder) {
|
||||||
it.updateChapterUpdatesIcon(!reference.getChapterUpdates)
|
it.updateChapterUpdatesIcon(!reference.getChapterUpdates)
|
||||||
}
|
}
|
||||||
@ -141,7 +149,9 @@ class EditMergedSettingsState(
|
|||||||
val (manga, reference) = pair
|
val (manga, reference) = pair
|
||||||
if (reference.id != adapterReference.id) return@map pair
|
if (reference.id != adapterReference.id) return@map pair
|
||||||
|
|
||||||
mergedMangaAdapter?.allBoundViewHolders?.firstOrNull { it is EditMergedMangaHolder && it.reference.id == reference.id }?.let {
|
mergedMangaAdapter?.allBoundViewHolders?.firstOrNull {
|
||||||
|
it is EditMergedMangaHolder && it.reference.id == reference.id
|
||||||
|
}?.let {
|
||||||
if (it is EditMergedMangaHolder) {
|
if (it is EditMergedMangaHolder) {
|
||||||
it.updateDownloadChaptersIcon(!reference.downloadChapters)
|
it.updateDownloadChaptersIcon(!reference.downloadChapters)
|
||||||
}
|
}
|
||||||
|
@ -133,7 +133,9 @@ class EditMergedSettingsHeaderAdapter(private val state: EditMergedSettingsState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.dedupeSwitch.isChecked = state.mergeReference?.let { it.chapterSortMode != MergedMangaReference.CHAPTER_SORT_NONE } ?: false
|
binding.dedupeSwitch.isChecked = state.mergeReference?.let {
|
||||||
|
it.chapterSortMode != MergedMangaReference.CHAPTER_SORT_NONE
|
||||||
|
} ?: false
|
||||||
binding.dedupeSwitch.setOnCheckedChangeListener { _, isChecked ->
|
binding.dedupeSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||||
binding.dedupeModeSpinner.isEnabled = isChecked
|
binding.dedupeModeSpinner.isEnabled = isChecked
|
||||||
binding.dedupeModeSpinner.alpha = when (isChecked) {
|
binding.dedupeModeSpinner.alpha = when (isChecked) {
|
||||||
|
@ -117,7 +117,6 @@ import tachiyomi.domain.source.service.SourceManager
|
|||||||
import tachiyomi.presentation.core.util.collectAsState
|
import tachiyomi.presentation.core.util.collectAsState
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import kotlin.math.abs
|
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
class ReaderActivity : BaseActivity() {
|
class ReaderActivity : BaseActivity() {
|
||||||
@ -402,7 +401,6 @@ class ReaderActivity : BaseActivity() {
|
|||||||
!showVerticalSeekbar -> NavBarType.Bottom
|
!showVerticalSeekbar -> NavBarType.Bottom
|
||||||
leftHandedVerticalSeekbar -> NavBarType.VerticalLeft
|
leftHandedVerticalSeekbar -> NavBarType.VerticalLeft
|
||||||
else -> NavBarType.VerticalRight
|
else -> NavBarType.VerticalRight
|
||||||
|
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
@ -569,7 +567,7 @@ class ReaderActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
state.dateRelativeTime
|
state.dateRelativeTime,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// SY -->
|
// SY -->
|
||||||
@ -605,7 +603,6 @@ class ReaderActivity : BaseActivity() {
|
|||||||
)
|
)
|
||||||
// SY <--
|
// SY <--
|
||||||
null -> {}
|
null -> {}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -883,7 +880,9 @@ class ReaderActivity : BaseActivity() {
|
|||||||
// SY -->
|
// SY -->
|
||||||
val state = viewModel.state.value
|
val state = viewModel.state.value
|
||||||
if (state.indexChapterToShift != null && state.indexPageToShift != null) {
|
if (state.indexChapterToShift != null && state.indexPageToShift != null) {
|
||||||
viewerChapters.currChapter.pages?.find { it.index == state.indexPageToShift && it.chapter.chapter.id == state.indexChapterToShift }?.let {
|
viewerChapters.currChapter.pages?.find {
|
||||||
|
it.index == state.indexPageToShift && it.chapter.chapter.id == state.indexChapterToShift
|
||||||
|
}?.let {
|
||||||
(viewModel.state.value.viewer as? PagerViewer)?.updateShifting(it)
|
(viewModel.state.value.viewer as? PagerViewer)?.updateShifting(it)
|
||||||
}
|
}
|
||||||
viewModel.setIndexChapterToShift(null)
|
viewModel.setIndexChapterToShift(null)
|
||||||
|
@ -210,13 +210,22 @@ class ReaderViewModel @JvmOverloads constructor(
|
|||||||
(manga.unreadFilterRaw == Manga.CHAPTER_SHOW_READ && !it.read) ||
|
(manga.unreadFilterRaw == Manga.CHAPTER_SHOW_READ && !it.read) ||
|
||||||
(manga.unreadFilterRaw == Manga.CHAPTER_SHOW_UNREAD && it.read) ||
|
(manga.unreadFilterRaw == Manga.CHAPTER_SHOW_UNREAD && it.read) ||
|
||||||
// SY -->
|
// SY -->
|
||||||
(manga.downloadedFilterRaw == Manga.CHAPTER_SHOW_DOWNLOADED && !isChapterDownloaded(it)) ||
|
(
|
||||||
(manga.downloadedFilterRaw == Manga.CHAPTER_SHOW_NOT_DOWNLOADED && isChapterDownloaded(it)) ||
|
manga.downloadedFilterRaw == Manga.CHAPTER_SHOW_DOWNLOADED &&
|
||||||
|
!isChapterDownloaded(it)
|
||||||
|
) ||
|
||||||
|
(
|
||||||
|
manga.downloadedFilterRaw == Manga.CHAPTER_SHOW_NOT_DOWNLOADED &&
|
||||||
|
isChapterDownloaded(it)
|
||||||
|
) ||
|
||||||
// SY <--
|
// SY <--
|
||||||
(manga.bookmarkedFilterRaw == Manga.CHAPTER_SHOW_BOOKMARKED && !it.bookmark) ||
|
(manga.bookmarkedFilterRaw == Manga.CHAPTER_SHOW_BOOKMARKED && !it.bookmark) ||
|
||||||
(manga.bookmarkedFilterRaw == Manga.CHAPTER_SHOW_NOT_BOOKMARKED && it.bookmark) ||
|
(manga.bookmarkedFilterRaw == Manga.CHAPTER_SHOW_NOT_BOOKMARKED && it.bookmark) ||
|
||||||
// SY -->
|
// SY -->
|
||||||
(manga.filteredScanlators != null && MdUtil.getScanlators(it.scanlator).none { group -> manga.filteredScanlators!!.contains(group) })
|
(
|
||||||
|
manga.filteredScanlators != null && MdUtil.getScanlators(it.scanlator)
|
||||||
|
.none { group -> manga.filteredScanlators!!.contains(group) }
|
||||||
|
)
|
||||||
// SY <--
|
// SY <--
|
||||||
}
|
}
|
||||||
else -> false
|
else -> false
|
||||||
@ -336,8 +345,20 @@ class ReaderViewModel @JvmOverloads constructor(
|
|||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
val mergedReferences = if (source is MergedSource) runBlocking { getMergedReferencesById.await(manga.id) } else emptyList()
|
val mergedReferences = if (source is MergedSource) {
|
||||||
val mergedManga = if (source is MergedSource) runBlocking { getMergedMangaById.await(manga.id) }.associateBy { it.id } else emptyMap()
|
runBlocking {
|
||||||
|
getMergedReferencesById.await(manga.id)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
val mergedManga = if (source is MergedSource) {
|
||||||
|
runBlocking {
|
||||||
|
getMergedMangaById.await(manga.id)
|
||||||
|
}.associateBy { it.id }
|
||||||
|
} else {
|
||||||
|
emptyMap()
|
||||||
|
}
|
||||||
val relativeTime = uiPreferences.relativeTime().get()
|
val relativeTime = uiPreferences.relativeTime().get()
|
||||||
val autoScrollFreq = readerPreferences.autoscrollInterval().get()
|
val autoScrollFreq = readerPreferences.autoscrollInterval().get()
|
||||||
// SY <--
|
// SY <--
|
||||||
@ -353,7 +374,7 @@ class ReaderViewModel @JvmOverloads constructor(
|
|||||||
} else {
|
} else {
|
||||||
autoScrollFreq.toString()
|
autoScrollFreq.toString()
|
||||||
},
|
},
|
||||||
isAutoScrollEnabled = autoScrollFreq != -1f
|
isAutoScrollEnabled = autoScrollFreq != -1f,
|
||||||
/* SY <-- */
|
/* SY <-- */
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -361,9 +382,23 @@ class ReaderViewModel @JvmOverloads constructor(
|
|||||||
|
|
||||||
val context = Injekt.get<Application>()
|
val context = Injekt.get<Application>()
|
||||||
// val source = sourceManager.getOrStub(manga.source)
|
// val source = sourceManager.getOrStub(manga.source)
|
||||||
loader = ChapterLoader(context, downloadManager, downloadProvider, manga, source, /* SY --> */sourceManager, readerPreferences, mergedReferences, mergedManga/* SY <-- */)
|
loader = ChapterLoader(
|
||||||
|
context = context,
|
||||||
|
downloadManager = downloadManager,
|
||||||
|
downloadProvider = downloadProvider,
|
||||||
|
manga = manga,
|
||||||
|
source = source, /* SY --> */
|
||||||
|
sourceManager = sourceManager,
|
||||||
|
readerPrefs = readerPreferences,
|
||||||
|
mergedReferences = mergedReferences,
|
||||||
|
mergedManga = mergedManga, /* SY <-- */
|
||||||
|
)
|
||||||
|
|
||||||
loadChapter(loader!!, chapterList.first { chapterId == it.chapter.id } /* SY --> */, page/* SY <-- */)
|
loadChapter(
|
||||||
|
loader!!,
|
||||||
|
chapterList.first { chapterId == it.chapter.id },
|
||||||
|
/* SY --> */page, /* SY <-- */
|
||||||
|
)
|
||||||
Result.success(true)
|
Result.success(true)
|
||||||
} else {
|
} else {
|
||||||
// Unlikely but okay
|
// Unlikely but okay
|
||||||
@ -634,7 +669,11 @@ class ReaderViewModel @JvmOverloads constructor(
|
|||||||
* Saves the chapter progress (last read page and whether it's read)
|
* Saves the chapter progress (last read page and whether it's read)
|
||||||
* if incognito mode isn't on.
|
* if incognito mode isn't on.
|
||||||
*/
|
*/
|
||||||
private suspend fun updateChapterProgress(readerChapter: ReaderChapter, page: Page/* SY --> */, hasExtraPage: Boolean/* SY <-- */) {
|
private suspend fun updateChapterProgress(
|
||||||
|
readerChapter: ReaderChapter,
|
||||||
|
page: Page/* SY --> */,
|
||||||
|
hasExtraPage: Boolean, /* SY <-- */
|
||||||
|
) {
|
||||||
val pageIndex = page.index
|
val pageIndex = page.index
|
||||||
|
|
||||||
mutableState.update {
|
mutableState.update {
|
||||||
@ -989,7 +1028,11 @@ class ReaderViewModel @JvmOverloads constructor(
|
|||||||
val filename = generateFilename(manga, page)
|
val filename = generateFilename(manga, page)
|
||||||
|
|
||||||
// Pictures directory.
|
// Pictures directory.
|
||||||
val relativePath = if (readerPreferences.folderPerManga().get()) DiskUtil.buildValidFilename(manga.title) else ""
|
val relativePath = if (readerPreferences.folderPerManga().get()) {
|
||||||
|
DiskUtil.buildValidFilename(manga.title)
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
|
||||||
// Copy file in background.
|
// Copy file in background.
|
||||||
viewModelScope.launchNonCancellable {
|
viewModelScope.launchNonCancellable {
|
||||||
@ -1036,7 +1079,7 @@ class ReaderViewModel @JvmOverloads constructor(
|
|||||||
page2 = secondPage,
|
page2 = secondPage,
|
||||||
isLTR = isLTR,
|
isLTR = isLTR,
|
||||||
bg = bg,
|
bg = bg,
|
||||||
location = Location.Pictures(DiskUtil.buildValidFilename(manga.title)),
|
location = Location.Pictures.create(DiskUtil.buildValidFilename(manga.title)),
|
||||||
manga = manga,
|
manga = manga,
|
||||||
)
|
)
|
||||||
eventChannel.send(Event.SavedImage(SaveImageResult.Success(uri)))
|
eventChannel.send(Event.SavedImage(SaveImageResult.Success(uri)))
|
||||||
@ -1288,7 +1331,10 @@ class ReaderViewModel @JvmOverloads constructor(
|
|||||||
data object ChapterList : Dialog
|
data object ChapterList : Dialog
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
data class PageActions(val page: ReaderPage/* SY --> */, val extraPage: ReaderPage? = null /* SY <-- */) : Dialog
|
data class PageActions(
|
||||||
|
val page: ReaderPage/* SY --> */,
|
||||||
|
val extraPage: ReaderPage? = null, /* SY <-- */
|
||||||
|
) : Dialog
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
data object AutoScrollHelp : Dialog
|
data object AutoScrollHelp : Dialog
|
||||||
@ -1304,6 +1350,10 @@ class ReaderViewModel @JvmOverloads constructor(
|
|||||||
data class SetCoverResult(val result: SetAsCoverResult) : Event
|
data class SetCoverResult(val result: SetAsCoverResult) : Event
|
||||||
|
|
||||||
data class SavedImage(val result: SaveImageResult) : Event
|
data class SavedImage(val result: SaveImageResult) : Event
|
||||||
data class ShareImage(val uri: Uri, val page: ReaderPage/* SY --> */, val secondPage: ReaderPage? = null /* SY <-- */) : Event
|
data class ShareImage(
|
||||||
|
val uri: Uri,
|
||||||
|
val page: ReaderPage/* SY --> */,
|
||||||
|
val secondPage: ReaderPage? = null, /* SY <-- */
|
||||||
|
) : Event
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,16 +88,37 @@ class ChapterLoader(
|
|||||||
*/
|
*/
|
||||||
private fun getPageLoader(chapter: ReaderChapter): PageLoader {
|
private fun getPageLoader(chapter: ReaderChapter): PageLoader {
|
||||||
val dbChapter = chapter.chapter
|
val dbChapter = chapter.chapter
|
||||||
val isDownloaded = downloadManager.isChapterDownloaded(dbChapter.name, dbChapter.scanlator, /* SY --> */ manga.ogTitle /* SY <-- */, manga.source, skipCache = true)
|
val isDownloaded = downloadManager.isChapterDownloaded(
|
||||||
|
chapterName = dbChapter.name,
|
||||||
|
chapterScanlator = dbChapter.scanlator, /* SY --> */
|
||||||
|
mangaTitle = manga.ogTitle /* SY <-- */,
|
||||||
|
sourceId = manga.source,
|
||||||
|
skipCache = true,
|
||||||
|
)
|
||||||
return when {
|
return when {
|
||||||
// SY -->
|
// SY -->
|
||||||
source is MergedSource -> {
|
source is MergedSource -> {
|
||||||
val mangaReference = mergedReferences.firstOrNull { it.mangaId == chapter.chapter.manga_id } ?: error("Merge reference null")
|
val mangaReference = mergedReferences.firstOrNull {
|
||||||
val source = sourceManager.get(mangaReference.mangaSourceId) ?: error("Source ${mangaReference.mangaSourceId} was null")
|
it.mangaId == chapter.chapter.manga_id
|
||||||
|
} ?: error("Merge reference null")
|
||||||
|
val source = sourceManager.get(mangaReference.mangaSourceId)
|
||||||
|
?: error("Source ${mangaReference.mangaSourceId} was null")
|
||||||
val manga = mergedManga[chapter.chapter.manga_id] ?: error("Manga for merged chapter was null")
|
val manga = mergedManga[chapter.chapter.manga_id] ?: error("Manga for merged chapter was null")
|
||||||
val isMergedMangaDownloaded = downloadManager.isChapterDownloaded(chapter.chapter.name, chapter.chapter.scanlator, manga.ogTitle, manga.source, true)
|
val isMergedMangaDownloaded = downloadManager.isChapterDownloaded(
|
||||||
|
chapterName = chapter.chapter.name,
|
||||||
|
chapterScanlator = chapter.chapter.scanlator,
|
||||||
|
mangaTitle = manga.ogTitle,
|
||||||
|
sourceId = manga.source,
|
||||||
|
skipCache = true,
|
||||||
|
)
|
||||||
when {
|
when {
|
||||||
isMergedMangaDownloaded -> DownloadPageLoader(chapter, manga, source, downloadManager, downloadProvider)
|
isMergedMangaDownloaded -> DownloadPageLoader(
|
||||||
|
chapter = chapter,
|
||||||
|
manga = manga,
|
||||||
|
source = source,
|
||||||
|
downloadManager = downloadManager,
|
||||||
|
downloadProvider = downloadProvider,
|
||||||
|
)
|
||||||
source is HttpSource -> HttpPageLoader(chapter, source)
|
source is HttpSource -> HttpPageLoader(chapter, source)
|
||||||
source is LocalSource -> source.getFormat(chapter.chapter).let { format ->
|
source is LocalSource -> source.getFormat(chapter.chapter).let { format ->
|
||||||
when (format) {
|
when (format) {
|
||||||
|
@ -5,14 +5,54 @@ import androidx.annotation.DrawableRes
|
|||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
|
||||||
enum class OrientationType(val flag: Int, @StringRes val stringRes: Int, @DrawableRes val iconRes: Int, val flagValue: Int) {
|
enum class OrientationType(
|
||||||
DEFAULT(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, R.string.label_default, R.drawable.ic_screen_rotation_24dp, 0x00000000),
|
val flag: Int,
|
||||||
FREE(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, R.string.rotation_free, R.drawable.ic_screen_rotation_24dp, 0x00000008),
|
@StringRes val stringRes: Int,
|
||||||
PORTRAIT(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT, R.string.rotation_portrait, R.drawable.ic_stay_current_portrait_24dp, 0x00000010),
|
@DrawableRes val iconRes: Int,
|
||||||
LANDSCAPE(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE, R.string.rotation_landscape, R.drawable.ic_stay_current_landscape_24dp, 0x00000018),
|
val flagValue: Int,
|
||||||
LOCKED_PORTRAIT(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, R.string.rotation_force_portrait, R.drawable.ic_screen_lock_portrait_24dp, 0x00000020),
|
) {
|
||||||
LOCKED_LANDSCAPE(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, R.string.rotation_force_landscape, R.drawable.ic_screen_lock_landscape_24dp, 0x00000028),
|
DEFAULT(
|
||||||
REVERSE_PORTRAIT(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT, R.string.rotation_reverse_portrait, R.drawable.ic_stay_current_portrait_24dp, 0x00000030),
|
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED,
|
||||||
|
R.string.label_default,
|
||||||
|
R.drawable.ic_screen_rotation_24dp,
|
||||||
|
0x00000000,
|
||||||
|
),
|
||||||
|
FREE(
|
||||||
|
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED,
|
||||||
|
R.string.rotation_free,
|
||||||
|
R.drawable.ic_screen_rotation_24dp,
|
||||||
|
0x00000008,
|
||||||
|
),
|
||||||
|
PORTRAIT(
|
||||||
|
ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT,
|
||||||
|
R.string.rotation_portrait,
|
||||||
|
R.drawable.ic_stay_current_portrait_24dp,
|
||||||
|
0x00000010,
|
||||||
|
),
|
||||||
|
LANDSCAPE(
|
||||||
|
ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE,
|
||||||
|
R.string.rotation_landscape,
|
||||||
|
R.drawable.ic_stay_current_landscape_24dp,
|
||||||
|
0x00000018,
|
||||||
|
),
|
||||||
|
LOCKED_PORTRAIT(
|
||||||
|
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT,
|
||||||
|
R.string.rotation_force_portrait,
|
||||||
|
R.drawable.ic_screen_lock_portrait_24dp,
|
||||||
|
0x00000020,
|
||||||
|
),
|
||||||
|
LOCKED_LANDSCAPE(
|
||||||
|
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,
|
||||||
|
R.string.rotation_force_landscape,
|
||||||
|
R.drawable.ic_screen_lock_landscape_24dp,
|
||||||
|
0x00000028,
|
||||||
|
),
|
||||||
|
REVERSE_PORTRAIT(
|
||||||
|
ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT,
|
||||||
|
R.string.rotation_reverse_portrait,
|
||||||
|
R.drawable.ic_stay_current_portrait_24dp,
|
||||||
|
0x00000030,
|
||||||
|
),
|
||||||
;
|
;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -35,9 +35,15 @@ class ReaderPreferences(
|
|||||||
|
|
||||||
fun keepScreenOn() = preferenceStore.getBoolean("pref_keep_screen_on_key", true)
|
fun keepScreenOn() = preferenceStore.getBoolean("pref_keep_screen_on_key", true)
|
||||||
|
|
||||||
fun defaultReadingMode() = preferenceStore.getInt("pref_default_reading_mode_key", ReadingModeType.RIGHT_TO_LEFT.flagValue)
|
fun defaultReadingMode() = preferenceStore.getInt(
|
||||||
|
"pref_default_reading_mode_key",
|
||||||
|
ReadingModeType.RIGHT_TO_LEFT.flagValue,
|
||||||
|
)
|
||||||
|
|
||||||
fun defaultOrientationType() = preferenceStore.getInt("pref_default_orientation_type_key", OrientationType.FREE.flagValue)
|
fun defaultOrientationType() = preferenceStore.getInt(
|
||||||
|
"pref_default_orientation_type_key",
|
||||||
|
OrientationType.FREE.flagValue,
|
||||||
|
)
|
||||||
|
|
||||||
fun webtoonDoubleTapZoomEnabled() = preferenceStore.getBoolean("pref_enable_double_tap_zoom_webtoon", true)
|
fun webtoonDoubleTapZoomEnabled() = preferenceStore.getBoolean("pref_enable_double_tap_zoom_webtoon", true)
|
||||||
|
|
||||||
|
@ -10,7 +10,11 @@ import eu.kanade.tachiyomi.ui.reader.viewer.pager.R2LPagerViewer
|
|||||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.VerticalPagerViewer
|
import eu.kanade.tachiyomi.ui.reader.viewer.pager.VerticalPagerViewer
|
||||||
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
|
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
|
||||||
|
|
||||||
enum class ReadingModeType(@StringRes val stringRes: Int, @DrawableRes val iconRes: Int, val flagValue: Int) {
|
enum class ReadingModeType(
|
||||||
|
@StringRes val stringRes: Int,
|
||||||
|
@DrawableRes val iconRes: Int,
|
||||||
|
val flagValue: Int,
|
||||||
|
) {
|
||||||
DEFAULT(R.string.label_default, R.drawable.ic_reader_default_24dp, 0x00000000),
|
DEFAULT(R.string.label_default, R.drawable.ic_reader_default_24dp, 0x00000000),
|
||||||
LEFT_TO_RIGHT(R.string.left_to_right_viewer, R.drawable.ic_reader_ltr_24dp, 0x00000001),
|
LEFT_TO_RIGHT(R.string.left_to_right_viewer, R.drawable.ic_reader_ltr_24dp, 0x00000001),
|
||||||
RIGHT_TO_LEFT(R.string.right_to_left_viewer, R.drawable.ic_reader_rtl_24dp, 0x00000002),
|
RIGHT_TO_LEFT(R.string.right_to_left_viewer, R.drawable.ic_reader_rtl_24dp, 0x00000002),
|
||||||
|
@ -350,7 +350,9 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
|
|||||||
val newPage =
|
val newPage =
|
||||||
when {
|
when {
|
||||||
(oldCurrent?.first as? ReaderPage)?.chapter != currentChapter &&
|
(oldCurrent?.first as? ReaderPage)?.chapter != currentChapter &&
|
||||||
(oldCurrent?.first as? ChapterTransition)?.from != currentChapter -> subItems.find { (it as? ReaderPage)?.chapter == currentChapter }
|
(oldCurrent?.first as? ChapterTransition)?.from != currentChapter -> subItems.find {
|
||||||
|
(it as? ReaderPage)?.chapter == currentChapter
|
||||||
|
}
|
||||||
useSecondPage -> (oldCurrent?.second ?: oldCurrent?.first)
|
useSecondPage -> (oldCurrent?.second ?: oldCurrent?.first)
|
||||||
else -> oldCurrent?.first ?: return
|
else -> oldCurrent?.first ?: return
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,11 @@ import kotlin.time.Duration
|
|||||||
/**
|
/**
|
||||||
* Implementation of a [Viewer] to display pages with a [RecyclerView].
|
* Implementation of a [Viewer] to display pages with a [RecyclerView].
|
||||||
*/
|
*/
|
||||||
class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = true, private val tapByPage: Boolean = false) : Viewer {
|
class WebtoonViewer(
|
||||||
|
val activity: ReaderActivity,
|
||||||
|
val isContinuous: Boolean = true,
|
||||||
|
private val tapByPage: Boolean = false,
|
||||||
|
) : Viewer {
|
||||||
|
|
||||||
val downloadManager: DownloadManager by injectLazy()
|
val downloadManager: DownloadManager by injectLazy()
|
||||||
|
|
||||||
@ -291,7 +295,12 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
|
|||||||
* Scrolls one screen over a period of time
|
* Scrolls one screen over a period of time
|
||||||
*/
|
*/
|
||||||
fun linearScroll(duration: Duration) {
|
fun linearScroll(duration: Duration) {
|
||||||
recycler.smoothScrollBy(0, activity.resources.displayMetrics.heightPixels, LinearInterpolator(), duration.inWholeMilliseconds.toInt())
|
recycler.smoothScrollBy(
|
||||||
|
0,
|
||||||
|
activity.resources.displayMetrics.heightPixels,
|
||||||
|
LinearInterpolator(),
|
||||||
|
duration.inWholeMilliseconds.toInt(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -106,7 +106,9 @@ object UpdatesTab : Tab {
|
|||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
screenModel.events.collectLatest { event ->
|
screenModel.events.collectLatest { event ->
|
||||||
when (event) {
|
when (event) {
|
||||||
Event.InternalError -> screenModel.snackbarHostState.showSnackbar(context.getString(R.string.internal_error))
|
Event.InternalError -> screenModel.snackbarHostState.showSnackbar(
|
||||||
|
context.getString(R.string.internal_error),
|
||||||
|
)
|
||||||
is Event.LibraryUpdateTriggered -> {
|
is Event.LibraryUpdateTriggered -> {
|
||||||
val msg = if (event.started) {
|
val msg = if (event.started) {
|
||||||
R.string.updating_library
|
R.string.updating_library
|
||||||
|
@ -10,7 +10,11 @@ import tachiyomi.domain.manga.model.Manga
|
|||||||
/**
|
/**
|
||||||
* Gets next unread chapter with filters and sorting applied
|
* Gets next unread chapter with filters and sorting applied
|
||||||
*/
|
*/
|
||||||
fun List<Chapter>.getNextUnread(manga: Manga, downloadManager: DownloadManager /* SY --> */, mergedManga: Map<Long, Manga>/* SY <-- */): Chapter? {
|
fun List<Chapter>.getNextUnread(
|
||||||
|
manga: Manga,
|
||||||
|
downloadManager: DownloadManager /* SY --> */,
|
||||||
|
mergedManga: Map<Long, Manga>, /* SY <-- */
|
||||||
|
): Chapter? {
|
||||||
return applyFilters(manga, downloadManager/* SY --> */, mergedManga/* SY <-- */).let { chapters ->
|
return applyFilters(manga, downloadManager/* SY --> */, mergedManga/* SY <-- */).let { chapters ->
|
||||||
// SY -->
|
// SY -->
|
||||||
if (manga.isEhBasedManga()) {
|
if (manga.isEhBasedManga()) {
|
||||||
|
@ -36,9 +36,17 @@ fun File.copyAndSetReadOnlyTo(target: File, overwrite: Boolean = false, bufferSi
|
|||||||
|
|
||||||
if (target.exists()) {
|
if (target.exists()) {
|
||||||
if (!overwrite) {
|
if (!overwrite) {
|
||||||
throw FileAlreadyExistsException(file = this, other = target, reason = "The destination file already exists.")
|
throw FileAlreadyExistsException(
|
||||||
|
file = this,
|
||||||
|
other = target,
|
||||||
|
reason = "The destination file already exists.",
|
||||||
|
)
|
||||||
} else if (!target.delete()) {
|
} else if (!target.delete()) {
|
||||||
throw FileAlreadyExistsException(file = this, other = target, reason = "Tried to overwrite the destination, but failed to delete it.")
|
throw FileAlreadyExistsException(
|
||||||
|
file = this,
|
||||||
|
other = target,
|
||||||
|
reason = "Tried to overwrite the destination, but failed to delete it.",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +59,9 @@ fun Context.copyToClipboard(label: String, content: String) {
|
|||||||
* @param permission the permission to check.
|
* @param permission the permission to check.
|
||||||
* @return true if it has permissions.
|
* @return true if it has permissions.
|
||||||
*/
|
*/
|
||||||
fun Context.hasPermission(permission: String) = PermissionChecker.checkSelfPermission(this, permission) == PermissionChecker.PERMISSION_GRANTED
|
fun Context.hasPermission(
|
||||||
|
permission: String,
|
||||||
|
) = PermissionChecker.checkSelfPermission(this, permission) == PermissionChecker.PERMISSION_GRANTED
|
||||||
|
|
||||||
val Context.powerManager: PowerManager
|
val Context.powerManager: PowerManager
|
||||||
get() = getSystemService()!!
|
get() = getSystemService()!!
|
||||||
@ -105,7 +107,10 @@ fun Context.openInBrowser(uri: Uri, forceDefaultBrowser: Boolean = false) {
|
|||||||
private fun Context.defaultBrowserPackageName(): String? {
|
private fun Context.defaultBrowserPackageName(): String? {
|
||||||
val browserIntent = Intent(Intent.ACTION_VIEW, "http://".toUri())
|
val browserIntent = Intent(Intent.ACTION_VIEW, "http://".toUri())
|
||||||
val resolveInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
val resolveInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
packageManager.resolveActivity(browserIntent, PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong()))
|
packageManager.resolveActivity(
|
||||||
|
browserIntent,
|
||||||
|
PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong()),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
packageManager.resolveActivity(browserIntent, PackageManager.MATCH_DEFAULT_ONLY)
|
packageManager.resolveActivity(browserIntent, PackageManager.MATCH_DEFAULT_ONLY)
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,10 @@ fun Context.cancelNotification(id: Int) {
|
|||||||
* @param block the function that will execute inside the builder.
|
* @param block the function that will execute inside the builder.
|
||||||
* @return a notification to be displayed or updated.
|
* @return a notification to be displayed or updated.
|
||||||
*/
|
*/
|
||||||
fun Context.notificationBuilder(channelId: String, block: (NotificationCompat.Builder.() -> Unit)? = null): NotificationCompat.Builder {
|
fun Context.notificationBuilder(
|
||||||
|
channelId: String,
|
||||||
|
block: (NotificationCompat.Builder.() -> Unit)? = null,
|
||||||
|
): NotificationCompat.Builder {
|
||||||
val builder = NotificationCompat.Builder(this, channelId)
|
val builder = NotificationCompat.Builder(this, channelId)
|
||||||
.setColor(getColor(R.color.accent_blue))
|
.setColor(getColor(R.color.accent_blue))
|
||||||
if (block != null) {
|
if (block != null) {
|
||||||
|
@ -91,6 +91,7 @@ fun View?.isVisibleOnScreen(): Boolean {
|
|||||||
}
|
}
|
||||||
val actualPosition = Rect()
|
val actualPosition = Rect()
|
||||||
this.getGlobalVisibleRect(actualPosition)
|
this.getGlobalVisibleRect(actualPosition)
|
||||||
val screen = Rect(0, 0, Resources.getSystem().displayMetrics.widthPixels, Resources.getSystem().displayMetrics.heightPixels)
|
val screen =
|
||||||
|
Rect(0, 0, Resources.getSystem().displayMetrics.widthPixels, Resources.getSystem().displayMetrics.heightPixels)
|
||||||
return actualPosition.intersect(screen)
|
return actualPosition.intersect(screen)
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,9 @@ object EXHMigrations {
|
|||||||
val mergedMangas = runBlocking { getMangaBySource.await(MERGED_SOURCE_ID) }
|
val mergedMangas = runBlocking { getMangaBySource.await(MERGED_SOURCE_ID) }
|
||||||
|
|
||||||
if (mergedMangas.isNotEmpty()) {
|
if (mergedMangas.isNotEmpty()) {
|
||||||
val mangaConfigs = mergedMangas.mapNotNull { mergedManga -> readMangaConfig(mergedManga)?.let { mergedManga to it } }
|
val mangaConfigs = mergedMangas.mapNotNull { mergedManga ->
|
||||||
|
readMangaConfig(mergedManga)?.let { mergedManga to it }
|
||||||
|
}
|
||||||
if (mangaConfigs.isNotEmpty()) {
|
if (mangaConfigs.isNotEmpty()) {
|
||||||
val mangaToUpdate = mutableListOf<MangaUpdate>()
|
val mangaToUpdate = mutableListOf<MangaUpdate>()
|
||||||
val mergedMangaReferences = mutableListOf<MergedMangaReference>()
|
val mergedMangaReferences = mutableListOf<MergedMangaReference>()
|
||||||
@ -183,15 +185,45 @@ object EXHMigrations {
|
|||||||
insertMergedReference.awaitAll(mergedMangaReferences)
|
insertMergedReference.awaitAll(mergedMangaReferences)
|
||||||
}
|
}
|
||||||
|
|
||||||
val loadedMangaList = mangaConfigs.map { it.second.children }.flatten().mapNotNull { it.load() }.distinct()
|
val loadedMangaList = mangaConfigs
|
||||||
val chapters = runBlocking { handler.awaitList { ehQueries.getChaptersByMangaIds(mergedMangas.map { it.id }, ChapterMapper::mapChapter) } }
|
.map { it.second.children }
|
||||||
val mergedMangaChapters = runBlocking { handler.awaitList { ehQueries.getChaptersByMangaIds(loadedMangaList.map { it.manga.id }, ChapterMapper::mapChapter) } }
|
.flatten()
|
||||||
|
.mapNotNull { it.load() }
|
||||||
|
.distinct()
|
||||||
|
val chapters =
|
||||||
|
runBlocking {
|
||||||
|
handler.awaitList {
|
||||||
|
ehQueries.getChaptersByMangaIds(
|
||||||
|
mergedMangas.map { it.id },
|
||||||
|
ChapterMapper::mapChapter,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val mergedMangaChapters =
|
||||||
|
runBlocking {
|
||||||
|
handler.awaitList {
|
||||||
|
ehQueries.getChaptersByMangaIds(
|
||||||
|
loadedMangaList.map { it.manga.id },
|
||||||
|
ChapterMapper::mapChapter,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val mergedMangaChaptersMatched = mergedMangaChapters.mapNotNull { chapter -> loadedMangaList.firstOrNull { it.manga.id == chapter.id }?.let { it to chapter } }
|
val mergedMangaChaptersMatched = mergedMangaChapters.mapNotNull { chapter ->
|
||||||
val parsedChapters = chapters.filter { it.read || it.lastPageRead != 0L }.mapNotNull { chapter -> readUrlConfig(chapter.url)?.let { chapter to it } }
|
loadedMangaList.firstOrNull {
|
||||||
|
it.manga.id == chapter.id
|
||||||
|
}?.let { it to chapter }
|
||||||
|
}
|
||||||
|
val parsedChapters = chapters.filter {
|
||||||
|
it.read || it.lastPageRead != 0L
|
||||||
|
}.mapNotNull { chapter -> readUrlConfig(chapter.url)?.let { chapter to it } }
|
||||||
val chaptersToUpdate = mutableListOf<ChapterUpdate>()
|
val chaptersToUpdate = mutableListOf<ChapterUpdate>()
|
||||||
parsedChapters.forEach { parsedChapter ->
|
parsedChapters.forEach { parsedChapter ->
|
||||||
mergedMangaChaptersMatched.firstOrNull { it.second.url == parsedChapter.second.url && it.first.source.id == parsedChapter.second.source && it.first.manga.url == parsedChapter.second.mangaUrl }?.let {
|
mergedMangaChaptersMatched.firstOrNull {
|
||||||
|
it.second.url == parsedChapter.second.url &&
|
||||||
|
it.first.source.id == parsedChapter.second.source &&
|
||||||
|
it.first.manga.url == parsedChapter.second.mangaUrl
|
||||||
|
}?.let {
|
||||||
chaptersToUpdate += ChapterUpdate(
|
chaptersToUpdate += ChapterUpdate(
|
||||||
it.second.id,
|
it.second.id,
|
||||||
read = parsedChapter.first.read,
|
read = parsedChapter.first.read,
|
||||||
@ -353,7 +385,11 @@ object EXHMigrations {
|
|||||||
if (oldSecureScreen) {
|
if (oldSecureScreen) {
|
||||||
securityPreferences.secureScreen().set(SecurityPreferences.SecureScreenMode.ALWAYS)
|
securityPreferences.secureScreen().set(SecurityPreferences.SecureScreenMode.ALWAYS)
|
||||||
}
|
}
|
||||||
if (DeviceUtil.isMiui && basePreferences.extensionInstaller().get() == BasePreferences.ExtensionInstaller.PACKAGEINSTALLER) {
|
if (
|
||||||
|
DeviceUtil.isMiui &&
|
||||||
|
basePreferences.extensionInstaller().get() == BasePreferences.ExtensionInstaller
|
||||||
|
.PACKAGEINSTALLER
|
||||||
|
) {
|
||||||
basePreferences.extensionInstaller().set(BasePreferences.ExtensionInstaller.LEGACY)
|
basePreferences.extensionInstaller().set(BasePreferences.ExtensionInstaller.LEGACY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -418,7 +454,9 @@ object EXHMigrations {
|
|||||||
}
|
}
|
||||||
if (oldVersion under 38) {
|
if (oldVersion under 38) {
|
||||||
// Handle renamed enum values
|
// Handle renamed enum values
|
||||||
val newSortingMode = when (val oldSortingMode = prefs.getString(libraryPreferences.sortingMode().key(), "ALPHABETICAL")) {
|
val newSortingMode = when (
|
||||||
|
val oldSortingMode = prefs.getString(libraryPreferences.sortingMode().key(), "ALPHABETICAL")
|
||||||
|
) {
|
||||||
"LAST_CHECKED" -> "LAST_MANGA_UPDATE"
|
"LAST_CHECKED" -> "LAST_MANGA_UPDATE"
|
||||||
"UNREAD" -> "UNREAD_COUNT"
|
"UNREAD" -> "UNREAD_COUNT"
|
||||||
"DATE_FETCHED" -> "CHAPTER_FETCH_DATE"
|
"DATE_FETCHED" -> "CHAPTER_FETCH_DATE"
|
||||||
@ -618,7 +656,7 @@ object EXHMigrations {
|
|||||||
"eh_exhSettingsProfile",
|
"eh_exhSettingsProfile",
|
||||||
"eh_settingsKey",
|
"eh_settingsKey",
|
||||||
"eh_sessionCookie",
|
"eh_sessionCookie",
|
||||||
"eh_hathPerksCookie"
|
"eh_hathPerksCookie",
|
||||||
)
|
)
|
||||||
|
|
||||||
replacePreferences(
|
replacePreferences(
|
||||||
@ -750,7 +788,6 @@ object EXHMigrations {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
private fun replacePreferences(
|
private fun replacePreferences(
|
||||||
preferenceStore: PreferenceStore,
|
preferenceStore: PreferenceStore,
|
||||||
|
@ -113,7 +113,9 @@ object DebugFunctions {
|
|||||||
}
|
}
|
||||||
private val throttleManager = EHentaiThrottleManager()
|
private val throttleManager = EHentaiThrottleManager()
|
||||||
|
|
||||||
fun getDelegatedSourceList(): String = AndroidSourceManager.currentDelegatedSources.map { it.value.sourceName + " : " + it.value.sourceId + " : " + it.value.factory }.joinToString(separator = "\n")
|
fun getDelegatedSourceList(): String = AndroidSourceManager.currentDelegatedSources.map {
|
||||||
|
it.value.sourceName + " : " + it.value.sourceId + " : " + it.value.factory
|
||||||
|
}.joinToString(separator = "\n")
|
||||||
|
|
||||||
fun resetEHGalleriesForUpdater() {
|
fun resetEHGalleriesForUpdater() {
|
||||||
throttleManager.resetThrottle()
|
throttleManager.resetThrottle()
|
||||||
|
@ -51,7 +51,10 @@ class EHentaiUpdateHelper(context: Context) {
|
|||||||
*
|
*
|
||||||
* @return Triple<Accepted, Discarded, HasNew>
|
* @return Triple<Accepted, Discarded, HasNew>
|
||||||
*/
|
*/
|
||||||
suspend fun findAcceptedRootAndDiscardOthers(sourceId: Long, chapters: List<Chapter>): Triple<ChapterChain, List<ChapterChain>, Boolean> {
|
suspend fun findAcceptedRootAndDiscardOthers(
|
||||||
|
sourceId: Long,
|
||||||
|
chapters: List<Chapter>,
|
||||||
|
): Triple<ChapterChain, List<ChapterChain>, Boolean> {
|
||||||
// Find other chains
|
// Find other chains
|
||||||
val chains = chapters
|
val chains = chapters
|
||||||
.flatMap { chapter ->
|
.flatMap { chapter ->
|
||||||
@ -115,7 +118,11 @@ class EHentaiUpdateHelper(context: Context) {
|
|||||||
chapterRepository.updateAll(chapterUpdates)
|
chapterRepository.updateAll(chapterUpdates)
|
||||||
chapterRepository.addAll(newChapters)
|
chapterRepository.addAll(newChapters)
|
||||||
|
|
||||||
val (newHistory, deleteHistory) = getHistory(getChaptersByMangaId.await(accepted.manga.id), chainsAsChapters, chainsAsHistory)
|
val (newHistory, deleteHistory) = getHistory(
|
||||||
|
getChaptersByMangaId.await(accepted.manga.id),
|
||||||
|
chainsAsChapters,
|
||||||
|
chainsAsHistory,
|
||||||
|
)
|
||||||
|
|
||||||
// Delete the duplicate history first
|
// Delete the duplicate history first
|
||||||
deleteHistory.forEach {
|
deleteHistory.forEach {
|
||||||
|
@ -102,7 +102,8 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
|
|
||||||
if (manga.id in seenManga) {
|
if (manga.id in seenManga) {
|
||||||
val inCategories = getCategories.await(manga.id)
|
val inCategories = getCategories.await(manga.id)
|
||||||
status.value = FavoritesSyncStatus.BadLibraryState.MangaInMultipleCategories(manga, inCategories, context)
|
status.value = FavoritesSyncStatus.BadLibraryState
|
||||||
|
.MangaInMultipleCategories(manga, inCategories, context)
|
||||||
|
|
||||||
logger.w(context.getString(R.string.favorites_sync_gallery_multiple_categories_error, manga.id))
|
logger.w(context.getString(R.string.favorites_sync_gallery_multiple_categories_error, manga.id))
|
||||||
return
|
return
|
||||||
@ -143,17 +144,23 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
// Do not update galleries while syncing favorites
|
// Do not update galleries while syncing favorites
|
||||||
EHentaiUpdateWorker.cancelBackground(context)
|
EHentaiUpdateWorker.cancelBackground(context)
|
||||||
|
|
||||||
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_calculating_remote_changes))
|
status.value = FavoritesSyncStatus.Processing(
|
||||||
|
context.getString(R.string.favorites_sync_calculating_remote_changes),
|
||||||
|
)
|
||||||
val remoteChanges = storage.getChangedRemoteEntries(favorites.first)
|
val remoteChanges = storage.getChangedRemoteEntries(favorites.first)
|
||||||
val localChanges = if (prefs.exhReadOnlySync().get()) {
|
val localChanges = if (prefs.exhReadOnlySync().get()) {
|
||||||
null // Do not build local changes if they are not going to be applied
|
null // Do not build local changes if they are not going to be applied
|
||||||
} else {
|
} else {
|
||||||
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_calculating_local_changes))
|
status.value = FavoritesSyncStatus.Processing(
|
||||||
|
context.getString(R.string.favorites_sync_calculating_local_changes),
|
||||||
|
)
|
||||||
storage.getChangedDbEntries()
|
storage.getChangedDbEntries()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply remote categories
|
// Apply remote categories
|
||||||
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_syncing_category_names))
|
status.value = FavoritesSyncStatus.Processing(
|
||||||
|
context.getString(R.string.favorites_sync_syncing_category_names),
|
||||||
|
)
|
||||||
applyRemoteCategories(favorites.second)
|
applyRemoteCategories(favorites.second)
|
||||||
|
|
||||||
// Apply change sets
|
// Apply change sets
|
||||||
@ -173,7 +180,9 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
logger.w(context.getString(R.string.favorites_sync_ignoring_exception), e)
|
logger.w(context.getString(R.string.favorites_sync_ignoring_exception), e)
|
||||||
return
|
return
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
status.value = FavoritesSyncStatus.Error(context.getString(R.string.favorites_sync_unknown_error, e.message))
|
status.value = FavoritesSyncStatus.Error(
|
||||||
|
context.getString(R.string.favorites_sync_unknown_error, e.message),
|
||||||
|
)
|
||||||
logger.e(context.getString(R.string.favorites_sync_sync_error), e)
|
logger.e(context.getString(R.string.favorites_sync_sync_error), e)
|
||||||
return
|
return
|
||||||
} finally {
|
} finally {
|
||||||
@ -273,7 +282,9 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
private suspend fun applyChangeSetToRemote(errorList: MutableList<String>, changeSet: ChangeSet) {
|
private suspend fun applyChangeSetToRemote(errorList: MutableList<String>, changeSet: ChangeSet) {
|
||||||
// Apply removals
|
// Apply removals
|
||||||
if (changeSet.removed.isNotEmpty()) {
|
if (changeSet.removed.isNotEmpty()) {
|
||||||
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_removing_galleries, changeSet.removed.size))
|
status.value = FavoritesSyncStatus.Processing(
|
||||||
|
context.getString(R.string.favorites_sync_removing_galleries, changeSet.removed.size),
|
||||||
|
)
|
||||||
|
|
||||||
val formBody = FormBody.Builder()
|
val formBody = FormBody.Builder()
|
||||||
.add("ddact", "delete")
|
.add("ddact", "delete")
|
||||||
@ -322,7 +333,10 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
|
|
||||||
// Apply removals
|
// Apply removals
|
||||||
changeSet.removed.forEachIndexed { index, it ->
|
changeSet.removed.forEachIndexed { index, it ->
|
||||||
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_remove_from_local, index + 1, changeSet.removed.size), title = it.title)
|
status.value = FavoritesSyncStatus.Processing(
|
||||||
|
context.getString(R.string.favorites_sync_remove_from_local, index + 1, changeSet.removed.size),
|
||||||
|
title = it.title,
|
||||||
|
)
|
||||||
val url = it.getUrl()
|
val url = it.getUrl()
|
||||||
|
|
||||||
// Consider both EX and EH sources
|
// Consider both EX and EH sources
|
||||||
@ -377,10 +391,17 @@ class FavoritesSyncHelper(val context: Context) {
|
|||||||
return@forEachIndexed
|
return@forEachIndexed
|
||||||
}
|
}
|
||||||
|
|
||||||
val errorString = context.getString(R.string.favorites_sync_failed_to_add_to_local) + when (result) {
|
val errorString = context.getString(R.string.favorites_sync_failed_to_add_to_local) +
|
||||||
is GalleryAddEvent.Fail.Error -> context.getString(R.string.favorites_sync_failed_to_add_to_local_error, it.title, result.logMessage)
|
when (result) {
|
||||||
is GalleryAddEvent.Fail.UnknownType -> context.getString(R.string.favorites_sync_failed_to_add_to_local_unknown_type, it.title, result.galleryUrl)
|
is GalleryAddEvent.Fail.Error -> context.getString(
|
||||||
is GalleryAddEvent.Fail.UnknownSource -> context.getString(R.string.favorites_sync_failed_to_add_to_local_unknown_type, it.title, result.galleryUrl)
|
R.string.favorites_sync_failed_to_add_to_local_error, it.title, result.logMessage,
|
||||||
|
)
|
||||||
|
is GalleryAddEvent.Fail.UnknownType -> context.getString(
|
||||||
|
R.string.favorites_sync_failed_to_add_to_local_unknown_type, it.title, result.galleryUrl,
|
||||||
|
)
|
||||||
|
is GalleryAddEvent.Fail.UnknownSource -> context.getString(
|
||||||
|
R.string.favorites_sync_failed_to_add_to_local_unknown_type, it.title, result.galleryUrl,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prefs.exhLenientSync().get()) {
|
if (prefs.exhLenientSync().get()) {
|
||||||
@ -427,7 +448,12 @@ sealed class FavoritesSyncStatus() {
|
|||||||
this(
|
this(
|
||||||
manga = manga,
|
manga = manga,
|
||||||
categories = categories,
|
categories = categories,
|
||||||
message = context.getString(R.string.favorites_sync_gallery_in_multiple_categories, manga.title, categories.joinToString { it.name }),
|
message = context.getString(
|
||||||
|
R.string.favorites_sync_gallery_in_multiple_categories, manga.title,
|
||||||
|
categories.joinToString {
|
||||||
|
it.name
|
||||||
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ import okhttp3.Response
|
|||||||
import rx.Observable
|
import rx.Observable
|
||||||
import tachiyomi.core.util.lang.runAsObservable
|
import tachiyomi.core.util.lang.runAsObservable
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import java.util.concurrent.TimeUnit
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
class BilibiliHandler(currentClient: OkHttpClient) {
|
class BilibiliHandler(currentClient: OkHttpClient) {
|
||||||
val baseUrl = "https://www.bilibilicomics.com"
|
val baseUrl = "https://www.bilibilicomics.com"
|
||||||
@ -34,7 +34,7 @@ class BilibiliHandler(currentClient: OkHttpClient) {
|
|||||||
.build()
|
.build()
|
||||||
|
|
||||||
val client: OkHttpClient = currentClient.newBuilder()
|
val client: OkHttpClient = currentClient.newBuilder()
|
||||||
.rateLimit(1, 1, TimeUnit.SECONDS)
|
.rateLimit(1, 1.seconds)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val json by injectLazy<Json>()
|
val json by injectLazy<Json>()
|
||||||
|
@ -28,7 +28,10 @@ class MangaHandler(
|
|||||||
val mangaId = MdUtil.getMangaId(manga.url)
|
val mangaId = MdUtil.getMangaId(manga.url)
|
||||||
val response = async(Dispatchers.IO) { service.viewManga(mangaId) }
|
val response = async(Dispatchers.IO) { service.viewManga(mangaId) }
|
||||||
val simpleChapters = async(Dispatchers.IO) { getSimpleChapters(manga) }
|
val simpleChapters = async(Dispatchers.IO) { getSimpleChapters(manga) }
|
||||||
val statistics = async(Dispatchers.IO) { kotlin.runCatching { service.mangasRating(mangaId) }.getOrNull()?.statistics?.get(mangaId) }
|
val statistics =
|
||||||
|
async(Dispatchers.IO) {
|
||||||
|
kotlin.runCatching { service.mangasRating(mangaId) }.getOrNull()?.statistics?.get(mangaId)
|
||||||
|
}
|
||||||
apiMangaParser.parseToManga(
|
apiMangaParser.parseToManga(
|
||||||
manga,
|
manga,
|
||||||
sourceId,
|
sourceId,
|
||||||
@ -45,7 +48,11 @@ class MangaHandler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fetchChapterListObservable(manga: SManga, blockedGroups: String, blockedUploaders: String): Observable<List<SChapter>> = runAsObservable {
|
fun fetchChapterListObservable(
|
||||||
|
manga: SManga,
|
||||||
|
blockedGroups: String,
|
||||||
|
blockedUploaders: String,
|
||||||
|
): Observable<List<SChapter>> = runAsObservable {
|
||||||
getChapterList(manga, blockedGroups, blockedUploaders)
|
getChapterList(manga, blockedGroups, blockedUploaders)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,13 @@ class MangaHotHandler(currentClient: OkHttpClient, userAgent: String) {
|
|||||||
val client: OkHttpClient = currentClient
|
val client: OkHttpClient = currentClient
|
||||||
|
|
||||||
suspend fun fetchPageList(externalUrl: String): List<Page> {
|
suspend fun fetchPageList(externalUrl: String): List<Page> {
|
||||||
val request = GET(externalUrl.substringBefore("?").replace(baseUrl, apiUrl).replace("viewer", "v1/works/storyDetail"), headers)
|
val request =
|
||||||
|
GET(
|
||||||
|
externalUrl.substringBefore("?")
|
||||||
|
.replace(baseUrl, apiUrl)
|
||||||
|
.replace("viewer", "v1/works/storyDetail"),
|
||||||
|
headers,
|
||||||
|
)
|
||||||
return pageListParse(client.newCall(request).awaitSuccess())
|
return pageListParse(client.newCall(request).awaitSuccess())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,12 @@ class SimilarHandler(
|
|||||||
MdUtil.createMangaEntry(it, lang)
|
MdUtil.createMangaEntry(it, lang)
|
||||||
}
|
}
|
||||||
|
|
||||||
return MetadataMangasPage(mangaList, false, List(mangaList.size) { MangaDexSearchMetadata().also { it.relation = MangaDexRelation.SIMILAR } })
|
return MetadataMangasPage(
|
||||||
|
mangaList, false,
|
||||||
|
List(mangaList.size) {
|
||||||
|
MangaDexSearchMetadata().also { it.relation = MangaDexRelation.SIMILAR }
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getRelated(manga: SManga): MetadataMangasPage {
|
suspend fun getRelated(manga: SManga): MetadataMangasPage {
|
||||||
@ -57,7 +62,8 @@ class SimilarHandler(
|
|||||||
hasNextPage = false,
|
hasNextPage = false,
|
||||||
mangasMetadata = mangaList.map { manga ->
|
mangasMetadata = mangaList.map { manga ->
|
||||||
MangaDexSearchMetadata().also {
|
MangaDexSearchMetadata().also {
|
||||||
it.relation = relatedListDto.data.firstOrNull { it.relationships.any { it.id == MdUtil.getMangaId(manga.url) } }
|
it.relation = relatedListDto.data
|
||||||
|
.firstOrNull { it.relationships.any { it.id == MdUtil.getMangaId(manga.url) } }
|
||||||
?.attributes?.relation?.let(MangaDexRelation::fromDex)
|
?.attributes?.relation?.let(MangaDexRelation::fromDex)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -15,7 +15,9 @@ enum class FollowStatus(val int: Int) {
|
|||||||
fun toDex(): String = this.name.lowercase(Locale.US)
|
fun toDex(): String = this.name.lowercase(Locale.US)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromDex(value: String?): FollowStatus = values().firstOrNull { it.name.lowercase(Locale.US) == value } ?: UNFOLLOWED
|
fun fromDex(
|
||||||
|
value: String?,
|
||||||
|
): FollowStatus = values().firstOrNull { it.name.lowercase(Locale.US) == value } ?: UNFOLLOWED
|
||||||
fun fromInt(value: Int): FollowStatus = values().firstOrNull { it.int == value } ?: UNFOLLOWED
|
fun fromInt(value: Int): FollowStatus = values().firstOrNull { it.int == value } ?: UNFOLLOWED
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,11 @@ class EhUConfigBuilder {
|
|||||||
|
|
||||||
configItems += Entry.LanguageSystem().getLanguages(preferences.exhSettingsLanguages().get().split("\n"))
|
configItems += Entry.LanguageSystem().getLanguages(preferences.exhSettingsLanguages().get().split("\n"))
|
||||||
|
|
||||||
configItems += Entry.Categories().categoryConfigs(preferences.exhEnabledCategories().get().split(",").map { it.toBoolean() })
|
configItems += Entry.Categories().categoryConfigs(
|
||||||
|
preferences.exhEnabledCategories().get().split(",").map {
|
||||||
|
it.toBoolean()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
// Actually build form body
|
// Actually build form body
|
||||||
val formBody = FormBody.Builder()
|
val formBody = FormBody.Builder()
|
||||||
|
@ -18,7 +18,10 @@ import exh.util.SourceTagsUtil
|
|||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
object MetadataUIUtil {
|
object MetadataUIUtil {
|
||||||
fun getRatingString(context: Context, @FloatRange(from = 0.0, to = 10.0) rating: Float? = null) = when (rating?.roundToInt()) {
|
fun getRatingString(
|
||||||
|
context: Context,
|
||||||
|
@FloatRange(from = 0.0, to = 10.0) rating: Float? = null,
|
||||||
|
) = when (rating?.roundToInt()) {
|
||||||
0 -> R.string.rating0
|
0 -> R.string.rating0
|
||||||
1 -> R.string.rating1
|
1 -> R.string.rating1
|
||||||
2 -> R.string.rating2
|
2 -> R.string.rating2
|
||||||
@ -36,7 +39,8 @@ object MetadataUIUtil {
|
|||||||
fun getGenreAndColour(context: Context, genre: String) = when (genre) {
|
fun getGenreAndColour(context: Context, genre: String) = when (genre) {
|
||||||
"doujinshi", "Doujinshi" -> SourceTagsUtil.GenreColor.DOUJINSHI_COLOR to R.string.doujinshi
|
"doujinshi", "Doujinshi" -> SourceTagsUtil.GenreColor.DOUJINSHI_COLOR to R.string.doujinshi
|
||||||
"manga", "Japanese Manga", "Manga" -> SourceTagsUtil.GenreColor.MANGA_COLOR to R.string.entry_type_manga
|
"manga", "Japanese Manga", "Manga" -> SourceTagsUtil.GenreColor.MANGA_COLOR to R.string.entry_type_manga
|
||||||
"artistcg", "artist CG", "artist-cg", "Artist CG" -> SourceTagsUtil.GenreColor.ARTIST_CG_COLOR to R.string.artist_cg
|
"artistcg", "artist CG", "artist-cg", "Artist CG" ->
|
||||||
|
SourceTagsUtil.GenreColor.ARTIST_CG_COLOR to R.string.artist_cg
|
||||||
"gamecg", "game CG", "game-cg", "Game CG" -> SourceTagsUtil.GenreColor.GAME_CG_COLOR to R.string.game_cg
|
"gamecg", "game CG", "game-cg", "Game CG" -> SourceTagsUtil.GenreColor.GAME_CG_COLOR to R.string.game_cg
|
||||||
"western" -> SourceTagsUtil.GenreColor.WESTERN_COLOR to R.string.western
|
"western" -> SourceTagsUtil.GenreColor.WESTERN_COLOR to R.string.western
|
||||||
"non-h", "non-H" -> SourceTagsUtil.GenreColor.NON_H_COLOR to R.string.non_h
|
"non-h", "non-H" -> SourceTagsUtil.GenreColor.NON_H_COLOR to R.string.non_h
|
||||||
|
@ -29,7 +29,9 @@ fun NHentaiDescription(state: State.Success, openMetadataViewer: () -> Unit) {
|
|||||||
if (meta == null || meta !is NHentaiSearchMetadata) return@AndroidView
|
if (meta == null || meta !is NHentaiSearchMetadata) return@AndroidView
|
||||||
val binding = DescriptionAdapterNhBinding.bind(it)
|
val binding = DescriptionAdapterNhBinding.bind(it)
|
||||||
|
|
||||||
binding.genre.text = meta.tags.filter { it.namespace == NHentaiSearchMetadata.NHENTAI_CATEGORIES_NAMESPACE }.let { tags ->
|
binding.genre.text = meta.tags.filter {
|
||||||
|
it.namespace == NHentaiSearchMetadata.NHENTAI_CATEGORIES_NAMESPACE
|
||||||
|
}.let { tags ->
|
||||||
if (tags.isNotEmpty()) tags.joinToString(transform = { it.name }) else null
|
if (tags.isNotEmpty()) tags.joinToString(transform = { it.name }) else null
|
||||||
}.let { categoriesString ->
|
}.let { categoriesString ->
|
||||||
categoriesString?.let { MetadataUIUtil.getGenreAndColour(context, it) }?.let {
|
categoriesString?.let { MetadataUIUtil.getGenreAndColour(context, it) }?.let {
|
||||||
@ -46,7 +48,11 @@ fun NHentaiDescription(state: State.Success, openMetadataViewer: () -> Unit) {
|
|||||||
|
|
||||||
binding.whenPosted.text = MetadataUtil.EX_DATE_FORMAT.format(Date((meta.uploadDate ?: 0) * 1000))
|
binding.whenPosted.text = MetadataUtil.EX_DATE_FORMAT.format(Date((meta.uploadDate ?: 0) * 1000))
|
||||||
|
|
||||||
binding.pages.text = context.resources.getQuantityString(R.plurals.num_pages, meta.pageImageTypes.size, meta.pageImageTypes.size)
|
binding.pages.text = context.resources.getQuantityString(
|
||||||
|
R.plurals.num_pages,
|
||||||
|
meta.pageImageTypes.size,
|
||||||
|
meta.pageImageTypes.size,
|
||||||
|
)
|
||||||
binding.pages.bindDrawable(context, R.drawable.ic_baseline_menu_book_24)
|
binding.pages.bindDrawable(context, R.drawable.ic_baseline_menu_book_24)
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
|
@ -16,7 +16,11 @@ private val galleryAdder by lazy {
|
|||||||
/**
|
/**
|
||||||
* A version of fetchSearchManga that supports URL importing
|
* A version of fetchSearchManga that supports URL importing
|
||||||
*/
|
*/
|
||||||
fun UrlImportableSource.urlImportFetchSearchManga(context: Context, query: String, fail: () -> Observable<MangasPage>): Observable<MangasPage> =
|
fun UrlImportableSource.urlImportFetchSearchManga(
|
||||||
|
context: Context,
|
||||||
|
query: String,
|
||||||
|
fail: () -> Observable<MangasPage>,
|
||||||
|
): Observable<MangasPage> =
|
||||||
when {
|
when {
|
||||||
query.startsWith("http://") || query.startsWith("https://") -> {
|
query.startsWith("http://") || query.startsWith("https://") -> {
|
||||||
runAsObservable {
|
runAsObservable {
|
||||||
@ -36,18 +40,21 @@ fun UrlImportableSource.urlImportFetchSearchManga(context: Context, query: Strin
|
|||||||
else -> fail()
|
else -> fail()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A version of fetchSearchManga that supports URL importing
|
* A version of fetchSearchManga that supports URL importing
|
||||||
*/
|
*/
|
||||||
suspend fun UrlImportableSource.urlImportFetchSearchMangaSuspend(context: Context, query: String, fail: suspend () -> MangasPage): MangasPage =
|
suspend fun UrlImportableSource.urlImportFetchSearchMangaSuspend(
|
||||||
|
context: Context,
|
||||||
|
query: String,
|
||||||
|
fail: suspend () -> MangasPage,
|
||||||
|
): MangasPage =
|
||||||
when {
|
when {
|
||||||
query.startsWith("http://") || query.startsWith("https://") -> {
|
query.startsWith("http://") || query.startsWith("https://") -> {
|
||||||
val res = galleryAdder.addGallery(
|
val res = galleryAdder.addGallery(
|
||||||
context = context,
|
context = context,
|
||||||
url = query,
|
url = query,
|
||||||
fav = false,
|
fav = false,
|
||||||
forceSource = this
|
forceSource = this,
|
||||||
)
|
)
|
||||||
|
|
||||||
MangasPage(
|
MangasPage(
|
||||||
|
@ -26,7 +26,10 @@ class SecurityPreferences(
|
|||||||
|
|
||||||
fun sqlPassword() = this.preferenceStore.getString(Preference.privateKey("sql_password"), "")
|
fun sqlPassword() = this.preferenceStore.getString(Preference.privateKey("sql_password"), "")
|
||||||
|
|
||||||
fun passwordProtectDownloads() = preferenceStore.getBoolean(Preference.privateKey("password_protect_downloads"), false)
|
fun passwordProtectDownloads() = preferenceStore.getBoolean(
|
||||||
|
Preference.privateKey("password_protect_downloads"),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
|
||||||
fun encryptionType() = this.preferenceStore.getEnum("encryption_type", EncryptionType.AES_256)
|
fun encryptionType() = this.preferenceStore.getEnum("encryption_type", EncryptionType.AES_256)
|
||||||
|
|
||||||
|
@ -89,7 +89,8 @@ abstract class WebViewInterceptor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Based on [IsRequestHeaderSafe] in https://source.chromium.org/chromium/chromium/src/+/main:services/network/public/cpp/header_util.cc
|
// Based on [IsRequestHeaderSafe] in
|
||||||
|
// https://source.chromium.org/chromium/chromium/src/+/main:services/network/public/cpp/header_util.cc
|
||||||
private fun isRequestHeaderSafe(_name: String, _value: String): Boolean {
|
private fun isRequestHeaderSafe(_name: String, _value: String): Boolean {
|
||||||
val name = _name.lowercase(Locale.ENGLISH)
|
val name = _name.lowercase(Locale.ENGLISH)
|
||||||
val value = _value.lowercase(Locale.ENGLISH)
|
val value = _value.lowercase(Locale.ENGLISH)
|
||||||
@ -97,4 +98,6 @@ private fun isRequestHeaderSafe(_name: String, _value: String): Boolean {
|
|||||||
if (name == "connection" && value == "upgrade") return false
|
if (name == "connection" && value == "upgrade") return false
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
private val unsafeHeaderNames = listOf("content-length", "host", "trailer", "te", "upgrade", "cookie2", "keep-alive", "transfer-encoding", "set-cookie")
|
private val unsafeHeaderNames = listOf(
|
||||||
|
"content-length", "host", "trailer", "te", "upgrade", "cookie2", "keep-alive", "transfer-encoding", "set-cookie",
|
||||||
|
)
|
||||||
|
@ -159,7 +159,9 @@ object CbzCrypto {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logcat(LogPriority.WARN) { "Wrong CBZ archive password for: ${zip4j.file.name} in: ${zip4j.file.parentFile?.name}" }
|
logcat(LogPriority.WARN) {
|
||||||
|
"Wrong CBZ archive password for: ${zip4j.file.name} in: ${zip4j.file.parentFile?.name}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,10 @@ fun Any.xLogE(log: Any?) = xLog().let { if (log == null) it.e("null") else it.e(
|
|||||||
fun Any.xLogW(log: Any?) = xLog().let { if (log == null) it.w("null") else it.w(log) }
|
fun Any.xLogW(log: Any?) = xLog().let { if (log == null) it.w("null") else it.w(log) }
|
||||||
fun Any.xLogD(log: Any?) = xLog().let { if (log == null) it.d("null") else it.d(log) }
|
fun Any.xLogD(log: Any?) = xLog().let { if (log == null) it.d("null") else it.d(log) }
|
||||||
fun Any.xLogI(log: Any?) = xLog().let { if (log == null) it.i("null") else it.i(log) }
|
fun Any.xLogI(log: Any?) = xLog().let { if (log == null) it.i("null") else it.i(log) }
|
||||||
fun Any.xLog(logLevel: LogLevel, log: Any?) = xLog().let { if (log == null) it.log(logLevel.int, "null") else it.log(logLevel.int, log) }
|
fun Any.xLog(
|
||||||
|
logLevel: LogLevel,
|
||||||
|
log: Any?,
|
||||||
|
) = xLog().let { if (log == null) it.log(logLevel.int, "null") else it.log(logLevel.int, log) }
|
||||||
|
|
||||||
/*fun Any.xLogE(vararg logs: Any) = xLog().e(logs)
|
/*fun Any.xLogE(vararg logs: Any) = xLog().e(logs)
|
||||||
fun Any.xLogW(vararg logs: Any) = xLog().w(logs)
|
fun Any.xLogW(vararg logs: Any) = xLog().w(logs)
|
||||||
|
@ -364,7 +364,9 @@ object ImageUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun splitImageName(filenamePrefix: String, index: Int) = "${filenamePrefix}__${"%03d".format(index + 1)}.jpg"
|
private fun splitImageName(filenamePrefix: String, index: Int) = "${filenamePrefix}__${"%03d".format(
|
||||||
|
index + 1,
|
||||||
|
)}.jpg"
|
||||||
|
|
||||||
private val BitmapFactory.Options.splitData
|
private val BitmapFactory.Options.splitData
|
||||||
get(): List<SplitData> {
|
get(): List<SplitData> {
|
||||||
@ -449,10 +451,12 @@ object ImageUtil {
|
|||||||
val botLeftIsDark = botLeftPixel.isDark()
|
val botLeftIsDark = botLeftPixel.isDark()
|
||||||
val botRightIsDark = botRightPixel.isDark()
|
val botRightIsDark = botRightPixel.isDark()
|
||||||
|
|
||||||
var darkBG = (topLeftIsDark && (botLeftIsDark || botRightIsDark || topRightIsDark || midLeftIsDark || topMidIsDark)) ||
|
var darkBG =
|
||||||
|
(topLeftIsDark && (botLeftIsDark || botRightIsDark || topRightIsDark || midLeftIsDark || topMidIsDark)) ||
|
||||||
(topRightIsDark && (botRightIsDark || botLeftIsDark || midRightIsDark || topMidIsDark))
|
(topRightIsDark && (botRightIsDark || botLeftIsDark || midRightIsDark || topMidIsDark))
|
||||||
|
|
||||||
val topAndBotPixels = listOf(topLeftPixel, topCenterPixel, topRightPixel, botRightPixel, bottomCenterPixel, botLeftPixel)
|
val topAndBotPixels =
|
||||||
|
listOf(topLeftPixel, topCenterPixel, topRightPixel, botRightPixel, bottomCenterPixel, botLeftPixel)
|
||||||
val isNotWhiteAndCloseTo = topAndBotPixels.mapIndexed { index, color ->
|
val isNotWhiteAndCloseTo = topAndBotPixels.mapIndexed { index, color ->
|
||||||
val other = topAndBotPixels[(index + 1) % topAndBotPixels.size]
|
val other = topAndBotPixels[(index + 1) % topAndBotPixels.size]
|
||||||
!color.isWhite() && color.isCloseTo(other)
|
!color.isWhite() && color.isCloseTo(other)
|
||||||
@ -597,10 +601,16 @@ object ImageUtil {
|
|||||||
darkBG -> {
|
darkBG -> {
|
||||||
return ColorDrawable(blackColor)
|
return ColorDrawable(blackColor)
|
||||||
}
|
}
|
||||||
topIsBlackStreak || (topCornersIsDark && topOffsetCornersIsDark && (topMidIsDark || overallBlackPixels > 9)) -> {
|
topIsBlackStreak || (
|
||||||
|
topCornersIsDark && topOffsetCornersIsDark &&
|
||||||
|
(topMidIsDark || overallBlackPixels > 9)
|
||||||
|
) -> {
|
||||||
intArrayOf(blackColor, blackColor, whiteColor, whiteColor)
|
intArrayOf(blackColor, blackColor, whiteColor, whiteColor)
|
||||||
}
|
}
|
||||||
bottomIsBlackStreak || (botCornersIsDark && botOffsetCornersIsDark && (bottomCenterPixel.isDark() || overallBlackPixels > 9)) -> {
|
bottomIsBlackStreak || (
|
||||||
|
botCornersIsDark && botOffsetCornersIsDark &&
|
||||||
|
(bottomCenterPixel.isDark() || overallBlackPixels > 9)
|
||||||
|
) -> {
|
||||||
intArrayOf(whiteColor, whiteColor, blackColor, blackColor)
|
intArrayOf(whiteColor, whiteColor, blackColor, blackColor)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user