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:
arkon 2023-11-04 23:28:41 -04:00 committed by Jobobby04
parent 0a9f15d74c
commit d9a6a7be50
123 changed files with 1366 additions and 382 deletions

View File

@ -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

View File

@ -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)

View File

@ -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) }

View File

@ -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 }

View File

@ -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)

View File

@ -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,
) )

View File

@ -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)
} }
} }

View File

@ -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),
)
} }
} }
} }

View File

@ -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(

View File

@ -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,

View File

@ -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))

View File

@ -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? {

View File

@ -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)

View File

@ -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())),
) )

View File

@ -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",
)
}, },
) )
} }

View File

@ -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)

View File

@ -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()

View File

@ -181,12 +181,13 @@ object SettingsEhScreen : SearchableSettings {
unsortedPreferences: UnsortedPreferences, unsortedPreferences: UnsortedPreferences,
openWarnConfigureDialogController: () -> Unit, openWarnConfigureDialogController: () -> Unit,
): Preference.PreferenceItem.SwitchPreference { ): Preference.PreferenceItem.SwitchPreference {
val activityResultContract = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { val activityResultContract =
if (it.resultCode == Activity.RESULT_OK) { rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
// Upload settings if (it.resultCode == Activity.RESULT_OK) {
openWarnConfigureDialogController() // Upload settings
openWarnConfigureDialogController()
}
} }
}
val context = LocalContext.current val context = LocalContext.current
val value by unsortedPreferences.enableExhentai().collectAsState() val value by unsortedPreferences.enableExhentai().collectAsState()
return Preference.PreferenceItem.SwitchPreference( return Preference.PreferenceItem.SwitchPreference(
@ -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)
} }

View File

@ -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()

View File

@ -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(), "")

View File

@ -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),
)
}
} }
} }
}, },

View File

@ -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)
} }
} }

View File

@ -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,
),
) )
} }
} }

View File

@ -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 = {},
) )
} }
} }

View File

@ -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,
) )
} }
} }

View File

@ -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,

View File

@ -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",
)
}, },
) )
} }

View File

@ -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
} }
} }

View File

@ -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"

View File

@ -104,11 +104,12 @@ 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 --> */ +
handler.awaitList { mangasQueries.getReadMangaNotInLibrary(MangaMapper::mapManga) } if (flags and BACKUP_READ_MANGA_MASK == BACKUP_READ_MANGA) {
} else { handler.awaitList { mangasQueries.getReadMangaNotInLibrary(MangaMapper::mapManga) }
emptyList() } else {
} + getMergedManga.await() // SY <-- emptyList()
} + getMergedManga.await() // SY <--
val backup = Backup( val backup = Backup(
backupMangas(databaseManga, flags), backupMangas(databaseManga, flags),
backupCategories(flags), backupCategories(flags),
@ -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<*, *>>()

View File

@ -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()) {

View File

@ -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(

View File

@ -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())
} }
@ -67,7 +69,7 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti
} }
// SY --> // SY -->
source.peek().inputStream().use { stream -> source.peek().inputStream().use { stream ->
if (CbzCrypto.detectCoverImageArchive(stream)) return true if (CbzCrypto.detectCoverImageArchive(stream)) return true
} }
// SY <-- // SY <--
return when (type) { return when (type) {

View File

@ -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

View File

@ -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) {

View File

@ -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)
} }

View File

@ -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 {

View File

@ -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)
} }

View File

@ -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 {

View File

@ -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,
)
} }
/** /**

View File

@ -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,
)
} }
} }
} }

View File

@ -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 }

View File

@ -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)
} }

View File

@ -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()))

View File

@ -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)) {

View File

@ -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 -> {}
} }

View File

@ -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)
} }

View File

@ -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,
)
} }
} }

View File

@ -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()
} }
/** /**

View File

@ -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,17 +186,23 @@ class AndroidSourceManager(
} }
// SY --> // SY -->
override fun getVisibleOnlineSources() = sourcesMapFlow.value.values.filterIsInstance<HttpSource>().filter { override fun getVisibleOnlineSources() = sourcesMapFlow.value.values
it.id !in BlacklistedSources.HIDDEN_SOURCES .filterIsInstance<HttpSource>()
} .filter {
it.id !in BlacklistedSources.HIDDEN_SOURCES
}
override fun getVisibleCatalogueSources() = sourcesMapFlow.value.values.filterIsInstance<CatalogueSource>().filter { override fun getVisibleCatalogueSources() = sourcesMapFlow.value.values
it.id !in BlacklistedSources.HIDDEN_SOURCES .filterIsInstance<CatalogueSource>()
} .filter {
it.id !in BlacklistedSources.HIDDEN_SOURCES
}
fun getDelegatedCatalogueSources() = sourcesMapFlow.value.values.filterIsInstance<EnhancedHttpSource>().mapNotNull { enhancedHttpSource -> fun getDelegatedCatalogueSources() = sourcesMapFlow.value.values
enhancedHttpSource.enhancedSource as? DelegatedHttpSource .filterIsInstance<EnhancedHttpSource>()
} .mapNotNull { enhancedHttpSource ->
enhancedHttpSource.enhancedSource as? DelegatedHttpSource
}
// SY <-- // SY <--
private fun registerStubSource(source: StubSource) { private fun registerStubSource(source: StubSource) {
@ -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,

View File

@ -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()
} }
/** /**

View File

@ -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 {

View File

@ -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,9 +195,10 @@ 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) =
"https://t3.nhentai.net/galleries/$mediaId/${page}t.$it" NHentaiSearchMetadata.typeToExtension(t)?.let {
} "https://t3.nhentai.net/galleries/$mediaId/${page}t.$it"
}
override suspend fun fetchPreviewImage(page: PagePreviewInfo, cacheControl: CacheControl?): Response { override suspend fun fetchPreviewImage(page: PagePreviewInfo, cacheControl: CacheControl?): Response {
return client.newCachelessCallWithProgress( return client.newCachelessCallWithProgress(

View File

@ -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)
} }
} }

View File

@ -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)
} }
} }

View File

@ -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
},
) )
} }
} }

View File

@ -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)
} }
} }

View File

@ -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) }

View File

@ -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(),
), ),
) )
} }

View File

@ -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
},
) )
} }
} }

View File

@ -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()

View File

@ -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,
) )
} }

View File

@ -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))
} }

View File

@ -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) {

View File

@ -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,

View File

@ -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.

View File

@ -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)
} }

View File

@ -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) {

View File

@ -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)

View File

@ -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
} }
} }

View File

@ -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) {

View File

@ -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 {

View File

@ -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)

View File

@ -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),

View File

@ -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
} }

View File

@ -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(),
)
} }
/** /**

View File

@ -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

View File

@ -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()) {

View File

@ -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.",
)
} }
} }

View File

@ -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)
} }

View File

@ -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) {

View File

@ -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)
} }

View File

@ -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,

View File

@ -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()

View File

@ -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 {

View File

@ -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,11 +391,18 @@ 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()) {
errorList += errorString errorList += errorString
@ -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
},
),
) )
} }
} }

View File

@ -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>()

View File

@ -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)
} }

View File

@ -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())
} }

View File

@ -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)
} }
}, },

View File

@ -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
} }
} }

View File

@ -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()

View File

@ -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

View File

@ -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")

View File

@ -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(

View File

@ -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)

View File

@ -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",
)

View File

@ -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
} }

View File

@ -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)

View File

@ -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 =
(topRightIsDark && (botRightIsDark || botLeftIsDark || midRightIsDark || topMidIsDark)) (topLeftIsDark && (botLeftIsDark || botRightIsDark || topRightIsDark || midLeftIsDark || 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