Restrict line length with ktlint
This commit is contained in:
parent
056dbaefda
commit
1d144e6767
61 changed files with 660 additions and 148 deletions
|
@ -1,7 +1,8 @@
|
||||||
[*.{kt,kts}]
|
[*.{kt,kts}]
|
||||||
indent_size=4
|
max_line_length = 120
|
||||||
insert_final_newline=true
|
indent_size = 4
|
||||||
ij_kotlin_allow_trailing_comma=true
|
insert_final_newline = true
|
||||||
ij_kotlin_allow_trailing_comma_on_call_site=true
|
ij_kotlin_allow_trailing_comma = true
|
||||||
ij_kotlin_name_count_to_use_star_import=2147483647
|
ij_kotlin_allow_trailing_comma_on_call_site = true
|
||||||
ij_kotlin_name_count_to_use_star_import_for_members=2147483647
|
ij_kotlin_name_count_to_use_star_import = 2147483647
|
||||||
|
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
|
||||||
|
|
|
@ -23,7 +23,12 @@ fun List<Chapter>.applyFilters(manga: Manga, downloadManager: DownloadManager):
|
||||||
.filter { chapter -> applyFilter(bookmarkedFilter) { chapter.bookmark } }
|
.filter { chapter -> applyFilter(bookmarkedFilter) { chapter.bookmark } }
|
||||||
.filter { chapter ->
|
.filter { chapter ->
|
||||||
applyFilter(downloadedFilter) {
|
applyFilter(downloadedFilter) {
|
||||||
val downloaded = downloadManager.isChapterDownloaded(chapter.name, chapter.scanlator, manga.title, manga.source)
|
val downloaded = downloadManager.isChapterDownloaded(
|
||||||
|
chapter.name,
|
||||||
|
chapter.scanlator,
|
||||||
|
manga.title,
|
||||||
|
manga.source,
|
||||||
|
)
|
||||||
downloaded || isLocalManga
|
downloaded || isLocalManga
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,12 @@ class SourcePreferences(
|
||||||
private val preferenceStore: PreferenceStore,
|
private val preferenceStore: PreferenceStore,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun sourceDisplayMode() = preferenceStore.getObject("pref_display_mode_catalogue", LibraryDisplayMode.default, LibraryDisplayMode.Serializer::serialize, LibraryDisplayMode.Serializer::deserialize)
|
fun sourceDisplayMode() = preferenceStore.getObject(
|
||||||
|
"pref_display_mode_catalogue",
|
||||||
|
LibraryDisplayMode.default,
|
||||||
|
LibraryDisplayMode.Serializer::serialize,
|
||||||
|
LibraryDisplayMode.Serializer::deserialize,
|
||||||
|
)
|
||||||
|
|
||||||
fun enabledLanguages() = preferenceStore.getStringSet("source_languages", LocaleHelper.getDefaultEnabledLanguages())
|
fun enabledLanguages() = preferenceStore.getStringSet("source_languages", LocaleHelper.getDefaultEnabledLanguages())
|
||||||
|
|
||||||
|
@ -28,7 +33,10 @@ class SourcePreferences(
|
||||||
|
|
||||||
fun migrationSortingMode() = preferenceStore.getEnum("pref_migration_sorting", SetMigrateSorting.Mode.ALPHABETICAL)
|
fun migrationSortingMode() = preferenceStore.getEnum("pref_migration_sorting", SetMigrateSorting.Mode.ALPHABETICAL)
|
||||||
|
|
||||||
fun migrationSortingDirection() = preferenceStore.getEnum("pref_migration_direction", SetMigrateSorting.Direction.ASCENDING)
|
fun migrationSortingDirection() = preferenceStore.getEnum(
|
||||||
|
"pref_migration_direction",
|
||||||
|
SetMigrateSorting.Direction.ASCENDING,
|
||||||
|
)
|
||||||
|
|
||||||
fun extensionUpdatesCount() = preferenceStore.getInt("ext_updates_count", 0)
|
fun extensionUpdatesCount() = preferenceStore.getInt("ext_updates_count", 0)
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,10 @@ class AddTracks(
|
||||||
?.readAt
|
?.readAt
|
||||||
|
|
||||||
firstReadChapterDate?.let {
|
firstReadChapterDate?.let {
|
||||||
val startDate = firstReadChapterDate.time.convertEpochMillisZone(ZoneOffset.systemDefault(), ZoneOffset.UTC)
|
val startDate = firstReadChapterDate.time.convertEpochMillisZone(
|
||||||
|
ZoneOffset.systemDefault(),
|
||||||
|
ZoneOffset.UTC,
|
||||||
|
)
|
||||||
track = track.copy(
|
track = track.copy(
|
||||||
startDate = startDate,
|
startDate = startDate,
|
||||||
)
|
)
|
||||||
|
|
|
@ -43,7 +43,9 @@ class DelayedTrackingUpdateJob(private val context: Context, workerParams: Worke
|
||||||
track?.copy(lastChapterRead = it.lastChapterRead.toDouble())
|
track?.copy(lastChapterRead = it.lastChapterRead.toDouble())
|
||||||
}
|
}
|
||||||
.forEach { track ->
|
.forEach { track ->
|
||||||
logcat(LogPriority.DEBUG) { "Updating delayed track item: ${track.mangaId}, last chapter read: ${track.lastChapterRead}" }
|
logcat(LogPriority.DEBUG) {
|
||||||
|
"Updating delayed track item: ${track.mangaId}, last chapter read: ${track.lastChapterRead}"
|
||||||
|
}
|
||||||
trackChapter.await(context, track.mangaId, track.lastChapterRead)
|
trackChapter.await(context, track.mangaId, track.lastChapterRead)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,14 +102,26 @@ private fun MigrateSourceList(
|
||||||
|
|
||||||
IconButton(onClick = onToggleSortingMode) {
|
IconButton(onClick = onToggleSortingMode) {
|
||||||
when (sortingMode) {
|
when (sortingMode) {
|
||||||
SetMigrateSorting.Mode.ALPHABETICAL -> Icon(Icons.Outlined.SortByAlpha, contentDescription = stringResource(R.string.action_sort_alpha))
|
SetMigrateSorting.Mode.ALPHABETICAL -> Icon(
|
||||||
SetMigrateSorting.Mode.TOTAL -> Icon(Icons.Outlined.Numbers, contentDescription = stringResource(R.string.action_sort_count))
|
Icons.Outlined.SortByAlpha,
|
||||||
|
contentDescription = stringResource(R.string.action_sort_alpha),
|
||||||
|
)
|
||||||
|
SetMigrateSorting.Mode.TOTAL -> Icon(
|
||||||
|
Icons.Outlined.Numbers,
|
||||||
|
contentDescription = stringResource(R.string.action_sort_count),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IconButton(onClick = onToggleSortingDirection) {
|
IconButton(onClick = onToggleSortingDirection) {
|
||||||
when (sortingDirection) {
|
when (sortingDirection) {
|
||||||
SetMigrateSorting.Direction.ASCENDING -> Icon(Icons.Outlined.ArrowUpward, contentDescription = stringResource(R.string.action_asc))
|
SetMigrateSorting.Direction.ASCENDING -> Icon(
|
||||||
SetMigrateSorting.Direction.DESCENDING -> Icon(Icons.Outlined.ArrowDownward, contentDescription = stringResource(R.string.action_desc))
|
Icons.Outlined.ArrowUpward,
|
||||||
|
contentDescription = stringResource(R.string.action_asc),
|
||||||
|
)
|
||||||
|
SetMigrateSorting.Direction.DESCENDING -> Icon(
|
||||||
|
Icons.Outlined.ArrowDownward,
|
||||||
|
contentDescription = stringResource(R.string.action_desc),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,7 +144,13 @@ private fun SourcePinButton(
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val icon = if (isPinned) Icons.Filled.PushPin else Icons.Outlined.PushPin
|
val icon = if (isPinned) Icons.Filled.PushPin else Icons.Outlined.PushPin
|
||||||
val tint = if (isPinned) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onBackground.copy(alpha = SecondaryItemAlpha)
|
val tint = if (isPinned) {
|
||||||
|
MaterialTheme.colorScheme.primary
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.onBackground.copy(
|
||||||
|
alpha = SecondaryItemAlpha,
|
||||||
|
)
|
||||||
|
}
|
||||||
val description = if (isPinned) R.string.action_unpin else R.string.action_pin
|
val description = if (isPinned) R.string.action_unpin else R.string.action_pin
|
||||||
IconButton(onClick = onClick) {
|
IconButton(onClick = onClick) {
|
||||||
Icon(
|
Icon(
|
||||||
|
|
|
@ -25,7 +25,9 @@ fun BaseSourceItem(
|
||||||
action: @Composable RowScope.(Source) -> Unit = {},
|
action: @Composable RowScope.(Source) -> Unit = {},
|
||||||
content: @Composable RowScope.(Source, String?) -> Unit = defaultContent,
|
content: @Composable RowScope.(Source, String?) -> Unit = defaultContent,
|
||||||
) {
|
) {
|
||||||
val sourceLangString = LocaleHelper.getSourceDisplayName(source.lang, LocalContext.current).takeIf { showLanguageInContent }
|
val sourceLangString = LocaleHelper.getSourceDisplayName(source.lang, LocalContext.current).takeIf {
|
||||||
|
showLanguageInContent
|
||||||
|
}
|
||||||
BaseBrowseItem(
|
BaseBrowseItem(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
onClickItem = onClickItem,
|
onClickItem = onClickItem,
|
||||||
|
|
|
@ -71,7 +71,10 @@ fun CategoryListItem(
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
IconButton(onClick = onRename) {
|
IconButton(onClick = onRename) {
|
||||||
Icon(imageVector = Icons.Outlined.Edit, contentDescription = stringResource(R.string.action_rename_category))
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Edit,
|
||||||
|
contentDescription = stringResource(R.string.action_rename_category),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
IconButton(onClick = onDelete) {
|
IconButton(onClick = onDelete) {
|
||||||
Icon(imageVector = Icons.Outlined.Delete, contentDescription = stringResource(R.string.action_delete))
|
Icon(imageVector = Icons.Outlined.Delete, contentDescription = stringResource(R.string.action_delete))
|
||||||
|
|
|
@ -614,7 +614,9 @@ fun MangaScreenLargeImpl(
|
||||||
val isReading = remember(state.chapters) {
|
val isReading = remember(state.chapters) {
|
||||||
state.chapters.fastAny { it.chapter.read }
|
state.chapters.fastAny { it.chapter.read }
|
||||||
}
|
}
|
||||||
Text(text = stringResource(if (isReading) R.string.action_resume else R.string.action_start))
|
Text(
|
||||||
|
text = stringResource(if (isReading) R.string.action_resume else R.string.action_start),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
|
icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) },
|
||||||
onClick = onContinueReading,
|
onClick = onContinueReading,
|
||||||
|
|
|
@ -189,7 +189,11 @@ fun MangaActionRow(
|
||||||
)
|
)
|
||||||
if (onEditIntervalClicked != null && fetchInterval != null) {
|
if (onEditIntervalClicked != null && fetchInterval != null) {
|
||||||
MangaActionButton(
|
MangaActionButton(
|
||||||
title = pluralStringResource(id = R.plurals.day, count = fetchInterval.absoluteValue, fetchInterval.absoluteValue),
|
title = pluralStringResource(
|
||||||
|
id = R.plurals.day,
|
||||||
|
count = fetchInterval.absoluteValue,
|
||||||
|
fetchInterval.absoluteValue,
|
||||||
|
),
|
||||||
icon = Icons.Default.HourglassEmpty,
|
icon = Icons.Default.HourglassEmpty,
|
||||||
color = if (isUserIntervalMode) MaterialTheme.colorScheme.primary else defaultActionButtonColor,
|
color = if (isUserIntervalMode) MaterialTheme.colorScheme.primary else defaultActionButtonColor,
|
||||||
onClick = onEditIntervalClicked,
|
onClick = onEditIntervalClicked,
|
||||||
|
@ -587,7 +591,9 @@ private fun MangaSummary(
|
||||||
val image = AnimatedImageVector.animatedVectorResource(R.drawable.anim_caret_down)
|
val image = AnimatedImageVector.animatedVectorResource(R.drawable.anim_caret_down)
|
||||||
Icon(
|
Icon(
|
||||||
painter = rememberAnimatedVectorPainter(image, !expanded),
|
painter = rememberAnimatedVectorPainter(image, !expanded),
|
||||||
contentDescription = stringResource(if (expanded) R.string.manga_info_collapse else R.string.manga_info_expand),
|
contentDescription = stringResource(
|
||||||
|
if (expanded) R.string.manga_info_collapse else R.string.manga_info_expand,
|
||||||
|
),
|
||||||
tint = MaterialTheme.colorScheme.onBackground,
|
tint = MaterialTheme.colorScheme.onBackground,
|
||||||
modifier = Modifier.background(Brush.radialGradient(colors = colors.asReversed())),
|
modifier = Modifier.background(Brush.radialGradient(colors = colors.asReversed())),
|
||||||
)
|
)
|
||||||
|
|
|
@ -62,7 +62,9 @@ fun MoreScreen(
|
||||||
WarningBanner(
|
WarningBanner(
|
||||||
textRes = R.string.fdroid_warning,
|
textRes = R.string.fdroid_warning,
|
||||||
modifier = Modifier.clickable {
|
modifier = Modifier.clickable {
|
||||||
uriHandler.openUri("https://tachiyomi.org/docs/faq/general#how-do-i-update-from-the-f-droid-builds")
|
uriHandler.openUri(
|
||||||
|
"https://tachiyomi.org/docs/faq/general#how-do-i-update-from-the-f-droid-builds",
|
||||||
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,8 @@ fun getCategoriesLabel(
|
||||||
|
|
||||||
val includedItemsText = when {
|
val includedItemsText = when {
|
||||||
// Some selected, but not all
|
// Some selected, but not all
|
||||||
includedCategories.isNotEmpty() && includedCategories.size != allCategories.size -> includedCategories.joinToString { it.visualName(context) }
|
includedCategories.isNotEmpty() && includedCategories.size != allCategories.size ->
|
||||||
|
includedCategories.joinToString { it.visualName(context) }
|
||||||
// All explicitly selected
|
// All explicitly selected
|
||||||
includedCategories.size == allCategories.size -> stringResource(R.string.all)
|
includedCategories.size == allCategories.size -> stringResource(R.string.all)
|
||||||
allExcluded -> stringResource(R.string.none)
|
allExcluded -> stringResource(R.string.none)
|
||||||
|
|
|
@ -120,7 +120,9 @@ object SettingsAppearanceScreen : SearchableSettings {
|
||||||
uiPreferences: UiPreferences,
|
uiPreferences: UiPreferences,
|
||||||
): Preference.PreferenceGroup {
|
): Preference.PreferenceGroup {
|
||||||
val langs = remember { getLangs(context) }
|
val langs = remember { getLangs(context) }
|
||||||
var currentLanguage by remember { mutableStateOf(AppCompatDelegate.getApplicationLocales().get(0)?.toLanguageTag() ?: "") }
|
var currentLanguage by remember {
|
||||||
|
mutableStateOf(AppCompatDelegate.getApplicationLocales().get(0)?.toLanguageTag() ?: "")
|
||||||
|
}
|
||||||
val now = remember { Date().time }
|
val now = remember { Date().time }
|
||||||
|
|
||||||
val dateFormat by uiPreferences.dateFormat().collectAsState()
|
val dateFormat by uiPreferences.dateFormat().collectAsState()
|
||||||
|
|
|
@ -115,7 +115,9 @@ class WorkerInfoScreen : Screen() {
|
||||||
private val workManager = context.workManager
|
private val workManager = context.workManager
|
||||||
|
|
||||||
val finished = workManager
|
val finished = workManager
|
||||||
.getWorkInfosLiveData(WorkQuery.fromStates(WorkInfo.State.SUCCEEDED, WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
|
.getWorkInfosLiveData(
|
||||||
|
WorkQuery.fromStates(WorkInfo.State.SUCCEEDED, WorkInfo.State.FAILED, WorkInfo.State.CANCELLED),
|
||||||
|
)
|
||||||
.asFlow()
|
.asFlow()
|
||||||
.map(::constructString)
|
.map(::constructString)
|
||||||
.stateIn(ioCoroutineScope, SharingStarted.WhileSubscribed(), "")
|
.stateIn(ioCoroutineScope, SharingStarted.WhileSubscribed(), "")
|
||||||
|
|
|
@ -115,8 +115,16 @@ fun <T> TriStateListDialog(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!listState.isScrolledToStart()) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter))
|
if (!listState.isScrolledToStart()) {
|
||||||
if (!listState.isScrolledToEnd()) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter))
|
HorizontalDivider(
|
||||||
|
modifier = Modifier.align(Alignment.TopCenter),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (!listState.isScrolledToEnd()) {
|
||||||
|
HorizontalDivider(
|
||||||
|
modifier = Modifier.align(Alignment.BottomCenter),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -77,7 +77,9 @@ fun ChapterNavigator(
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Outlined.SkipPrevious,
|
imageVector = Icons.Outlined.SkipPrevious,
|
||||||
contentDescription = stringResource(if (isRtl) R.string.action_next_chapter else R.string.action_previous_chapter),
|
contentDescription = stringResource(
|
||||||
|
if (isRtl) R.string.action_next_chapter else R.string.action_previous_chapter,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +129,9 @@ fun ChapterNavigator(
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Outlined.SkipNext,
|
imageVector = Icons.Outlined.SkipNext,
|
||||||
contentDescription = stringResource(if (isRtl) R.string.action_previous_chapter else R.string.action_next_chapter),
|
contentDescription = stringResource(
|
||||||
|
if (isRtl) R.string.action_previous_chapter else R.string.action_next_chapter,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,9 @@ fun ReaderAppBars(
|
||||||
AppBarActions(
|
AppBarActions(
|
||||||
listOfNotNull(
|
listOfNotNull(
|
||||||
AppBar.Action(
|
AppBar.Action(
|
||||||
title = stringResource(if (bookmarked) R.string.action_remove_bookmark else R.string.action_bookmark),
|
title = stringResource(
|
||||||
|
if (bookmarked) R.string.action_remove_bookmark else R.string.action_bookmark,
|
||||||
|
),
|
||||||
icon = if (bookmarked) Icons.Outlined.Bookmark else Icons.Outlined.BookmarkBorder,
|
icon = if (bookmarked) Icons.Outlined.Bookmark else Icons.Outlined.BookmarkBorder,
|
||||||
onClick = onToggleBookmarked,
|
onClick = onToggleBookmarked,
|
||||||
),
|
),
|
||||||
|
|
|
@ -173,7 +173,9 @@ fun WebViewScreenContent(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clip(MaterialTheme.shapes.small)
|
.clip(MaterialTheme.shapes.small)
|
||||||
.clickable {
|
.clickable {
|
||||||
uriHandler.openUri("https://tachiyomi.org/docs/guides/troubleshooting/#cloudflare")
|
uriHandler.openUri(
|
||||||
|
"https://tachiyomi.org/docs/guides/troubleshooting/#cloudflare",
|
||||||
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -223,7 +223,12 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
|
||||||
|
|
||||||
fun register() {
|
fun register() {
|
||||||
if (!registered) {
|
if (!registered) {
|
||||||
ContextCompat.registerReceiver(this@App, this, IntentFilter(ACTION_DISABLE_INCOGNITO_MODE), ContextCompat.RECEIVER_NOT_EXPORTED)
|
ContextCompat.registerReceiver(
|
||||||
|
this@App,
|
||||||
|
this,
|
||||||
|
IntentFilter(ACTION_DISABLE_INCOGNITO_MODE),
|
||||||
|
ContextCompat.RECEIVER_NOT_EXPORTED,
|
||||||
|
)
|
||||||
registered = true
|
registered = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,13 +121,22 @@ object Migrations {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
prefs.edit {
|
prefs.edit {
|
||||||
putInt(libraryPreferences.filterDownloaded().key(), convertBooleanPrefToTriState("pref_filter_downloaded_key"))
|
putInt(
|
||||||
|
libraryPreferences.filterDownloaded().key(),
|
||||||
|
convertBooleanPrefToTriState("pref_filter_downloaded_key"),
|
||||||
|
)
|
||||||
remove("pref_filter_downloaded_key")
|
remove("pref_filter_downloaded_key")
|
||||||
|
|
||||||
putInt(libraryPreferences.filterUnread().key(), convertBooleanPrefToTriState("pref_filter_unread_key"))
|
putInt(
|
||||||
|
libraryPreferences.filterUnread().key(),
|
||||||
|
convertBooleanPrefToTriState("pref_filter_unread_key"),
|
||||||
|
)
|
||||||
remove("pref_filter_unread_key")
|
remove("pref_filter_unread_key")
|
||||||
|
|
||||||
putInt(libraryPreferences.filterCompleted().key(), convertBooleanPrefToTriState("pref_filter_completed_key"))
|
putInt(
|
||||||
|
libraryPreferences.filterCompleted().key(),
|
||||||
|
convertBooleanPrefToTriState("pref_filter_completed_key"),
|
||||||
|
)
|
||||||
remove("pref_filter_completed_key")
|
remove("pref_filter_completed_key")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,7 +251,10 @@ object Migrations {
|
||||||
if (oldSecureScreen) {
|
if (oldSecureScreen) {
|
||||||
securityPreferences.secureScreen().set(SecurityPreferences.SecureScreenMode.ALWAYS)
|
securityPreferences.secureScreen().set(SecurityPreferences.SecureScreenMode.ALWAYS)
|
||||||
}
|
}
|
||||||
if (DeviceUtil.isMiui && basePreferences.extensionInstaller().get() == BasePreferences.ExtensionInstaller.PACKAGEINSTALLER) {
|
if (
|
||||||
|
DeviceUtil.isMiui &&
|
||||||
|
basePreferences.extensionInstaller().get() == BasePreferences.ExtensionInstaller.PACKAGEINSTALLER
|
||||||
|
) {
|
||||||
basePreferences.extensionInstaller().set(BasePreferences.ExtensionInstaller.LEGACY)
|
basePreferences.extensionInstaller().set(BasePreferences.ExtensionInstaller.LEGACY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -259,7 +271,12 @@ object Migrations {
|
||||||
if (oldVersion < 81) {
|
if (oldVersion < 81) {
|
||||||
// Handle renamed enum values
|
// Handle renamed enum values
|
||||||
prefs.edit {
|
prefs.edit {
|
||||||
val newSortingMode = when (val oldSortingMode = prefs.getString(libraryPreferences.sortingMode().key(), "ALPHABETICAL")) {
|
val newSortingMode = when (
|
||||||
|
val oldSortingMode = prefs.getString(
|
||||||
|
libraryPreferences.sortingMode().key(),
|
||||||
|
"ALPHABETICAL",
|
||||||
|
)
|
||||||
|
) {
|
||||||
"LAST_CHECKED" -> "LAST_MANGA_UPDATE"
|
"LAST_CHECKED" -> "LAST_MANGA_UPDATE"
|
||||||
"UNREAD" -> "UNREAD_COUNT"
|
"UNREAD" -> "UNREAD_COUNT"
|
||||||
"DATE_FETCHED" -> "CHAPTER_FETCH_DATE"
|
"DATE_FETCHED" -> "CHAPTER_FETCH_DATE"
|
||||||
|
|
|
@ -20,7 +20,9 @@ class BackupNotifier(private val context: Context) {
|
||||||
|
|
||||||
private val preferences: SecurityPreferences by injectLazy()
|
private val preferences: SecurityPreferences by injectLazy()
|
||||||
|
|
||||||
private val progressNotificationBuilder = context.notificationBuilder(Notifications.CHANNEL_BACKUP_RESTORE_PROGRESS) {
|
private val progressNotificationBuilder = context.notificationBuilder(
|
||||||
|
Notifications.CHANNEL_BACKUP_RESTORE_PROGRESS,
|
||||||
|
) {
|
||||||
setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher))
|
setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher))
|
||||||
setSmallIcon(R.drawable.ic_tachi)
|
setSmallIcon(R.drawable.ic_tachi)
|
||||||
setAutoCancel(false)
|
setAutoCancel(false)
|
||||||
|
@ -28,7 +30,9 @@ class BackupNotifier(private val context: Context) {
|
||||||
setOnlyAlertOnce(true)
|
setOnlyAlertOnce(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val completeNotificationBuilder = context.notificationBuilder(Notifications.CHANNEL_BACKUP_RESTORE_COMPLETE) {
|
private val completeNotificationBuilder = context.notificationBuilder(
|
||||||
|
Notifications.CHANNEL_BACKUP_RESTORE_COMPLETE,
|
||||||
|
) {
|
||||||
setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher))
|
setLargeIcon(BitmapFactory.decodeResource(context.resources, R.mipmap.ic_launcher))
|
||||||
setSmallIcon(R.drawable.ic_tachi)
|
setSmallIcon(R.drawable.ic_tachi)
|
||||||
setAutoCancel(false)
|
setAutoCancel(false)
|
||||||
|
@ -72,14 +76,25 @@ class BackupNotifier(private val context: Context) {
|
||||||
addAction(
|
addAction(
|
||||||
R.drawable.ic_share_24dp,
|
R.drawable.ic_share_24dp,
|
||||||
context.getString(R.string.action_share),
|
context.getString(R.string.action_share),
|
||||||
NotificationReceiver.shareBackupPendingBroadcast(context, unifile.uri, Notifications.ID_BACKUP_COMPLETE),
|
NotificationReceiver.shareBackupPendingBroadcast(
|
||||||
|
context,
|
||||||
|
unifile.uri,
|
||||||
|
Notifications.ID_BACKUP_COMPLETE,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
show(Notifications.ID_BACKUP_COMPLETE)
|
show(Notifications.ID_BACKUP_COMPLETE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showRestoreProgress(content: String = "", contentTitle: String = context.getString(R.string.restoring_backup), progress: Int = 0, maxAmount: Int = 100): NotificationCompat.Builder {
|
fun showRestoreProgress(
|
||||||
|
content: String = "",
|
||||||
|
contentTitle: String = context.getString(
|
||||||
|
R.string.restoring_backup,
|
||||||
|
),
|
||||||
|
progress: Int = 0,
|
||||||
|
maxAmount: Int = 100,
|
||||||
|
): NotificationCompat.Builder {
|
||||||
val builder = with(progressNotificationBuilder) {
|
val builder = with(progressNotificationBuilder) {
|
||||||
setContentTitle(contentTitle)
|
setContentTitle(contentTitle)
|
||||||
|
|
||||||
|
@ -114,7 +129,15 @@ class BackupNotifier(private val context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showRestoreComplete(time: Long, errorCount: Int, path: String?, file: String?, contentTitle: String = context.getString(R.string.restore_completed)) {
|
fun showRestoreComplete(
|
||||||
|
time: Long,
|
||||||
|
errorCount: Int,
|
||||||
|
path: String?,
|
||||||
|
file: String?,
|
||||||
|
contentTitle: String = context.getString(
|
||||||
|
R.string.restore_completed,
|
||||||
|
),
|
||||||
|
) {
|
||||||
context.cancelNotification(Notifications.ID_RESTORE_PROGRESS)
|
context.cancelNotification(Notifications.ID_RESTORE_PROGRESS)
|
||||||
|
|
||||||
val timeString = context.getString(
|
val timeString = context.getString(
|
||||||
|
@ -127,7 +150,14 @@ class BackupNotifier(private val context: Context) {
|
||||||
|
|
||||||
with(completeNotificationBuilder) {
|
with(completeNotificationBuilder) {
|
||||||
setContentTitle(contentTitle)
|
setContentTitle(contentTitle)
|
||||||
setContentText(context.resources.getQuantityString(R.plurals.restore_completed_message, errorCount, timeString, errorCount))
|
setContentText(
|
||||||
|
context.resources.getQuantityString(
|
||||||
|
R.plurals.restore_completed_message,
|
||||||
|
errorCount,
|
||||||
|
timeString,
|
||||||
|
errorCount,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
clearActions()
|
clearActions()
|
||||||
if (errorCount > 0 && !path.isNullOrEmpty() && !file.isNullOrEmpty()) {
|
if (errorCount > 0 && !path.isNullOrEmpty() && !file.isNullOrEmpty()) {
|
||||||
|
|
|
@ -88,7 +88,13 @@ class BackupRestorer(
|
||||||
val logFile = writeErrorLog()
|
val logFile = writeErrorLog()
|
||||||
|
|
||||||
if (sync) {
|
if (sync) {
|
||||||
notifier.showRestoreComplete(time, errors.size, logFile.parent, logFile.name, contentTitle = context.getString(R.string.library_sync_complete))
|
notifier.showRestoreComplete(
|
||||||
|
time,
|
||||||
|
errors.size,
|
||||||
|
logFile.parent,
|
||||||
|
logFile.name,
|
||||||
|
contentTitle = context.getString(R.string.library_sync_complete),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
notifier.showRestoreComplete(time, errors.size, logFile.parent, logFile.name)
|
notifier.showRestoreComplete(time, errors.size, logFile.parent, logFile.name)
|
||||||
}
|
}
|
||||||
|
@ -183,7 +189,12 @@ class BackupRestorer(
|
||||||
)
|
)
|
||||||
|
|
||||||
restoreProgress += 1
|
restoreProgress += 1
|
||||||
showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.categories), context.getString(R.string.restoring_backup))
|
showRestoreProgress(
|
||||||
|
restoreProgress,
|
||||||
|
restoreAmount,
|
||||||
|
context.getString(R.string.categories),
|
||||||
|
context.getString(R.string.restoring_backup),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun restoreManga(backupManga: BackupManga, backupCategories: List<BackupCategory>, sync: Boolean) {
|
private suspend fun restoreManga(backupManga: BackupManga, backupCategories: List<BackupCategory>, sync: Boolean) {
|
||||||
|
@ -214,9 +225,19 @@ class BackupRestorer(
|
||||||
|
|
||||||
restoreProgress += 1
|
restoreProgress += 1
|
||||||
if (sync) {
|
if (sync) {
|
||||||
showRestoreProgress(restoreProgress, restoreAmount, manga.title, context.getString(R.string.syncing_library))
|
showRestoreProgress(
|
||||||
|
restoreProgress,
|
||||||
|
restoreAmount,
|
||||||
|
manga.title,
|
||||||
|
context.getString(R.string.syncing_library),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
showRestoreProgress(restoreProgress, restoreAmount, manga.title, context.getString(R.string.restoring_backup))
|
showRestoreProgress(
|
||||||
|
restoreProgress,
|
||||||
|
restoreAmount,
|
||||||
|
manga.title,
|
||||||
|
context.getString(R.string.restoring_backup),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -603,7 +624,12 @@ class BackupRestorer(
|
||||||
BackupCreateJob.setupTask(context)
|
BackupCreateJob.setupTask(context)
|
||||||
|
|
||||||
restoreProgress += 1
|
restoreProgress += 1
|
||||||
showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.app_settings), context.getString(R.string.restoring_backup))
|
showRestoreProgress(
|
||||||
|
restoreProgress,
|
||||||
|
restoreAmount,
|
||||||
|
context.getString(R.string.app_settings),
|
||||||
|
context.getString(R.string.restoring_backup),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun restoreSourcePreferences(preferences: List<BackupSourcePreferences>) {
|
private fun restoreSourcePreferences(preferences: List<BackupSourcePreferences>) {
|
||||||
|
@ -613,7 +639,12 @@ class BackupRestorer(
|
||||||
}
|
}
|
||||||
|
|
||||||
restoreProgress += 1
|
restoreProgress += 1
|
||||||
showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.source_settings), context.getString(R.string.restoring_backup))
|
showRestoreProgress(
|
||||||
|
restoreProgress,
|
||||||
|
restoreAmount,
|
||||||
|
context.getString(R.string.source_settings),
|
||||||
|
context.getString(R.string.restoring_backup),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun restorePreferences(
|
private fun restorePreferences(
|
||||||
|
|
|
@ -148,7 +148,10 @@ class DownloadCache(
|
||||||
if (sourceDir != null) {
|
if (sourceDir != null) {
|
||||||
val mangaDir = sourceDir.mangaDirs[provider.getMangaDirName(mangaTitle)]
|
val mangaDir = sourceDir.mangaDirs[provider.getMangaDirName(mangaTitle)]
|
||||||
if (mangaDir != null) {
|
if (mangaDir != null) {
|
||||||
return provider.getValidChapterDirNames(chapterName, chapterScanlator).any { it in mangaDir.chapterDirs }
|
return provider.getValidChapterDirNames(
|
||||||
|
chapterName,
|
||||||
|
chapterScanlator,
|
||||||
|
).any { it in mangaDir.chapterDirs }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -95,7 +95,10 @@ internal class DownloadNotifier(private val context: Context) {
|
||||||
} else {
|
} else {
|
||||||
val title = download.manga.title.chop(15)
|
val title = download.manga.title.chop(15)
|
||||||
val quotedTitle = Pattern.quote(title)
|
val quotedTitle = Pattern.quote(title)
|
||||||
val chapter = download.chapter.name.replaceFirst("$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE), "")
|
val chapter = download.chapter.name.replaceFirst(
|
||||||
|
"$quotedTitle[\\s]*[-]*[\\s]*".toRegex(RegexOption.IGNORE_CASE),
|
||||||
|
"",
|
||||||
|
)
|
||||||
setContentTitle("$title - $chapter".chop(30))
|
setContentTitle("$title - $chapter".chop(30))
|
||||||
setContentText(downloadingProgressText)
|
setContentText(downloadingProgressText)
|
||||||
}
|
}
|
||||||
|
|
|
@ -324,7 +324,11 @@ class Downloader(
|
||||||
val availSpace = DiskUtil.getAvailableStorageSpace(mangaDir)
|
val availSpace = DiskUtil.getAvailableStorageSpace(mangaDir)
|
||||||
if (availSpace != -1L && availSpace < MIN_DISK_SPACE) {
|
if (availSpace != -1L && availSpace < MIN_DISK_SPACE) {
|
||||||
download.status = Download.State.ERROR
|
download.status = Download.State.ERROR
|
||||||
notifier.onError(context.getString(R.string.download_insufficient_space), download.chapter.name, download.manga.title)
|
notifier.onError(
|
||||||
|
context.getString(R.string.download_insufficient_space),
|
||||||
|
download.chapter.name,
|
||||||
|
download.manga.title,
|
||||||
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,13 +436,17 @@ class Downloader(
|
||||||
tmpFile?.delete()
|
tmpFile?.delete()
|
||||||
|
|
||||||
// Try to find the image file
|
// Try to find the image file
|
||||||
val imageFile = tmpDir.listFiles()?.firstOrNull { it.name!!.startsWith("$filename.") || it.name!!.startsWith("${filename}__001") }
|
val imageFile = tmpDir.listFiles()?.firstOrNull {
|
||||||
|
it.name!!.startsWith("$filename.") || it.name!!.startsWith("${filename}__001")
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// If the image is already downloaded, do nothing. Otherwise download from network
|
// If the image is already downloaded, do nothing. Otherwise download from network
|
||||||
val file = when {
|
val file = when {
|
||||||
imageFile != null -> imageFile
|
imageFile != null -> imageFile
|
||||||
chapterCache.isImageInCache(page.imageUrl!!) -> copyImageFromCache(chapterCache.getImageFile(page.imageUrl!!), tmpDir, filename)
|
chapterCache.isImageInCache(
|
||||||
|
page.imageUrl!!,
|
||||||
|
) -> copyImageFromCache(chapterCache.getImageFile(page.imageUrl!!), tmpDir, filename)
|
||||||
else -> downloadImage(page, download.source, tmpDir, filename)
|
else -> downloadImage(page, download.source, tmpDir, filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -296,7 +296,9 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||||
val errorMessage = when (e) {
|
val errorMessage = when (e) {
|
||||||
is NoChaptersException -> context.getString(R.string.no_chapters_error)
|
is NoChaptersException -> context.getString(R.string.no_chapters_error)
|
||||||
// failedUpdates will already have the source, don't need to copy it into the message
|
// failedUpdates will already have the source, don't need to copy it into the message
|
||||||
is SourceNotInstalledException -> context.getString(R.string.loader_not_implemented_error)
|
is SourceNotInstalledException -> context.getString(
|
||||||
|
R.string.loader_not_implemented_error,
|
||||||
|
)
|
||||||
else -> e.message
|
else -> e.message
|
||||||
}
|
}
|
||||||
failedUpdates.add(manga to errorMessage)
|
failedUpdates.add(manga to errorMessage)
|
||||||
|
@ -500,7 +502,9 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||||
if (interval > 0) {
|
if (interval > 0) {
|
||||||
val restrictions = preferences.autoUpdateDeviceRestrictions().get()
|
val restrictions = preferences.autoUpdateDeviceRestrictions().get()
|
||||||
val constraints = Constraints(
|
val constraints = Constraints(
|
||||||
requiredNetworkType = if (DEVICE_NETWORK_NOT_METERED in restrictions) { NetworkType.UNMETERED } else { NetworkType.CONNECTED },
|
requiredNetworkType = if (DEVICE_NETWORK_NOT_METERED in restrictions) {
|
||||||
|
NetworkType.UNMETERED
|
||||||
|
} else { NetworkType.CONNECTED },
|
||||||
requiresCharging = DEVICE_CHARGING in restrictions,
|
requiresCharging = DEVICE_CHARGING in restrictions,
|
||||||
requiresBatteryNotLow = true,
|
requiresBatteryNotLow = true,
|
||||||
)
|
)
|
||||||
|
@ -517,7 +521,11 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
|
||||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.MINUTES)
|
.setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.MINUTES)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
context.workManager.enqueueUniquePeriodicWork(WORK_NAME_AUTO, ExistingPeriodicWorkPolicy.UPDATE, request)
|
context.workManager.enqueueUniquePeriodicWork(
|
||||||
|
WORK_NAME_AUTO,
|
||||||
|
ExistingPeriodicWorkPolicy.UPDATE,
|
||||||
|
request,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
context.workManager.cancelUniqueWork(WORK_NAME_AUTO)
|
context.workManager.cancelUniqueWork(WORK_NAME_AUTO)
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,12 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||||
} else {
|
} else {
|
||||||
val updatingText = manga.joinToString("\n") { it.title.chop(40) }
|
val updatingText = manga.joinToString("\n") { it.title.chop(40) }
|
||||||
progressNotificationBuilder
|
progressNotificationBuilder
|
||||||
.setContentTitle(context.getString(R.string.notification_updating_progress, percentFormatter.format(current.toFloat() / total)))
|
.setContentTitle(
|
||||||
|
context.getString(
|
||||||
|
R.string.notification_updating_progress,
|
||||||
|
percentFormatter.format(current.toFloat() / total),
|
||||||
|
),
|
||||||
|
)
|
||||||
.setStyle(NotificationCompat.BigTextStyle().bigText(updatingText))
|
.setStyle(NotificationCompat.BigTextStyle().bigText(updatingText))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +171,13 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||||
if (updates.size == 1 && !preferences.hideNotificationContent().get()) {
|
if (updates.size == 1 && !preferences.hideNotificationContent().get()) {
|
||||||
setContentText(updates.first().first.title.chop(NOTIF_TITLE_MAX_LEN))
|
setContentText(updates.first().first.title.chop(NOTIF_TITLE_MAX_LEN))
|
||||||
} else {
|
} else {
|
||||||
setContentText(context.resources.getQuantityString(R.plurals.notification_new_chapters_summary, updates.size, updates.size))
|
setContentText(
|
||||||
|
context.resources.getQuantityString(
|
||||||
|
R.plurals.notification_new_chapters_summary,
|
||||||
|
updates.size,
|
||||||
|
updates.size,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
if (!preferences.hideNotificationContent().get()) {
|
if (!preferences.hideNotificationContent().get()) {
|
||||||
setStyle(
|
setStyle(
|
||||||
|
@ -196,7 +207,10 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||||
launchUI {
|
launchUI {
|
||||||
context.notify(
|
context.notify(
|
||||||
updates.map { (manga, chapters) ->
|
updates.map { (manga, chapters) ->
|
||||||
NotificationManagerCompat.NotificationWithIdAndTag(manga.id.hashCode(), createNewChaptersNotification(manga, chapters))
|
NotificationManagerCompat.NotificationWithIdAndTag(
|
||||||
|
manga.id.hashCode(),
|
||||||
|
createNewChaptersNotification(manga, chapters),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -292,17 +306,28 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||||
// No sensible chapter numbers to show (i.e. no chapters have parsed chapter number)
|
// No sensible chapter numbers to show (i.e. no chapters have parsed chapter number)
|
||||||
0 -> {
|
0 -> {
|
||||||
// "1 new chapter" or "5 new chapters"
|
// "1 new chapter" or "5 new chapters"
|
||||||
context.resources.getQuantityString(R.plurals.notification_chapters_generic, chapters.size, chapters.size)
|
context.resources.getQuantityString(
|
||||||
|
R.plurals.notification_chapters_generic,
|
||||||
|
chapters.size,
|
||||||
|
chapters.size,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
// Only 1 chapter has a parsed chapter number
|
// Only 1 chapter has a parsed chapter number
|
||||||
1 -> {
|
1 -> {
|
||||||
val remaining = chapters.size - displayableChapterNumbers.size
|
val remaining = chapters.size - displayableChapterNumbers.size
|
||||||
if (remaining == 0) {
|
if (remaining == 0) {
|
||||||
// "Chapter 2.5"
|
// "Chapter 2.5"
|
||||||
context.resources.getString(R.string.notification_chapters_single, displayableChapterNumbers.first())
|
context.resources.getString(
|
||||||
|
R.string.notification_chapters_single,
|
||||||
|
displayableChapterNumbers.first(),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// "Chapter 2.5 and 10 more"
|
// "Chapter 2.5 and 10 more"
|
||||||
context.resources.getString(R.string.notification_chapters_single_and_more, displayableChapterNumbers.first(), remaining)
|
context.resources.getString(
|
||||||
|
R.string.notification_chapters_single_and_more,
|
||||||
|
displayableChapterNumbers.first(),
|
||||||
|
remaining,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Everything else (i.e. multiple parsed chapter numbers)
|
// Everything else (i.e. multiple parsed chapter numbers)
|
||||||
|
@ -312,10 +337,18 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||||
// "Chapters 1, 2.5, 3, 4, 5 and 10 more"
|
// "Chapters 1, 2.5, 3, 4, 5 and 10 more"
|
||||||
val remaining = displayableChapterNumbers.size - NOTIF_MAX_CHAPTERS
|
val remaining = displayableChapterNumbers.size - NOTIF_MAX_CHAPTERS
|
||||||
val joinedChapterNumbers = displayableChapterNumbers.take(NOTIF_MAX_CHAPTERS).joinToString(", ")
|
val joinedChapterNumbers = displayableChapterNumbers.take(NOTIF_MAX_CHAPTERS).joinToString(", ")
|
||||||
context.resources.getQuantityString(R.plurals.notification_chapters_multiple_and_more, remaining, joinedChapterNumbers, remaining)
|
context.resources.getQuantityString(
|
||||||
|
R.plurals.notification_chapters_multiple_and_more,
|
||||||
|
remaining,
|
||||||
|
joinedChapterNumbers,
|
||||||
|
remaining,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
// "Chapters 1, 2.5, 3"
|
// "Chapters 1, 2.5, 3"
|
||||||
context.resources.getString(R.string.notification_chapters_multiple, displayableChapterNumbers.joinToString(", "))
|
context.resources.getString(
|
||||||
|
R.string.notification_chapters_multiple,
|
||||||
|
displayableChapterNumbers.joinToString(", "),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -329,7 +362,12 @@ class LibraryUpdateNotifier(private val context: Context) {
|
||||||
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||||
action = Constants.SHORTCUT_UPDATES
|
action = Constants.SHORTCUT_UPDATES
|
||||||
}
|
}
|
||||||
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -23,7 +23,12 @@ object NotificationHandler {
|
||||||
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||||
action = Constants.SHORTCUT_DOWNLOADS
|
action = Constants.SHORTCUT_DOWNLOADS
|
||||||
}
|
}
|
||||||
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,7 +42,12 @@ object NotificationHandler {
|
||||||
setDataAndType(uri, "image/*")
|
setDataAndType(uri, "image/*")
|
||||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
}
|
}
|
||||||
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -306,7 +306,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
||||||
action = ACTION_RESUME_DOWNLOADS
|
action = ACTION_RESUME_DOWNLOADS
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -319,7 +324,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
||||||
action = ACTION_PAUSE_DOWNLOADS
|
action = ACTION_PAUSE_DOWNLOADS
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -332,7 +342,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
||||||
action = ACTION_CLEAR_DOWNLOADS
|
action = ACTION_CLEAR_DOWNLOADS
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -347,7 +362,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
action = ACTION_DISMISS_NOTIFICATION
|
action = ACTION_DISMISS_NOTIFICATION
|
||||||
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -402,7 +422,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
putExtra(EXTRA_FILE_LOCATION, path)
|
putExtra(EXTRA_FILE_LOCATION, path)
|
||||||
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -419,7 +444,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
putExtra(EXTRA_FILE_LOCATION, path)
|
putExtra(EXTRA_FILE_LOCATION, path)
|
||||||
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -431,7 +461,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
*/
|
*/
|
||||||
internal fun openChapterPendingActivity(context: Context, manga: Manga, chapter: Chapter): PendingIntent {
|
internal fun openChapterPendingActivity(context: Context, manga: Manga, chapter: Chapter): PendingIntent {
|
||||||
val newIntent = ReaderActivity.newIntent(context, manga.id, chapter.id)
|
val newIntent = ReaderActivity.newIntent(context, manga.id, chapter.id)
|
||||||
return PendingIntent.getActivity(context, manga.id.hashCode(), newIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
manga.id.hashCode(),
|
||||||
|
newIntent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -447,7 +482,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
.putExtra(Constants.MANGA_EXTRA, manga.id)
|
.putExtra(Constants.MANGA_EXTRA, manga.id)
|
||||||
.putExtra("notificationId", manga.id.hashCode())
|
.putExtra("notificationId", manga.id.hashCode())
|
||||||
.putExtra("groupId", groupId)
|
.putExtra("groupId", groupId)
|
||||||
return PendingIntent.getActivity(context, manga.id.hashCode(), newIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
manga.id.hashCode(),
|
||||||
|
newIntent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -469,7 +509,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
putExtra(EXTRA_NOTIFICATION_ID, manga.id.hashCode())
|
putExtra(EXTRA_NOTIFICATION_ID, manga.id.hashCode())
|
||||||
putExtra(EXTRA_GROUP_ID, groupId)
|
putExtra(EXTRA_GROUP_ID, groupId)
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, manga.id.hashCode(), newIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
manga.id.hashCode(),
|
||||||
|
newIntent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -491,7 +536,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
putExtra(EXTRA_NOTIFICATION_ID, manga.id.hashCode())
|
putExtra(EXTRA_NOTIFICATION_ID, manga.id.hashCode())
|
||||||
putExtra(EXTRA_GROUP_ID, groupId)
|
putExtra(EXTRA_GROUP_ID, groupId)
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, manga.id.hashCode(), newIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
manga.id.hashCode(),
|
||||||
|
newIntent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -504,7 +554,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
||||||
action = ACTION_CANCEL_LIBRARY_UPDATE
|
action = ACTION_CANCEL_LIBRARY_UPDATE
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -513,12 +568,21 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
* @param context context of application
|
* @param context context of application
|
||||||
* @return [PendingIntent]
|
* @return [PendingIntent]
|
||||||
*/
|
*/
|
||||||
internal fun downloadAppUpdatePendingBroadcast(context: Context, url: String, title: String? = null): PendingIntent {
|
internal fun downloadAppUpdatePendingBroadcast(
|
||||||
|
context: Context,
|
||||||
|
url: String,
|
||||||
|
title: String? = null,
|
||||||
|
): PendingIntent {
|
||||||
return Intent(context, NotificationReceiver::class.java).run {
|
return Intent(context, NotificationReceiver::class.java).run {
|
||||||
action = ACTION_START_APP_UPDATE
|
action = ACTION_START_APP_UPDATE
|
||||||
putExtra(AppUpdateDownloadJob.EXTRA_DOWNLOAD_URL, url)
|
putExtra(AppUpdateDownloadJob.EXTRA_DOWNLOAD_URL, url)
|
||||||
title?.let { putExtra(AppUpdateDownloadJob.EXTRA_DOWNLOAD_TITLE, it) }
|
title?.let { putExtra(AppUpdateDownloadJob.EXTRA_DOWNLOAD_TITLE, it) }
|
||||||
PendingIntent.getBroadcast(context, 0, this, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
this,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,7 +593,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
val intent = Intent(context, NotificationReceiver::class.java).apply {
|
||||||
action = ACTION_CANCEL_APP_UPDATE_DOWNLOAD
|
action = ACTION_CANCEL_APP_UPDATE_DOWNLOAD
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -543,7 +612,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
action = Constants.SHORTCUT_EXTENSIONS
|
action = Constants.SHORTCUT_EXTENSIONS
|
||||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||||
}
|
}
|
||||||
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -560,7 +634,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
putExtra(EXTRA_URI, uri)
|
putExtra(EXTRA_URI, uri)
|
||||||
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -591,7 +670,12 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||||
action = ACTION_CANCEL_RESTORE
|
action = ACTION_CANCEL_RESTORE
|
||||||
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
|
||||||
}
|
}
|
||||||
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,11 +48,15 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
|
||||||
when (it.code) {
|
when (it.code) {
|
||||||
200 -> return it.parseAs<AuthenticationDto>().token
|
200 -> return it.parseAs<AuthenticationDto>().token
|
||||||
401 -> {
|
401 -> {
|
||||||
logcat(LogPriority.WARN) { "Unauthorized / api key not valid: Cleaned api URL: $apiUrl, Api key is empty: ${apiKey.isEmpty()}" }
|
logcat(LogPriority.WARN) {
|
||||||
|
"Unauthorized / API key not valid: API URL: $apiUrl, empty API key: ${apiKey.isEmpty()}"
|
||||||
|
}
|
||||||
throw IOException("Unauthorized / api key not valid")
|
throw IOException("Unauthorized / api key not valid")
|
||||||
}
|
}
|
||||||
500 -> {
|
500 -> {
|
||||||
logcat(LogPriority.WARN) { "Error fetching JWT token. Cleaned api URL: $apiUrl, Api key is empty: ${apiKey.isEmpty()}" }
|
logcat(
|
||||||
|
LogPriority.WARN,
|
||||||
|
) { "Error fetching JWT token. API URL: $apiUrl, empty API key: ${apiKey.isEmpty()}" }
|
||||||
throw IOException("Error fetching JWT token")
|
throw IOException("Error fetching JWT token")
|
||||||
}
|
}
|
||||||
else -> {}
|
else -> {}
|
||||||
|
@ -62,12 +66,12 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
|
||||||
// Not sure which one to catch
|
// Not sure which one to catch
|
||||||
} catch (e: SocketTimeoutException) {
|
} catch (e: SocketTimeoutException) {
|
||||||
logcat(LogPriority.WARN) {
|
logcat(LogPriority.WARN) {
|
||||||
"Could not fetch JWT token. Probably due to connectivity issue or the url '$apiUrl' is not available, skipping"
|
"Could not fetch JWT token. Probably due to connectivity issue or URL '$apiUrl' not available, skipping"
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logcat(LogPriority.ERROR) {
|
logcat(LogPriority.ERROR) {
|
||||||
"Unhandled exception fetching JWT token for url: '$apiUrl'"
|
"Unhandled exception fetching JWT token for URL: '$apiUrl'"
|
||||||
}
|
}
|
||||||
throw IOException(e)
|
throw IOException(e)
|
||||||
}
|
}
|
||||||
|
@ -129,7 +133,10 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logcat(LogPriority.WARN, e) { "Exception getting latest chapter read. Could not get itemRequest: $requestUrl" }
|
logcat(
|
||||||
|
LogPriority.WARN,
|
||||||
|
e,
|
||||||
|
) { "Exception getting latest chapter read. Could not get itemRequest: $requestUrl" }
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
return 0F
|
return 0F
|
||||||
|
@ -164,8 +171,14 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun updateProgress(track: Track): Track {
|
suspend fun updateProgress(track: Track): Track {
|
||||||
val requestUrl = "${getApiFromUrl(track.tracking_url)}/Tachiyomi/mark-chapter-until-as-read?seriesId=${getIdFromUrl(track.tracking_url)}&chapterNumber=${track.last_chapter_read}"
|
val requestUrl = "${getApiFromUrl(
|
||||||
authClient.newCall(POST(requestUrl, body = "{}".toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())))
|
track.tracking_url,
|
||||||
|
)}/Tachiyomi/mark-chapter-until-as-read?seriesId=${getIdFromUrl(
|
||||||
|
track.tracking_url,
|
||||||
|
)}&chapterNumber=${track.last_chapter_read}"
|
||||||
|
authClient.newCall(
|
||||||
|
POST(requestUrl, body = "{}".toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())),
|
||||||
|
)
|
||||||
.awaitSuccess()
|
.awaitSuccess()
|
||||||
return getTrackSearch(track.tracking_url)
|
return getTrackSearch(track.tracking_url)
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,7 +101,10 @@ class MyAnimeListApi(
|
||||||
return withIOContext {
|
return withIOContext {
|
||||||
val url = "$baseApiUrl/manga".toUri().buildUpon()
|
val url = "$baseApiUrl/manga".toUri().buildUpon()
|
||||||
.appendPath(id.toString())
|
.appendPath(id.toString())
|
||||||
.appendQueryParameter("fields", "id,title,synopsis,num_chapters,main_picture,status,media_type,start_date")
|
.appendQueryParameter(
|
||||||
|
"fields",
|
||||||
|
"id,title,synopsis,num_chapters,main_picture,status,media_type,start_date",
|
||||||
|
)
|
||||||
.build()
|
.build()
|
||||||
with(json) {
|
with(json) {
|
||||||
authClient.newCall(GET(url.toString()))
|
authClient.newCall(GET(url.toString()))
|
||||||
|
|
|
@ -91,7 +91,9 @@ class Suwayomi(id: Long) : BaseTracker(id, "Suwayomi"), EnhancedTracker {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isTrackFrom(track: DomainTrack, manga: DomainManga, source: Source?): Boolean = source?.let { accept(it) } == true
|
override fun isTrackFrom(track: DomainTrack, manga: DomainManga, source: Source?): Boolean = source?.let {
|
||||||
|
accept(it)
|
||||||
|
} == true
|
||||||
|
|
||||||
override fun migrateTrack(track: DomainTrack, manga: DomainManga, newSource: Source): DomainTrack? =
|
override fun migrateTrack(track: DomainTrack, manga: DomainManga, newSource: Source): DomainTrack? =
|
||||||
if (accept(newSource)) {
|
if (accept(newSource)) {
|
||||||
|
|
|
@ -32,7 +32,9 @@ class AppUpdateChecker {
|
||||||
|
|
||||||
when (result) {
|
when (result) {
|
||||||
is GetApplicationRelease.Result.NewUpdate -> AppUpdateNotifier(context).promptUpdate(result.release)
|
is GetApplicationRelease.Result.NewUpdate -> AppUpdateNotifier(context).promptUpdate(result.release)
|
||||||
is GetApplicationRelease.Result.ThirdPartyInstallation -> AppUpdateNotifier(context).promptFdroidUpdate()
|
is GetApplicationRelease.Result.ThirdPartyInstallation -> AppUpdateNotifier(
|
||||||
|
context,
|
||||||
|
).promptFdroidUpdate()
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,12 @@ internal class AppUpdateNotifier(private val context: Context) {
|
||||||
|
|
||||||
val releaseIntent = Intent(Intent.ACTION_VIEW, release.releaseLink.toUri()).run {
|
val releaseIntent = Intent(Intent.ACTION_VIEW, release.releaseLink.toUri()).run {
|
||||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||||
PendingIntent.getActivity(context, release.hashCode(), this, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
PendingIntent.getActivity(
|
||||||
|
context,
|
||||||
|
release.hashCode(),
|
||||||
|
this,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
with(notificationBuilder) {
|
with(notificationBuilder) {
|
||||||
|
@ -143,7 +148,12 @@ internal class AppUpdateNotifier(private val context: Context) {
|
||||||
setContentTitle(context.getString(R.string.update_check_notification_update_available))
|
setContentTitle(context.getString(R.string.update_check_notification_update_available))
|
||||||
setContentText(context.getString(R.string.update_check_fdroid_migration_info))
|
setContentText(context.getString(R.string.update_check_fdroid_migration_info))
|
||||||
setSmallIcon(R.drawable.ic_tachi)
|
setSmallIcon(R.drawable.ic_tachi)
|
||||||
setContentIntent(NotificationHandler.openUrl(context, "https://tachiyomi.org/docs/faq/general#how-do-i-update-from-the-f-droid-builds"))
|
setContentIntent(
|
||||||
|
NotificationHandler.openUrl(
|
||||||
|
context,
|
||||||
|
"https://tachiyomi.org/docs/faq/general#how-do-i-update-from-the-f-droid-builds",
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
notificationBuilder.show(Notifications.ID_APP_UPDATE_PROMPT)
|
notificationBuilder.show(Notifications.ID_APP_UPDATE_PROMPT)
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,7 +101,12 @@ class PackageInstallerInstaller(private val service: Service) : Installer(servic
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
ContextCompat.registerReceiver(service, packageActionReceiver, IntentFilter(INSTALL_ACTION), ContextCompat.RECEIVER_EXPORTED)
|
ContextCompat.registerReceiver(
|
||||||
|
service,
|
||||||
|
packageActionReceiver,
|
||||||
|
IntentFilter(INSTALL_ACTION),
|
||||||
|
ContextCompat.RECEIVER_EXPORTED,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,9 @@ internal class ExtensionInstallReceiver(private val listener: Listener) :
|
||||||
logcat(LogPriority.WARN) { "Package name not found" }
|
logcat(LogPriority.WARN) { "Package name not found" }
|
||||||
return LoadResult.Error
|
return LoadResult.Error
|
||||||
}
|
}
|
||||||
return GlobalScope.async(Dispatchers.Default, CoroutineStart.DEFAULT) { ExtensionLoader.loadExtensionFromPkgName(context, pkgName) }.await()
|
return GlobalScope.async(Dispatchers.Default, CoroutineStart.DEFAULT) {
|
||||||
|
ExtensionLoader.loadExtensionFromPkgName(context, pkgName)
|
||||||
|
}.await()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -36,7 +36,9 @@ class AndroidSourceManager(
|
||||||
|
|
||||||
private val stubSourcesMap = ConcurrentHashMap<Long, StubSource>()
|
private val stubSourcesMap = ConcurrentHashMap<Long, StubSource>()
|
||||||
|
|
||||||
override val catalogueSources: Flow<List<CatalogueSource>> = sourcesMapFlow.map { it.values.filterIsInstance<CatalogueSource>() }
|
override val catalogueSources: Flow<List<CatalogueSource>> = sourcesMapFlow.map {
|
||||||
|
it.values.filterIsInstance<CatalogueSource>()
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
|
|
|
@ -264,7 +264,9 @@ class BrowseSourceScreenModel(
|
||||||
// Choose a category
|
// Choose a category
|
||||||
else -> {
|
else -> {
|
||||||
val preselectedIds = getCategories.await(manga.id).map { it.id }
|
val preselectedIds = getCategories.await(manga.id).map { it.id }
|
||||||
setDialog(Dialog.ChangeMangaCategory(manga, categories.mapAsCheckboxState { it.id in preselectedIds }))
|
setDialog(
|
||||||
|
Dialog.ChangeMangaCategory(manga, categories.mapAsCheckboxState { it.id in preselectedIds }),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -314,7 +316,10 @@ class BrowseSourceScreenModel(
|
||||||
sealed class Listing(open val query: String?, open val filters: FilterList) {
|
sealed class Listing(open val query: String?, open val filters: FilterList) {
|
||||||
data object Popular : Listing(query = GetRemoteManga.QUERY_POPULAR, filters = FilterList())
|
data object Popular : Listing(query = GetRemoteManga.QUERY_POPULAR, filters = FilterList())
|
||||||
data object Latest : Listing(query = GetRemoteManga.QUERY_LATEST, filters = FilterList())
|
data object Latest : Listing(query = GetRemoteManga.QUERY_LATEST, filters = FilterList())
|
||||||
data class Search(override val query: String?, override val filters: FilterList) : Listing(query = query, filters = filters)
|
data class Search(
|
||||||
|
override val query: String?,
|
||||||
|
override val filters: FilterList,
|
||||||
|
) : Listing(query = query, filters = filters)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun valueOf(query: String?): Listing {
|
fun valueOf(query: String?): Listing {
|
||||||
|
|
|
@ -543,7 +543,8 @@ class LibraryScreenModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getColumnsPreferenceForCurrentOrientation(isLandscape: Boolean): PreferenceMutableState<Int> {
|
fun getColumnsPreferenceForCurrentOrientation(isLandscape: Boolean): PreferenceMutableState<Int> {
|
||||||
return (if (isLandscape) libraryPreferences.landscapeColumns() else libraryPreferences.portraitColumns()).asState(screenModelScope)
|
return (if (isLandscape) libraryPreferences.landscapeColumns() else libraryPreferences.portraitColumns())
|
||||||
|
.asState(screenModelScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getRandomLibraryItemForCurrentCategory(): LibraryItem? {
|
suspend fun getRandomLibraryItemForCurrentCategory(): LibraryItem? {
|
||||||
|
|
|
@ -178,7 +178,9 @@ object LibraryTab : Tab {
|
||||||
scope.launchIO {
|
scope.launchIO {
|
||||||
val chapter = screenModel.getNextUnreadChapter(it.manga)
|
val chapter = screenModel.getNextUnreadChapter(it.manga)
|
||||||
if (chapter != null) {
|
if (chapter != null) {
|
||||||
context.startActivity(ReaderActivity.newIntent(context, chapter.mangaId, chapter.id))
|
context.startActivity(
|
||||||
|
ReaderActivity.newIntent(context, chapter.mangaId, chapter.id),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
snackbarHostState.showSnackbar(context.getString(R.string.no_next_chapter))
|
snackbarHostState.showSnackbar(context.getString(R.string.no_next_chapter))
|
||||||
}
|
}
|
||||||
|
|
|
@ -388,7 +388,11 @@ class MainActivity : BaseActivity() {
|
||||||
private fun handleIntentAction(intent: Intent, navigator: Navigator): Boolean {
|
private fun handleIntentAction(intent: Intent, navigator: Navigator): Boolean {
|
||||||
val notificationId = intent.getIntExtra("notificationId", -1)
|
val notificationId = intent.getIntExtra("notificationId", -1)
|
||||||
if (notificationId > -1) {
|
if (notificationId > -1) {
|
||||||
NotificationReceiver.dismissNotification(applicationContext, notificationId, intent.getIntExtra("groupId", 0))
|
NotificationReceiver.dismissNotification(
|
||||||
|
applicationContext,
|
||||||
|
notificationId,
|
||||||
|
intent.getIntExtra("groupId", 0),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val tabToOpen = when (intent.action) {
|
val tabToOpen = when (intent.action) {
|
||||||
|
|
|
@ -112,8 +112,20 @@ class MangaScreen(
|
||||||
screenModel.toggleFavorite()
|
screenModel.toggleFavorite()
|
||||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||||
},
|
},
|
||||||
onWebViewClicked = { openMangaInWebView(navigator, screenModel.manga, screenModel.source) }.takeIf { isHttpSource },
|
onWebViewClicked = {
|
||||||
onWebViewLongClicked = { copyMangaUrl(context, screenModel.manga, screenModel.source) }.takeIf { isHttpSource },
|
openMangaInWebView(
|
||||||
|
navigator,
|
||||||
|
screenModel.manga,
|
||||||
|
screenModel.source,
|
||||||
|
)
|
||||||
|
}.takeIf { isHttpSource },
|
||||||
|
onWebViewLongClicked = {
|
||||||
|
copyMangaUrl(
|
||||||
|
context,
|
||||||
|
screenModel.manga,
|
||||||
|
screenModel.source,
|
||||||
|
)
|
||||||
|
}.takeIf { isHttpSource },
|
||||||
onTrackingClicked = screenModel::showTrackDialog.takeIf { successState.trackingAvailable },
|
onTrackingClicked = screenModel::showTrackDialog.takeIf { successState.trackingAvailable },
|
||||||
onTagSearch = { scope.launch { performGenreSearch(navigator, it, screenModel.source!!) } },
|
onTagSearch = { scope.launch { performGenreSearch(navigator, it, screenModel.source!!) } },
|
||||||
onFilterButtonClicked = screenModel::showSettingsDialog,
|
onFilterButtonClicked = screenModel::showSettingsDialog,
|
||||||
|
@ -124,8 +136,12 @@ class MangaScreen(
|
||||||
onShareClicked = { shareManga(context, screenModel.manga, screenModel.source) }.takeIf { isHttpSource },
|
onShareClicked = { shareManga(context, screenModel.manga, screenModel.source) }.takeIf { isHttpSource },
|
||||||
onDownloadActionClicked = screenModel::runDownloadAction.takeIf { !successState.source.isLocalOrStub() },
|
onDownloadActionClicked = screenModel::runDownloadAction.takeIf { !successState.source.isLocalOrStub() },
|
||||||
onEditCategoryClicked = screenModel::showChangeCategoryDialog.takeIf { successState.manga.favorite },
|
onEditCategoryClicked = screenModel::showChangeCategoryDialog.takeIf { successState.manga.favorite },
|
||||||
onEditFetchIntervalClicked = screenModel::showSetFetchIntervalDialog.takeIf { screenModel.isUpdateIntervalEnabled && successState.manga.favorite },
|
onEditFetchIntervalClicked = screenModel::showSetFetchIntervalDialog.takeIf {
|
||||||
onMigrateClicked = { navigator.push(MigrateSearchScreen(successState.manga.id)) }.takeIf { successState.manga.favorite },
|
screenModel.isUpdateIntervalEnabled && successState.manga.favorite
|
||||||
|
},
|
||||||
|
onMigrateClicked = {
|
||||||
|
navigator.push(MigrateSearchScreen(successState.manga.id))
|
||||||
|
}.takeIf { successState.manga.favorite },
|
||||||
onMultiBookmarkClicked = screenModel::bookmarkChapters,
|
onMultiBookmarkClicked = screenModel::bookmarkChapters,
|
||||||
onMultiMarkAsReadClicked = screenModel::markChaptersRead,
|
onMultiMarkAsReadClicked = screenModel::markChaptersRead,
|
||||||
onMarkPreviousAsReadClicked = screenModel::markPreviousChapterRead,
|
onMarkPreviousAsReadClicked = screenModel::markPreviousChapterRead,
|
||||||
|
|
|
@ -160,8 +160,18 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||||
readerPreferences.skipFiltered().get() -> {
|
readerPreferences.skipFiltered().get() -> {
|
||||||
(manga.unreadFilterRaw == Manga.CHAPTER_SHOW_READ && !it.read) ||
|
(manga.unreadFilterRaw == Manga.CHAPTER_SHOW_READ && !it.read) ||
|
||||||
(manga.unreadFilterRaw == Manga.CHAPTER_SHOW_UNREAD && it.read) ||
|
(manga.unreadFilterRaw == Manga.CHAPTER_SHOW_UNREAD && it.read) ||
|
||||||
(manga.downloadedFilterRaw == Manga.CHAPTER_SHOW_DOWNLOADED && !downloadManager.isChapterDownloaded(it.name, it.scanlator, manga.title, manga.source)) ||
|
(
|
||||||
(manga.downloadedFilterRaw == Manga.CHAPTER_SHOW_NOT_DOWNLOADED && downloadManager.isChapterDownloaded(it.name, it.scanlator, manga.title, manga.source)) ||
|
manga.downloadedFilterRaw == Manga.CHAPTER_SHOW_DOWNLOADED &&
|
||||||
|
!downloadManager.isChapterDownloaded(
|
||||||
|
it.name, it.scanlator, manga.title, manga.source,
|
||||||
|
)
|
||||||
|
) ||
|
||||||
|
(
|
||||||
|
manga.downloadedFilterRaw == Manga.CHAPTER_SHOW_NOT_DOWNLOADED &&
|
||||||
|
downloadManager.isChapterDownloaded(
|
||||||
|
it.name, it.scanlator, manga.title, manga.source,
|
||||||
|
)
|
||||||
|
) ||
|
||||||
(manga.bookmarkedFilterRaw == Manga.CHAPTER_SHOW_BOOKMARKED && !it.bookmark) ||
|
(manga.bookmarkedFilterRaw == Manga.CHAPTER_SHOW_BOOKMARKED && !it.bookmark) ||
|
||||||
(manga.bookmarkedFilterRaw == Manga.CHAPTER_SHOW_NOT_BOOKMARKED && it.bookmark)
|
(manga.bookmarkedFilterRaw == Manga.CHAPTER_SHOW_NOT_BOOKMARKED && it.bookmark)
|
||||||
}
|
}
|
||||||
|
@ -759,7 +769,13 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||||
val filename = generateFilename(manga, page)
|
val filename = generateFilename(manga, page)
|
||||||
|
|
||||||
// Pictures directory.
|
// Pictures directory.
|
||||||
val relativePath = if (readerPreferences.folderPerManga().get()) DiskUtil.buildValidFilename(manga.title) else ""
|
val relativePath = if (readerPreferences.folderPerManga().get()) {
|
||||||
|
DiskUtil.buildValidFilename(
|
||||||
|
manga.title,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
|
||||||
// Copy file in background.
|
// Copy file in background.
|
||||||
viewModelScope.launchNonCancellable {
|
viewModelScope.launchNonCancellable {
|
||||||
|
|
|
@ -75,7 +75,13 @@ class ChapterLoader(
|
||||||
*/
|
*/
|
||||||
private fun getPageLoader(chapter: ReaderChapter): PageLoader {
|
private fun getPageLoader(chapter: ReaderChapter): PageLoader {
|
||||||
val dbChapter = chapter.chapter
|
val dbChapter = chapter.chapter
|
||||||
val isDownloaded = downloadManager.isChapterDownloaded(dbChapter.name, dbChapter.scanlator, manga.title, manga.source, skipCache = true)
|
val isDownloaded = downloadManager.isChapterDownloaded(
|
||||||
|
dbChapter.name,
|
||||||
|
dbChapter.scanlator,
|
||||||
|
manga.title,
|
||||||
|
manga.source,
|
||||||
|
skipCache = true,
|
||||||
|
)
|
||||||
return when {
|
return when {
|
||||||
isDownloaded -> DownloadPageLoader(chapter, manga, source, downloadManager, downloadProvider)
|
isDownloaded -> DownloadPageLoader(chapter, manga, source, downloadManager, downloadProvider)
|
||||||
source is LocalSource -> source.getFormat(chapter.chapter).let { format ->
|
source is LocalSource -> source.getFormat(chapter.chapter).let { format ->
|
||||||
|
|
|
@ -5,14 +5,54 @@ import androidx.annotation.DrawableRes
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
|
||||||
enum class OrientationType(val flag: Int, @StringRes val stringRes: Int, @DrawableRes val iconRes: Int, val flagValue: Int) {
|
enum class OrientationType(
|
||||||
DEFAULT(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, R.string.label_default, R.drawable.ic_screen_rotation_24dp, 0x00000000),
|
val flag: Int,
|
||||||
FREE(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED, R.string.rotation_free, R.drawable.ic_screen_rotation_24dp, 0x00000008),
|
@StringRes val stringRes: Int,
|
||||||
PORTRAIT(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT, R.string.rotation_portrait, R.drawable.ic_stay_current_portrait_24dp, 0x00000010),
|
@DrawableRes val iconRes: Int,
|
||||||
LANDSCAPE(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE, R.string.rotation_landscape, R.drawable.ic_stay_current_landscape_24dp, 0x00000018),
|
val flagValue: Int,
|
||||||
LOCKED_PORTRAIT(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, R.string.rotation_force_portrait, R.drawable.ic_screen_lock_portrait_24dp, 0x00000020),
|
) {
|
||||||
LOCKED_LANDSCAPE(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, R.string.rotation_force_landscape, R.drawable.ic_screen_lock_landscape_24dp, 0x00000028),
|
DEFAULT(
|
||||||
REVERSE_PORTRAIT(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT, R.string.rotation_reverse_portrait, R.drawable.ic_stay_current_portrait_24dp, 0x00000030),
|
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED,
|
||||||
|
R.string.label_default,
|
||||||
|
R.drawable.ic_screen_rotation_24dp,
|
||||||
|
0x00000000,
|
||||||
|
),
|
||||||
|
FREE(
|
||||||
|
ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED,
|
||||||
|
R.string.rotation_free,
|
||||||
|
R.drawable.ic_screen_rotation_24dp,
|
||||||
|
0x00000008,
|
||||||
|
),
|
||||||
|
PORTRAIT(
|
||||||
|
ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT,
|
||||||
|
R.string.rotation_portrait,
|
||||||
|
R.drawable.ic_stay_current_portrait_24dp,
|
||||||
|
0x00000010,
|
||||||
|
),
|
||||||
|
LANDSCAPE(
|
||||||
|
ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE,
|
||||||
|
R.string.rotation_landscape,
|
||||||
|
R.drawable.ic_stay_current_landscape_24dp,
|
||||||
|
0x00000018,
|
||||||
|
),
|
||||||
|
LOCKED_PORTRAIT(
|
||||||
|
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT,
|
||||||
|
R.string.rotation_force_portrait,
|
||||||
|
R.drawable.ic_screen_lock_portrait_24dp,
|
||||||
|
0x00000020,
|
||||||
|
),
|
||||||
|
LOCKED_LANDSCAPE(
|
||||||
|
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,
|
||||||
|
R.string.rotation_force_landscape,
|
||||||
|
R.drawable.ic_screen_lock_landscape_24dp,
|
||||||
|
0x00000028,
|
||||||
|
),
|
||||||
|
REVERSE_PORTRAIT(
|
||||||
|
ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT,
|
||||||
|
R.string.rotation_reverse_portrait,
|
||||||
|
R.drawable.ic_stay_current_portrait_24dp,
|
||||||
|
0x00000030,
|
||||||
|
),
|
||||||
;
|
;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -30,9 +30,15 @@ class ReaderPreferences(
|
||||||
|
|
||||||
fun keepScreenOn() = preferenceStore.getBoolean("pref_keep_screen_on_key", true)
|
fun keepScreenOn() = preferenceStore.getBoolean("pref_keep_screen_on_key", true)
|
||||||
|
|
||||||
fun defaultReadingMode() = preferenceStore.getInt("pref_default_reading_mode_key", ReadingModeType.RIGHT_TO_LEFT.flagValue)
|
fun defaultReadingMode() = preferenceStore.getInt(
|
||||||
|
"pref_default_reading_mode_key",
|
||||||
|
ReadingModeType.RIGHT_TO_LEFT.flagValue,
|
||||||
|
)
|
||||||
|
|
||||||
fun defaultOrientationType() = preferenceStore.getInt("pref_default_orientation_type_key", OrientationType.FREE.flagValue)
|
fun defaultOrientationType() = preferenceStore.getInt(
|
||||||
|
"pref_default_orientation_type_key",
|
||||||
|
OrientationType.FREE.flagValue,
|
||||||
|
)
|
||||||
|
|
||||||
fun webtoonDoubleTapZoomEnabled() = preferenceStore.getBoolean("pref_enable_double_tap_zoom_webtoon", true)
|
fun webtoonDoubleTapZoomEnabled() = preferenceStore.getBoolean("pref_enable_double_tap_zoom_webtoon", true)
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,11 @@ import eu.kanade.tachiyomi.ui.reader.viewer.pager.R2LPagerViewer
|
||||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.VerticalPagerViewer
|
import eu.kanade.tachiyomi.ui.reader.viewer.pager.VerticalPagerViewer
|
||||||
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
|
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
|
||||||
|
|
||||||
enum class ReadingModeType(@StringRes val stringRes: Int, @DrawableRes val iconRes: Int, val flagValue: Int) {
|
enum class ReadingModeType(
|
||||||
|
@StringRes val stringRes: Int,
|
||||||
|
@DrawableRes val iconRes: Int,
|
||||||
|
val flagValue: Int,
|
||||||
|
) {
|
||||||
DEFAULT(R.string.label_default, R.drawable.ic_reader_default_24dp, 0x00000000),
|
DEFAULT(R.string.label_default, R.drawable.ic_reader_default_24dp, 0x00000000),
|
||||||
LEFT_TO_RIGHT(R.string.left_to_right_viewer, R.drawable.ic_reader_ltr_24dp, 0x00000001),
|
LEFT_TO_RIGHT(R.string.left_to_right_viewer, R.drawable.ic_reader_ltr_24dp, 0x00000001),
|
||||||
RIGHT_TO_LEFT(R.string.right_to_left_viewer, R.drawable.ic_reader_rtl_24dp, 0x00000002),
|
RIGHT_TO_LEFT(R.string.right_to_left_viewer, R.drawable.ic_reader_rtl_24dp, 0x00000002),
|
||||||
|
|
|
@ -87,7 +87,9 @@ object UpdatesTab : Tab {
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
screenModel.events.collectLatest { event ->
|
screenModel.events.collectLatest { event ->
|
||||||
when (event) {
|
when (event) {
|
||||||
Event.InternalError -> screenModel.snackbarHostState.showSnackbar(context.getString(R.string.internal_error))
|
Event.InternalError -> screenModel.snackbarHostState.showSnackbar(
|
||||||
|
context.getString(R.string.internal_error),
|
||||||
|
)
|
||||||
is Event.LibraryUpdateTriggered -> {
|
is Event.LibraryUpdateTriggered -> {
|
||||||
val msg = if (event.started) {
|
val msg = if (event.started) {
|
||||||
R.string.updating_library
|
R.string.updating_library
|
||||||
|
|
|
@ -36,9 +36,17 @@ fun File.copyAndSetReadOnlyTo(target: File, overwrite: Boolean = false, bufferSi
|
||||||
|
|
||||||
if (target.exists()) {
|
if (target.exists()) {
|
||||||
if (!overwrite) {
|
if (!overwrite) {
|
||||||
throw FileAlreadyExistsException(file = this, other = target, reason = "The destination file already exists.")
|
throw FileAlreadyExistsException(
|
||||||
|
file = this,
|
||||||
|
other = target,
|
||||||
|
reason = "The destination file already exists.",
|
||||||
|
)
|
||||||
} else if (!target.delete()) {
|
} else if (!target.delete()) {
|
||||||
throw FileAlreadyExistsException(file = this, other = target, reason = "Tried to overwrite the destination, but failed to delete it.")
|
throw FileAlreadyExistsException(
|
||||||
|
file = this,
|
||||||
|
other = target,
|
||||||
|
reason = "Tried to overwrite the destination, but failed to delete it.",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,9 @@ fun Context.copyToClipboard(label: String, content: String) {
|
||||||
* @param permission the permission to check.
|
* @param permission the permission to check.
|
||||||
* @return true if it has permissions.
|
* @return true if it has permissions.
|
||||||
*/
|
*/
|
||||||
fun Context.hasPermission(permission: String) = PermissionChecker.checkSelfPermission(this, permission) == PermissionChecker.PERMISSION_GRANTED
|
fun Context.hasPermission(
|
||||||
|
permission: String,
|
||||||
|
) = PermissionChecker.checkSelfPermission(this, permission) == PermissionChecker.PERMISSION_GRANTED
|
||||||
|
|
||||||
val Context.powerManager: PowerManager
|
val Context.powerManager: PowerManager
|
||||||
get() = getSystemService()!!
|
get() = getSystemService()!!
|
||||||
|
@ -105,7 +107,10 @@ fun Context.openInBrowser(uri: Uri, forceDefaultBrowser: Boolean = false) {
|
||||||
private fun Context.defaultBrowserPackageName(): String? {
|
private fun Context.defaultBrowserPackageName(): String? {
|
||||||
val browserIntent = Intent(Intent.ACTION_VIEW, "http://".toUri())
|
val browserIntent = Intent(Intent.ACTION_VIEW, "http://".toUri())
|
||||||
val resolveInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
val resolveInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
packageManager.resolveActivity(browserIntent, PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong()))
|
packageManager.resolveActivity(
|
||||||
|
browserIntent,
|
||||||
|
PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong()),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
packageManager.resolveActivity(browserIntent, PackageManager.MATCH_DEFAULT_ONLY)
|
packageManager.resolveActivity(browserIntent, PackageManager.MATCH_DEFAULT_ONLY)
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,10 @@ fun Context.cancelNotification(id: Int) {
|
||||||
* @param block the function that will execute inside the builder.
|
* @param block the function that will execute inside the builder.
|
||||||
* @return a notification to be displayed or updated.
|
* @return a notification to be displayed or updated.
|
||||||
*/
|
*/
|
||||||
fun Context.notificationBuilder(channelId: String, block: (NotificationCompat.Builder.() -> Unit)? = null): NotificationCompat.Builder {
|
fun Context.notificationBuilder(
|
||||||
|
channelId: String,
|
||||||
|
block: (NotificationCompat.Builder.() -> Unit)? = null,
|
||||||
|
): NotificationCompat.Builder {
|
||||||
val builder = NotificationCompat.Builder(this, channelId)
|
val builder = NotificationCompat.Builder(this, channelId)
|
||||||
.setColor(getColor(R.color.accent_blue))
|
.setColor(getColor(R.color.accent_blue))
|
||||||
if (block != null) {
|
if (block != null) {
|
||||||
|
|
|
@ -91,6 +91,7 @@ fun View?.isVisibleOnScreen(): Boolean {
|
||||||
}
|
}
|
||||||
val actualPosition = Rect()
|
val actualPosition = Rect()
|
||||||
this.getGlobalVisibleRect(actualPosition)
|
this.getGlobalVisibleRect(actualPosition)
|
||||||
val screen = Rect(0, 0, Resources.getSystem().displayMetrics.widthPixels, Resources.getSystem().displayMetrics.heightPixels)
|
val screen =
|
||||||
|
Rect(0, 0, Resources.getSystem().displayMetrics.widthPixels, Resources.getSystem().displayMetrics.heightPixels)
|
||||||
return actualPosition.intersect(screen)
|
return actualPosition.intersect(screen)
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,8 @@ abstract class WebViewInterceptor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Based on [IsRequestHeaderSafe] in https://source.chromium.org/chromium/chromium/src/+/main:services/network/public/cpp/header_util.cc
|
// Based on [IsRequestHeaderSafe] in
|
||||||
|
// https://source.chromium.org/chromium/chromium/src/+/main:services/network/public/cpp/header_util.cc
|
||||||
private fun isRequestHeaderSafe(_name: String, _value: String): Boolean {
|
private fun isRequestHeaderSafe(_name: String, _value: String): Boolean {
|
||||||
val name = _name.lowercase(Locale.ENGLISH)
|
val name = _name.lowercase(Locale.ENGLISH)
|
||||||
val value = _value.lowercase(Locale.ENGLISH)
|
val value = _value.lowercase(Locale.ENGLISH)
|
||||||
|
@ -97,4 +98,6 @@ private fun isRequestHeaderSafe(_name: String, _value: String): Boolean {
|
||||||
if (name == "connection" && value == "upgrade") return false
|
if (name == "connection" && value == "upgrade") return false
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
private val unsafeHeaderNames = listOf("content-length", "host", "trailer", "te", "upgrade", "cookie2", "keep-alive", "transfer-encoding", "set-cookie")
|
private val unsafeHeaderNames = listOf(
|
||||||
|
"content-length", "host", "trailer", "te", "upgrade", "cookie2", "keep-alive", "transfer-encoding", "set-cookie",
|
||||||
|
)
|
||||||
|
|
|
@ -271,7 +271,9 @@ object ImageUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun splitImageName(filenamePrefix: String, index: Int) = "${filenamePrefix}__${"%03d".format(index + 1)}.jpg"
|
private fun splitImageName(filenamePrefix: String, index: Int) = "${filenamePrefix}__${"%03d".format(
|
||||||
|
index + 1,
|
||||||
|
)}.jpg"
|
||||||
|
|
||||||
private val BitmapFactory.Options.splitData
|
private val BitmapFactory.Options.splitData
|
||||||
get(): List<SplitData> {
|
get(): List<SplitData> {
|
||||||
|
@ -356,10 +358,12 @@ object ImageUtil {
|
||||||
val botLeftIsDark = botLeftPixel.isDark()
|
val botLeftIsDark = botLeftPixel.isDark()
|
||||||
val botRightIsDark = botRightPixel.isDark()
|
val botRightIsDark = botRightPixel.isDark()
|
||||||
|
|
||||||
var darkBG = (topLeftIsDark && (botLeftIsDark || botRightIsDark || topRightIsDark || midLeftIsDark || topMidIsDark)) ||
|
var darkBG =
|
||||||
|
(topLeftIsDark && (botLeftIsDark || botRightIsDark || topRightIsDark || midLeftIsDark || topMidIsDark)) ||
|
||||||
(topRightIsDark && (botRightIsDark || botLeftIsDark || midRightIsDark || topMidIsDark))
|
(topRightIsDark && (botRightIsDark || botLeftIsDark || midRightIsDark || topMidIsDark))
|
||||||
|
|
||||||
val topAndBotPixels = listOf(topLeftPixel, topCenterPixel, topRightPixel, botRightPixel, bottomCenterPixel, botLeftPixel)
|
val topAndBotPixels =
|
||||||
|
listOf(topLeftPixel, topCenterPixel, topRightPixel, botRightPixel, bottomCenterPixel, botLeftPixel)
|
||||||
val isNotWhiteAndCloseTo = topAndBotPixels.mapIndexed { index, color ->
|
val isNotWhiteAndCloseTo = topAndBotPixels.mapIndexed { index, color ->
|
||||||
val other = topAndBotPixels[(index + 1) % topAndBotPixels.size]
|
val other = topAndBotPixels[(index + 1) % topAndBotPixels.size]
|
||||||
!color.isWhite() && color.isCloseTo(other)
|
!color.isWhite() && color.isCloseTo(other)
|
||||||
|
@ -504,10 +508,16 @@ object ImageUtil {
|
||||||
darkBG -> {
|
darkBG -> {
|
||||||
return ColorDrawable(blackColor)
|
return ColorDrawable(blackColor)
|
||||||
}
|
}
|
||||||
topIsBlackStreak || (topCornersIsDark && topOffsetCornersIsDark && (topMidIsDark || overallBlackPixels > 9)) -> {
|
topIsBlackStreak || (
|
||||||
|
topCornersIsDark && topOffsetCornersIsDark &&
|
||||||
|
(topMidIsDark || overallBlackPixels > 9)
|
||||||
|
) -> {
|
||||||
intArrayOf(blackColor, blackColor, whiteColor, whiteColor)
|
intArrayOf(blackColor, blackColor, whiteColor, whiteColor)
|
||||||
}
|
}
|
||||||
bottomIsBlackStreak || (botCornersIsDark && botOffsetCornersIsDark && (bottomCenterPixel.isDark() || overallBlackPixels > 9)) -> {
|
bottomIsBlackStreak || (
|
||||||
|
botCornersIsDark && botOffsetCornersIsDark &&
|
||||||
|
(bottomCenterPixel.isDark() || overallBlackPixels > 9)
|
||||||
|
) -> {
|
||||||
intArrayOf(whiteColor, whiteColor, blackColor, blackColor)
|
intArrayOf(whiteColor, whiteColor, blackColor, blackColor)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
|
|
@ -8,9 +8,8 @@ import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import tachiyomi.core.util.lang.withIOContext
|
import tachiyomi.core.util.lang.withIOContext
|
||||||
import tachiyomi.domain.source.repository.SourcePagingSourceType
|
import tachiyomi.domain.source.repository.SourcePagingSourceType
|
||||||
|
|
||||||
class SourceSearchPagingSource(source: CatalogueSource, val query: String, val filters: FilterList) : SourcePagingSource(
|
class SourceSearchPagingSource(source: CatalogueSource, val query: String, val filters: FilterList) :
|
||||||
source,
|
SourcePagingSource(source) {
|
||||||
) {
|
|
||||||
override suspend fun requestNextPage(currentPage: Int): MangasPage {
|
override suspend fun requestNextPage(currentPage: Int): MangasPage {
|
||||||
return source.getSearchManga(currentPage, query, filters)
|
return source.getSearchManga(currentPage, query, filters)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,5 +16,18 @@ data class ChapterUpdate(
|
||||||
)
|
)
|
||||||
|
|
||||||
fun Chapter.toChapterUpdate(): ChapterUpdate {
|
fun Chapter.toChapterUpdate(): ChapterUpdate {
|
||||||
return ChapterUpdate(id, mangaId, read, bookmark, lastPageRead, dateFetch, sourceOrder, url, name, dateUpload, chapterNumber, scanlator)
|
return ChapterUpdate(
|
||||||
|
id,
|
||||||
|
mangaId,
|
||||||
|
read,
|
||||||
|
bookmark,
|
||||||
|
lastPageRead,
|
||||||
|
dateFetch,
|
||||||
|
sourceOrder,
|
||||||
|
url,
|
||||||
|
name,
|
||||||
|
dateUpload,
|
||||||
|
chapterNumber,
|
||||||
|
scanlator,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,7 +95,10 @@ fun VerticalFastScroller(
|
||||||
}
|
}
|
||||||
|
|
||||||
val thumbBottomPadding = with(LocalDensity.current) { bottomContentPadding.toPx() }
|
val thumbBottomPadding = with(LocalDensity.current) { bottomContentPadding.toPx() }
|
||||||
val heightPx = contentHeight.toFloat() - thumbTopPadding - thumbBottomPadding - listState.layoutInfo.afterContentPadding
|
val heightPx = contentHeight.toFloat() -
|
||||||
|
thumbTopPadding -
|
||||||
|
thumbBottomPadding -
|
||||||
|
listState.layoutInfo.afterContentPadding
|
||||||
val thumbHeightPx = with(LocalDensity.current) { ThumbLength.toPx() }
|
val thumbHeightPx = with(LocalDensity.current) { ThumbLength.toPx() }
|
||||||
val trackHeightPx = heightPx - thumbHeightPx
|
val trackHeightPx = heightPx - thumbHeightPx
|
||||||
|
|
||||||
|
@ -261,7 +264,10 @@ fun VerticalGridFastScroller(
|
||||||
}
|
}
|
||||||
|
|
||||||
val thumbBottomPadding = with(LocalDensity.current) { bottomContentPadding.toPx() }
|
val thumbBottomPadding = with(LocalDensity.current) { bottomContentPadding.toPx() }
|
||||||
val heightPx = contentHeight.toFloat() - thumbTopPadding - thumbBottomPadding - state.layoutInfo.afterContentPadding
|
val heightPx = contentHeight.toFloat() -
|
||||||
|
thumbTopPadding -
|
||||||
|
thumbBottomPadding -
|
||||||
|
state.layoutInfo.afterContentPadding
|
||||||
val thumbHeightPx = with(LocalDensity.current) { ThumbLength.toPx() }
|
val thumbHeightPx = with(LocalDensity.current) { ThumbLength.toPx() }
|
||||||
val trackHeightPx = heightPx - thumbHeightPx
|
val trackHeightPx = heightPx - thumbHeightPx
|
||||||
|
|
||||||
|
|
|
@ -121,7 +121,11 @@ private fun Modifier.drawScrollbar(
|
||||||
items
|
items
|
||||||
.fastFirstOrNull { (it.key as? String)?.startsWith(STICKY_HEADER_KEY_PREFIX)?.not() ?: true }!!
|
.fastFirstOrNull { (it.key as? String)?.startsWith(STICKY_HEADER_KEY_PREFIX)?.not() ?: true }!!
|
||||||
.run {
|
.run {
|
||||||
val startPadding = if (reverseDirection) layoutInfo.afterContentPadding else layoutInfo.beforeContentPadding
|
val startPadding = if (reverseDirection) {
|
||||||
|
layoutInfo.afterContentPadding
|
||||||
|
} else {
|
||||||
|
layoutInfo.beforeContentPadding
|
||||||
|
}
|
||||||
startPadding + ((estimatedItemSize * index - offset) / totalSize * viewportSize)
|
startPadding + ((estimatedItemSize * index - offset) / totalSize * viewportSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,10 @@ fun UpdatesWidget(
|
||||||
.padding(horizontal = 3.dp),
|
.padding(horizontal = 3.dp),
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
val intent = Intent(LocalContext.current, Class.forName(Constants.MAIN_ACTIVITY)).apply {
|
val intent = Intent(
|
||||||
|
LocalContext.current,
|
||||||
|
Class.forName(Constants.MAIN_ACTIVITY),
|
||||||
|
).apply {
|
||||||
action = Constants.SHORTCUT_MANGA
|
action = Constants.SHORTCUT_MANGA
|
||||||
putExtra(Constants.MANGA_EXTRA, mangaId)
|
putExtra(Constants.MANGA_EXTRA, mangaId)
|
||||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
|
|
@ -72,7 +72,13 @@ actual class LocalSource(
|
||||||
|
|
||||||
override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage {
|
override suspend fun getSearchManga(page: Int, query: String, filters: FilterList): MangasPage {
|
||||||
val baseDirsFiles = fileSystem.getFilesInBaseDirectories()
|
val baseDirsFiles = fileSystem.getFilesInBaseDirectories()
|
||||||
val lastModifiedLimit by lazy { if (filters === LATEST_FILTERS) System.currentTimeMillis() - LATEST_THRESHOLD else 0L }
|
val lastModifiedLimit by lazy {
|
||||||
|
if (filters === LATEST_FILTERS) {
|
||||||
|
System.currentTimeMillis() - LATEST_THRESHOLD
|
||||||
|
} else {
|
||||||
|
0L
|
||||||
|
}
|
||||||
|
}
|
||||||
var mangaDirs = baseDirsFiles
|
var mangaDirs = baseDirsFiles
|
||||||
// Filter out files that are hidden and is not a folder
|
// Filter out files that are hidden and is not a folder
|
||||||
.filter { it.isDirectory && !it.name.startsWith('.') }
|
.filter { it.isDirectory && !it.name.startsWith('.') }
|
||||||
|
|
Reference in a new issue