Restrict line length with ktlint
(cherry picked from commit 1d144e67678a99ec7198e5efcb1410b5da4bc42e) # Conflicts: # .editorconfig # app/src/main/java/eu/kanade/domain/chapter/model/ChapterFilter.kt # app/src/main/java/eu/kanade/presentation/reader/appbars/ReaderAppBars.kt # app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt # source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt
This commit is contained in:
parent
0a9f15d74c
commit
d9a6a7be50
@ -1,15 +1,16 @@
|
||||
[*.{kt,kts}]
|
||||
indent_size=4
|
||||
insert_final_newline=true
|
||||
ij_kotlin_allow_trailing_comma=true
|
||||
ij_kotlin_allow_trailing_comma_on_call_site=true
|
||||
ij_kotlin_name_count_to_use_star_import=2147483647
|
||||
ij_kotlin_name_count_to_use_star_import_for_members=2147483647
|
||||
max_line_length = 120
|
||||
indent_size = 4
|
||||
insert_final_newline = true
|
||||
ij_kotlin_allow_trailing_comma = true
|
||||
ij_kotlin_allow_trailing_comma_on_call_site = true
|
||||
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_argument-list-wrapping=disabled
|
||||
ktlint_standard_function-naming=disabled
|
||||
ktlint_standard_property-naming=disabled
|
||||
ktlint_standard_multiline-expression-wrapping=disabled
|
||||
ktlint_standard_string-template-indent=disabled
|
||||
ktlint_standard_comment-wrapping=disabled
|
||||
ktlint_standard_filename = disabled
|
||||
ktlint_standard_argument-list-wrapping = disabled
|
||||
ktlint_standard_function-naming = disabled
|
||||
ktlint_standard_property-naming = disabled
|
||||
ktlint_standard_multiline-expression-wrapping = disabled
|
||||
ktlint_standard_string-template-indent = disabled
|
||||
ktlint_standard_comment-wrapping = disabled
|
||||
|
@ -100,7 +100,11 @@ class SyncChaptersWithSource(
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
val dbChapter = dbChapters.find { it.url == chapter.url }
|
||||
@ -117,7 +121,12 @@ class SyncChaptersWithSource(
|
||||
} else {
|
||||
if (shouldUpdateDbChapter.await(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) {
|
||||
downloadManager.renameChapter(source, manga, dbChapter, chapter)
|
||||
|
@ -14,7 +14,11 @@ import tachiyomi.source.local.isLocal
|
||||
* Applies the view filters to the list of chapters obtained from the database.
|
||||
* @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 unreadFilter = manga.unreadFilter
|
||||
val downloadedFilter = manga.downloadedFilter
|
||||
@ -28,13 +32,21 @@ fun List<Chapter>.applyFilters(manga: Manga, downloadManager: DownloadManager/*
|
||||
val manga = mergedManga.getOrElse(chapter.mangaId) { manga }
|
||||
// SY <--
|
||||
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
|
||||
}
|
||||
}
|
||||
// SY -->
|
||||
.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 <--
|
||||
.sortedWith(getChapterSort(manga))
|
||||
@ -55,7 +67,10 @@ fun List<ChapterList.Item>.applyFilters(manga: Manga): Sequence<ChapterList.Item
|
||||
.filter { applyFilter(downloadedFilter) { it.isDownloaded || isLocalManga } }
|
||||
// SY -->
|
||||
.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 <--
|
||||
.sortedWith { (chapter1), (chapter2) -> getChapterSort(manga).invoke(chapter1, chapter2) }
|
||||
|
@ -24,8 +24,9 @@ class GetExtensionsByType(
|
||||
val (updates, installed) = _installed
|
||||
.filter { (showNsfwSources || it.isNsfw.not()) }
|
||||
.sortedWith(
|
||||
compareBy<Extension.Installed> { it.isObsolete.not() /* SY --> */ && it.isRedundant.not() /* SY <-- */ }
|
||||
.thenBy(String.CASE_INSENSITIVE_ORDER) { it.name },
|
||||
compareBy<Extension.Installed> {
|
||||
it.isObsolete.not() /* SY --> */ && it.isRedundant.not() /* SY <-- */
|
||||
}.thenBy(String.CASE_INSENSITIVE_ORDER) { it.name },
|
||||
)
|
||||
.partition { it.hasUpdate }
|
||||
|
||||
|
@ -11,7 +11,12 @@ class SourcePreferences(
|
||||
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())
|
||||
|
||||
@ -28,7 +33,10 @@ class SourcePreferences(
|
||||
|
||||
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)
|
||||
|
||||
|
@ -61,7 +61,10 @@ class AddTracks(
|
||||
?.readAt
|
||||
|
||||
firstReadChapterDate?.let {
|
||||
val startDate = firstReadChapterDate.time.convertEpochMillisZone(ZoneOffset.systemDefault(), ZoneOffset.UTC)
|
||||
val startDate = firstReadChapterDate.time.convertEpochMillisZone(
|
||||
ZoneOffset.systemDefault(),
|
||||
ZoneOffset.UTC,
|
||||
)
|
||||
track = track.copy(
|
||||
startDate = startDate,
|
||||
)
|
||||
|
@ -43,7 +43,9 @@ class DelayedTrackingUpdateJob(private val context: Context, workerParams: Worke
|
||||
track?.copy(lastChapterRead = it.lastChapterRead.toDouble())
|
||||
}
|
||||
.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)
|
||||
}
|
||||
}
|
||||
|
@ -113,14 +113,26 @@ private fun MigrateSourceList(
|
||||
|
||||
IconButton(onClick = onToggleSortingMode) {
|
||||
when (sortingMode) {
|
||||
SetMigrateSorting.Mode.ALPHABETICAL -> Icon(Icons.Outlined.SortByAlpha, contentDescription = stringResource(R.string.action_sort_alpha))
|
||||
SetMigrateSorting.Mode.TOTAL -> Icon(Icons.Outlined.Numbers, contentDescription = stringResource(R.string.action_sort_count))
|
||||
SetMigrateSorting.Mode.ALPHABETICAL -> Icon(
|
||||
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) {
|
||||
when (sortingDirection) {
|
||||
SetMigrateSorting.Direction.ASCENDING -> Icon(Icons.Outlined.ArrowUpward, contentDescription = stringResource(R.string.action_asc))
|
||||
SetMigrateSorting.Direction.DESCENDING -> Icon(Icons.Outlined.ArrowDownward, contentDescription = stringResource(R.string.action_desc))
|
||||
SetMigrateSorting.Direction.ASCENDING -> Icon(
|
||||
Icons.Outlined.ArrowUpward,
|
||||
contentDescription = stringResource(R.string.action_asc),
|
||||
)
|
||||
SetMigrateSorting.Direction.DESCENDING -> Icon(
|
||||
Icons.Outlined.ArrowDownward,
|
||||
contentDescription = stringResource(R.string.action_desc),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -173,7 +173,13 @@ private fun SourcePinButton(
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
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
|
||||
IconButton(onClick = onClick) {
|
||||
Icon(
|
||||
|
@ -25,7 +25,9 @@ fun BaseSourceItem(
|
||||
action: @Composable RowScope.(Source) -> Unit = {},
|
||||
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(
|
||||
modifier = modifier,
|
||||
onClickItem = onClickItem,
|
||||
|
@ -71,7 +71,10 @@ fun CategoryListItem(
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
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) {
|
||||
Icon(imageVector = Icons.Outlined.Delete, contentDescription = stringResource(R.string.action_delete))
|
||||
|
@ -764,7 +764,9 @@ fun MangaScreenLargeImpl(
|
||||
val isReading = remember(state.chapters) {
|
||||
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) },
|
||||
onClick = onContinueReading,
|
||||
@ -1030,7 +1032,9 @@ private fun LazyListScope.sharedChapterItems(
|
||||
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 -->
|
||||
sourceName = item.sourceName,
|
||||
// SY <--
|
||||
@ -1082,7 +1086,11 @@ private fun onChapterItemClick(
|
||||
}
|
||||
|
||||
// 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
|
||||
fun metadataDescription(source: Source): MetadataDescriptionComposable? {
|
||||
|
@ -248,7 +248,8 @@ fun LibraryBottomActionMenu(
|
||||
tonalElevation = 3.dp,
|
||||
) {
|
||||
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 }
|
||||
val onLongClickItem: (Int) -> Unit = { toConfirmIndex ->
|
||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
|
@ -42,7 +42,6 @@ import androidx.compose.material.icons.outlined.Sync
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.LocalMinimumInteractiveComponentEnforcement
|
||||
import androidx.compose.material3.LocalTextStyle
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ProvideTextStyle
|
||||
@ -192,7 +191,11 @@ fun MangaActionRow(
|
||||
)
|
||||
if (onEditIntervalClicked != null && fetchInterval != null) {
|
||||
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,
|
||||
color = if (isUserIntervalMode) MaterialTheme.colorScheme.primary else defaultActionButtonColor,
|
||||
onClick = onEditIntervalClicked,
|
||||
@ -625,7 +628,9 @@ private fun MangaSummary(
|
||||
val image = AnimatedImageVector.animatedVectorResource(R.drawable.anim_caret_down)
|
||||
Icon(
|
||||
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,
|
||||
modifier = Modifier.background(Brush.radialGradient(colors = colors.asReversed())),
|
||||
)
|
||||
|
@ -72,7 +72,9 @@ fun MoreScreen(
|
||||
WarningBanner(
|
||||
textRes = R.string.fdroid_warning,
|
||||
modifier = Modifier.clickable {
|
||||
uriHandler.openUri("https://tachiyomi.org/docs/faq/general#how-do-i-update-from-the-f-droid-builds")
|
||||
uriHandler.openUri(
|
||||
"https://tachiyomi.org/docs/faq/general#how-do-i-update-from-the-f-droid-builds",
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -31,7 +31,8 @@ fun getCategoriesLabel(
|
||||
|
||||
val includedItemsText = when {
|
||||
// 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
|
||||
includedCategories.size == allCategories.size -> stringResource(R.string.all)
|
||||
allExcluded -> stringResource(R.string.none)
|
||||
|
@ -124,7 +124,9 @@ object SettingsAppearanceScreen : SearchableSettings {
|
||||
uiPreferences: UiPreferences,
|
||||
): Preference.PreferenceGroup {
|
||||
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 dateFormat by uiPreferences.dateFormat().collectAsState()
|
||||
|
@ -181,7 +181,8 @@ object SettingsEhScreen : SearchableSettings {
|
||||
unsortedPreferences: UnsortedPreferences,
|
||||
openWarnConfigureDialogController: () -> Unit,
|
||||
): Preference.PreferenceItem.SwitchPreference {
|
||||
val activityResultContract = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
val activityResultContract =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
// Upload settings
|
||||
openWarnConfigureDialogController()
|
||||
@ -932,7 +933,9 @@ object SettingsEhScreen : SearchableSettings {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun updateCheckerFrequency(unsortedPreferences: UnsortedPreferences): Preference.PreferenceItem.ListPreference<Int> {
|
||||
fun updateCheckerFrequency(
|
||||
unsortedPreferences: UnsortedPreferences,
|
||||
): Preference.PreferenceItem.ListPreference<Int> {
|
||||
val value by unsortedPreferences.exhAutoUpdateFrequency().collectAsState()
|
||||
val context = LocalContext.current
|
||||
return Preference.PreferenceItem.ListPreference(
|
||||
@ -941,7 +944,12 @@ object SettingsEhScreen : SearchableSettings {
|
||||
subtitle = if (value == 0) {
|
||||
stringResource(R.string.time_between_batches_summary_1, stringResource(R.string.app_name))
|
||||
} 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(
|
||||
0 to stringResource(R.string.time_between_batches_never),
|
||||
@ -961,7 +969,9 @@ object SettingsEhScreen : SearchableSettings {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun autoUpdateRequirements(unsortedPreferences: UnsortedPreferences): Preference.PreferenceItem.MultiSelectListPreference {
|
||||
fun autoUpdateRequirements(
|
||||
unsortedPreferences: UnsortedPreferences,
|
||||
): Preference.PreferenceItem.MultiSelectListPreference {
|
||||
val value by unsortedPreferences.exhAutoUpdateRequirements().collectAsState()
|
||||
val context = LocalContext.current
|
||||
return Preference.PreferenceItem.MultiSelectListPreference(
|
||||
@ -1100,12 +1110,18 @@ object SettingsEhScreen : SearchableSettings {
|
||||
|
||||
private fun getRelativeTimeString(relativeTime: RelativeTime, context: Context): String {
|
||||
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.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.minutes?.let { context.resources.getQuantityString(R.plurals.humanize_minute, it.toInt(), it) }
|
||||
?: relativeTime.seconds?.let { context.resources.getQuantityString(R.plurals.humanize_second, it.toInt(), it) }
|
||||
?: relativeTime.minutes?.let {
|
||||
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)
|
||||
}
|
||||
|
||||
@ -1138,7 +1154,12 @@ object SettingsEhScreen : SearchableSettings {
|
||||
}
|
||||
|
||||
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 {
|
||||
context.getString(R.string.gallery_updater_not_ran_yet)
|
||||
}
|
||||
|
@ -100,7 +100,10 @@ object SettingsMangadexScreen : SearchableSettings {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun loginPreference(mdex: MangaDex, trackPreferences: TrackPreferences): Preference.PreferenceItem.CustomPreference {
|
||||
fun loginPreference(
|
||||
mdex: MangaDex,
|
||||
trackPreferences: TrackPreferences,
|
||||
): Preference.PreferenceItem.CustomPreference {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val loggedIn by remember { trackPreferences.trackToken(mdex.mdList) }.collectAsState()
|
||||
|
@ -115,7 +115,9 @@ class WorkerInfoScreen : Screen() {
|
||||
private val workManager = context.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()
|
||||
.map(::constructString)
|
||||
.stateIn(ioCoroutineScope, SharingStarted.WhileSubscribed(), "")
|
||||
|
@ -115,8 +115,16 @@ fun <T> TriStateListDialog(
|
||||
}
|
||||
}
|
||||
|
||||
if (!listState.isScrolledToStart()) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter))
|
||||
if (!listState.isScrolledToEnd()) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter))
|
||||
if (!listState.isScrolledToStart()) {
|
||||
HorizontalDivider(
|
||||
modifier = Modifier.align(Alignment.TopCenter),
|
||||
)
|
||||
}
|
||||
if (!listState.isScrolledToEnd()) {
|
||||
HorizontalDivider(
|
||||
modifier = Modifier.align(Alignment.BottomCenter),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -53,6 +53,6 @@ fun PageIndicatorText(
|
||||
@Composable
|
||||
private fun PageIndicatorTextPreview() {
|
||||
TachiyomiTheme {
|
||||
PageIndicatorText(currentPage = 10, totalPages = 69)
|
||||
PageIndicatorText(currentPage = "10", totalPages = 69)
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +103,9 @@ fun ChapterNavigator(
|
||||
) {
|
||||
Icon(
|
||||
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(
|
||||
imageVector = Icons.Outlined.SkipNext,
|
||||
contentDescription = stringResource(if (isRtl) R.string.action_previous_chapter else R.string.action_next_chapter),
|
||||
contentDescription = stringResource(
|
||||
if (isRtl) R.string.action_previous_chapter else R.string.action_next_chapter,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ fun ExhUtils(
|
||||
Column(
|
||||
modifier
|
||||
.fillMaxWidth()
|
||||
.background(backgroundColor)
|
||||
.background(backgroundColor),
|
||||
) {
|
||||
AnimatedVisibility(visible = isVisible) {
|
||||
Column {
|
||||
@ -83,12 +83,12 @@ fun ExhUtils(
|
||||
fontSize = 13.sp,
|
||||
fontFamily = FontFamily.SansSerif,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
modifier = Modifier.padding(start = 24.dp)
|
||||
modifier = Modifier.padding(start = 24.dp),
|
||||
)
|
||||
Switch(
|
||||
checked = isAutoScroll,
|
||||
onCheckedChange = null,
|
||||
enabled = isAutoScrollEnabled
|
||||
enabled = isAutoScrollEnabled,
|
||||
)
|
||||
}
|
||||
Row(
|
||||
@ -112,18 +112,18 @@ fun ExhUtils(
|
||||
focusedContainerColor = Color.Transparent,
|
||||
unfocusedContainerColor = Color.Transparent,
|
||||
focusedTextColor = MaterialTheme.colorScheme.onSurface,
|
||||
unfocusedTextColor = MaterialTheme.colorScheme.onSurface
|
||||
unfocusedTextColor = MaterialTheme.colorScheme.onSurface,
|
||||
),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Decimal
|
||||
keyboardType = KeyboardType.Decimal,
|
||||
),
|
||||
)
|
||||
AnimatedVisibility(!isAutoScrollEnabled) {
|
||||
Text(
|
||||
text = stringResource(R.string.eh_autoscroll_freq_invalid),
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
style = MaterialTheme.typography.labelSmall
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -147,7 +147,7 @@ fun ExhUtils(
|
||||
Row(
|
||||
Modifier.fillMaxWidth(0.5f),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
TextButton(
|
||||
onClick = onClickRetryAll,
|
||||
@ -157,7 +157,7 @@ fun ExhUtils(
|
||||
text = stringResource(R.string.eh_retry_all),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
fontSize = 13.sp,
|
||||
fontFamily = FontFamily.SansSerif
|
||||
fontFamily = FontFamily.SansSerif,
|
||||
)
|
||||
}
|
||||
TextButton(
|
||||
@ -175,7 +175,7 @@ fun ExhUtils(
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
TextButton(
|
||||
onClick = onClickBoostPage,
|
||||
@ -185,7 +185,7 @@ fun ExhUtils(
|
||||
text = stringResource(R.string.eh_boost_page),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
fontSize = 13.sp,
|
||||
fontFamily = FontFamily.SansSerif
|
||||
fontFamily = FontFamily.SansSerif,
|
||||
)
|
||||
}
|
||||
TextButton(
|
||||
@ -206,7 +206,7 @@ fun ExhUtils(
|
||||
|
||||
IconButton(
|
||||
onClick = { onSetExhUtilsVisibility(!isVisible) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (isVisible) {
|
||||
@ -237,7 +237,7 @@ private fun ExhUtilsPreview() {
|
||||
onClickBoostPage = {},
|
||||
onClickBoostPageHelp = {},
|
||||
onClickRetryAll = {},
|
||||
onClickRetryAllHelp = {}
|
||||
onClickRetryAllHelp = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -46,11 +46,11 @@ enum class NavBarType {
|
||||
fun BoxIgnoreLayoutDirection(modifier: Modifier, content: @Composable BoxScope.() -> Unit) {
|
||||
val layoutDirection = LocalLayoutDirection.current
|
||||
CompositionLocalProvider(
|
||||
LocalLayoutDirection provides LayoutDirection.Ltr
|
||||
LocalLayoutDirection provides LayoutDirection.Ltr,
|
||||
) {
|
||||
Box(modifier) {
|
||||
CompositionLocalProvider(
|
||||
LocalLayoutDirection provides layoutDirection
|
||||
LocalLayoutDirection provides layoutDirection,
|
||||
) {
|
||||
content()
|
||||
}
|
||||
@ -125,7 +125,7 @@ fun ReaderAppBars(
|
||||
|
||||
// SY -->
|
||||
BoxIgnoreLayoutDirection(
|
||||
Modifier.fillMaxWidth()
|
||||
Modifier.fillMaxWidth(),
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = visible && navBarType == NavBarType.VerticalLeft,
|
||||
@ -139,7 +139,7 @@ fun ReaderAppBars(
|
||||
),
|
||||
modifier = modifierWithInsetsPadding
|
||||
.padding(bottom = 48.dp, top = 120.dp)
|
||||
.align(Alignment.TopStart)
|
||||
.align(Alignment.TopStart),
|
||||
) {
|
||||
ChapterNavigator(
|
||||
isRtl = isRtl,
|
||||
@ -167,7 +167,7 @@ fun ReaderAppBars(
|
||||
),
|
||||
modifier = modifierWithInsetsPadding
|
||||
.padding(bottom = 48.dp, top = 120.dp)
|
||||
.align(Alignment.TopEnd)
|
||||
.align(Alignment.TopEnd),
|
||||
) {
|
||||
ChapterNavigator(
|
||||
isRtl = isRtl,
|
||||
@ -246,13 +246,12 @@ fun ReaderAppBars(
|
||||
onClickRetryAll = onClickRetryAll,
|
||||
onClickRetryAllHelp = onClickRetryAllHelp,
|
||||
onClickBoostPage = onClickBoostPage,
|
||||
onClickBoostPageHelp = onClickBoostPageHelp
|
||||
onClickBoostPageHelp = onClickBoostPageHelp,
|
||||
)
|
||||
// SY <--
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
AnimatedVisibility(
|
||||
@ -304,7 +303,7 @@ fun ReaderAppBars(
|
||||
onClickWebView = onOpenInWebView,
|
||||
onClickShare = onShare,
|
||||
onClickPageLayout = onClickPageLayout,
|
||||
onClickShiftPage = onClickShiftPage
|
||||
onClickShiftPage = onClickShiftPage,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -104,7 +104,11 @@ internal fun LazyListScope.updatesUiItems(
|
||||
update = updatesItem.update,
|
||||
selected = updatesItem.selected,
|
||||
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 {
|
||||
stringResource(
|
||||
R.string.chapter_progress,
|
||||
|
@ -173,7 +173,9 @@ fun WebViewScreenContent(
|
||||
modifier = Modifier
|
||||
.clip(MaterialTheme.shapes.small)
|
||||
.clickable {
|
||||
uriHandler.openUri("https://tachiyomi.org/docs/guides/troubleshooting/#cloudflare")
|
||||
uriHandler.openUri(
|
||||
"https://tachiyomi.org/docs/guides/troubleshooting/#cloudflare",
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -304,7 +304,12 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
|
||||
|
||||
fun register() {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -123,13 +123,22 @@ object Migrations {
|
||||
}
|
||||
}
|
||||
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")
|
||||
|
||||
putInt(libraryPreferences.filterUnread().key(), convertBooleanPrefToTriState("pref_filter_unread_key"))
|
||||
putInt(
|
||||
libraryPreferences.filterUnread().key(),
|
||||
convertBooleanPrefToTriState("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")
|
||||
}
|
||||
}
|
||||
@ -245,7 +254,10 @@ object Migrations {
|
||||
if (oldSecureScreen) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -262,7 +274,12 @@ object Migrations {
|
||||
if (oldVersion < 81) {
|
||||
// Handle renamed enum values
|
||||
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"
|
||||
"UNREAD" -> "UNREAD_COUNT"
|
||||
"DATE_FETCHED" -> "CHAPTER_FETCH_DATE"
|
||||
|
@ -104,7 +104,8 @@ class BackupCreator(
|
||||
throw IllegalStateException(context.getString(R.string.missing_storage_permission))
|
||||
}
|
||||
|
||||
val databaseManga = getFavorites.await() /* SY --> */ + if (flags and BACKUP_READ_MANGA_MASK == BACKUP_READ_MANGA) {
|
||||
val databaseManga = getFavorites.await() /* SY --> */ +
|
||||
if (flags and BACKUP_READ_MANGA_MASK == BACKUP_READ_MANGA) {
|
||||
handler.awaitList { mangasQueries.getReadMangaNotInLibrary(MangaMapper::mapManga) }
|
||||
} else {
|
||||
emptyList()
|
||||
@ -226,12 +227,18 @@ class BackupCreator(
|
||||
val mangaObject = BackupManga.copyFrom(
|
||||
manga,
|
||||
// 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 -->
|
||||
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<*, *>>()
|
||||
|
@ -20,7 +20,9 @@ class BackupNotifier(private val context: Context) {
|
||||
|
||||
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))
|
||||
setSmallIcon(R.drawable.ic_tachi)
|
||||
setAutoCancel(false)
|
||||
@ -28,7 +30,9 @@ class BackupNotifier(private val context: Context) {
|
||||
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))
|
||||
setSmallIcon(R.drawable.ic_tachi)
|
||||
setAutoCancel(false)
|
||||
@ -72,14 +76,25 @@ class BackupNotifier(private val context: Context) {
|
||||
addAction(
|
||||
R.drawable.ic_share_24dp,
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
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)
|
||||
|
||||
val timeString = context.getString(
|
||||
@ -127,7 +150,14 @@ class BackupNotifier(private val context: Context) {
|
||||
|
||||
with(completeNotificationBuilder) {
|
||||
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()
|
||||
if (errorCount > 0 && !path.isNullOrEmpty() && !file.isNullOrEmpty()) {
|
||||
|
@ -108,7 +108,13 @@ class BackupRestorer(
|
||||
val logFile = writeErrorLog()
|
||||
|
||||
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 {
|
||||
notifier.showRestoreComplete(time, errors.size, logFile.parent, logFile.name)
|
||||
}
|
||||
@ -210,7 +216,12 @@ class BackupRestorer(
|
||||
)
|
||||
|
||||
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 -->
|
||||
@ -234,7 +245,12 @@ class BackupRestorer(
|
||||
}
|
||||
|
||||
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 <--
|
||||
|
||||
@ -259,13 +275,33 @@ class BackupRestorer(
|
||||
val dbManga = getMangaFromDatabase(manga.url, manga.source)
|
||||
val restoredManga = if (dbManga == null) {
|
||||
// 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 {
|
||||
// Manga in database
|
||||
// Copy information from manga already in database
|
||||
val updatedManga = restoreExistingManga(manga, dbManga)
|
||||
// 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)
|
||||
} catch (e: Exception) {
|
||||
@ -275,9 +311,19 @@ class BackupRestorer(
|
||||
|
||||
restoreProgress += 1
|
||||
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 {
|
||||
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 {
|
||||
val fetchedManga = restoreNewManga(manga)
|
||||
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
|
||||
}
|
||||
|
||||
@ -498,7 +553,16 @@ class BackupRestorer(
|
||||
// SY <--
|
||||
): Manga {
|
||||
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
|
||||
}
|
||||
|
||||
@ -696,17 +760,33 @@ class BackupRestorer(
|
||||
* @param manga the merge manga for the references
|
||||
* @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
|
||||
val dbMergedMangaReferences = handler.awaitList { mergedQueries.selectAll(MergedMangaMapper::map) }
|
||||
val dbMergedMangaReferences = handler.awaitList {
|
||||
mergedQueries.selectAll(MergedMangaMapper::map)
|
||||
}
|
||||
|
||||
// Iterate over them
|
||||
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
|
||||
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
|
||||
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 {
|
||||
handler.await {
|
||||
mergedQueries.insert(
|
||||
@ -746,7 +826,12 @@ class BackupRestorer(
|
||||
BackupCreateJob.setupTask(context)
|
||||
|
||||
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>) {
|
||||
@ -756,7 +841,12 @@ class BackupRestorer(
|
||||
}
|
||||
|
||||
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(
|
||||
|
@ -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.source().peek().use { ImageUtil.findImageType(it.inputStream()) == null }) {
|
||||
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())
|
||||
}
|
||||
|
@ -148,7 +148,10 @@ class DownloadCache(
|
||||
if (sourceDir != null) {
|
||||
val mangaDir = sourceDir.mangaDirs[provider.getMangaDirName(mangaTitle)]
|
||||
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
|
||||
|
@ -156,7 +156,12 @@ class DownloadManager(
|
||||
* @return the list of pages from the chapter.
|
||||
*/
|
||||
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()
|
||||
.filter { "image" in it.type.orEmpty() }
|
||||
|
||||
@ -292,7 +297,13 @@ class DownloadManager(
|
||||
* @param manga the manga 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
|
||||
|
||||
if (removeNonFavorite && !manga.favorite) {
|
||||
|
@ -95,7 +95,10 @@ internal class DownloadNotifier(private val context: Context) {
|
||||
} else {
|
||||
val title = download.manga.title.chop(15)
|
||||
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))
|
||||
setContentText(downloadingProgressText)
|
||||
}
|
||||
|
@ -286,7 +286,9 @@ class Downloader(
|
||||
val wasEmpty = queueState.value.isEmpty()
|
||||
val chaptersWithoutDir = chapters
|
||||
// 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.
|
||||
.sortedByDescending { it.sourceOrder }
|
||||
|
||||
@ -335,7 +337,11 @@ class Downloader(
|
||||
val availSpace = DiskUtil.getAvailableStorageSpace(mangaDir)
|
||||
if (availSpace != -1L && availSpace < MIN_DISK_SPACE) {
|
||||
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
|
||||
}
|
||||
|
||||
@ -449,13 +455,16 @@ class Downloader(
|
||||
tmpFile?.delete()
|
||||
|
||||
// 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 {
|
||||
// If the image is already downloaded, do nothing. Otherwise download from network
|
||||
val file = when {
|
||||
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)
|
||||
}
|
||||
|
||||
@ -482,7 +491,13 @@ class Downloader(
|
||||
* @param tmpDir the temporary directory of the download.
|
||||
* @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.progress = 0
|
||||
return flow {
|
||||
|
@ -402,7 +402,9 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||
val errorMessage = when (e) {
|
||||
is NoChaptersException -> context.getString(R.string.no_chapters_error)
|
||||
// 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
|
||||
}
|
||||
failedUpdates.add(manga to errorMessage)
|
||||
@ -722,7 +724,9 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||
if (interval > 0) {
|
||||
val restrictions = preferences.autoUpdateDeviceRestrictions().get()
|
||||
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,
|
||||
requiresBatteryNotLow = true,
|
||||
)
|
||||
@ -739,7 +743,11 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.MINUTES)
|
||||
.build()
|
||||
|
||||
context.workManager.enqueueUniquePeriodicWork(WORK_NAME_AUTO, ExistingPeriodicWorkPolicy.UPDATE, request)
|
||||
context.workManager.enqueueUniquePeriodicWork(
|
||||
WORK_NAME_AUTO,
|
||||
ExistingPeriodicWorkPolicy.UPDATE,
|
||||
request,
|
||||
)
|
||||
} else {
|
||||
context.workManager.cancelUniqueWork(WORK_NAME_AUTO)
|
||||
}
|
||||
|
@ -82,7 +82,12 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||
} else {
|
||||
val updatingText = manga.joinToString("\n") { it.title.chop(40) }
|
||||
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))
|
||||
}
|
||||
|
||||
@ -166,7 +171,13 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||
if (updates.size == 1 && !preferences.hideNotificationContent().get()) {
|
||||
setContentText(updates.first().first.title.chop(NOTIF_TITLE_MAX_LEN))
|
||||
} 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()) {
|
||||
setStyle(
|
||||
@ -196,7 +207,10 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||
launchUI {
|
||||
context.notify(
|
||||
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)
|
||||
0 -> {
|
||||
// "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
|
||||
1 -> {
|
||||
val remaining = chapters.size - displayableChapterNumbers.size
|
||||
if (remaining == 0) {
|
||||
// "Chapter 2.5"
|
||||
context.resources.getString(R.string.notification_chapters_single, displayableChapterNumbers.first())
|
||||
context.resources.getString(
|
||||
R.string.notification_chapters_single,
|
||||
displayableChapterNumbers.first(),
|
||||
)
|
||||
} else {
|
||||
// "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)
|
||||
@ -312,10 +337,18 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||
// "Chapters 1, 2.5, 3, 4, 5 and 10 more"
|
||||
val remaining = displayableChapterNumbers.size - NOTIF_MAX_CHAPTERS
|
||||
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 {
|
||||
// "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
|
||||
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 {
|
||||
|
@ -23,7 +23,12 @@ object NotificationHandler {
|
||||
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
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/*")
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
}
|
||||
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
||||
return PendingIntent.getActivity(
|
||||
context,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -306,7 +306,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
||||
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 {
|
||||
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 {
|
||||
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
|
||||
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_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_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 {
|
||||
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("notificationId", manga.id.hashCode())
|
||||
.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_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_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 {
|
||||
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
|
||||
* @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 {
|
||||
action = ACTION_START_APP_UPDATE
|
||||
putExtra(AppUpdateDownloadJob.EXTRA_DOWNLOAD_URL, url)
|
||||
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 {
|
||||
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
|
||||
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_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
|
||||
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
||||
}
|
||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
||||
return PendingIntent.getBroadcast(
|
||||
context,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,8 @@ class TrackerManager {
|
||||
val kavita = Kavita(KAVITA)
|
||||
val 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 }
|
||||
|
||||
|
@ -48,11 +48,15 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
|
||||
when (it.code) {
|
||||
200 -> return it.parseAs<AuthenticationDto>().token
|
||||
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")
|
||||
}
|
||||
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")
|
||||
}
|
||||
else -> {}
|
||||
@ -62,12 +66,12 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
|
||||
// Not sure which one to catch
|
||||
} catch (e: SocketTimeoutException) {
|
||||
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
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR) {
|
||||
"Unhandled exception fetching JWT token for url: '$apiUrl'"
|
||||
"Unhandled exception fetching JWT token for URL: '$apiUrl'"
|
||||
}
|
||||
throw IOException(e)
|
||||
}
|
||||
@ -129,7 +133,10 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
|
||||
}
|
||||
}
|
||||
} 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
|
||||
}
|
||||
return 0F
|
||||
@ -164,8 +171,14 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
|
||||
}
|
||||
|
||||
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}"
|
||||
authClient.newCall(POST(requestUrl, body = "{}".toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())))
|
||||
val requestUrl = "${getApiFromUrl(
|
||||
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()
|
||||
return getTrackSearch(track.tracking_url)
|
||||
}
|
||||
|
@ -101,7 +101,10 @@ class MyAnimeListApi(
|
||||
return withIOContext {
|
||||
val url = "$baseApiUrl/manga".toUri().buildUpon()
|
||||
.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()
|
||||
with(json) {
|
||||
authClient.newCall(GET(url.toString()))
|
||||
|
@ -91,7 +91,9 @@ class Suwayomi(id: Long) : BaseTracker(id, "Suwayomi"), EnhancedTracker {
|
||||
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? =
|
||||
if (accept(newSource)) {
|
||||
|
@ -38,7 +38,9 @@ class AppUpdateChecker {
|
||||
|
||||
when (result) {
|
||||
is GetApplicationRelease.Result.NewUpdate -> AppUpdateNotifier(context).promptUpdate(result.release)
|
||||
is GetApplicationRelease.Result.ThirdPartyInstallation -> AppUpdateNotifier(context).promptFdroidUpdate()
|
||||
is GetApplicationRelease.Result.ThirdPartyInstallation -> AppUpdateNotifier(
|
||||
context,
|
||||
).promptFdroidUpdate()
|
||||
else -> {}
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,12 @@ internal class AppUpdateNotifier(private val context: Context) {
|
||||
|
||||
val releaseIntent = Intent(Intent.ACTION_VIEW, release.releaseLink.toUri()).run {
|
||||
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) {
|
||||
@ -143,7 +148,12 @@ internal class AppUpdateNotifier(private val context: Context) {
|
||||
setContentTitle(context.getString(R.string.update_check_notification_update_available))
|
||||
setContentText(context.getString(R.string.update_check_fdroid_migration_info))
|
||||
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)
|
||||
}
|
||||
|
@ -101,7 +101,12 @@ class PackageInstallerInstaller(private val service: Service) : Installer(servic
|
||||
}
|
||||
|
||||
init {
|
||||
ContextCompat.registerReceiver(service, packageActionReceiver, IntentFilter(INSTALL_ACTION), ContextCompat.RECEIVER_EXPORTED)
|
||||
ContextCompat.registerReceiver(
|
||||
service,
|
||||
packageActionReceiver,
|
||||
IntentFilter(INSTALL_ACTION),
|
||||
ContextCompat.RECEIVER_EXPORTED,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,9 @@ internal class ExtensionInstallReceiver(private val listener: Listener) :
|
||||
logcat(LogPriority.WARN) { "Package name not found" }
|
||||
return LoadResult.Error
|
||||
}
|
||||
return GlobalScope.async(Dispatchers.Default, CoroutineStart.DEFAULT) { ExtensionLoader.loadExtensionFromPkgName(context, pkgName) }.await()
|
||||
return GlobalScope.async(Dispatchers.Default, CoroutineStart.DEFAULT) {
|
||||
ExtensionLoader.loadExtensionFromPkgName(context, pkgName)
|
||||
}.await()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -59,7 +59,9 @@ class AndroidSourceManager(
|
||||
|
||||
private val stubSourcesMap = ConcurrentHashMap<Long, StubSource>()
|
||||
|
||||
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 -->
|
||||
private val preferences: UnsortedPreferences by injectLazy()
|
||||
@ -118,7 +120,9 @@ class AndroidSourceManager(
|
||||
private fun Source.toInternalSource(): Source? {
|
||||
// EXH -->
|
||||
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 matched = factories.find { sourceQName.startsWith(it) }
|
||||
if (matched != null) {
|
||||
@ -149,7 +153,12 @@ class AndroidSourceManager(
|
||||
}
|
||||
|
||||
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
|
||||
} else {
|
||||
newSource
|
||||
@ -177,15 +186,21 @@ class AndroidSourceManager(
|
||||
}
|
||||
|
||||
// SY -->
|
||||
override fun getVisibleOnlineSources() = sourcesMapFlow.value.values.filterIsInstance<HttpSource>().filter {
|
||||
override fun getVisibleOnlineSources() = sourcesMapFlow.value.values
|
||||
.filterIsInstance<HttpSource>()
|
||||
.filter {
|
||||
it.id !in BlacklistedSources.HIDDEN_SOURCES
|
||||
}
|
||||
|
||||
override fun getVisibleCatalogueSources() = sourcesMapFlow.value.values.filterIsInstance<CatalogueSource>().filter {
|
||||
override fun getVisibleCatalogueSources() = sourcesMapFlow.value.values
|
||||
.filterIsInstance<CatalogueSource>()
|
||||
.filter {
|
||||
it.id !in BlacklistedSources.HIDDEN_SOURCES
|
||||
}
|
||||
|
||||
fun getDelegatedCatalogueSources() = sourcesMapFlow.value.values.filterIsInstance<EnhancedHttpSource>().mapNotNull { enhancedHttpSource ->
|
||||
fun getDelegatedCatalogueSources() = sourcesMapFlow.value.values
|
||||
.filterIsInstance<EnhancedHttpSource>()
|
||||
.mapNotNull { enhancedHttpSource ->
|
||||
enhancedHttpSource.enhancedSource as? DelegatedHttpSource
|
||||
}
|
||||
// SY <--
|
||||
@ -256,7 +271,8 @@ class AndroidSourceManager(
|
||||
),
|
||||
).associateBy { it.originalSourceQualifiedClassName }
|
||||
|
||||
val currentDelegatedSources: MutableMap<Long, DelegatedSource> = ListenMutableMap(mutableMapOf(), ::handleSourceLibrary)
|
||||
val currentDelegatedSources: MutableMap<Long, DelegatedSource> =
|
||||
ListenMutableMap(mutableMapOf(), ::handleSourceLibrary)
|
||||
|
||||
data class DelegatedSource(
|
||||
val sourceName: String,
|
||||
|
@ -281,7 +281,9 @@ class EHentai(
|
||||
private fun getRating(element: Element?): Double? {
|
||||
val ratingStyle = element?.attr("style")?.nullIfBlank()
|
||||
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) {
|
||||
var rate = 5 - matches[0] / 16
|
||||
if (matches[1] == 21) {
|
||||
@ -314,7 +316,9 @@ class EHentai(
|
||||
/**
|
||||
* 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(
|
||||
parsedManga.map { it.manga },
|
||||
nextPage != null,
|
||||
@ -409,7 +413,9 @@ class EHentai(
|
||||
}
|
||||
|
||||
@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 {
|
||||
it.mapIndexed { i, s ->
|
||||
Page(i, s)
|
||||
@ -461,7 +467,9 @@ class EHentai(
|
||||
|
||||
private fun <T : MangasPage> T.checkValid(): MangasPage =
|
||||
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 {
|
||||
this
|
||||
}
|
||||
@ -521,7 +529,11 @@ class EHentai(
|
||||
if (jumpSeekValue != null && page == 1) {
|
||||
if (
|
||||
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)
|
||||
} else if (MATCH_JUMP_REGEX.matches(jumpSeekValue)) {
|
||||
@ -590,7 +602,9 @@ class EHentai(
|
||||
// Pull to most recent
|
||||
val doc = response.asJsoup()
|
||||
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"))
|
||||
client.newCall(mangaDetailsRequest(manga))
|
||||
.asObservableSuccess().map { it.asJsoup() }
|
||||
@ -627,7 +641,9 @@ class EHentai(
|
||||
// Pull to most recent
|
||||
val doc = response.asJsoup()
|
||||
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(
|
||||
url = EHentaiSearchMetadata.normalizeUrl(newerGallery.attr("href")),
|
||||
)
|
||||
@ -756,16 +772,24 @@ class EHentai(
|
||||
tags += RaisedTag(EH_UPLOADER_NAMESPACE, it, TAG_TYPE_VIRTUAL)
|
||||
}
|
||||
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 chapterPageParse(response: Response) = throw UnsupportedOperationException("Unused method was called somehow!")
|
||||
override fun chapterListParse(response: Response) =
|
||||
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> {
|
||||
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) {
|
||||
if (state) {
|
||||
builder.appendQueryParameter(param, "on")
|
||||
@ -1140,7 +1168,9 @@ class EHentai(
|
||||
)
|
||||
|
||||
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(
|
||||
@ -1190,7 +1220,8 @@ class EHentai(
|
||||
}
|
||||
|
||||
override suspend fun fetchPreviewImage(page: PagePreviewInfo, cacheControl: CacheControl?): Response {
|
||||
return client.newCachelessCallWithProgress(exGet(page.imageUrl, cacheControl = cacheControl), page).awaitSuccess()
|
||||
return client.newCachelessCallWithProgress(exGet(page.imageUrl, cacheControl = cacheControl), page)
|
||||
.awaitSuccess()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,7 +49,11 @@ class MergedSource : HttpSource() {
|
||||
|
||||
override fun popularMangaRequest(page: Int) = throw UnsupportedOperationException()
|
||||
override fun 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 latestUpdatesRequest(page: Int) = throw UnsupportedOperationException()
|
||||
override fun latestUpdatesParse(response: Response) = throw UnsupportedOperationException()
|
||||
@ -63,6 +67,7 @@ class MergedSource : HttpSource() {
|
||||
override fun fetchChapterList(manga: SManga) = throw UnsupportedOperationException()
|
||||
override suspend fun getChapterList(manga: SManga) = throw UnsupportedOperationException()
|
||||
override suspend fun getImage(page: Page): Response = throw UnsupportedOperationException()
|
||||
|
||||
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getImageUrl"))
|
||||
override fun fetchImageUrl(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"))
|
||||
override fun fetchPageList(chapter: SChapter) = throw UnsupportedOperationException()
|
||||
override suspend fun getPageList(chapter: SChapter) = throw UnsupportedOperationException()
|
||||
|
||||
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getLatestUpdates"))
|
||||
override fun fetchLatestUpdates(page: Int) = throw UnsupportedOperationException()
|
||||
override suspend fun getLatestUpdates(page: Int) = throw UnsupportedOperationException()
|
||||
|
||||
@Deprecated("Use the non-RxJava API instead", replaceWith = ReplaceWith("getPopularManga"))
|
||||
override fun fetchPopularManga(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)
|
||||
}
|
||||
|
||||
@ -109,7 +121,12 @@ class MergedSource : HttpSource() {
|
||||
"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)
|
||||
var exception: Exception? = null
|
||||
return supervisorScope {
|
||||
|
@ -58,7 +58,7 @@ class NHentai(delegate: HttpSource, val context: Context) :
|
||||
|
||||
override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage {
|
||||
return urlImportFetchSearchMangaSuspend(context, query) {
|
||||
super<DelegatedHttpSource>.getSearchManga(page, query, filters,)
|
||||
super<DelegatedHttpSource>.getSearchManga(page, query, filters)
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,7 +195,8 @@ class NHentai(delegate: HttpSource, val context: Context) :
|
||||
)
|
||||
}
|
||||
|
||||
private fun thumbnailUrlFromType(mediaId: String, page: Int, t: String) = NHentaiSearchMetadata.typeToExtension(t)?.let {
|
||||
private fun thumbnailUrlFromType(mediaId: String, page: Int, t: String) =
|
||||
NHentaiSearchMetadata.typeToExtension(t)?.let {
|
||||
"https://t3.nhentai.net/galleries/$mediaId/${page}t.$it"
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ class EightMuses(delegate: HttpSource, val context: Context) :
|
||||
|
||||
override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage {
|
||||
return urlImportFetchSearchMangaSuspend(context, query) {
|
||||
super<DelegatedHttpSource>.getSearchManga(page, query, filters,)
|
||||
super<DelegatedHttpSource>.getSearchManga(page, query, filters)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ class HBrowse(delegate: HttpSource, val context: Context) :
|
||||
|
||||
override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage {
|
||||
return urlImportFetchSearchMangaSuspend(context, query) {
|
||||
super<DelegatedHttpSource>.getSearchManga(page, query, filters,)
|
||||
super<DelegatedHttpSource>.getSearchManga(page, query, filters)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ class Pururin(delegate: HttpSource, val context: Context) :
|
||||
query
|
||||
}
|
||||
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(
|
||||
namespace,
|
||||
searchUrl.lastPathSegment!!.substringBefore("."),
|
||||
if (namespace != PururinSearchMetadata.TAG_NAMESPACE_CATEGORY) PururinSearchMetadata.TAG_TYPE_DEFAULT else RaisedSearchMetadata.TAG_TYPE_VIRTUAL,
|
||||
if (namespace != PururinSearchMetadata.TAG_NAMESPACE_CATEGORY) {
|
||||
PururinSearchMetadata.TAG_TYPE_DEFAULT
|
||||
} else {
|
||||
RaisedSearchMetadata.TAG_TYPE_VIRTUAL
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ class Tsumino(delegate: HttpSource, val context: Context) :
|
||||
|
||||
override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage {
|
||||
return urlImportFetchSearchMangaSuspend(context, query) {
|
||||
super<DelegatedHttpSource>.getSearchManga(page, query, filters,)
|
||||
super<DelegatedHttpSource>.getSearchManga(page, query, filters)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,8 @@ class MigrateSearchScreen(private val mangaId: Long, private val validSources: L
|
||||
@Composable
|
||||
override fun Content() {
|
||||
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 dialogScreenModel = rememberScreenModel { MigrateSearchScreenDialogScreenModel(mangaId = mangaId) }
|
||||
|
@ -19,7 +19,6 @@ import eu.kanade.domain.manga.interactor.UpdateManga
|
||||
import eu.kanade.domain.manga.model.toDomainManga
|
||||
import eu.kanade.domain.source.interactor.GetExhSavedSearch
|
||||
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.ui.UiPreferences
|
||||
import eu.kanade.presentation.util.ioCoroutineScope
|
||||
@ -52,13 +51,11 @@ import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.preference.CheckboxState
|
||||
import tachiyomi.core.preference.mapAsCheckboxState
|
||||
import tachiyomi.core.util.lang.launchIO
|
||||
import tachiyomi.core.util.lang.launchNonCancellable
|
||||
import tachiyomi.core.util.lang.withUIContext
|
||||
import tachiyomi.core.util.system.logcat
|
||||
import tachiyomi.domain.UnsortedPreferences
|
||||
import tachiyomi.domain.category.interactor.GetCategories
|
||||
import tachiyomi.domain.category.interactor.SetMangaCategories
|
||||
@ -370,7 +367,9 @@ open class BrowseSourceScreenModel(
|
||||
// Choose a category
|
||||
else -> {
|
||||
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) {
|
||||
data object Popular : Listing(query = GetRemoteManga.QUERY_POPULAR, 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 {
|
||||
fun valueOf(query: String?): Listing {
|
||||
@ -530,7 +532,9 @@ open class BrowseSourceScreenModel(
|
||||
source = source.id,
|
||||
name = name.trim(),
|
||||
query = query,
|
||||
filtersJson = runCatching { filterSerializer.serialize(filterList).ifEmpty { null }?.let { Json.encodeToString(it) } }.getOrNull(),
|
||||
filtersJson = runCatching {
|
||||
filterSerializer.serialize(filterList).ifEmpty { null }?.let { Json.encodeToString(it) }
|
||||
}.getOrNull(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -29,10 +29,8 @@ import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import tachiyomi.core.util.lang.awaitSingle
|
||||
import tachiyomi.core.util.lang.launchIO
|
||||
import tachiyomi.core.util.lang.launchNonCancellable
|
||||
import tachiyomi.core.util.lang.withIOContext
|
||||
@ -175,7 +173,9 @@ open class SourceFeedScreenModel(
|
||||
|
||||
mutableState.update { state ->
|
||||
state.copy(
|
||||
items = state.items.map { item -> if (item.id == sourceFeed.id) sourceFeed.withResults(titles) else item },
|
||||
items = state.items.map { item ->
|
||||
if (item.id == sourceFeed.id) sourceFeed.withResults(titles) else item
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -188,7 +188,10 @@ object HomeScreen : Screen() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
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 navigator = LocalNavigator.currentOrThrow
|
||||
val scope = rememberCoroutineScope()
|
||||
|
@ -352,7 +352,8 @@ class LibraryScreenModel(
|
||||
private fun LibraryMap.applySort(
|
||||
// Map<MangaId, List<Track>>
|
||||
trackMap: Map<Long, List<Track>>,
|
||||
/* SY --> */ groupSort: LibrarySort? = null, /* SY <-- */
|
||||
/* SY --> */
|
||||
groupSort: LibrarySort? = null, /* SY <-- */
|
||||
): LibraryMap {
|
||||
// SY -->
|
||||
val listOfTags by lazy {
|
||||
@ -813,7 +814,8 @@ class LibraryScreenModel(
|
||||
}
|
||||
|
||||
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? {
|
||||
@ -1163,7 +1165,9 @@ class LibraryScreenModel(
|
||||
.find { it.int == id }
|
||||
.let { it ?: TrackStatus.OTHER }
|
||||
.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,
|
||||
)
|
||||
}
|
||||
|
@ -141,7 +141,9 @@ object LibraryTab : Tab {
|
||||
onClickSelectAll = { screenModel.selectAll(screenModel.activeCategoryIndex) },
|
||||
onClickInvertSelection = { screenModel.invertSelection(screenModel.activeCategoryIndex) },
|
||||
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) },
|
||||
onClickOpenRandomManga = {
|
||||
scope.launch {
|
||||
@ -224,7 +226,9 @@ object LibraryTab : Tab {
|
||||
scope.launchIO {
|
||||
val chapter = screenModel.getNextUnreadChapter(it.manga)
|
||||
if (chapter != null) {
|
||||
context.startActivity(ReaderActivity.newIntent(context, chapter.mangaId, chapter.id))
|
||||
context.startActivity(
|
||||
ReaderActivity.newIntent(context, chapter.mangaId, chapter.id),
|
||||
)
|
||||
} else {
|
||||
snackbarHostState.showSnackbar(context.getString(R.string.no_next_chapter))
|
||||
}
|
||||
|
@ -453,7 +453,11 @@ class MainActivity : BaseActivity() {
|
||||
private fun handleIntentAction(intent: Intent, navigator: Navigator): Boolean {
|
||||
val notificationId = intent.getIntExtra("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) {
|
||||
|
@ -99,7 +99,8 @@ class MangaScreen(
|
||||
val context = LocalContext.current
|
||||
val haptic = LocalHapticFeedback.current
|
||||
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()
|
||||
|
||||
@ -172,7 +173,9 @@ class MangaScreen(
|
||||
onShareClicked = { shareManga(context, screenModel.manga, screenModel.source) }.takeIf { isHttpSource },
|
||||
onDownloadActionClicked = screenModel::runDownloadAction.takeIf { !successState.source.isLocalOrStub() },
|
||||
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 -->
|
||||
onMigrateClicked = { migrateManga(navigator, screenModel.manga!!) }.takeIf { successState.manga.favorite },
|
||||
onMetadataViewerClicked = { openMetadataViewer(navigator, successState.manga) },
|
||||
@ -181,7 +184,9 @@ class MangaScreen(
|
||||
onMergedSettingsClicked = screenModel::showEditMergedSettingsDialog,
|
||||
onMergeClicked = { openSmartSearch(navigator, successState.manga) },
|
||||
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) },
|
||||
// SY <--
|
||||
onMultiBookmarkClicked = screenModel::bookmarkChapters,
|
||||
|
@ -869,7 +869,9 @@ class MangaScreenModel(
|
||||
// SY <--
|
||||
screenModelScope.launchIO {
|
||||
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) }
|
||||
.collect {
|
||||
withUIContext {
|
||||
@ -880,7 +882,9 @@ class MangaScreenModel(
|
||||
|
||||
screenModelScope.launchIO {
|
||||
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) }
|
||||
.collect {
|
||||
withUIContext {
|
||||
@ -1454,7 +1458,10 @@ class MangaScreenModel(
|
||||
}
|
||||
// SY -->
|
||||
.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 }
|
||||
when {
|
||||
mdTrack == null -> {
|
||||
@ -1614,7 +1621,9 @@ class MangaScreenModel(
|
||||
get() = trackItems.isNotEmpty()
|
||||
|
||||
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.
|
||||
|
@ -53,7 +53,9 @@ class EditMergedSettingsState(
|
||||
context.toast(R.string.merged_references_invalid)
|
||||
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 }
|
||||
|
||||
val isPriorityOrder = mergeReference?.let { it.chapterSortMode == MergedMangaReference.CHAPTER_SORT_PRIORITY } ?: false
|
||||
@ -66,7 +68,11 @@ class EditMergedSettingsState(
|
||||
|
||||
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) {
|
||||
@ -113,7 +119,9 @@ class EditMergedSettingsState(
|
||||
val (manga, reference) = 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) {
|
||||
it.updateChapterUpdatesIcon(!reference.getChapterUpdates)
|
||||
}
|
||||
@ -141,7 +149,9 @@ class EditMergedSettingsState(
|
||||
val (manga, reference) = 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) {
|
||||
it.updateDownloadChaptersIcon(!reference.downloadChapters)
|
||||
}
|
||||
|
@ -133,7 +133,9 @@ class EditMergedSettingsHeaderAdapter(private val state: EditMergedSettingsState
|
||||
}
|
||||
}
|
||||
|
||||
binding.dedupeSwitch.isChecked = state.mergeReference?.let { it.chapterSortMode != MergedMangaReference.CHAPTER_SORT_NONE } ?: false
|
||||
binding.dedupeSwitch.isChecked = state.mergeReference?.let {
|
||||
it.chapterSortMode != MergedMangaReference.CHAPTER_SORT_NONE
|
||||
} ?: false
|
||||
binding.dedupeSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||
binding.dedupeModeSpinner.isEnabled = isChecked
|
||||
binding.dedupeModeSpinner.alpha = when (isChecked) {
|
||||
|
@ -117,7 +117,6 @@ import tachiyomi.domain.source.service.SourceManager
|
||||
import tachiyomi.presentation.core.util.collectAsState
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import kotlin.math.abs
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
class ReaderActivity : BaseActivity() {
|
||||
@ -402,7 +401,6 @@ class ReaderActivity : BaseActivity() {
|
||||
!showVerticalSeekbar -> NavBarType.Bottom
|
||||
leftHandedVerticalSeekbar -> NavBarType.VerticalLeft
|
||||
else -> NavBarType.VerticalRight
|
||||
|
||||
}
|
||||
// SY <--
|
||||
|
||||
@ -569,7 +567,7 @@ class ReaderActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
},
|
||||
state.dateRelativeTime
|
||||
state.dateRelativeTime,
|
||||
)
|
||||
}
|
||||
// SY -->
|
||||
@ -605,7 +603,6 @@ class ReaderActivity : BaseActivity() {
|
||||
)
|
||||
// SY <--
|
||||
null -> {}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -883,7 +880,9 @@ class ReaderActivity : BaseActivity() {
|
||||
// SY -->
|
||||
val state = viewModel.state.value
|
||||
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.setIndexChapterToShift(null)
|
||||
|
@ -210,13 +210,22 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||
(manga.unreadFilterRaw == Manga.CHAPTER_SHOW_READ && !it.read) ||
|
||||
(manga.unreadFilterRaw == Manga.CHAPTER_SHOW_UNREAD && it.read) ||
|
||||
// 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 <--
|
||||
(manga.bookmarkedFilterRaw == Manga.CHAPTER_SHOW_BOOKMARKED && !it.bookmark) ||
|
||||
(manga.bookmarkedFilterRaw == Manga.CHAPTER_SHOW_NOT_BOOKMARKED && it.bookmark) ||
|
||||
// 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 <--
|
||||
}
|
||||
else -> false
|
||||
@ -336,8 +345,20 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val mergedReferences = if (source is MergedSource) runBlocking { getMergedReferencesById.await(manga.id) } else emptyList()
|
||||
val mergedManga = if (source is MergedSource) runBlocking { getMergedMangaById.await(manga.id) }.associateBy { it.id } else emptyMap()
|
||||
val mergedReferences = if (source is MergedSource) {
|
||||
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 autoScrollFreq = readerPreferences.autoscrollInterval().get()
|
||||
// SY <--
|
||||
@ -353,7 +374,7 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||
} else {
|
||||
autoScrollFreq.toString()
|
||||
},
|
||||
isAutoScrollEnabled = autoScrollFreq != -1f
|
||||
isAutoScrollEnabled = autoScrollFreq != -1f,
|
||||
/* SY <-- */
|
||||
)
|
||||
}
|
||||
@ -361,9 +382,23 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||
|
||||
val context = Injekt.get<Application>()
|
||||
// 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)
|
||||
} else {
|
||||
// Unlikely but okay
|
||||
@ -634,7 +669,11 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||
* Saves the chapter progress (last read page and whether it's read)
|
||||
* 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
|
||||
|
||||
mutableState.update {
|
||||
@ -989,7 +1028,11 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||
val filename = generateFilename(manga, page)
|
||||
|
||||
// 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.
|
||||
viewModelScope.launchNonCancellable {
|
||||
@ -1036,7 +1079,7 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||
page2 = secondPage,
|
||||
isLTR = isLTR,
|
||||
bg = bg,
|
||||
location = Location.Pictures(DiskUtil.buildValidFilename(manga.title)),
|
||||
location = Location.Pictures.create(DiskUtil.buildValidFilename(manga.title)),
|
||||
manga = manga,
|
||||
)
|
||||
eventChannel.send(Event.SavedImage(SaveImageResult.Success(uri)))
|
||||
@ -1288,7 +1331,10 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||
data object ChapterList : Dialog
|
||||
// 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 -->
|
||||
data object AutoScrollHelp : Dialog
|
||||
@ -1304,6 +1350,10 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||
data class SetCoverResult(val result: SetAsCoverResult) : Event
|
||||
|
||||
data class SavedImage(val result: SaveImageResult) : Event
|
||||
data class ShareImage(val uri: Uri, val page: ReaderPage/* SY --> */, val secondPage: ReaderPage? = null /* SY <-- */) : Event
|
||||
data class ShareImage(
|
||||
val uri: Uri,
|
||||
val page: ReaderPage/* SY --> */,
|
||||
val secondPage: ReaderPage? = null, /* SY <-- */
|
||||
) : Event
|
||||
}
|
||||
}
|
||||
|
@ -88,16 +88,37 @@ class ChapterLoader(
|
||||
*/
|
||||
private fun getPageLoader(chapter: ReaderChapter): PageLoader {
|
||||
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 {
|
||||
// SY -->
|
||||
source is MergedSource -> {
|
||||
val mangaReference = mergedReferences.firstOrNull { it.mangaId == chapter.chapter.manga_id } ?: error("Merge reference null")
|
||||
val source = sourceManager.get(mangaReference.mangaSourceId) ?: error("Source ${mangaReference.mangaSourceId} was null")
|
||||
val mangaReference = mergedReferences.firstOrNull {
|
||||
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 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 {
|
||||
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 LocalSource -> source.getFormat(chapter.chapter).let { format ->
|
||||
when (format) {
|
||||
|
@ -5,14 +5,54 @@ import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import eu.kanade.tachiyomi.R
|
||||
|
||||
enum class OrientationType(val flag: Int, @StringRes val stringRes: Int, @DrawableRes val iconRes: Int, val flagValue: Int) {
|
||||
DEFAULT(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),
|
||||
enum class OrientationType(
|
||||
val flag: Int,
|
||||
@StringRes val stringRes: Int,
|
||||
@DrawableRes val iconRes: Int,
|
||||
val flagValue: Int,
|
||||
) {
|
||||
DEFAULT(
|
||||
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 {
|
||||
|
@ -35,9 +35,15 @@ class ReaderPreferences(
|
||||
|
||||
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)
|
||||
|
||||
|
@ -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.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),
|
||||
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),
|
||||
|
@ -350,7 +350,9 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
|
||||
val newPage =
|
||||
when {
|
||||
(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)
|
||||
else -> oldCurrent?.first ?: return
|
||||
}
|
||||
|
@ -33,7 +33,11 @@ import kotlin.time.Duration
|
||||
/**
|
||||
* 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()
|
||||
|
||||
@ -291,7 +295,12 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
|
||||
* Scrolls one screen over a period of time
|
||||
*/
|
||||
fun linearScroll(duration: Duration) {
|
||||
recycler.smoothScrollBy(0, activity.resources.displayMetrics.heightPixels, LinearInterpolator(), duration.inWholeMilliseconds.toInt())
|
||||
recycler.smoothScrollBy(
|
||||
0,
|
||||
activity.resources.displayMetrics.heightPixels,
|
||||
LinearInterpolator(),
|
||||
duration.inWholeMilliseconds.toInt(),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -106,7 +106,9 @@ object UpdatesTab : Tab {
|
||||
LaunchedEffect(Unit) {
|
||||
screenModel.events.collectLatest { 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 -> {
|
||||
val msg = if (event.started) {
|
||||
R.string.updating_library
|
||||
|
@ -10,7 +10,11 @@ import tachiyomi.domain.manga.model.Manga
|
||||
/**
|
||||
* 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 ->
|
||||
// SY -->
|
||||
if (manga.isEhBasedManga()) {
|
||||
|
@ -36,9 +36,17 @@ fun File.copyAndSetReadOnlyTo(target: File, overwrite: Boolean = false, bufferSi
|
||||
|
||||
if (target.exists()) {
|
||||
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()) {
|
||||
throw FileAlreadyExistsException(file = this, other = target, reason = "Tried to overwrite the destination, but failed to delete it.")
|
||||
throw FileAlreadyExistsException(
|
||||
file = this,
|
||||
other = target,
|
||||
reason = "Tried to overwrite the destination, but failed to delete it.",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,9 @@ fun Context.copyToClipboard(label: String, content: String) {
|
||||
* @param permission the permission to check.
|
||||
* @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
|
||||
get() = getSystemService()!!
|
||||
@ -105,7 +107,10 @@ fun Context.openInBrowser(uri: Uri, forceDefaultBrowser: Boolean = false) {
|
||||
private fun Context.defaultBrowserPackageName(): String? {
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, "http://".toUri())
|
||||
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 {
|
||||
packageManager.resolveActivity(browserIntent, PackageManager.MATCH_DEFAULT_ONLY)
|
||||
}
|
||||
|
@ -49,7 +49,10 @@ fun Context.cancelNotification(id: Int) {
|
||||
* @param block the function that will execute inside the builder.
|
||||
* @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)
|
||||
.setColor(getColor(R.color.accent_blue))
|
||||
if (block != null) {
|
||||
|
@ -91,6 +91,7 @@ fun View?.isVisibleOnScreen(): Boolean {
|
||||
}
|
||||
val actualPosition = Rect()
|
||||
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)
|
||||
}
|
||||
|
@ -138,7 +138,9 @@ object EXHMigrations {
|
||||
val mergedMangas = runBlocking { getMangaBySource.await(MERGED_SOURCE_ID) }
|
||||
|
||||
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()) {
|
||||
val mangaToUpdate = mutableListOf<MangaUpdate>()
|
||||
val mergedMangaReferences = mutableListOf<MergedMangaReference>()
|
||||
@ -183,15 +185,45 @@ object EXHMigrations {
|
||||
insertMergedReference.awaitAll(mergedMangaReferences)
|
||||
}
|
||||
|
||||
val loadedMangaList = mangaConfigs.map { it.second.children }.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 loadedMangaList = mangaConfigs
|
||||
.map { it.second.children }
|
||||
.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 parsedChapters = chapters.filter { it.read || it.lastPageRead != 0L }.mapNotNull { chapter -> readUrlConfig(chapter.url)?.let { chapter to it } }
|
||||
val mergedMangaChaptersMatched = mergedMangaChapters.mapNotNull { chapter ->
|
||||
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>()
|
||||
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(
|
||||
it.second.id,
|
||||
read = parsedChapter.first.read,
|
||||
@ -353,7 +385,11 @@ object EXHMigrations {
|
||||
if (oldSecureScreen) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -418,7 +454,9 @@ object EXHMigrations {
|
||||
}
|
||||
if (oldVersion under 38) {
|
||||
// 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"
|
||||
"UNREAD" -> "UNREAD_COUNT"
|
||||
"DATE_FETCHED" -> "CHAPTER_FETCH_DATE"
|
||||
@ -618,7 +656,7 @@ object EXHMigrations {
|
||||
"eh_exhSettingsProfile",
|
||||
"eh_settingsKey",
|
||||
"eh_sessionCookie",
|
||||
"eh_hathPerksCookie"
|
||||
"eh_hathPerksCookie",
|
||||
)
|
||||
|
||||
replacePreferences(
|
||||
@ -750,7 +788,6 @@ object EXHMigrations {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun replacePreferences(
|
||||
preferenceStore: PreferenceStore,
|
||||
|
@ -113,7 +113,9 @@ object DebugFunctions {
|
||||
}
|
||||
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() {
|
||||
throttleManager.resetThrottle()
|
||||
|
@ -51,7 +51,10 @@ class EHentaiUpdateHelper(context: Context) {
|
||||
*
|
||||
* @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
|
||||
val chains = chapters
|
||||
.flatMap { chapter ->
|
||||
@ -115,7 +118,11 @@ class EHentaiUpdateHelper(context: Context) {
|
||||
chapterRepository.updateAll(chapterUpdates)
|
||||
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
|
||||
deleteHistory.forEach {
|
||||
|
@ -102,7 +102,8 @@ class FavoritesSyncHelper(val context: Context) {
|
||||
|
||||
if (manga.id in seenManga) {
|
||||
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))
|
||||
return
|
||||
@ -143,17 +144,23 @@ class FavoritesSyncHelper(val context: Context) {
|
||||
// Do not update galleries while syncing favorites
|
||||
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 localChanges = if (prefs.exhReadOnlySync().get()) {
|
||||
null // Do not build local changes if they are not going to be applied
|
||||
} 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()
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
// Apply change sets
|
||||
@ -173,7 +180,9 @@ class FavoritesSyncHelper(val context: Context) {
|
||||
logger.w(context.getString(R.string.favorites_sync_ignoring_exception), e)
|
||||
return
|
||||
} 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)
|
||||
return
|
||||
} finally {
|
||||
@ -273,7 +282,9 @@ class FavoritesSyncHelper(val context: Context) {
|
||||
private suspend fun applyChangeSetToRemote(errorList: MutableList<String>, changeSet: ChangeSet) {
|
||||
// Apply removals
|
||||
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()
|
||||
.add("ddact", "delete")
|
||||
@ -322,7 +333,10 @@ class FavoritesSyncHelper(val context: Context) {
|
||||
|
||||
// Apply removals
|
||||
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()
|
||||
|
||||
// Consider both EX and EH sources
|
||||
@ -377,10 +391,17 @@ class FavoritesSyncHelper(val context: Context) {
|
||||
return@forEachIndexed
|
||||
}
|
||||
|
||||
val errorString = context.getString(R.string.favorites_sync_failed_to_add_to_local) + when (result) {
|
||||
is GalleryAddEvent.Fail.Error -> context.getString(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)
|
||||
val errorString = context.getString(R.string.favorites_sync_failed_to_add_to_local) +
|
||||
when (result) {
|
||||
is GalleryAddEvent.Fail.Error -> context.getString(
|
||||
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()) {
|
||||
@ -427,7 +448,12 @@ sealed class FavoritesSyncStatus() {
|
||||
this(
|
||||
manga = manga,
|
||||
categories = categories,
|
||||
message = context.getString(R.string.favorites_sync_gallery_in_multiple_categories, manga.title, categories.joinToString { it.name }),
|
||||
message = context.getString(
|
||||
R.string.favorites_sync_gallery_in_multiple_categories, manga.title,
|
||||
categories.joinToString {
|
||||
it.name
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ import okhttp3.Response
|
||||
import rx.Observable
|
||||
import tachiyomi.core.util.lang.runAsObservable
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
class BilibiliHandler(currentClient: OkHttpClient) {
|
||||
val baseUrl = "https://www.bilibilicomics.com"
|
||||
@ -34,7 +34,7 @@ class BilibiliHandler(currentClient: OkHttpClient) {
|
||||
.build()
|
||||
|
||||
val client: OkHttpClient = currentClient.newBuilder()
|
||||
.rateLimit(1, 1, TimeUnit.SECONDS)
|
||||
.rateLimit(1, 1.seconds)
|
||||
.build()
|
||||
|
||||
val json by injectLazy<Json>()
|
||||
|
@ -28,7 +28,10 @@ class MangaHandler(
|
||||
val mangaId = MdUtil.getMangaId(manga.url)
|
||||
val response = async(Dispatchers.IO) { service.viewManga(mangaId) }
|
||||
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(
|
||||
manga,
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,13 @@ class MangaHotHandler(currentClient: OkHttpClient, userAgent: String) {
|
||||
val client: OkHttpClient = currentClient
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,12 @@ class SimilarHandler(
|
||||
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 {
|
||||
@ -57,7 +62,8 @@ class SimilarHandler(
|
||||
hasNextPage = false,
|
||||
mangasMetadata = mangaList.map { manga ->
|
||||
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)
|
||||
}
|
||||
},
|
||||
|
@ -15,7 +15,9 @@ enum class FollowStatus(val int: Int) {
|
||||
fun toDex(): String = this.name.lowercase(Locale.US)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,11 @@ class EhUConfigBuilder {
|
||||
|
||||
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
|
||||
val formBody = FormBody.Builder()
|
||||
|
@ -18,7 +18,10 @@ import exh.util.SourceTagsUtil
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
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
|
||||
1 -> R.string.rating1
|
||||
2 -> R.string.rating2
|
||||
@ -36,7 +39,8 @@ object MetadataUIUtil {
|
||||
fun getGenreAndColour(context: Context, genre: String) = when (genre) {
|
||||
"doujinshi", "Doujinshi" -> SourceTagsUtil.GenreColor.DOUJINSHI_COLOR to R.string.doujinshi
|
||||
"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
|
||||
"western" -> SourceTagsUtil.GenreColor.WESTERN_COLOR to R.string.western
|
||||
"non-h", "non-H" -> SourceTagsUtil.GenreColor.NON_H_COLOR to R.string.non_h
|
||||
|
@ -29,7 +29,9 @@ fun NHentaiDescription(state: State.Success, openMetadataViewer: () -> Unit) {
|
||||
if (meta == null || meta !is NHentaiSearchMetadata) return@AndroidView
|
||||
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
|
||||
}.let { categoriesString ->
|
||||
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.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)
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
|
@ -16,7 +16,11 @@ private val galleryAdder by lazy {
|
||||
/**
|
||||
* 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 {
|
||||
query.startsWith("http://") || query.startsWith("https://") -> {
|
||||
runAsObservable {
|
||||
@ -36,18 +40,21 @@ fun UrlImportableSource.urlImportFetchSearchManga(context: Context, query: Strin
|
||||
else -> fail()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
query.startsWith("http://") || query.startsWith("https://") -> {
|
||||
val res = galleryAdder.addGallery(
|
||||
context = context,
|
||||
url = query,
|
||||
fav = false,
|
||||
forceSource = this
|
||||
forceSource = this,
|
||||
)
|
||||
|
||||
MangasPage(
|
||||
|
@ -26,7 +26,10 @@ class SecurityPreferences(
|
||||
|
||||
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)
|
||||
|
||||
|
@ -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 {
|
||||
val name = _name.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
|
||||
return true
|
||||
}
|
||||
private val unsafeHeaderNames = listOf("content-length", "host", "trailer", "te", "upgrade", "cookie2", "keep-alive", "transfer-encoding", "set-cookie")
|
||||
private val unsafeHeaderNames = listOf(
|
||||
"content-length", "host", "trailer", "te", "upgrade", "cookie2", "keep-alive", "transfer-encoding", "set-cookie",
|
||||
)
|
||||
|
@ -159,7 +159,9 @@ object CbzCrypto {
|
||||
}
|
||||
return true
|
||||
} 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
|
||||
}
|
||||
|
@ -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.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.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.xLogW(vararg logs: Any) = xLog().w(logs)
|
||||
|
@ -364,7 +364,9 @@ object ImageUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private fun splitImageName(filenamePrefix: String, index: Int) = "${filenamePrefix}__${"%03d".format(index + 1)}.jpg"
|
||||
private fun splitImageName(filenamePrefix: String, index: Int) = "${filenamePrefix}__${"%03d".format(
|
||||
index + 1,
|
||||
)}.jpg"
|
||||
|
||||
private val BitmapFactory.Options.splitData
|
||||
get(): List<SplitData> {
|
||||
@ -449,10 +451,12 @@ object ImageUtil {
|
||||
val botLeftIsDark = botLeftPixel.isDark()
|
||||
val botRightIsDark = botRightPixel.isDark()
|
||||
|
||||
var darkBG = (topLeftIsDark && (botLeftIsDark || botRightIsDark || topRightIsDark || midLeftIsDark || topMidIsDark)) ||
|
||||
var darkBG =
|
||||
(topLeftIsDark && (botLeftIsDark || botRightIsDark || topRightIsDark || midLeftIsDark || topMidIsDark)) ||
|
||||
(topRightIsDark && (botRightIsDark || botLeftIsDark || midRightIsDark || topMidIsDark))
|
||||
|
||||
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 other = topAndBotPixels[(index + 1) % topAndBotPixels.size]
|
||||
!color.isWhite() && color.isCloseTo(other)
|
||||
@ -597,10 +601,16 @@ object ImageUtil {
|
||||
darkBG -> {
|
||||
return ColorDrawable(blackColor)
|
||||
}
|
||||
topIsBlackStreak || (topCornersIsDark && topOffsetCornersIsDark && (topMidIsDark || overallBlackPixels > 9)) -> {
|
||||
topIsBlackStreak || (
|
||||
topCornersIsDark && topOffsetCornersIsDark &&
|
||||
(topMidIsDark || overallBlackPixels > 9)
|
||||
) -> {
|
||||
intArrayOf(blackColor, blackColor, whiteColor, whiteColor)
|
||||
}
|
||||
bottomIsBlackStreak || (botCornersIsDark && botOffsetCornersIsDark && (bottomCenterPixel.isDark() || overallBlackPixels > 9)) -> {
|
||||
bottomIsBlackStreak || (
|
||||
botCornersIsDark && botOffsetCornersIsDark &&
|
||||
(bottomCenterPixel.isDark() || overallBlackPixels > 9)
|
||||
) -> {
|
||||
intArrayOf(whiteColor, whiteColor, blackColor, blackColor)
|
||||
}
|
||||
else -> {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user