Implement Mihon's spotless PR (#1257)

* Remove detekt (mihonapp/mihon#1130)

Annoying. More annoying in this project.

(cherry picked from commit 777ae2461e1eb277a3aa0c998ff69e4f100387a1)

* Add spotless (with ktlint) (mihonapp/mihon#1136)

(cherry picked from commit 5ae8095ef1ed2ae9f98486f9148e933c77a28692)

* Address spotless lint errors (mihonapp/mihon#1138)

* Add spotless (with ktlint)

* Run spotlessApply

* screaming case screaming case screaming case

* Update PagerViewerAdapter.kt

* Update ReaderTransitionView.kt

(cherry picked from commit d6252ab7703d52ecf9f43de3ee36fd63e665a31f)

* Generate locales_config.xml in build dir

(cherry picked from commit ac41bffdc97b4cfed923de6b9e8e01cccf3eb6eb)

* Address more spotless lint errors in SY

* some more missed

* more missed

* still missing, not sure while it won't report error when running locally

* one more

* more

* more

* correct comment

---------

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
This commit is contained in:
Tran M. Cuong 2024-08-23 08:24:50 +07:00 committed by GitHub
parent 759fd4d4e3
commit 3705880a77
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
363 changed files with 1223 additions and 3138 deletions

View File

@ -36,7 +36,7 @@ jobs:
uses: gradle/actions/setup-gradle@v4
- name: Build app
run: ./gradlew detekt assembleDevDebug
run: ./gradlew spotlessCheck assembleDevDebug
- name: Upload APK
uses: actions/upload-artifact@v4

View File

@ -50,7 +50,7 @@ jobs:
# SY -->
- name: Build app and run unit tests
run: ./gradlew detekt assembleStandardRelease testStandardReleaseUnitTest --stacktrace
run: ./gradlew spotlessCheck assembleStandardRelease testStandardReleaseUnitTest --stacktrace
- name: Sign APK
uses: r0adkll/sign-android-release@v1

View File

@ -24,10 +24,6 @@ Before you start, please note that the ability to use following technologies is
- [Android Studio](https://developer.android.com/studio)
- Emulator or phone with developer options enabled to test changes.
## Linting
Run the `detekt` gradle task. If the build fails, a report of issues can be found in `app/build/reports/detekt/`. The report is availble in several formats and details each issue that needs attention.
## Getting help
- Join [the Discord server](https://discord.gg/tachiyomi) for online help and to ask questions while developing.

View File

@ -21,7 +21,7 @@ if (gradle.startParameter.taskRequests.toString().contains("Standard")) {
// shortcutHelper.setFilePath("./shortcuts.xml")
val SUPPORTED_ABIS = setOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
val supportedAbis = setOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
android {
namespace = "eu.kanade.tachiyomi"
@ -38,7 +38,7 @@ android {
buildConfigField("boolean", "INCLUDE_UPDATER", "false")
ndk {
abiFilters += SUPPORTED_ABIS
abiFilters += supportedAbis
}
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@ -47,7 +47,7 @@ android {
abi {
isEnable = true
reset()
include(*SUPPORTED_ABIS.toTypedArray())
include(*supportedAbis.toTypedArray())
isUniversalApk = true
}
}
@ -243,7 +243,6 @@ dependencies {
implementation(libs.compose.webview)
implementation(libs.compose.grid)
// Logging
implementation(libs.logcat)

View File

@ -32,10 +32,11 @@ class GetEnabledSources(
) { a, b, c -> Triple(a, b, c) },
// SY <--
repository.getSources(),
) { pinnedSourceIds,
(enabledLanguages, disabledSources, lastUsedSource),
(excludedFromDataSaver, sourcesInCategories, sourceCategoriesFilter),
sources,
) {
pinnedSourceIds,
(enabledLanguages, disabledSources, lastUsedSource),
(excludedFromDataSaver, sourcesInCategories, sourceCategoriesFilter),
sources,
->
val sourcesAndCategories = sourcesInCategories.map {

View File

@ -13,7 +13,7 @@ class SyncPreferences(
fun clientAPIKey() = preferenceStore.getString("sync_client_api_key", "")
fun lastSyncTimestamp() = preferenceStore.getLong(Preference.appStateKey("last_sync_timestamp"), 0L)
fun lastSyncEtag() = preferenceStore.getString("sync_etag", "")
fun lastSyncEtag() = preferenceStore.getString("sync_etag", "")
fun syncInterval() = preferenceStore.getInt("sync_interval", 0)
fun syncService() = preferenceStore.getInt("sync_service", 0)

View File

@ -18,12 +18,20 @@ class UiPreferences(
fun themeMode() = preferenceStore.getEnum(
"pref_theme_mode_key",
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { ThemeMode.SYSTEM } else { ThemeMode.LIGHT },
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ThemeMode.SYSTEM
} else {
ThemeMode.LIGHT
},
)
fun appTheme() = preferenceStore.getEnum(
"pref_app_theme",
if (DeviceUtil.isDynamicColorAvailable) { AppTheme.MONET } else { AppTheme.DEFAULT },
if (DeviceUtil.isDynamicColorAvailable) {
AppTheme.MONET
} else {
AppTheme.DEFAULT
},
)
fun themeDarkAmoled() = preferenceStore.getBoolean("pref_theme_dark_amoled_key", false)

View File

@ -240,7 +240,7 @@ private fun DetailsHeader(
Extension name: ${extension.name} (lang: ${extension.lang}; package: ${extension.pkgName})
Extension version: ${extension.versionName} (lib: ${extension.libVersion}; version code: ${extension.versionCode})
NSFW: ${extension.isNsfw}
""".trimIndent()
""".trimIndent(),
)
if (extension is Extension.Installed) {
@ -251,7 +251,7 @@ private fun DetailsHeader(
Obsolete: ${extension.isObsolete}
Shared: ${extension.isShared}
Repository: ${extension.repoUrl}
""".trimIndent()
""".trimIndent(),
)
}
}

View File

@ -219,7 +219,9 @@ private fun ExtensionContent(
when (it) {
is Extension.Available -> onInstallExtension(it)
is Extension.Installed -> onOpenExtension(it)
is Extension.Untrusted -> { trustState = it }
is Extension.Untrusted -> {
trustState = it
}
}
},
onLongClickItem = onLongClickItem,
@ -241,7 +243,9 @@ private fun ExtensionContent(
onOpenExtension(it)
}
}
is Extension.Untrusted -> { trustState = it }
is Extension.Untrusted -> {
trustState = it
}
}
},
)

View File

@ -35,7 +35,7 @@ import tachiyomi.i18n.MR
import tachiyomi.i18n.sy.SYMR
import tachiyomi.presentation.core.components.LabeledCheckbox
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
import tachiyomi.presentation.core.components.material.SECONDARY_ALPHA
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
import tachiyomi.presentation.core.i18n.stringResource
@ -179,7 +179,7 @@ private fun SourcePinButton(
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.onBackground.copy(
alpha = SecondaryItemAlpha,
alpha = SECONDARY_ALPHA,
)
}
val description = if (isPinned) MR.strings.action_unpin else MR.strings.action_pin

View File

@ -79,7 +79,7 @@ fun TabbedDialog(
modifier = Modifier.animateContentSize(),
state = pagerState,
verticalAlignment = Alignment.Top,
pageContent = { page -> content(page) }
pageContent = { page -> content(page) },
)
}
}

View File

@ -207,7 +207,6 @@ private fun ColumnScope.SortPage(
}.collectAsState(initial = screenModel.libraryPreferences.sortTagsForLibrary().get().isNotEmpty())
// SY <--
val trackerSortOption = if (trackers.isEmpty()) {
emptyList()
} else {

View File

@ -62,7 +62,7 @@ private val ContinueReadingButtonIconSizeLarge = 20.dp
private val ContinueReadingButtonGridPadding = 6.dp
private val ContinueReadingButtonListSpacing = 8.dp
private const val GridSelectedCoverAlpha = 0.76f
private const val GRID_SELECTED_COVER_ALPHA = 0.76f
/**
* Layout of grid list item with title overlaying the cover.
@ -90,7 +90,7 @@ fun MangaCompactGridItem(
MangaCover.Book(
modifier = Modifier
.fillMaxWidth()
.alpha(if (isSelected) GridSelectedCoverAlpha else coverAlpha),
.alpha(if (isSelected) GRID_SELECTED_COVER_ALPHA else coverAlpha),
data = coverData,
)
},
@ -197,7 +197,7 @@ fun MangaComfortableGridItem(
MangaCover.Book(
modifier = Modifier
.fillMaxWidth()
.alpha(if (isSelected) GridSelectedCoverAlpha else coverAlpha),
.alpha(if (isSelected) GRID_SELECTED_COVER_ALPHA else coverAlpha),
data = coverData,
)
},
@ -371,7 +371,7 @@ fun MangaListItem(
size = ContinueReadingButtonSizeSmall,
iconSize = ContinueReadingButtonIconSizeSmall,
onClick = onClickContinueReading,
modifier = Modifier.padding(start = ContinueReadingButtonListSpacing)
modifier = Modifier.padding(start = ContinueReadingButtonListSpacing),
)
}
}
@ -392,7 +392,7 @@ private fun ContinueReadingButton(
containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.9f),
contentColor = contentColorFor(MaterialTheme.colorScheme.primaryContainer),
),
modifier = Modifier.size(size)
modifier = Modifier.size(size),
) {
Icon(
imageVector = Icons.Filled.PlayArrow,

View File

@ -995,7 +995,9 @@ private fun LazyListScope.sharedChapterItems(
// SY <--
},
readProgress = item.chapter.lastPageRead
.takeIf { /* SY --> */(!item.chapter.read || alwaysShowReadingProgress)/* SY <-- */ && it > 0L }
.takeIf {
/* SY --> */(!item.chapter.read || alwaysShowReadingProgress)/* SY <-- */ && it > 0L
}
?.let {
stringResource(
MR.strings.chapter_progress,

View File

@ -12,7 +12,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
import tachiyomi.presentation.core.components.material.SECONDARY_ALPHA
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource
@ -60,6 +60,6 @@ private fun MissingChaptersWarning(count: Int) {
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.error.copy(alpha = SecondaryItemAlpha),
color = MaterialTheme.colorScheme.error.copy(alpha = SECONDARY_ALPHA),
)
}

View File

@ -40,8 +40,8 @@ import eu.kanade.tachiyomi.data.download.model.Download
import me.saket.swipe.SwipeableActionsBox
import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.material.ReadItemAlpha
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
import tachiyomi.presentation.core.components.material.DISABLED_ALPHA
import tachiyomi.presentation.core.components.material.SECONDARY_ALPHA
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.selectedBackground
@ -135,7 +135,7 @@ fun MangaChapterListItem(
maxLines = 1,
overflow = TextOverflow.Ellipsis,
onTextLayout = { textHeight = it.size.height },
color = LocalContentColor.current.copy(alpha = if (read) ReadItemAlpha else 1f),
color = LocalContentColor.current.copy(alpha = if (read) DISABLED_ALPHA else 1f),
)
}
@ -143,7 +143,7 @@ fun MangaChapterListItem(
val subtitleStyle = MaterialTheme.typography.bodySmall
.merge(
color = LocalContentColor.current
.copy(alpha = if (read) ReadItemAlpha else SecondaryItemAlpha)
.copy(alpha = if (read) DISABLED_ALPHA else SECONDARY_ALPHA),
)
ProvideTextStyle(value = subtitleStyle) {
if (date != null) {
@ -152,14 +152,19 @@ fun MangaChapterListItem(
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
if (readProgress != null || scanlator != null/* SY --> */ || sourceName != null/* SY <-- */) DotSeparatorText()
if (readProgress != null ||
scanlator != null/* SY --> */ ||
sourceName != null/* SY <-- */
) {
DotSeparatorText()
}
}
if (readProgress != null) {
Text(
text = readProgress,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = LocalContentColor.current.copy(alpha = ReadItemAlpha),
color = LocalContentColor.current.copy(alpha = DISABLED_ALPHA),
)
if (scanlator != null/* SY --> */ || sourceName != null/* SY <-- */) DotSeparatorText()
}

View File

@ -82,6 +82,7 @@ import eu.kanade.tachiyomi.util.system.copyToClipboard
import tachiyomi.domain.manga.model.Manga
import tachiyomi.i18n.MR
import tachiyomi.i18n.sy.SYMR
import tachiyomi.presentation.core.components.material.DISABLED_ALPHA
import tachiyomi.presentation.core.components.material.TextButton
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.pluralStringResource
@ -181,7 +182,7 @@ fun MangaActionRow(
// SY <--
modifier: Modifier = Modifier,
) {
val defaultActionButtonColor = MaterialTheme.colorScheme.onSurface.copy(alpha = .38f)
val defaultActionButtonColor = MaterialTheme.colorScheme.onSurface.copy(alpha = DISABLED_ALPHA)
// TODO: show something better when using custom interval
val nextUpdateDays = remember(nextUpdate) {

View File

@ -44,7 +44,7 @@ import tachiyomi.presentation.core.i18n.stringResource
@Composable
private fun PagePreviewLoading(
setMaxWidth: (Dp) -> Unit
setMaxWidth: (Dp) -> Unit,
) {
val density = LocalDensity.current
Box(
@ -63,7 +63,7 @@ private fun PagePreviewLoading(
@Composable
private fun PagePreviewRow(
onOpenPage: (Int) -> Unit,
items: ImmutableList<PagePreview>
items: ImmutableList<PagePreview>,
) {
Row(
modifier = Modifier
@ -88,7 +88,7 @@ private fun PagePreviewMore(
) {
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center
contentAlignment = Alignment.Center,
) {
TextButton(onClick = onMorePreviewsClicked) {
Text(stringResource(SYMR.strings.more_previews))
@ -116,7 +116,7 @@ fun PagePreviews(
pagePreviewState.pagePreviews.take(rowCount * itemPerRowCount).chunked(itemPerRowCount).forEach {
PagePreviewRow(
onOpenPage = onOpenPage,
items = remember(it) { it.toImmutableList() }
items = remember(it) { it.toImmutableList() },
)
}
@ -153,7 +153,7 @@ fun LazyListScope.PagePreviewItems(
) {
PagePreviewRow(
onOpenPage = onOpenPage,
items = remember(it) { it.toImmutableList() }
items = remember(it) { it.toImmutableList() },
)
}
item(

View File

@ -3,7 +3,6 @@ package eu.kanade.presentation.more.settings.screen
import android.annotation.SuppressLint
import android.content.ActivityNotFoundException
import android.content.Intent
import android.os.Build
import android.provider.Settings
import android.webkit.WebStorage
import android.webkit.WebView
@ -376,7 +375,7 @@ object SettingsAdvancedScreen : SearchableSettings {
chooseColorProfile.launch(arrayOf("*/*"))
},
),
)
),
)
}

View File

@ -180,11 +180,15 @@ object SettingsAppearanceScreen : SearchableSettings {
Preference.PreferenceItem.SliderPreference(
value = previewsRowCount,
title = stringResource(SYMR.strings.pref_previews_row_count),
subtitle = if (previewsRowCount > 0) pluralStringResource(
SYMR.plurals.row_count,
previewsRowCount,
previewsRowCount,
) else stringResource(MR.strings.disabled),
subtitle = if (previewsRowCount > 0) {
pluralStringResource(
SYMR.plurals.row_count,
previewsRowCount,
previewsRowCount,
)
} else {
stringResource(MR.strings.disabled)
},
min = 0,
max = 10,
onValueChanged = {

View File

@ -94,7 +94,7 @@ object SettingsBrowseScreen : SearchableSettings {
pref = uiPreferences.feedTabInFront(),
title = stringResource(SYMR.strings.pref_feed_position),
subtitle = stringResource(SYMR.strings.pref_feed_position_summery),
enabled = hideFeedTab.not()
enabled = hideFeedTab.not(),
),
),
),

View File

@ -37,7 +37,7 @@ class OpenSourceLicensesScreen : Screen() {
name = it.name,
website = it.website,
license = it.licenses.firstOrNull()?.htmlReadyLicenseContent.orEmpty(),
)
),
)
},
)

View File

@ -26,7 +26,6 @@ import eu.kanade.tachiyomi.util.system.toast
import kotlinx.collections.immutable.ImmutableList
import kotlinx.coroutines.flow.update
import tachiyomi.i18n.MR
import tachiyomi.i18n.sy.SYMR
import tachiyomi.presentation.core.components.LabeledCheckbox
import tachiyomi.presentation.core.components.LazyColumnWithAction
import tachiyomi.presentation.core.components.SectionCard

View File

@ -28,7 +28,7 @@ import tachiyomi.presentation.core.i18n.stringResource
class BackupSchemaScreen : Screen() {
companion object {
const val title = "Backup file schema"
const val TITLE = "Backup file schema"
}
@Composable
@ -41,7 +41,7 @@ class BackupSchemaScreen : Screen() {
Scaffold(
topBar = {
AppBar(
title = title,
title = TITLE,
navigateUp = navigator::pop,
actions = {
AppBarActions(
@ -50,7 +50,7 @@ class BackupSchemaScreen : Screen() {
title = stringResource(MR.strings.action_copy_to_clipboard),
icon = Icons.Default.ContentCopy,
onClick = {
context.copyToClipboard(title, schema)
context.copyToClipboard(TITLE, schema)
},
),
),

View File

@ -31,11 +31,11 @@ class DebugInfoScreen : Screen() {
itemsProvider = {
listOf(
Preference.PreferenceItem.TextPreference(
title = WorkerInfoScreen.title,
title = WorkerInfoScreen.TITLE,
onClick = { navigator.push(WorkerInfoScreen()) },
),
Preference.PreferenceItem.TextPreference(
title = BackupSchemaScreen.title,
title = BackupSchemaScreen.TITLE,
onClick = { navigator.push(BackupSchemaScreen()) },
),
getAppInfoGroup(),

View File

@ -49,7 +49,7 @@ import java.time.ZoneId
class WorkerInfoScreen : Screen() {
companion object {
const val title = "Worker info"
const val TITLE = "Worker info"
}
@Composable
@ -65,7 +65,7 @@ class WorkerInfoScreen : Screen() {
Scaffold(
topBar = {
AppBar(
title = title,
title = TITLE,
navigateUp = navigator::pop,
actions = {
AppBarActions(
@ -74,7 +74,7 @@ class WorkerInfoScreen : Screen() {
title = stringResource(MR.strings.action_copy_to_clipboard),
icon = Icons.Default.ContentCopy,
onClick = {
context.copyToClipboard(title, enqueued + finished + running)
context.copyToClipboard(TITLE, enqueued + finished + running)
},
),
),
@ -159,7 +159,7 @@ class WorkerInfoScreen : Screen() {
Injekt.get<UiPreferences>().dateFormat().get(),
),
)
appendLine("Next scheduled run: $timestamp",)
appendLine("Next scheduled run: $timestamp")
appendLine("Attempt #${workInfo.runAttemptCount + 1}")
}
appendLine()

View File

@ -34,7 +34,9 @@ import tachiyomi.presentation.core.util.isScrolledToEnd
import tachiyomi.presentation.core.util.isScrolledToStart
private enum class State {
CHECKED, INVERSED, UNCHECKED
CHECKED,
INVERSED,
UNCHECKED,
}
@Composable

View File

@ -15,7 +15,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
import tachiyomi.presentation.core.components.material.SECONDARY_ALPHA
import tachiyomi.presentation.core.components.material.padding
@Composable
@ -73,7 +73,7 @@ private fun RowScope.BaseStatsItem(
style = subtitleStyle
.copy(
color = MaterialTheme.colorScheme.onSurface
.copy(alpha = SecondaryItemAlpha),
.copy(alpha = SECONDARY_ALPHA),
),
textAlign = TextAlign.Center,
)

View File

@ -226,7 +226,7 @@ private fun ChapterText(
Text(
text = buildAnnotatedString {
if (downloaded) {
appendInlineContent(DownloadedIconContentId)
appendInlineContent(DOWNLOADED_ICON_ID)
append(' ')
}
append(name)
@ -236,7 +236,7 @@ private fun ChapterText(
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.titleLarge,
inlineContent = persistentMapOf(
DownloadedIconContentId to InlineTextContent(
DOWNLOADED_ICON_ID to InlineTextContent(
Placeholder(
width = 22.sp,
height = 22.sp,
@ -273,7 +273,7 @@ private val CardColor: CardColors
)
private val VerticalSpacerSize = 24.dp
private const val DownloadedIconContentId = "downloaded"
private const val DOWNLOADED_ICON_ID = "downloaded"
private fun previewChapter(name: String, scanlator: String, chapterNumber: Double) = Chapter.create().copy(
id = 0L,

View File

@ -63,7 +63,7 @@ fun ExhUtils(
modifier
.fillMaxWidth()
.background(backgroundColor),
horizontalAlignment = Alignment.CenterHorizontally
horizontalAlignment = Alignment.CenterHorizontally,
) {
AnimatedVisibility(visible = isVisible) {
Column {
@ -84,7 +84,7 @@ fun ExhUtils(
) {
Column(
Modifier.weight(3f),
horizontalAlignment = Alignment.CenterHorizontally
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
text = stringResource(SYMR.strings.eh_autoscroll),
@ -93,17 +93,17 @@ fun ExhUtils(
fontFamily = FontFamily.SansSerif,
style = MaterialTheme.typography.labelLarge,
modifier = Modifier.fillMaxWidth(0.75f),
textAlign = TextAlign.Center
textAlign = TextAlign.Center,
)
}
Column(
Modifier.weight(1f),
horizontalAlignment = Alignment.CenterHorizontally
horizontalAlignment = Alignment.CenterHorizontally,
) {
Switch(
checked = isAutoScroll,
onCheckedChange = null,
enabled = isAutoScrollEnabled
enabled = isAutoScrollEnabled,
)
}
}
@ -114,7 +114,7 @@ fun ExhUtils(
) {
Column(
Modifier.weight(3f),
horizontalAlignment = Alignment.CenterHorizontally
horizontalAlignment = Alignment.CenterHorizontally,
) {
var autoScrollFrequencyState by remember {
mutableStateOf(autoScrollFrequency)

View File

@ -38,7 +38,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign
@ -58,8 +57,6 @@ import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.stringResource
import java.time.format.DateTimeFormatter
private const val UnsetStatusTextAlpha = 0.5F
@Composable
fun TrackInfoDialogHome(
trackItems: List<TrackItem>,
@ -211,10 +208,9 @@ private fun TrackInfoItem(
if (onScoreClick != null) {
VerticalDivider()
TrackDetailsItem(
modifier = Modifier
.weight(1f)
.alpha(if (score == null) UnsetStatusTextAlpha else 1f),
text = score ?: stringResource(MR.strings.score),
modifier = Modifier.weight(1f),
text = score,
placeholder = stringResource(MR.strings.score),
onClick = onScoreClick,
)
}
@ -243,6 +239,8 @@ private fun TrackInfoItem(
}
}
private const val UNSET_TEXT_ALPHA = 0.5F
@Composable
private fun TrackDetailsItem(
text: String?,
@ -263,7 +261,7 @@ private fun TrackDetailsItem(
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = if (text == null) UnsetStatusTextAlpha else 1f),
color = MaterialTheme.colorScheme.onSurface.copy(alpha = if (text == null) UNSET_TEXT_ALPHA else 1f),
)
}
}

View File

@ -43,7 +43,7 @@ import eu.kanade.tachiyomi.ui.updates.UpdatesItem
import tachiyomi.domain.updates.model.UpdatesWithRelations
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.ListGroupHeader
import tachiyomi.presentation.core.components.material.ReadItemAlpha
import tachiyomi.presentation.core.components.material.DISABLED_ALPHA
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.selectedBackground
@ -107,8 +107,10 @@ internal fun LazyListScope.updatesUiItems(
readProgress = updatesItem.update.lastPageRead
.takeIf {
/* SY --> */(
!updatesItem.update.read || (preserveReadingPosition && updatesItem.isEhBasedUpdate())
)/* SY <-- */ && it > 0L
!updatesItem.update.read ||
(preserveReadingPosition && updatesItem.isEhBasedUpdate())
)/* SY <-- */ &&
it > 0L
}
?.let {
stringResource(
@ -152,7 +154,7 @@ private fun UpdatesUiItem(
modifier: Modifier = Modifier,
) {
val haptic = LocalHapticFeedback.current
val textAlpha = if (update.read) ReadItemAlpha else 1f
val textAlpha = if (update.read) DISABLED_ALPHA else 1f
Row(
modifier = modifier
@ -226,7 +228,7 @@ private fun UpdatesUiItem(
Text(
text = readProgress,
maxLines = 1,
color = LocalContentColor.current.copy(alpha = ReadItemAlpha),
color = LocalContentColor.current.copy(alpha = DISABLED_ALPHA),
overflow = TextOverflow.Ellipsis,
)
}

View File

@ -1,6 +1,5 @@
package eu.kanade.presentation.util
import android.annotation.SuppressLint
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.ContentTransform
@ -28,7 +27,6 @@ import soup.compose.material.motion.animation.rememberSlideDistance
/**
* For invoking back press to the parent activity
*/
@SuppressLint("ComposeCompositionLocalUsage")
val LocalBackPress: ProvidableCompositionLocal<(() -> Unit)?> = staticCompositionLocalOf { null }
interface Tab : cafe.adriel.voyager.navigator.tab.Tab {

View File

@ -174,8 +174,7 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
val syncPreferences: SyncPreferences = Injekt.get()
val syncTriggerOpt = syncPreferences.getSyncTriggerOptions()
if (syncPreferences.isSyncEnabled() && syncTriggerOpt.syncOnAppStart
) {
if (syncPreferences.isSyncEnabled() && syncTriggerOpt.syncOnAppStart) {
SyncDataJob.startNow(this@App)
}
@ -199,7 +198,6 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
)
}
@Suppress("MagicNumber")
override fun newImageLoader(context: Context): ImageLoader {
return ImageLoader.Builder(this).apply {
val callFactoryLazy = lazy { Injekt.get<NetworkHelper>().client }
@ -239,8 +237,7 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
val syncPreferences: SyncPreferences = Injekt.get()
val syncTriggerOpt = syncPreferences.getSyncTriggerOptions()
if (syncPreferences.isSyncEnabled() && syncTriggerOpt.syncOnAppResume
) {
if (syncPreferences.isSyncEnabled() && syncTriggerOpt.syncOnAppResume) {
SyncDataJob.startNow(this@App)
}
}

View File

@ -57,7 +57,7 @@ class BackupCreator(
// SY -->
private val savedSearchBackupCreator: SavedSearchBackupCreator = SavedSearchBackupCreator(),
private val getMergedManga: GetMergedManga = Injekt.get(),
private val handler: DatabaseHandler = Injekt.get()
private val handler: DatabaseHandler = Injekt.get(),
// SY <--
) {
@ -92,7 +92,7 @@ class BackupCreator(
} else {
emptyList()
} + getMergedManga.await(), // SY <--
options
options,
)
val backup = Backup(
backupManga = backupManga,

View File

@ -39,7 +39,8 @@ data class BackupOptions(
// SY <--
)
fun canCreate() = libraryEntries || categories || appSettings || extensionRepoSettings || sourceSettings || savedSearches
fun canCreate() =
libraryEntries || categories || appSettings || extensionRepoSettings || sourceSettings || savedSearches
companion object {
val libraryOptions = persistentListOf(

View File

@ -7,7 +7,7 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class SavedSearchBackupCreator(
private val handler: DatabaseHandler = Injekt.get()
private val handler: DatabaseHandler = Injekt.get(),
) {
suspend operator fun invoke(): List<BackupSavedSearch> {

View File

@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.data.backup.models
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
@Suppress("MagicNumber")
@Serializable
data class Backup(
@ProtoNumber(1) val backupManga: List<BackupManga>,

View File

@ -4,7 +4,6 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import tachiyomi.domain.chapter.model.Chapter
@Suppress("MagicNumber")
@Serializable
data class BackupChapter(
// in 1.x some of these values have different names

View File

@ -4,7 +4,6 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import mihon.domain.extensionrepo.model.ExtensionRepo
@Suppress("MagicNumber")
@Serializable
class BackupExtensionRepos(
@ProtoNumber(1) var baseUrl: String,

View File

@ -3,13 +3,9 @@ package eu.kanade.tachiyomi.data.backup.models
import eu.kanade.tachiyomi.source.model.UpdateStrategy
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import tachiyomi.domain.manga.model.CustomMangaInfo
import tachiyomi.domain.manga.model.Manga
@Suppress(
"DEPRECATION",
"MagicNumber",
)
@Suppress("DEPRECATION")
@Serializable
data class BackupManga(
// in 1.x some of these values have different names

View File

@ -36,7 +36,8 @@ data class BackupMergedMangaReference(
}
val backupMergedMangaReferenceMapper =
{ _: Long,
{
_: Long,
isInfoManga: Boolean,
getChapterUpdates: Boolean,
chapterSortMode: Long,

View File

@ -53,7 +53,20 @@ data class BackupTracking(
}
val backupTrackMapper = {
_: Long, _: Long, syncId: Long, mediaId: Long, libraryId: Long?, title: String, lastChapterRead: Double, totalChapters: Long, status: Long, score: Double, remoteUrl: String, startDate: Long, finishDate: Long ->
_: Long,
_: Long,
syncId: Long,
mediaId: Long,
libraryId: Long?,
title: String,
lastChapterRead: Double,
totalChapters: Long,
status: Long,
score: Double,
remoteUrl: String,
startDate: Long,
finishDate: Long,
->
BackupTracking(
syncId = syncId.toInt(),
mediaId = mediaId,

View File

@ -200,7 +200,7 @@ class BackupRestorer(
}
private fun CoroutineScope.restoreExtensionRepos(
backupExtensionRepo: List<BackupExtensionRepos>
backupExtensionRepo: List<BackupExtensionRepos>,
) = launch {
backupExtensionRepo
.forEach {

View File

@ -27,7 +27,13 @@ data class RestoreOptions(
// SY <--
)
fun canRestore() = libraryEntries || categories || appSettings || extensionRepoSettings || sourceSettings /* SY --> */ || savedSearches /* SY <-- */
fun canRestore() =
libraryEntries ||
categories ||
appSettings ||
extensionRepoSettings ||
sourceSettings /* SY --> */ ||
savedSearches /* SY <-- */
companion object {
val options = persistentListOf(
@ -72,7 +78,7 @@ data class RestoreOptions(
extensionRepoSettings = array[3],
sourceSettings = array[4],
// SY -->
savedSearches = array[5]
savedSearches = array[5],
// SY <--
)
}

View File

@ -8,7 +8,7 @@ import uy.kohesive.injekt.api.get
class ExtensionRepoRestorer(
private val handler: DatabaseHandler = Injekt.get(),
private val getExtensionRepos: GetExtensionRepo = Injekt.get()
private val getExtensionRepos: GetExtensionRepo = Injekt.get(),
) {
suspend operator fun invoke(
@ -32,7 +32,7 @@ class ExtensionRepoRestorer(
backupRepo.name,
backupRepo.shortName,
backupRepo.website,
backupRepo.signingKeyFingerprint
backupRepo.signingKeyFingerprint,
)
}
}

View File

@ -36,8 +36,8 @@ class ChapterCache(
private val context: Context,
private val json: Json,
// SY -->
readerPreferences: ReaderPreferences
//S Y <--
readerPreferences: ReaderPreferences,
// SY <--
) {
// --> EH

View File

@ -45,7 +45,6 @@ import java.io.IOException
* Available request parameter:
* - [USE_CUSTOM_COVER_KEY]: Use custom cover if set by user, default is true
*/
@Suppress("LongParameterList")
class MangaCoverFetcher(
private val url: String?,
private val isLibraryManga: Boolean,
@ -86,7 +85,7 @@ class MangaCoverFetcher(
source = ImageSource(
file = file.toOkioPath(),
fileSystem = FileSystem.SYSTEM,
diskCacheKey = diskCacheKey
diskCacheKey = diskCacheKey,
),
mimeType = "image/*",
dataSource = DataSource.DISK,

View File

@ -58,7 +58,7 @@ class PagePreviewFetcher(
source = ImageSource(
file = file.toOkioPath(),
fileSystem = FileSystem.SYSTEM,
diskCacheKey = diskCacheKey
diskCacheKey = diskCacheKey,
),
mimeType = "image/*",
dataSource = DataSource.DISK,
@ -230,7 +230,7 @@ class PagePreviewFetcher(
file = data,
fileSystem = FileSystem.SYSTEM,
diskCacheKey = diskCacheKey,
closeable = this
closeable = this,
)
}

View File

@ -1,3 +1,5 @@
@file:Suppress("PropertyName", "ktlint:standard:property-naming")
package eu.kanade.tachiyomi.data.database.models
import eu.kanade.tachiyomi.source.model.SChapter

View File

@ -1,3 +1,5 @@
@file:Suppress("PropertyName", "ktlint:standard:property-naming")
package eu.kanade.tachiyomi.data.database.models
class ChapterImpl : Chapter {

View File

@ -1,3 +1,5 @@
@file:Suppress("PropertyName", "ktlint:standard:property-naming")
package eu.kanade.tachiyomi.data.database.models
import java.io.Serializable

View File

@ -1,3 +1,5 @@
@file:Suppress("PropertyName", "ktlint:standard:property-naming")
package eu.kanade.tachiyomi.data.database.models
class TrackImpl : Track {

View File

@ -121,7 +121,8 @@ class DownloadProvider(
getValidChapterDirNames(chp.name, chp.scanlator).any { dir ->
mangaDir.findFile(dir) != null
}
} == null || it.name?.endsWith(Downloader.TMP_DIR_SUFFIX) == true
} == null ||
it.name?.endsWith(Downloader.TMP_DIR_SUFFIX) == true
}
}
// SY <--

View File

@ -190,7 +190,7 @@ class Downloader(
fun clearQueue() {
cancelDownloaderJob()
_clearQueue()
internalClearQueue()
notifier.dismissProgress()
}
@ -204,9 +204,12 @@ class Downloader(
val activeDownloadsFlow = queueState.transformLatest { queue ->
while (true) {
val activeDownloads = queue.asSequence()
.filter { it.status.value <= Download.State.DOWNLOADING.value } // Ignore completed downloads, leave them in the queue
// Ignore completed downloads, leave them in the queue
.filter { it.status.value <= Download.State.DOWNLOADING.value }
.groupBy { it.source }
.toList().take(5) // Concurrently download from 5 different sources
.toList()
// Concurrently download from 5 different sources
.take(5)
.map { (_, downloads) -> downloads.first() }
emit(activeDownloads)
@ -650,7 +653,7 @@ class Downloader(
chapter,
urls,
categories,
source.name
source.name,
)
// Remove the old file
@ -710,7 +713,7 @@ class Downloader(
removeFromQueueIf { it.manga.id == manga.id }
}
private fun _clearQueue() {
private fun internalClearQueue() {
_queueState.update {
it.forEach { download ->
if (download.status == Download.State.DOWNLOADING || download.status == Download.State.QUEUE) {
@ -732,7 +735,7 @@ class Downloader(
}
pause()
_clearQueue()
internalClearQueue()
addAllToQueue(downloads)
if (wasRunning) {

View File

@ -363,14 +363,17 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
async {
semaphore.withPermit {
if (
mdlistLogged && mangaInSource.firstOrNull()
mdlistLogged &&
mangaInSource.firstOrNull()
?.let { it.manga.source in mangaDexSourceIds } == true
) {
launch {
mangaInSource.forEach { (manga) ->
try {
val tracks = getTracks.await(manga.id)
if (tracks.isEmpty() || tracks.none { it.trackerId == TrackerManager.MDLIST }) {
if (tracks.isEmpty() ||
tracks.none { it.trackerId == TrackerManager.MDLIST }
) {
val track = mdList.createInitialTracker(manga)
insertTrack.await(mdList.refresh(track).toDomainTrack(false)!!)
}
@ -400,10 +403,14 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
// SY -->
.sortedByDescending { it.sourceOrder }.run {
if (libraryPreferences.libraryReadDuplicateChapters().get()) {
val readChapters = getChaptersByMangaId.await(manga.id).filter { it.read }
val readChapters = getChaptersByMangaId.await(manga.id).filter {
it.read
}
val newReadChapters = this.filter { chapter ->
chapter.chapterNumber > 0 &&
readChapters.any { it.chapterNumber == chapter.chapterNumber }
readChapters.any {
it.chapterNumber == chapter.chapterNumber
}
}
if (newReadChapters.isNotEmpty()) {
@ -415,7 +422,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
this
}
}
//SY <--
// SY <--
if (newChapters.isNotEmpty()) {
val categoryIds = getCategories.await(manga.id).map { it.id }
@ -766,7 +773,9 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
val constraints = Constraints(
requiredNetworkType = if (DEVICE_NETWORK_NOT_METERED in restrictions) {
NetworkType.UNMETERED
} else { NetworkType.CONNECTED },
} else {
NetworkType.CONNECTED
},
requiresCharging = DEVICE_CHARGING in restrictions,
requiresBatteryNotLow = true,
)

View File

@ -147,7 +147,7 @@ class SyncManager(
return
}
if (remoteBackup === syncData.backup){
if (remoteBackup === syncData.backup) {
// nothing changed
logcat(LogPriority.DEBUG) { "Skip restore due to remote was overwrite from local" }
syncPreferences.lastSyncTimestamp().set(Date().time)

View File

@ -72,7 +72,7 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
try {
val remoteSData = pullSyncData()
if (remoteSData != null ){
if (remoteSData != null) {
// Get local unique device ID
val localDeviceId = syncPreferences.uniqueDeviceID()
val lastSyncDeviceId = remoteSData.deviceId
@ -86,7 +86,7 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
return if (lastSyncDeviceId == localDeviceId) {
pushSyncData(syncData)
syncData.backup
}else{
} else {
// Merge the local and remote sync data
val mergedSyncData = mergeSyncData(syncData, remoteSData)
pushSyncData(mergedSyncData)
@ -165,7 +165,9 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
appProperties = mapOf("deviceId" to syncData.deviceId)
}
drive.files().update(fileId, fileMetadata, mediaContent).execute()
logcat(LogPriority.DEBUG) { "Updated existing sync data file in Google Drive with file ID: $fileId" }
logcat(LogPriority.DEBUG) {
"Updated existing sync data file in Google Drive with file ID: $fileId"
}
} else {
val fileMetadata = File().apply {
name = remoteFileName
@ -176,7 +178,9 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
val uploadedFile = drive.files().create(fileMetadata, mediaContent)
.setFields("id")
.execute()
logcat(LogPriority.DEBUG) { "Created new sync data file in Google Drive with file ID: ${uploadedFile.id}" }
logcat(LogPriority.DEBUG) {
"Created new sync data file in Google Drive with file ID: ${uploadedFile.id}"
}
}
}
}
@ -203,7 +207,6 @@ class GoogleDriveSyncService(context: Context, json: Json, syncPreferences: Sync
}
}
suspend fun deleteSyncDataFromGoogleDrive(): DeleteSyncDataStatus {
val drive = googleDriveService.driveService

View File

@ -26,7 +26,7 @@ abstract class SyncService(
val json: Json,
val syncPreferences: SyncPreferences,
) {
abstract suspend fun doSync(syncData: SyncData): Backup?;
abstract suspend fun doSync(syncData: SyncData): Backup?
/**
* Merges the local and remote sync data into a single JSON string.
@ -44,7 +44,8 @@ abstract class SyncService(
remoteSyncData.backup?.backupManga,
localSyncData.backup?.backupCategories ?: emptyList(),
remoteSyncData.backup?.backupCategories ?: emptyList(),
mergedCategoriesList)
mergedCategoriesList,
)
val mergedSourcesList =
mergeSourcesLists(localSyncData.backup?.backupSources, remoteSyncData.backup?.backupSources)
@ -120,11 +121,13 @@ abstract class SyncService(
val mergedCategoriesMapByName = mergedCategories.associateBy { it.name }
fun updateCategories(theManga: BackupManga, theMap: Map<Long, BackupCategory>): BackupManga {
return theManga.copy(categories = theManga.categories.mapNotNull {
theMap[it]?.let { category ->
mergedCategoriesMapByName[category.name]?.order
}
})
return theManga.copy(
categories = theManga.categories.mapNotNull {
theMap[it]?.let { category ->
mergedCategoriesMapByName[category.name]?.order
}
},
)
}
logcat(LogPriority.DEBUG, logTag) {
@ -147,7 +150,7 @@ abstract class SyncService(
}
updateCategories(
local.copy(chapters = mergeChapters(local.chapters, remote.chapters)),
localCategoriesMapByOrder
localCategoriesMapByOrder,
)
} else {
logcat(LogPriority.DEBUG, logTag) {
@ -155,7 +158,7 @@ abstract class SyncService(
}
updateCategories(
remote.copy(chapters = mergeChapters(local.chapters, remote.chapters)),
remoteCategoriesMapByOrder
remoteCategoriesMapByOrder,
)
}
}
@ -301,7 +304,7 @@ abstract class SyncService(
private fun mergeSourcesLists(
localSources: List<BackupSource>?,
remoteSources: List<BackupSource>?
remoteSources: List<BackupSource>?,
): List<BackupSource> {
val logTag = "MergeSources"
@ -346,7 +349,7 @@ abstract class SyncService(
private fun mergePreferencesLists(
localPreferences: List<BackupPreference>?,
remotePreferences: List<BackupPreference>?
remotePreferences: List<BackupPreference>?,
): List<BackupPreference> {
val logTag = "MergePreferences"
@ -394,7 +397,7 @@ abstract class SyncService(
private fun mergeSourcePreferencesLists(
localPreferences: List<BackupSourcePreferences>?,
remotePreferences: List<BackupSourcePreferences>?
remotePreferences: List<BackupSourcePreferences>?,
): List<BackupSourcePreferences> {
val logTag = "MergeSourcePreferences"
@ -408,38 +411,39 @@ abstract class SyncService(
}
// Merge both source preferences maps
val mergedSourcePreferences = (localPreferencesMap.keys + remotePreferencesMap.keys).distinct().mapNotNull { sourceKey ->
val localSourcePreference = localPreferencesMap[sourceKey]
val remoteSourcePreference = remotePreferencesMap[sourceKey]
val mergedSourcePreferences = (localPreferencesMap.keys + remotePreferencesMap.keys).distinct()
.mapNotNull { sourceKey ->
val localSourcePreference = localPreferencesMap[sourceKey]
val remoteSourcePreference = remotePreferencesMap[sourceKey]
logcat(LogPriority.DEBUG, logTag) {
"Processing source preference key: $sourceKey. " +
"Local source preference: ${localSourcePreference != null}, " +
"Remote source preference: ${remoteSourcePreference != null}"
}
logcat(LogPriority.DEBUG, logTag) {
"Processing source preference key: $sourceKey. " +
"Local source preference: ${localSourcePreference != null}, " +
"Remote source preference: ${remoteSourcePreference != null}"
}
when {
localSourcePreference != null && remoteSourcePreference == null -> {
logcat(LogPriority.DEBUG, logTag) {
"Using local source preference: ${localSourcePreference.sourceKey}."
when {
localSourcePreference != null && remoteSourcePreference == null -> {
logcat(LogPriority.DEBUG, logTag) {
"Using local source preference: ${localSourcePreference.sourceKey}."
}
localSourcePreference
}
localSourcePreference
}
remoteSourcePreference != null && localSourcePreference == null -> {
logcat(LogPriority.DEBUG, logTag) {
"Using remote source preference: ${remoteSourcePreference.sourceKey}."
remoteSourcePreference != null && localSourcePreference == null -> {
logcat(LogPriority.DEBUG, logTag) {
"Using remote source preference: ${remoteSourcePreference.sourceKey}."
}
remoteSourcePreference
}
remoteSourcePreference
localSourcePreference != null && remoteSourcePreference != null -> {
// Merge the individual preferences within the source preferences
val mergedPrefs =
mergeIndividualPreferences(localSourcePreference.prefs, remoteSourcePreference.prefs)
BackupSourcePreferences(sourceKey, mergedPrefs)
}
else -> null
}
localSourcePreference != null && remoteSourcePreference != null -> {
// Merge the individual preferences within the source preferences
val mergedPrefs =
mergeIndividualPreferences(localSourcePreference.prefs, remoteSourcePreference.prefs)
BackupSourcePreferences(sourceKey, mergedPrefs)
}
else -> null
}
}
logcat(LogPriority.DEBUG, logTag) {
"Source preferences merge completed. Total merged source preferences: ${mergedSourcePreferences.size}"
@ -450,7 +454,7 @@ abstract class SyncService(
private fun mergeIndividualPreferences(
localPrefs: List<BackupPreference>,
remotePrefs: List<BackupPreference>
remotePrefs: List<BackupPreference>,
): List<BackupPreference> {
val mergedPrefsMap = (localPrefs + remotePrefs).associateBy { it.key }
return mergedPrefsMap.values.toList()
@ -459,7 +463,7 @@ abstract class SyncService(
// SY -->
private fun mergeSavedSearchesLists(
localSearches: List<BackupSavedSearch>?,
remoteSearches: List<BackupSavedSearch>?
remoteSearches: List<BackupSavedSearch>?,
): List<BackupSavedSearch> {
val logTag = "MergeSavedSearches"

View File

@ -38,7 +38,7 @@ class SyncYomiSyncService(
try {
val (remoteData, etag) = pullSyncData()
val finalSyncData = if (remoteData != null){
val finalSyncData = if (remoteData != null) {
assert(etag.isNotEmpty()) { "ETag should never be empty if remote data is not null" }
logcat(LogPriority.DEBUG, "SyncService") {
"Try update remote data with ETag($etag)"
@ -54,7 +54,6 @@ class SyncYomiSyncService(
pushSyncData(finalSyncData, etag)
return finalSyncData.backup
} catch (e: Exception) {
logcat(LogPriority.ERROR) { "Error syncing: ${e.message}" }
notifier.showSyncError(e.message)
@ -113,7 +112,6 @@ class SyncYomiSyncService(
// return default value so we can overwrite it
Pair(null, "")
}
} else {
val responseBody = response.body.string()
notifier.showSyncError("Failed to download sync data: $responseBody")
@ -165,11 +163,9 @@ class SyncYomiSyncService(
.takeIf { it?.isNotEmpty() == true } ?: throw SyncYomiException("Missing ETag")
syncPreferences.lastSyncEtag().set(newETag)
logcat(LogPriority.DEBUG) { "SyncYomi sync completed" }
} else if (response.code == HttpStatus.SC_PRECONDITION_FAILED) {
// other clients updated remote data, will try next time
logcat(LogPriority.DEBUG) { "SyncYomi sync failed with 412" }
} else {
val responseBody = response.body.string()
notifier.showSyncError("Failed to upload sync data: $responseBody")

View File

@ -65,7 +65,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
with(json) {
authClient.newCall(
POST(
apiUrl,
API_URL,
body = payload.toString().toRequestBody(jsonMime),
),
)
@ -109,7 +109,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
put("completedAt", createDate(track.finished_reading_date))
}
}
authClient.newCall(POST(apiUrl, body = payload.toString().toRequestBody(jsonMime)))
authClient.newCall(POST(API_URL, body = payload.toString().toRequestBody(jsonMime)))
.awaitSuccess()
track
}
@ -131,7 +131,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
put("listId", track.libraryId)
}
}
authClient.newCall(POST(apiUrl, body = payload.toString().toRequestBody(jsonMime)))
authClient.newCall(POST(API_URL, body = payload.toString().toRequestBody(jsonMime)))
.awaitSuccess()
}
}
@ -172,7 +172,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
with(json) {
authClient.newCall(
POST(
apiUrl,
API_URL,
body = payload.toString().toRequestBody(jsonMime),
),
)
@ -242,7 +242,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
with(json) {
authClient.newCall(
POST(
apiUrl,
API_URL,
body = payload.toString().toRequestBody(jsonMime),
),
)
@ -286,7 +286,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
with(json) {
authClient.newCall(
POST(
apiUrl,
API_URL,
body = payload.toString().toRequestBody(jsonMime),
),
)
@ -364,17 +364,17 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
}
companion object {
private const val clientId = "16329"
private const val apiUrl = "https://graphql.anilist.co/"
private const val baseUrl = "https://anilist.co/api/v2/"
private const val baseMangaUrl = "https://anilist.co/manga/"
private const val CLIENT_ID = "16329"
private const val API_URL = "https://graphql.anilist.co/"
private const val BASE_URL = "https://anilist.co/api/v2/"
private const val BASE_MANGA_URL = "https://anilist.co/manga/"
fun mangaUrl(mediaId: Long): String {
return baseMangaUrl + mediaId
return BASE_MANGA_URL + mediaId
}
fun authUrl(): Uri = "${baseUrl}oauth/authorize".toUri().buildUpon()
.appendQueryParameter("client_id", clientId)
fun authUrl(): Uri = "${BASE_URL}oauth/authorize".toUri().buildUpon()
.appendQueryParameter("client_id", CLIENT_ID)
.appendQueryParameter("response_type", "token")
.build()
}

View File

@ -42,7 +42,7 @@ class BangumiApi(
.add("rating", track.score.toInt().toString())
.add("status", track.toBangumiStatus())
.build()
authClient.newCall(POST("$apiUrl/collection/${track.remote_id}/update", body = body))
authClient.newCall(POST("$API_URL/collection/${track.remote_id}/update", body = body))
.awaitSuccess()
track
}
@ -55,7 +55,7 @@ class BangumiApi(
.add("rating", track.score.toInt().toString())
.add("status", track.toBangumiStatus())
.build()
authClient.newCall(POST("$apiUrl/collection/${track.remote_id}/update", body = sbody))
authClient.newCall(POST("$API_URL/collection/${track.remote_id}/update", body = sbody))
.awaitSuccess()
// chapter update
@ -64,7 +64,7 @@ class BangumiApi(
.build()
authClient.newCall(
POST(
"$apiUrl/subject/${track.remote_id}/update/watched_eps",
"$API_URL/subject/${track.remote_id}/update/watched_eps",
body = body,
),
).awaitSuccess()
@ -75,7 +75,7 @@ class BangumiApi(
suspend fun search(search: String): List<TrackSearch> {
return withIOContext {
val url = "$apiUrl/search/subject/${URLEncoder.encode(search, StandardCharsets.UTF_8.name())}"
val url = "$API_URL/search/subject/${URLEncoder.encode(search, StandardCharsets.UTF_8.name())}"
.toUri()
.buildUpon()
.appendQueryParameter("max_results", "20")
@ -124,7 +124,7 @@ class BangumiApi(
suspend fun findLibManga(track: Track): Track? {
return withIOContext {
with(json) {
authClient.newCall(GET("$apiUrl/subject/${track.remote_id}"))
authClient.newCall(GET("$API_URL/subject/${track.remote_id}"))
.awaitSuccess()
.parseAs<JsonObject>()
.let { jsonToSearch(it) }
@ -134,7 +134,7 @@ class BangumiApi(
suspend fun statusLibManga(track: Track): Track? {
return withIOContext {
val urlUserRead = "$apiUrl/collection/${track.remote_id}"
val urlUserRead = "$API_URL/collection/${track.remote_id}"
val requestUserRead = Request.Builder()
.url(urlUserRead)
.cacheControl(CacheControl.FORCE_NETWORK)
@ -171,41 +171,41 @@ class BangumiApi(
}
private fun accessTokenRequest(code: String) = POST(
oauthUrl,
OAUTH_URL,
body = FormBody.Builder()
.add("grant_type", "authorization_code")
.add("client_id", clientId)
.add("client_secret", clientSecret)
.add("client_id", CLIENT_ID)
.add("client_secret", CLIENT_SECRET)
.add("code", code)
.add("redirect_uri", redirectUrl)
.add("redirect_uri", REDIRECT_URL)
.build(),
)
companion object {
private const val clientId = "bgm291665acbd06a4c28"
private const val clientSecret = "43e5ce36b207de16e5d3cfd3e79118db"
private const val CLIENT_ID = "bgm291665acbd06a4c28"
private const val CLIENT_SECRET = "43e5ce36b207de16e5d3cfd3e79118db"
private const val apiUrl = "https://api.bgm.tv"
private const val oauthUrl = "https://bgm.tv/oauth/access_token"
private const val loginUrl = "https://bgm.tv/oauth/authorize"
private const val API_URL = "https://api.bgm.tv"
private const val OAUTH_URL = "https://bgm.tv/oauth/access_token"
private const val LOGIN_URL = "https://bgm.tv/oauth/authorize"
private const val redirectUrl = "mihon://bangumi-auth"
private const val REDIRECT_URL = "mihon://bangumi-auth"
fun authUrl(): Uri =
loginUrl.toUri().buildUpon()
.appendQueryParameter("client_id", clientId)
LOGIN_URL.toUri().buildUpon()
.appendQueryParameter("client_id", CLIENT_ID)
.appendQueryParameter("response_type", "code")
.appendQueryParameter("redirect_uri", redirectUrl)
.appendQueryParameter("redirect_uri", REDIRECT_URL)
.build()
fun refreshTokenRequest(token: String) = POST(
oauthUrl,
OAUTH_URL,
body = FormBody.Builder()
.add("grant_type", "refresh_token")
.add("client_id", clientId)
.add("client_secret", clientSecret)
.add("client_id", CLIENT_ID)
.add("client_secret", CLIENT_SECRET)
.add("refresh_token", token)
.add("redirect_uri", redirectUrl)
.add("redirect_uri", REDIRECT_URL)
.build(),
)
}

View File

@ -66,7 +66,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
with(json) {
authClient.newCall(
POST(
"${baseUrl}library-entries",
"${BASE_URL}library-entries",
headers = headersOf(
"Content-Type",
"application/vnd.api+json",
@ -104,7 +104,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
with(json) {
authClient.newCall(
Request.Builder()
.url("${baseUrl}library-entries/${track.remote_id}")
.url("${BASE_URL}library-entries/${track.remote_id}")
.headers(
headersOf(
"Content-Type",
@ -130,7 +130,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
authClient
.newCall(
DELETE(
"${baseUrl}library-entries/${track.remoteId}",
"${BASE_URL}library-entries/${track.remoteId}",
headers = headersOf(
"Content-Type",
"application/vnd.api+json",
@ -143,7 +143,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
suspend fun search(query: String): List<TrackSearch> {
return withIOContext {
with(json) {
authClient.newCall(GET(algoliaKeyUrl))
authClient.newCall(GET(ALGOLIA_KEY_URL))
.awaitSuccess()
.parseAs<JsonObject>()
.let {
@ -157,16 +157,16 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
private suspend fun algoliaSearch(key: String, query: String): List<TrackSearch> {
return withIOContext {
val jsonObject = buildJsonObject {
put("params", "query=${URLEncoder.encode(query, StandardCharsets.UTF_8.name())}$algoliaFilter")
put("params", "query=${URLEncoder.encode(query, StandardCharsets.UTF_8.name())}$ALGOLIA_FILTER")
}
with(json) {
client.newCall(
POST(
algoliaUrl,
ALGOLIA_URL,
headers = headersOf(
"X-Algolia-Application-Id",
algoliaAppId,
ALGOLIA_APP_ID,
"X-Algolia-API-Key",
key,
),
@ -187,7 +187,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
suspend fun findLibManga(track: Track, userId: String): Track? {
return withIOContext {
val url = "${baseUrl}library-entries".toUri().buildUpon()
val url = "${BASE_URL}library-entries".toUri().buildUpon()
.encodedQuery("filter[manga_id]=${track.remote_id}&filter[user_id]=$userId")
.appendQueryParameter("include", "manga")
.build()
@ -210,7 +210,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
suspend fun getLibManga(track: Track): Track {
return withIOContext {
val url = "${baseUrl}library-entries".toUri().buildUpon()
val url = "${BASE_URL}library-entries".toUri().buildUpon()
.encodedQuery("filter[id]=${track.remote_id}")
.appendQueryParameter("include", "manga")
.build()
@ -237,11 +237,11 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
.add("username", username)
.add("password", password)
.add("grant_type", "password")
.add("client_id", clientId)
.add("client_secret", clientSecret)
.add("client_id", CLIENT_ID)
.add("client_secret", CLIENT_SECRET)
.build()
with(json) {
client.newCall(POST(loginUrl, body = formBody))
client.newCall(POST(LOGIN_URL, body = formBody))
.awaitSuccess()
.parseAs()
}
@ -250,7 +250,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
suspend fun getCurrentUser(): String {
return withIOContext {
val url = "${baseUrl}users".toUri().buildUpon()
val url = "${BASE_URL}users".toUri().buildUpon()
.encodedQuery("filter[self]=true")
.build()
with(json) {
@ -265,35 +265,31 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
}
companion object {
private const val clientId =
"dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd"
private const val clientSecret =
"54d7307928f63414defd96399fc31ba847961ceaecef3a5fd93144e960c0e151"
private const val CLIENT_ID = "dd031b32d2f56c990b1425efe6c42ad847e7fe3ab46bf1299f05ecd856bdb7dd"
private const val CLIENT_SECRET = "54d7307928f63414defd96399fc31ba847961ceaecef3a5fd93144e960c0e151"
private const val baseUrl = "https://kitsu.app/api/edge/"
private const val loginUrl = "https://kitsu.app/api/oauth/token"
private const val baseMangaUrl = "https://kitsu.app/manga/"
private const val algoliaKeyUrl = "https://kitsu.app/api/edge/algolia-keys/media/"
private const val BASE_URL = "https://kitsu.app/api/edge/"
private const val LOGIN_URL = "https://kitsu.app/api/oauth/token"
private const val BASE_MANGA_URL = "https://kitsu.app/manga/"
private const val ALGOLIA_KEY_URL = "https://kitsu.app/api/edge/algolia-keys/media/"
private const val algoliaUrl =
"https://AWQO5J657S-dsn.algolia.net/1/indexes/production_media/query/"
private const val algoliaAppId = "AWQO5J657S"
private const val algoliaFilter =
"&facetFilters=%5B%22kind%3Amanga%22%5D&attributesToRetrieve=" +
"%5B%22synopsis%22%2C%22averageRating%22%2C%22canonicalTitle%22%2C%22chapterCount%22%2C%22" +
"posterImage%22%2C%22startDate%22%2C%22subtype%22%2C%22endDate%22%2C%20%22id%22%5D"
private const val ALGOLIA_APP_ID = "AWQO5J657S"
private const val ALGOLIA_URL = "https://$ALGOLIA_APP_ID-dsn.algolia.net/1/indexes/production_media/query/"
private const val ALGOLIA_FILTER = "&facetFilters=%5B%22kind%3Amanga%22%5D&attributesToRetrieve=" +
"%5B%22synopsis%22%2C%22averageRating%22%2C%22canonicalTitle%22%2C%22chapterCount%22%2C%22" +
"posterImage%22%2C%22startDate%22%2C%22subtype%22%2C%22endDate%22%2C%20%22id%22%5D"
fun mangaUrl(remoteId: Long): String {
return baseMangaUrl + remoteId
return BASE_MANGA_URL + remoteId
}
fun refreshTokenRequest(token: String) = POST(
loginUrl,
LOGIN_URL,
body = FormBody.Builder()
.add("grant_type", "refresh_token")
.add("refresh_token", token)
.add("client_id", clientId)
.add("client_secret", clientSecret)
.add("client_id", CLIENT_ID)
.add("client_secret", CLIENT_SECRET)
.build(),
)
}

View File

@ -6,8 +6,8 @@ import java.util.Locale
object KitsuDateHelper {
private const val pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
private val formatter = SimpleDateFormat(pattern, Locale.ENGLISH)
private const val PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
private val formatter = SimpleDateFormat(PATTERN, Locale.ENGLISH)
fun convert(dateValue: Long): String? {
if (dateValue == 0L) return null

View File

@ -1,3 +1,5 @@
@file:Suppress("PropertyName", "ktlint:standard:property-naming")
package eu.kanade.tachiyomi.data.track.model
import eu.kanade.tachiyomi.data.database.models.Track

View File

@ -54,7 +54,7 @@ class ShikimoriApi(
}
authClient.newCall(
POST(
"$apiUrl/v2/user_rates",
"$API_URL/v2/user_rates",
body = payload.toString().toRequestBody(jsonMime),
),
).awaitSuccess()
@ -72,14 +72,14 @@ class ShikimoriApi(
suspend fun deleteLibManga(track: DomainTrack) {
withIOContext {
authClient
.newCall(DELETE("$apiUrl/v2/user_rates/${track.libraryId}"))
.newCall(DELETE("$API_URL/v2/user_rates/${track.libraryId}"))
.awaitSuccess()
}
}
suspend fun search(search: String): List<TrackSearch> {
return withIOContext {
val url = "$apiUrl/mangas".toUri().buildUpon()
val url = "$API_URL/mangas".toUri().buildUpon()
.appendQueryParameter("order", "popularity")
.appendQueryParameter("search", search)
.appendQueryParameter("limit", "20")
@ -102,10 +102,10 @@ class ShikimoriApi(
remote_id = obj["id"]!!.jsonPrimitive.long
title = obj["name"]!!.jsonPrimitive.content
total_chapters = obj["chapters"]!!.jsonPrimitive.long
cover_url = baseUrl + obj["image"]!!.jsonObject["preview"]!!.jsonPrimitive.content
cover_url = BASE_URL + obj["image"]!!.jsonObject["preview"]!!.jsonPrimitive.content
summary = ""
score = obj["score"]!!.jsonPrimitive.double
tracking_url = baseUrl + obj["url"]!!.jsonPrimitive.content
tracking_url = BASE_URL + obj["url"]!!.jsonPrimitive.content
publishing_status = obj["status"]!!.jsonPrimitive.content
publishing_type = obj["kind"]!!.jsonPrimitive.content
start_date = obj["aired_on"]!!.jsonPrimitive.contentOrNull ?: ""
@ -121,13 +121,13 @@ class ShikimoriApi(
last_chapter_read = obj["chapters"]!!.jsonPrimitive.double
score = obj["score"]!!.jsonPrimitive.int.toDouble()
status = toTrackStatus(obj["status"]!!.jsonPrimitive.content)
tracking_url = baseUrl + mangas["url"]!!.jsonPrimitive.content
tracking_url = BASE_URL + mangas["url"]!!.jsonPrimitive.content
}
}
suspend fun findLibManga(track: Track, userId: String): Track? {
return withIOContext {
val urlMangas = "$apiUrl/mangas".toUri().buildUpon()
val urlMangas = "$API_URL/mangas".toUri().buildUpon()
.appendPath(track.remote_id.toString())
.build()
val mangas = with(json) {
@ -136,7 +136,7 @@ class ShikimoriApi(
.parseAs<JsonObject>()
}
val url = "$apiUrl/v2/user_rates".toUri().buildUpon()
val url = "$API_URL/v2/user_rates".toUri().buildUpon()
.appendQueryParameter("user_id", userId)
.appendQueryParameter("target_id", track.remote_id.toString())
.appendQueryParameter("target_type", "Manga")
@ -160,7 +160,7 @@ class ShikimoriApi(
suspend fun getCurrentUser(): Int {
return with(json) {
authClient.newCall(GET("$apiUrl/users/whoami"))
authClient.newCall(GET("$API_URL/users/whoami"))
.awaitSuccess()
.parseAs<JsonObject>()
.let {
@ -180,39 +180,39 @@ class ShikimoriApi(
}
private fun accessTokenRequest(code: String) = POST(
oauthUrl,
OAUTH_URL,
body = FormBody.Builder()
.add("grant_type", "authorization_code")
.add("client_id", clientId)
.add("client_secret", clientSecret)
.add("client_id", CLIENT_ID)
.add("client_secret", CLIENT_SECRET)
.add("code", code)
.add("redirect_uri", redirectUrl)
.add("redirect_uri", REDIRECT_URL)
.build(),
)
companion object {
private const val clientId = "PB9dq8DzI405s7wdtwTdirYqHiyVMh--djnP7lBUqSA"
private const val clientSecret = "NajpZcOBKB9sJtgNcejf8OB9jBN1OYYoo-k4h2WWZus"
private const val CLIENT_ID = "PB9dq8DzI405s7wdtwTdirYqHiyVMh--djnP7lBUqSA"
private const val CLIENT_SECRET = "NajpZcOBKB9sJtgNcejf8OB9jBN1OYYoo-k4h2WWZus"
private const val baseUrl = "https://shikimori.one"
private const val apiUrl = "$baseUrl/api"
private const val oauthUrl = "$baseUrl/oauth/token"
private const val loginUrl = "$baseUrl/oauth/authorize"
private const val BASE_URL = "https://shikimori.one"
private const val API_URL = "$BASE_URL/api"
private const val OAUTH_URL = "$BASE_URL/oauth/token"
private const val LOGIN_URL = "$BASE_URL/oauth/authorize"
private const val redirectUrl = "mihon://shikimori-auth"
private const val REDIRECT_URL = "mihon://shikimori-auth"
fun authUrl(): Uri = loginUrl.toUri().buildUpon()
.appendQueryParameter("client_id", clientId)
.appendQueryParameter("redirect_uri", redirectUrl)
fun authUrl(): Uri = LOGIN_URL.toUri().buildUpon()
.appendQueryParameter("client_id", CLIENT_ID)
.appendQueryParameter("redirect_uri", REDIRECT_URL)
.appendQueryParameter("response_type", "code")
.build()
fun refreshTokenRequest(token: String) = POST(
oauthUrl,
OAUTH_URL,
body = FormBody.Builder()
.add("grant_type", "refresh_token")
.add("client_id", clientId)
.add("client_secret", clientSecret)
.add("client_id", CLIENT_ID)
.add("client_secret", CLIENT_SECRET)
.add("refresh_token", token)
.build(),
)

View File

@ -69,17 +69,18 @@ class ExtensionManager(
private val iconMap = mutableMapOf<String, Drawable>()
private val _installedExtensionsMapFlow = MutableStateFlow(emptyMap<String, Extension.Installed>())
val installedExtensionsFlow = _installedExtensionsMapFlow.mapExtensions(scope)
private val installedExtensionMapFlow = MutableStateFlow(emptyMap<String, Extension.Installed>())
val installedExtensionsFlow = installedExtensionMapFlow.mapExtensions(scope)
private val availableExtensionMapFlow = MutableStateFlow(emptyMap<String, Extension.Available>())
private val _availableExtensionsMapFlow = MutableStateFlow(emptyMap<String, Extension.Available>())
// SY -->
val availableExtensionsFlow = _availableExtensionsMapFlow.map { it.filterNotBlacklisted().values.toList() }
.stateIn(scope, SharingStarted.Lazily, _availableExtensionsMapFlow.value.values.toList())
val availableExtensionsFlow = availableExtensionMapFlow.map { it.filterNotBlacklisted().values.toList() }
.stateIn(scope, SharingStarted.Lazily, availableExtensionMapFlow.value.values.toList())
// SY <--
private val _untrustedExtensionsMapFlow = MutableStateFlow(emptyMap<String, Extension.Untrusted>())
val untrustedExtensionsFlow = _untrustedExtensionsMapFlow.mapExtensions(scope)
private val untrustedExtensionMapFlow = MutableStateFlow(emptyMap<String, Extension.Untrusted>())
val untrustedExtensionsFlow = untrustedExtensionMapFlow.mapExtensions(scope)
init {
initExtensions()
@ -89,7 +90,7 @@ class ExtensionManager(
private var subLanguagesEnabledOnFirstRun = preferences.enabledLanguages().isSet()
fun getAppIconForSource(sourceId: Long): Drawable? {
val pkgName = _installedExtensionsMapFlow.value.values
val pkgName = installedExtensionMapFlow.value.values
.find { ext ->
ext.sources.any { it.id == sourceId }
}
@ -128,11 +129,11 @@ class ExtensionManager(
private fun initExtensions() {
val extensions = ExtensionLoader.loadExtensions(context)
_installedExtensionsMapFlow.value = extensions
installedExtensionMapFlow.value = extensions
.filterIsInstance<LoadResult.Success>()
.associate { it.extension.pkgName to it.extension }
_untrustedExtensionsMapFlow.value = extensions
untrustedExtensionMapFlow.value = extensions
.filterIsInstance<LoadResult.Untrusted>()
.associate { it.extension.pkgName to it.extension }
// SY -->
@ -159,7 +160,7 @@ class ExtensionManager(
// EXH <--
/**
* Finds the available extensions in the [api] and updates [_availableExtensionsMapFlow].
* Finds the available extensions in the [api] and updates [availableExtensionMapFlow].
*/
suspend fun findAvailableExtensions() {
val extensions: List<Extension.Available> = try {
@ -172,7 +173,7 @@ class ExtensionManager(
enableAdditionalSubLanguages(extensions)
_availableExtensionsMapFlow.value = extensions.associateBy { it.pkgName }
availableExtensionMapFlow.value = extensions.associateBy { it.pkgName }
updatedInstalledExtensionsStatuses(extensions)
setupAvailableExtensionsSourcesDataMap(extensions)
}
@ -218,7 +219,7 @@ class ExtensionManager(
return
}
val installedExtensionsMap = _installedExtensionsMapFlow.value.toMutableMap()
val installedExtensionsMap = installedExtensionMapFlow.value.toMutableMap()
var changed = false
for ((pkgName, extension) in installedExtensionsMap) {
val availableExt = availableExtensions.find { it.pkgName == pkgName }
@ -247,7 +248,7 @@ class ExtensionManager(
}
}
if (changed) {
_installedExtensionsMapFlow.value = installedExtensionsMap
installedExtensionMapFlow.value = installedExtensionsMap
}
updatePendingUpdatesCount()
}
@ -271,7 +272,7 @@ class ExtensionManager(
* @param extension The extension to be updated.
*/
fun updateExtension(extension: Extension.Installed): Flow<InstallStep> {
val availableExt = _availableExtensionsMapFlow.value[extension.pkgName] ?: return emptyFlow()
val availableExt = availableExtensionMapFlow.value[extension.pkgName] ?: return emptyFlow()
return installExtension(availableExt)
}
@ -308,11 +309,11 @@ class ExtensionManager(
* @param extension the extension to trust
*/
suspend fun trust(extension: Extension.Untrusted) {
_untrustedExtensionsMapFlow.value[extension.pkgName] ?: return
untrustedExtensionMapFlow.value[extension.pkgName] ?: return
trustExtension.trust(extension.pkgName, extension.versionCode, extension.signatureHash)
_untrustedExtensionsMapFlow.value -= extension.pkgName
untrustedExtensionMapFlow.value -= extension.pkgName
ExtensionLoader.loadExtensionFromPkgName(context, extension.pkgName)
.let { it as? LoadResult.Success }
@ -332,7 +333,7 @@ class ExtensionManager(
}
// SY <--
_installedExtensionsMapFlow.value += extension
installedExtensionMapFlow.value += extension
}
/**
@ -349,7 +350,7 @@ class ExtensionManager(
}
// SY <--
_installedExtensionsMapFlow.value += extension
installedExtensionMapFlow.value += extension
}
/**
@ -359,8 +360,8 @@ class ExtensionManager(
* @param pkgName The package name of the uninstalled application.
*/
private fun unregisterExtension(pkgName: String) {
_installedExtensionsMapFlow.value -= pkgName
_untrustedExtensionsMapFlow.value -= pkgName
installedExtensionMapFlow.value -= pkgName
untrustedExtensionMapFlow.value -= pkgName
}
/**
@ -379,8 +380,8 @@ class ExtensionManager(
}
override fun onExtensionUntrusted(extension: Extension.Untrusted) {
_installedExtensionsMapFlow.value -= extension.pkgName
_untrustedExtensionsMapFlow.value += extension
installedExtensionMapFlow.value -= extension.pkgName
untrustedExtensionMapFlow.value += extension
updatePendingUpdatesCount()
}
@ -404,14 +405,14 @@ class ExtensionManager(
private fun Extension.Installed.updateExists(availableExtension: Extension.Available? = null): Boolean {
val availableExt = availableExtension
?: _availableExtensionsMapFlow.value[pkgName]
?: availableExtensionMapFlow.value[pkgName]
?: return false
return (availableExt.versionCode > versionCode || availableExt.libVersion > libVersion)
}
private fun updatePendingUpdatesCount() {
val pendingUpdateCount = _installedExtensionsMapFlow.value.values.count { it.hasUpdate }
val pendingUpdateCount = installedExtensionMapFlow.value.values.count { it.hasUpdate }
preferences.extensionUpdatesCount().set(pendingUpdateCount)
if (pendingUpdateCount == 0) {
ExtensionUpdateNotifier(context).dismiss()

View File

@ -34,8 +34,10 @@ internal class ExtensionApi {
private val getExtensionRepo: GetExtensionRepo by injectLazy()
private val updateExtensionRepo: UpdateExtensionRepo by injectLazy()
private val extensionManager: ExtensionManager by injectLazy()
// SY -->
private val sourcePreferences: SourcePreferences by injectLazy()
// SY <--
private val json: Json by injectLazy()

View File

@ -1,7 +1,13 @@
package eu.kanade.tachiyomi.extension.model
enum class InstallStep {
Idle, Pending, Downloading, Installing, Installed, Error;
Idle,
Pending,
Downloading,
Installing,
Installed,
Error,
;
fun isCompleted(): Boolean {
return this == Installed || this == Error || this == Idle

View File

@ -223,7 +223,6 @@ internal object ExtensionLoader {
* @param context The application context.
* @param extensionInfo The extension to load.
*/
@Suppress("LongMethod", "CyclomaticComplexMethod", "ReturnCount")
private suspend fun loadExtension(context: Context, extensionInfo: ExtensionInfo): LoadResult {
val pkgManager = context.packageManager
val pkgInfo = extensionInfo.packageInfo

View File

@ -383,7 +383,7 @@ class EHentai(
doc.select("#gdd .gdt1").find { el ->
el.text().lowercase() == "posted:"
}!!.nextElementSibling()!!.text(),
MetadataUtil.EX_DATE_FORMAT.withZone(ZoneOffset.UTC)
MetadataUtil.EX_DATE_FORMAT.withZone(ZoneOffset.UTC),
)!!.toInstant().toEpochMilli(),
scanlator = EHentaiSearchMetadata.galleryId(location),
)
@ -401,7 +401,7 @@ class EHentai(
chapter_number = index + 2f,
date_upload = ZonedDateTime.parse(
posted,
MetadataUtil.EX_DATE_FORMAT.withZone(ZoneOffset.UTC)
MetadataUtil.EX_DATE_FORMAT.withZone(ZoneOffset.UTC),
).toInstant().toEpochMilli(),
scanlator = EHentaiSearchMetadata.galleryId(link),
)
@ -542,9 +542,10 @@ class EHentai(
if (
MATCH_SEEK_REGEX.matches(jumpSeekValue) ||
(
MATCH_YEAR_REGEX.matches(jumpSeekValue) && jumpSeekValue.toIntOrNull()?.let {
it in 2007..2099
} == true
MATCH_YEAR_REGEX.matches(jumpSeekValue) &&
jumpSeekValue.toIntOrNull()?.let {
it in 2007..2099
} == true
)
) {
uri.appendQueryParameter("seek", jumpSeekValue)
@ -715,7 +716,7 @@ class EHentai(
when (left.removeSuffix(":").lowercase()) {
"posted" -> datePosted = ZonedDateTime.parse(
right,
MetadataUtil.EX_DATE_FORMAT.withZone(ZoneOffset.UTC)
MetadataUtil.EX_DATE_FORMAT.withZone(ZoneOffset.UTC),
).toInstant().toEpochMilli()
// Example gallery with parent: https://e-hentai.org/g/1390451/7f181c2426/
// Example JP gallery: https://exhentai.org/g/1375385/03519d541b/

View File

@ -119,12 +119,13 @@ class MergedSource : HttpSource() {
"Manga references are empty, chapters unavailable, merge is likely corrupted"
}
val ifDownloadNewChapters = downloadChapters && manga.shouldDownloadNewChapters(
getCategories.await(manga.id).map {
it.id
},
downloadPreferences,
)
val ifDownloadNewChapters = downloadChapters &&
manga.shouldDownloadNewChapters(
getCategories.await(manga.id).map {
it.id
},
downloadPreferences,
)
val semaphore = Semaphore(5)
var exception: Exception? = null
return supervisorScope {

View File

@ -73,16 +73,17 @@ interface SecureActivityDelegate {
}
val lockedDays = preferences.authenticatorDays().get()
val canLockToday = lockedDays == LOCK_ALL_DAYS || when (today.get(Calendar.DAY_OF_WEEK)) {
Calendar.SUNDAY -> (lockedDays and LOCK_SUNDAY) == LOCK_SUNDAY
Calendar.MONDAY -> (lockedDays and LOCK_MONDAY) == LOCK_MONDAY
Calendar.TUESDAY -> (lockedDays and LOCK_TUESDAY) == LOCK_TUESDAY
Calendar.WEDNESDAY -> (lockedDays and LOCK_WEDNESDAY) == LOCK_WEDNESDAY
Calendar.THURSDAY -> (lockedDays and LOCK_THURSDAY) == LOCK_THURSDAY
Calendar.FRIDAY -> (lockedDays and LOCK_FRIDAY) == LOCK_FRIDAY
Calendar.SATURDAY -> (lockedDays and LOCK_SATURDAY) == LOCK_SATURDAY
else -> false
}
val canLockToday = lockedDays == LOCK_ALL_DAYS ||
when (today.get(Calendar.DAY_OF_WEEK)) {
Calendar.SUNDAY -> (lockedDays and LOCK_SUNDAY) == LOCK_SUNDAY
Calendar.MONDAY -> (lockedDays and LOCK_MONDAY) == LOCK_MONDAY
Calendar.TUESDAY -> (lockedDays and LOCK_TUESDAY) == LOCK_TUESDAY
Calendar.WEDNESDAY -> (lockedDays and LOCK_WEDNESDAY) == LOCK_WEDNESDAY
Calendar.THURSDAY -> (lockedDays and LOCK_THURSDAY) == LOCK_THURSDAY
Calendar.FRIDAY -> (lockedDays and LOCK_FRIDAY) == LOCK_FRIDAY
Calendar.SATURDAY -> (lockedDays and LOCK_SATURDAY) == LOCK_SATURDAY
else -> false
}
return canLockNow && canLockToday
}
@ -99,11 +100,13 @@ interface SecureActivityDelegate {
// `requireUnlock` can be true on process start or if app was closed in locked state
if (!AuthenticatorUtil.isAuthenticating && !requireUnlock) {
requireUnlock = /* SY --> */ canLockNow(preferences) && /* SY <-- */ when (val lockDelay = preferences.lockAppAfter().get()) {
-1 -> false // Never
0 -> true // Always
else -> lastClosedPref.get() + lockDelay * 60_000 <= System.currentTimeMillis()
}
requireUnlock =
/* SY --> */ canLockNow(preferences) &&
/* SY <-- */ when (val lockDelay = preferences.lockAppAfter().get()) {
-1 -> false // Never
0 -> true // Always
else -> lastClosedPref.get() + lockDelay * 60_000 <= System.currentTimeMillis()
}
}
lastClosedPref.delete()
@ -140,7 +143,7 @@ class SecureActivityDelegateImpl : SecureActivityDelegate, DefaultLifecycleObser
val incognitoModeFlow = preferences.incognitoMode().changes()
combine(secureScreenFlow, incognitoModeFlow) { secureScreen, incognitoMode ->
secureScreen == SecurityPreferences.SecureScreenMode.ALWAYS ||
secureScreen == SecurityPreferences.SecureScreenMode.INCOGNITO && incognitoMode
(secureScreen == SecurityPreferences.SecureScreenMode.INCOGNITO && incognitoMode)
}
.onEach(activity.window::setSecureScreen)
.launchIn(activity.lifecycleScope)

View File

@ -41,7 +41,7 @@ class ExtensionsScreenModel(
private val getExtensions: GetExtensionsByType = Injekt.get(),
) : StateScreenModel<ExtensionsScreenModel.State>(State()) {
private var _currentDownloads = MutableStateFlow<Map<String, InstallStep>>(hashMapOf())
private val currentDownloads = MutableStateFlow<Map<String, InstallStep>>(hashMapOf())
init {
val context = Injekt.get<Application>()
@ -62,14 +62,20 @@ class ExtensionsScreenModel(
it.name.contains(input, ignoreCase = true) ||
it.baseUrl.contains(input, ignoreCase = true) ||
it.id == input.toLongOrNull()
} || extension.name.contains(input, ignoreCase = true)
} ||
extension.name.contains(input, ignoreCase = true)
}
is Extension.Installed -> {
extension.sources.any {
it.name.contains(input, ignoreCase = true) ||
it.id == input.toLongOrNull() ||
if (it is HttpSource) { it.baseUrl.contains(input, ignoreCase = true) } else false
} || extension.name.contains(input, ignoreCase = true)
if (it is HttpSource) {
it.baseUrl.contains(input, ignoreCase = true)
} else {
false
}
} ||
extension.name.contains(input, ignoreCase = true)
}
is Extension.Untrusted -> extension.name.contains(input, ignoreCase = true)
}
@ -80,7 +86,7 @@ class ExtensionsScreenModel(
screenModelScope.launchIO {
combine(
state.map { it.searchQuery }.distinctUntilChanged().debounce(SEARCH_DEBOUNCE_MILLIS),
_currentDownloads,
currentDownloads,
getExtensions.subscribe(),
) { query, downloads, (_updates, _installed, _available, _untrusted) ->
val searchQuery = query ?: ""
@ -103,7 +109,8 @@ class ExtensionsScreenModel(
.groupBy { it.lang }
.toSortedMap(LocaleHelper.comparator)
.map { (lang, exts) ->
ExtensionUiModel.Header.Text(LocaleHelper.getSourceDisplayName(lang, context)) to exts.map(extensionMapper(downloads))
ExtensionUiModel.Header.Text(LocaleHelper.getSourceDisplayName(lang, context)) to
exts.map(extensionMapper(downloads))
}
if (languagesWithExtensions.isNotEmpty()) {
itemsGroups.putAll(languagesWithExtensions)
@ -165,11 +172,11 @@ class ExtensionsScreenModel(
}
private fun addDownloadState(extension: Extension, installStep: InstallStep) {
_currentDownloads.update { it + Pair(extension.pkgName, installStep) }
currentDownloads.update { it + Pair(extension.pkgName, installStep) }
}
private fun removeDownloadState(extension: Extension) {
_currentDownloads.update { it - extension.pkgName }
currentDownloads.update { it - extension.pkgName }
}
private suspend fun Flow<InstallStep>.collectToInstallUpdate(extension: Extension) =

View File

@ -121,7 +121,11 @@ class MigrationListScreen(private val config: MigrationProcedureConfig) : Screen
)
val onDismissRequest = { screenModel.dialog.value = null }
when (@Suppress("NAME_SHADOWING") val dialog = dialog) {
when
(
@Suppress("NAME_SHADOWING")
val dialog = dialog
) {
is MigrationListScreenModel.Dialog.MigrateMangaDialog -> {
MigrationMangaDialog(
onDismissRequest = onDismissRequest,

View File

@ -223,7 +223,9 @@ class MigrationListScreenModel(
smartSearchEngine.normalSearch(source, mangaObj.ogTitle)
}
if (searchResult != null && !(searchResult.url == mangaObj.url && source.id == mangaObj.source)) {
if (searchResult != null &&
!(searchResult.url == mangaObj.url && source.id == mangaObj.source)
) {
val localManga = networkToLocalManga.await(searchResult)
val chapters = if (source is EHentai) {
@ -237,7 +239,8 @@ class MigrationListScreenModel(
} catch (e: Exception) {
return@async2 null
}
manga.progress.value = validSources.size to processedSources.incrementAndGet()
manga.progress.value =
validSources.size to processedSources.incrementAndGet()
localManga to chapters.size
} else {
null
@ -314,7 +317,8 @@ class MigrationListScreenModel(
if (result == null && hideNotFound) {
removeManga(manga)
}
if (result != null && showOnlyUpdates &&
if (result != null &&
showOnlyUpdates &&
(getChapterInfo(result.id).latestChapter ?: 0.0) <= (manga.chapterInfo.latestChapter ?: 0.0)
) {
removeManga(manga)
@ -363,7 +367,10 @@ class MigrationListScreenModel(
dbChapters.forEach { chapter ->
if (chapter.isRecognizedNumber) {
val prevChapter = prevMangaChapters.find { it.isRecognizedNumber && it.chapterNumber == chapter.chapterNumber }
val prevChapter = prevMangaChapters.find {
it.isRecognizedNumber &&
it.chapterNumber == chapter.chapterNumber
}
if (prevChapter != null) {
chapterUpdates += ChapterUpdate(
id = chapter.id,

View File

@ -119,7 +119,10 @@ class SourcesScreenModel(
items = byLang
.flatMap {
listOf(
SourceUiModel.Header(it.key.removePrefix(CATEGORY_KEY_PREFIX), it.value.firstOrNull()?.category != null),
SourceUiModel.Header(
it.key.removePrefix(CATEGORY_KEY_PREFIX),
it.value.firstOrNull()?.category != null,
),
*it.value.map { source ->
SourceUiModel.Item(source)
}.toTypedArray(),

View File

@ -48,7 +48,15 @@ class BiometricTimesScreen : Screen() {
fun showTimePicker(startTime: Duration? = null) {
val activity = context as? MainActivity ?: return
val picker = MaterialTimePicker.Builder()
.setTitleText(if (startTime == null) SYMR.strings.biometric_lock_start_time.getString(context) else SYMR.strings.biometric_lock_end_time.getString(context))
.setTitleText(
if (startTime ==
null
) {
SYMR.strings.biometric_lock_start_time.getString(context)
} else {
SYMR.strings.biometric_lock_end_time.getString(context)
},
)
.setInputMode(MaterialTimePicker.INPUT_MODE_CLOCK)
.build()
picker.addOnPositiveButtonClickListener {

View File

@ -70,8 +70,8 @@ object HomeScreen : Screen() {
private val openTabEvent = Channel<Tab>()
private val showBottomNavEvent = Channel<Boolean>()
private const val TabFadeDuration = 200
private const val TabNavigatorKey = "HomeTabs"
private const val TAB_FADE_DURATION = 200
private const val TAB_NAVIGATOR_KEY = "HomeTabs"
private val tabs = listOf(
LibraryTab,
@ -94,7 +94,7 @@ object HomeScreen : Screen() {
TabNavigator(
tab = LibraryTab,
key = TabNavigatorKey,
key = TAB_NAVIGATOR_KEY,
) { tabNavigator ->
// Provide usable navigator to content screen
CompositionLocalProvider(LocalNavigator provides navigator) {
@ -144,8 +144,11 @@ object HomeScreen : Screen() {
AnimatedContent(
targetState = tabNavigator.current,
transitionSpec = {
materialFadeThroughIn(initialScale = 1f, durationMillis = TabFadeDuration) togetherWith
materialFadeThroughOut(durationMillis = TabFadeDuration)
materialFadeThroughIn(
initialScale = 1f,
durationMillis = TAB_FADE_DURATION,
) togetherWith
materialFadeThroughOut(durationMillis = TAB_FADE_DURATION)
},
label = "tabContent",
) {

View File

@ -184,7 +184,11 @@ class LibraryScreenModel(
.applyGrouping(groupType)
// SY <--
.applyFilters(tracks, trackingFiler)
.applySort(tracks, trackingFiler.keys,/* SY --> */sort.takeIf { groupType != LibraryGroup.BY_DEFAULT } /* SY <-- */)
.applySort(
tracks, trackingFiler.keys, /* SY --> */sort.takeIf {
groupType != LibraryGroup.BY_DEFAULT
}, /* SY <-- */
)
.mapValues { (_, value) ->
if (searchQuery != null) {
// Filter query
@ -278,7 +282,6 @@ class LibraryScreenModel(
/**
* Applies library filters to the given map of manga.
*/
@Suppress("LongMethod", "CyclomaticComplexMethod")
private suspend fun LibraryMap.applyFilters(
trackMap: Map<Long, List<Track>>,
trackingFiler: Map<Long, TriState>,
@ -373,7 +376,6 @@ class LibraryScreenModel(
/**
* Applies library sorting to the given map of manga.
*/
@Suppress("LongMethod", "CyclomaticComplexMethod")
private fun LibraryMap.applySort(
// Map<MangaId, List<Track>>
trackMap: Map<Long, List<Track>>,
@ -387,7 +389,8 @@ class LibraryScreenModel(
.asSequence()
.mapNotNull {
val list = it.split("|")
(list.getOrNull(0)?.toIntOrNull() ?: return@mapNotNull null) to (list.getOrNull(1) ?: return@mapNotNull null)
(list.getOrNull(0)?.toIntOrNull() ?: return@mapNotNull null) to
(list.getOrNull(1) ?: return@mapNotNull null)
}
.sortedBy { it.first }
.map { it.second }
@ -453,8 +456,12 @@ class LibraryScreenModel(
}
// SY -->
LibrarySort.Type.TagList -> {
val manga1IndexOfTag = listOfTags.indexOfFirst { i1.libraryManga.manga.genre?.contains(it) ?: false }
val manga2IndexOfTag = listOfTags.indexOfFirst { i2.libraryManga.manga.genre?.contains(it) ?: false }
val manga1IndexOfTag = listOfTags.indexOfFirst {
i1.libraryManga.manga.genre?.contains(it) ?: false
}
val manga2IndexOfTag = listOfTags.indexOfFirst {
i2.libraryManga.manga.genre?.contains(it) ?: false
}
manga1IndexOfTag.compareTo(manga2IndexOfTag)
}
// SY <--
@ -822,9 +829,12 @@ class LibraryScreenModel(
if (source != null) {
if (source is MergedSource) {
val mergedMangas = getMergedMangaById.await(manga.id)
val sources = mergedMangas.distinctBy { it.source }.map { sourceManager.getOrStub(it.source) }
val sources = mergedMangas.distinctBy {
it.source
}.map { sourceManager.getOrStub(it.source) }
mergedMangas.forEach merge@{ mergedManga ->
val mergedSource = sources.firstOrNull { mergedManga.source == it.id } as? HttpSource ?: return@merge
val mergedSource =
sources.firstOrNull { mergedManga.source == it.id } as? HttpSource ?: return@merge
downloadManager.deleteManga(mergedManga, mergedSource)
}
} else {
@ -903,10 +913,11 @@ class LibraryScreenModel(
} else {
categoryName
}
LibraryGroup.BY_TRACK_STATUS -> TrackStatus.entries
.find { it.int.toLong() == category?.id }
.let { it ?: TrackStatus.OTHER }
.let { context.stringResource(it.res) }
LibraryGroup.BY_TRACK_STATUS ->
TrackStatus.entries
.find { it.int.toLong() == category?.id }
.let { it ?: TrackStatus.OTHER }
.let { context.stringResource(it.res) }
LibraryGroup.UNGROUPED -> context.stringResource(SYMR.strings.ungrouped)
else -> categoryName
}
@ -993,49 +1004,66 @@ class LibraryScreenModel(
(manga.description?.contains(query, true) == true) ||
(source?.name?.contains(query, true) == true) ||
(sourceIdString != null && sourceIdString == query) ||
(loggedInTrackServices.isNotEmpty() && tracks != null && filterTracks(query, tracks, context)) ||
(
loggedInTrackServices.isNotEmpty() &&
tracks != null &&
filterTracks(query, tracks, context)
) ||
(genre.fastAny { it.contains(query, true) }) ||
(searchTags?.fastAny { it.name.contains(query, true) } == true) ||
(searchTitles?.fastAny { it.title.contains(query, true) } == true)
}
is Namespace -> {
searchTags != null && searchTags.fastAny {
val tag = queryComponent.tag
(it.namespace.equals(queryComponent.namespace, true) && tag?.run { it.name.contains(tag.asQuery(), true) } == true) ||
(tag == null && it.namespace.equals(queryComponent.namespace, true))
}
searchTags != null &&
searchTags.fastAny {
val tag = queryComponent.tag
(
it.namespace.equals(queryComponent.namespace, true) &&
tag?.run { it.name.contains(tag.asQuery(), true) } == true
) ||
(tag == null && it.namespace.equals(queryComponent.namespace, true))
}
}
else -> true
}
true -> when (queryComponent) {
is Text -> {
val query = queryComponent.asQuery()
query.isBlank() || (
(!manga.title.contains(query, true)) &&
(manga.author?.contains(query, true) != true) &&
(manga.artist?.contains(query, true) != true) &&
(manga.description?.contains(query, true) != true) &&
(source?.name?.contains(query, true) != true) &&
(sourceIdString != null && sourceIdString != query) &&
(loggedInTrackServices.isEmpty() || tracks == null || !filterTracks(query, tracks, context)) &&
(!genre.fastAny { it.contains(query, true) }) &&
(searchTags?.fastAny { it.name.contains(query, true) } != true) &&
(searchTitles?.fastAny { it.title.contains(query, true) } != true)
)
query.isBlank() ||
(
(!manga.title.contains(query, true)) &&
(manga.author?.contains(query, true) != true) &&
(manga.artist?.contains(query, true) != true) &&
(manga.description?.contains(query, true) != true) &&
(source?.name?.contains(query, true) != true) &&
(sourceIdString != null && sourceIdString != query) &&
(
loggedInTrackServices.isEmpty() ||
tracks == null ||
!filterTracks(query, tracks, context)
) &&
(!genre.fastAny { it.contains(query, true) }) &&
(searchTags?.fastAny { it.name.contains(query, true) } != true) &&
(searchTitles?.fastAny { it.title.contains(query, true) } != true)
)
}
is Namespace -> {
val searchedTag = queryComponent.tag?.asQuery()
searchTags == null || (queryComponent.namespace.isBlank() && searchedTag.isNullOrBlank()) || searchTags.fastAll { mangaTag ->
if (queryComponent.namespace.isBlank() && !searchedTag.isNullOrBlank()) {
!mangaTag.name.contains(searchedTag, true)
} else if (searchedTag.isNullOrBlank()) {
mangaTag.namespace == null || !mangaTag.namespace.equals(queryComponent.namespace, true)
} else if (mangaTag.namespace.isNullOrBlank()) {
true
} else {
!mangaTag.name.contains(searchedTag, true) || !mangaTag.namespace.equals(queryComponent.namespace, true)
searchTags == null ||
(queryComponent.namespace.isBlank() && searchedTag.isNullOrBlank()) ||
searchTags.fastAll { mangaTag ->
if (queryComponent.namespace.isBlank() && !searchedTag.isNullOrBlank()) {
!mangaTag.name.contains(searchedTag, true)
} else if (searchedTag.isNullOrBlank()) {
mangaTag.namespace == null ||
!mangaTag.namespace.equals(queryComponent.namespace, true)
} else if (mangaTag.namespace.isNullOrBlank()) {
true
} else {
!mangaTag.name.contains(searchedTag, true) ||
!mangaTag.namespace.equals(queryComponent.namespace, true)
}
}
}
}
else -> true
}

View File

@ -34,7 +34,7 @@ class LibrarySettingsScreenModel(
.stateIn(
scope = screenModelScope,
started = SharingStarted.WhileSubscribed(5.seconds.inWholeMilliseconds),
initialValue = trackerManager.loggedInTrackers()
initialValue = trackerManager.loggedInTrackers(),
)
// SY -->

View File

@ -189,14 +189,14 @@ private fun onViewCreated(manga: Manga, context: Context, binding: EditMangaDial
binding.mangaDescription.hint =
context.stringResource(
SYMR.strings.description_hint,
manga.ogDescription?.takeIf { it.isNotBlank() }?.replace("\n", " ")?.chop(20) ?: ""
manga.ogDescription?.takeIf { it.isNotBlank() }?.replace("\n", " ")?.chop(20) ?: "",
)
binding.thumbnailUrl.hint =
context.stringResource(
SYMR.strings.thumbnail_url_hint,
manga.ogThumbnailUrl?.let {
it.chop(40) + if (it.length > 46) "." + it.substringAfterLast(".").chop(6) else ""
} ?: ""
} ?: "",
)
}
binding.mangaGenresTags.clearFocus()

View File

@ -209,10 +209,14 @@ class MangaScreen(
onMigrateClicked = { migrateManga(navigator, screenModel.manga!!) }.takeIf { successState.manga.favorite },
onMetadataViewerClicked = { openMetadataViewer(navigator, successState.manga) },
onEditInfoClicked = screenModel::showEditMangaInfoDialog,
onRecommendClicked = { openRecommends(context, navigator, screenModel.source?.getMainSource(), successState.manga) },
onRecommendClicked = {
openRecommends(context, navigator, screenModel.source?.getMainSource(), successState.manga)
},
onMergedSettingsClicked = screenModel::showEditMergedSettingsDialog,
onMergeClicked = { openSmartSearch(navigator, successState.manga) },
onMergeWithAnotherClicked = { mergeWithAnother(navigator, context, successState.manga, screenModel::smartSearchMerge) },
onMergeWithAnotherClicked = {
mergeWithAnother(navigator, context, successState.manga, screenModel::smartSearchMerge)
},
onOpenPagePreview = {
openPagePreview(context, successState.chapters.minByOrNull { it.chapter.sourceOrder }?.chapter, it)
},

View File

@ -35,14 +35,12 @@ import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.data.track.EnhancedTracker
import eu.kanade.tachiyomi.data.track.TrackerManager
import eu.kanade.tachiyomi.data.track.mdlist.MdList
import eu.kanade.tachiyomi.network.HttpException
import eu.kanade.tachiyomi.source.PagePreviewSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.getNameForMangaInfo
import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.source.online.all.MergedSource
import eu.kanade.tachiyomi.ui.manga.track.TrackItem
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.util.chapter.getNextUnread
import eu.kanade.tachiyomi.util.removeCovers
@ -208,7 +206,8 @@ class MangaScreenModel(
private val skipFiltered by readerPreferences.skipFiltered().asState(screenModelScope)
val isUpdateIntervalEnabled = LibraryPreferences.MANGA_OUTSIDE_RELEASE_PERIOD in libraryPreferences.autoUpdateMangaRestrictions().get()
val isUpdateIntervalEnabled =
LibraryPreferences.MANGA_OUTSIDE_RELEASE_PERIOD in libraryPreferences.autoUpdateMangaRestrictions().get()
private val selectedPositions: Array<Int> = arrayOf(-1, -1) // first and last selected index in list
private val selectedChapterIds: HashSet<Long> = HashSet()
@ -262,7 +261,10 @@ class MangaScreenModel(
}
}
.onEach { (manga, chapters) ->
if (chapters.isNotEmpty() && manga.isEhBasedManga() && DebugToggles.ENABLE_EXH_ROOT_REDIRECT.enabled) {
if (chapters.isNotEmpty() &&
manga.isEhBasedManga() &&
DebugToggles.ENABLE_EXH_ROOT_REDIRECT.enabled
) {
// Check for gallery in library and accept manga with lowest id
// Find chapters sharing same root
launchIO {
@ -350,7 +352,7 @@ class MangaScreenModel(
} else {
flowOf(emptySet())
}
}
},
) { mangaScanlators, mergeScanlators ->
mangaScanlators + mergeScanlators
} // SY <--
@ -366,7 +368,15 @@ class MangaScreenModel(
screenModelScope.launchIO {
val manga = getMangaAndChapters.awaitManga(mangaId)
// SY -->
val chapters = (if (manga.source == MERGED_SOURCE_ID) getMergedChaptersByMangaId.await(mangaId, applyScanlatorFilter = true) else getMangaAndChapters.awaitChapters(mangaId, applyScanlatorFilter = true))
val chapters = (
if (manga.source ==
MERGED_SOURCE_ID
) {
getMergedChaptersByMangaId.await(mangaId, applyScanlatorFilter = true)
} else {
getMangaAndChapters.awaitChapters(mangaId, applyScanlatorFilter = true)
}
)
.toChapterListItems(manga, null)
val mergedData = getMergedReferencesById.await(mangaId).takeIf { it.isNotEmpty() }?.let { references ->
MergedMangaData(
@ -416,7 +426,8 @@ class MangaScreenModel(
} else {
PagePreviewState.Unused
},
alwaysShowReadingProgress = readerPreferences.preserveReadingPosition().get() && manga.isEhBasedManga(),
alwaysShowReadingProgress =
readerPreferences.preserveReadingPosition().get() && manga.isEhBasedManga(),
previewsRowCount = uiPreferences.previewsRowCount().get(),
// SY <--
)
@ -920,7 +931,12 @@ class MangaScreenModel(
screenModelScope.launchIO {
downloadManager.statusFlow()
.filter {
/* SY --> */ if (isMergedSource) it.manga.id in mergedIds else /* SY <-- */ it.manga.id == successState?.manga?.id
/* SY --> */ if (isMergedSource) {
it.manga.id in mergedIds
} else {
/* SY <-- */ it.manga.id ==
successState?.manga?.id
}
}
.catch { error -> logcat(LogPriority.ERROR, error) }
.collect {
@ -933,7 +949,12 @@ class MangaScreenModel(
screenModelScope.launchIO {
downloadManager.progressFlow()
.filter {
/* SY --> */ if (isMergedSource) it.manga.id in mergedIds else /* SY <-- */ it.manga.id == successState?.manga?.id
/* SY --> */ if (isMergedSource) {
it.manga.id in mergedIds
} else {
/* SY <-- */ it.manga.id ==
successState?.manga?.id
}
}
.catch { error -> logcat(LogPriority.ERROR, error) }
.collect {
@ -974,6 +995,7 @@ class MangaScreenModel(
} else {
downloadManager.getQueuedDownloadOrNull(chapter.id)
}
// SY -->
@Suppress("NAME_SHADOWING")
val manga = mergedData?.manga?.get(chapter.mangaId) ?: manga
@ -1292,7 +1314,12 @@ class MangaScreenModel(
screenModelScope.launchNonCancellable {
val manga = successState?.manga ?: return@launchNonCancellable
val categories = getCategories.await(manga.id).map { it.id }
if (chapters.isEmpty() || !manga.shouldDownloadNewChapters(categories, downloadPreferences) || manga.isEhBasedManga()) return@launchNonCancellable
if (chapters.isEmpty() ||
!manga.shouldDownloadNewChapters(categories, downloadPreferences) ||
manga.isEhBasedManga()
) {
return@launchNonCancellable
}
downloadChapters(chapters)
}
}
@ -1493,7 +1520,8 @@ class MangaScreenModel(
getTracks.subscribe(manga.id)
// SY -->
.map { trackItems ->
if (manga.source in mangaDexSourceIds || state.mergedData?.manga?.values.orEmpty().any {
if (manga.source in mangaDexSourceIds ||
state.mergedData?.manga?.values.orEmpty().any {
it.source in mangaDexSourceIds
}
) {
@ -1524,7 +1552,6 @@ class MangaScreenModel(
trackingCount to supportedTrackers.isNotEmpty()
// SY <--
}
.distinctUntilChanged()
.collectLatest { (trackingCount, hasLoggedInTrackers) ->
updateSuccessState {
@ -1559,6 +1586,7 @@ class MangaScreenModel(
) : Dialog
data class DeleteChapters(val chapters: List<Chapter>) : Dialog
data class DuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog
/* SY -->
data class Migrate(val newManga: Manga, val oldManga: Manga) : Dialog
SY <-- */

View File

@ -59,7 +59,8 @@ class EditMergedSettingsState(
}.map { reference -> mergedManga.firstOrNull { it.id == reference.mangaId } to reference }
mergeReference = mergedReferences.firstOrNull { it.mangaSourceId == MERGED_SOURCE_ID }
val isPriorityOrder = mergeReference?.let { it.chapterSortMode == MergedMangaReference.CHAPTER_SORT_PRIORITY } ?: false
val isPriorityOrder =
mergeReference?.let { it.chapterSortMode == MergedMangaReference.CHAPTER_SORT_PRIORITY } ?: false
mergedMangaAdapter = EditMergedMangaAdapter(this, isPriorityOrder)
mergedMangaHeaderAdapter = EditMergedSettingsHeaderAdapter(this, mergedMangaAdapter!!)

View File

@ -161,7 +161,8 @@ class EditMergedSettingsHeaderAdapter(private val state: EditMergedSettingsState
}
}
fun canMove() = state.mergeReference?.let { it.chapterSortMode == MergedMangaReference.CHAPTER_SORT_PRIORITY } ?: false
fun canMove() =
state.mergeReference?.let { it.chapterSortMode == MergedMangaReference.CHAPTER_SORT_PRIORITY } ?: false
interface SortingListener {
fun onSetPrioritySort(isPriorityOrder: Boolean)

View File

@ -108,8 +108,8 @@ private class MoreScreenModel(
val showNavHistory by uiPreferences.showNavHistory().asState(screenModelScope)
// SY <--
private var _state: MutableStateFlow<DownloadQueueState> = MutableStateFlow(DownloadQueueState.Stopped)
val downloadQueueState: StateFlow<DownloadQueueState> = _state.asStateFlow()
private var _downloadQueueState: MutableStateFlow<DownloadQueueState> = MutableStateFlow(DownloadQueueState.Stopped)
val downloadQueueState: StateFlow<DownloadQueueState> = _downloadQueueState.asStateFlow()
init {
// Handle running/paused status change and queue progress updating
@ -120,7 +120,7 @@ private class MoreScreenModel(
) { isRunning, downloadQueue -> Pair(isRunning, downloadQueue.size) }
.collectLatest { (isDownloading, downloadQueueSize) ->
val pendingDownloadExists = downloadQueueSize != 0
_state.value = when {
_downloadQueueState.value = when {
!pendingDownloadExists -> DownloadQueueState.Stopped
!isDownloading -> DownloadQueueState.Paused(downloadQueueSize)
else -> DownloadQueueState.Downloading(downloadQueueSize)

View File

@ -8,7 +8,6 @@ import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.ColorMatrix
import android.graphics.ColorMatrixColorFilter
@ -438,10 +437,12 @@ class ReaderActivity : BaseActivity() {
val landscapeVerticalSeekbar by readerPreferences.landscapeVerticalSeekbar().collectAsState()
val leftHandedVerticalSeekbar by readerPreferences.leftVerticalSeekbar().collectAsState()
val configuration = LocalConfiguration.current
val verticalSeekbarLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && landscapeVerticalSeekbar
val verticalSeekbarLandscape =
configuration.orientation == Configuration.ORIENTATION_LANDSCAPE && landscapeVerticalSeekbar
val verticalSeekbarHorizontal = configuration.orientation == Configuration.ORIENTATION_PORTRAIT
val viewerIsVertical = (state.viewer is WebtoonViewer || state.viewer is VerticalPagerViewer)
val showVerticalSeekbar = !forceHorizontalSeekbar && (verticalSeekbarLandscape || verticalSeekbarHorizontal) && viewerIsVertical
val showVerticalSeekbar =
!forceHorizontalSeekbar && (verticalSeekbarLandscape || verticalSeekbarHorizontal) && viewerIsVertical
val navBarType = when {
!showVerticalSeekbar -> NavBarType.Bottom
leftHandedVerticalSeekbar -> NavBarType.VerticalLeft
@ -828,7 +829,8 @@ class ReaderActivity : BaseActivity() {
} else {
if (readerPreferences.fullscreen().get()) {
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
windowInsetsController.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
}
}
@ -1031,7 +1033,13 @@ class ReaderActivity : BaseActivity() {
// SY -->
val currentPageText = if (hasExtraPage) {
val invertDoublePage = (viewModel.state.value.viewer as? PagerViewer)?.config?.invertDoublePages ?: false
if ((resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR) xor invertDoublePage) "${page.number}-${page.number + 1}" else "${page.number + 1}-${page.number}"
if ((resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR) xor
invertDoublePage
) {
"${page.number}-${page.number + 1}"
} else {
"${page.number + 1}-${page.number}"
}
} else {
"${page.number}"
}
@ -1093,7 +1101,16 @@ class ReaderActivity : BaseActivity() {
// SY -->
val text = if (secondPage != null) {
stringResource(SYMR.strings.share_pages_info, manga.title, chapter.name, if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR) "${page.number}-${page.number + 1}" else "${page.number + 1}-${page.number}")
stringResource(
SYMR.strings.share_pages_info, manga.title, chapter.name,
if (resources.configuration.layoutDirection ==
View.LAYOUT_DIRECTION_LTR
) {
"${page.number}-${page.number + 1}"
} else {
"${page.number + 1}-${page.number}"
},
)
} else {
stringResource(MR.strings.share_page_info, manga.title, chapter.name, page.number)
}
@ -1257,11 +1274,14 @@ class ReaderActivity : BaseActivity() {
.onEach {
if (viewModel.state.value.viewer !is PagerViewer) return@onEach
reloadChapters(
!it && when (readerPreferences.pageLayout().get()) {
PagerConfig.PageLayout.DOUBLE_PAGES -> true
PagerConfig.PageLayout.AUTOMATIC -> resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
else -> false
},
!it &&
when (readerPreferences.pageLayout().get()) {
PagerConfig.PageLayout.DOUBLE_PAGES -> true
PagerConfig.PageLayout.AUTOMATIC ->
resources.configuration.orientation ==
Configuration.ORIENTATION_LANDSCAPE
else -> false
},
true,
)
}

View File

@ -134,7 +134,7 @@ class ReaderViewModel @JvmOverloads constructor(
private val getMergedMangaById: GetMergedMangaById = Injekt.get(),
private val getMergedReferencesById: GetMergedReferencesById = Injekt.get(),
private val getMergedChaptersByMangaId: GetMergedChaptersByMangaId = Injekt.get(),
private val setReadStatus: SetReadStatus = Injekt.get()
private val setReadStatus: SetReadStatus = Injekt.get(),
// SY <--
) : ViewModel() {
@ -189,8 +189,9 @@ class ReaderViewModel @JvmOverloads constructor(
// SY -->
val (chapters, mangaMap) = runBlocking {
if (manga.source == MERGED_SOURCE_ID) {
getMergedChaptersByMangaId.await(manga.id, applyScanlatorFilter = true) to getMergedMangaById.await(manga.id)
.associateBy { it.id }
getMergedChaptersByMangaId.await(manga.id, applyScanlatorFilter = true) to
getMergedMangaById.await(manga.id)
.associateBy { it.id }
} else {
getChaptersByMangaId.await(manga.id, applyScanlatorFilter = true) to null
}

View File

@ -62,9 +62,11 @@ class ChapterLoader(
// If the chapter is partially read, set the starting page to the last the user read
// otherwise use the requested page.
if (!chapter.chapter.read /* --> EH */ || readerPrefs
if (!chapter.chapter.read /* --> EH */ ||
readerPrefs
.preserveReadingPosition()
.get() || page != null // <-- EH
.get() ||
page != null // <-- EH
) {
chapter.requestedPage = /* SY --> */ page ?: /* SY <-- */ chapter.chapter.last_page_read
}

View File

@ -191,7 +191,7 @@ class ReaderPreferences(
enum class FlashColor {
BLACK,
WHITE,
WHITE_BLACK
WHITE_BLACK,
}
enum class TappingInvertMode(
@ -285,7 +285,7 @@ class ReaderPreferences(
val archiveModeTypes = listOf(
SYMR.strings.archive_mode_load_from_file,
SYMR.strings.archive_mode_load_into_memory,
SYMR.strings.archive_mode_cache_to_disk
SYMR.strings.archive_mode_cache_to_disk,
)
// SY <--
}

View File

@ -116,7 +116,12 @@ open class ReaderPageImageView @JvmOverloads constructor(
}
private fun SubsamplingScaleImageView.landscapeZoom(forward: Boolean) {
if (config != null && config!!.landscapeZoom && config!!.minimumScaleType == SCALE_TYPE_CENTER_INSIDE && sWidth > sHeight && scale == minScale) {
if (config != null &&
config!!.landscapeZoom &&
config!!.minimumScaleType == SCALE_TYPE_CENTER_INSIDE &&
sWidth > sHeight &&
scale == minScale
) {
handler?.postDelayed(500) {
val point = when (config!!.zoomStartPosition) {
ZoomStartPosition.LEFT -> if (forward) PointF(0F, 0F) else PointF(sWidth.toFloat(), 0F)
@ -398,7 +403,9 @@ open class ReaderPageImageView @JvmOverloads constructor(
)
enum class ZoomStartPosition {
LEFT, CENTER, RIGHT
LEFT,
CENTER,
RIGHT,
}
}

View File

@ -32,15 +32,16 @@ class ReaderTransitionView @JvmOverloads constructor(context: Context, attrs: At
Data(
transition = transition,
currChapterDownloaded = transition.from.pageLoader?.isLocal == true,
goingToChapterDownloaded = manga.isLocal() || transition.to?.chapter?.let { goingToChapter ->
downloadManager.isChapterDownloaded(
chapterName = goingToChapter.name,
chapterScanlator = goingToChapter.scanlator,
mangaTitle = /* SY --> */ manga.ogTitle, /* SY <-- */
sourceId = manga.source,
skipCache = true,
)
} ?: false,
goingToChapterDownloaded = manga.isLocal() ||
transition.to?.chapter?.let { goingToChapter ->
downloadManager.isChapterDownloaded(
chapterName = goingToChapter.name,
chapterScanlator = goingToChapter.scanlator,
mangaTitle = /* SY --> */ manga.ogTitle, /* SY <-- */
sourceId = manga.source,
skipCache = true,
)
} ?: false,
)
} else {
null

View File

@ -57,7 +57,8 @@ class PagerConfig(
var shiftDoublePage = false
var doublePages = readerPreferences.pageLayout().get() == PageLayout.DOUBLE_PAGES && !readerPreferences.dualPageSplitPaged().get()
var doublePages =
readerPreferences.pageLayout().get() == PageLayout.DOUBLE_PAGES && !readerPreferences.dualPageSplitPaged().get()
set(value) {
field = value
if (!value) {

View File

@ -310,9 +310,9 @@ class PagerPageHolder(
private fun handleWideImage(imageSource: BufferedSource): BufferedSource {
return if (
!ImageUtil.isAnimatedAndSupported(imageSource) &&
ImageUtil.isWideImage(imageSource) &&
viewer.config.centerMarginType and PagerConfig.CenterMarginType.WIDE_PAGE_CENTER_MARGIN > 0 &&
!viewer.config.imageCropBorders
ImageUtil.isWideImage(imageSource) &&
viewer.config.centerMarginType and PagerConfig.CenterMarginType.WIDE_PAGE_CENTER_MARGIN > 0 &&
!viewer.config.imageCropBorders
) {
ImageUtil.addHorizontalCenterMargin(imageSource, height, context)
} else {
@ -330,7 +330,9 @@ class PagerPageHolder(
}
private fun calculateCenterMargin(height: Int, height2: Int): Int {
return if (viewer.config.centerMarginType and PagerConfig.CenterMarginType.DOUBLE_PAGE_CENTER_MARGIN > 0 && !viewer.config.imageCropBorders) {
return if (viewer.config.centerMarginType and PagerConfig.CenterMarginType.DOUBLE_PAGE_CENTER_MARGIN > 0 &&
!viewer.config.imageCropBorders
) {
96 / (this.height.coerceAtLeast(1) / max(height, height2).coerceAtLeast(1)).coerceAtLeast(1)
} else {
0
@ -373,8 +375,10 @@ class PagerPageHolder(
}
}
val sideMargin = if ((viewer.config.centerMarginType and PagerConfig.CenterMarginType.DOUBLE_PAGE_CENTER_MARGIN) > 0 &&
viewer.config.doublePages && !viewer.config.imageCropBorders
val sideMargin = if ((viewer.config.centerMarginType and PagerConfig.CenterMarginType.DOUBLE_PAGE_CENTER_MARGIN) >
0 &&
viewer.config.doublePages &&
!viewer.config.imageCropBorders
) {
48
} else {

View File

@ -291,7 +291,9 @@ abstract class PagerViewer(val activity: ReaderActivity) : Viewer {
* Sets the active [chapters] on this pager.
*/
private fun setChaptersInternal(chapters: ViewerChapters) {
val forceTransition = config.alwaysShowChapterTransition || adapter.joinedItems.getOrNull(pager.currentItem)?.first is ChapterTransition
val forceTransition =
config.alwaysShowChapterTransition ||
adapter.joinedItems.getOrNull(pager.currentItem)?.first is ChapterTransition
adapter.setChapters(chapters, forceTransition)
// Layout the pager once a chapter is being set

View File

@ -109,7 +109,9 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
// Add next chapter transition and pages.
nextTransition = ChapterTransition.Next(chapters.currChapter, chapters.nextChapter)
.also {
if (nextHasMissingChapters || forceTransition ||
if (
nextHasMissingChapters ||
forceTransition ||
chapters.nextChapter?.state !is ReaderChapter.State.Loaded
) {
newItems.add(it)
@ -248,7 +250,9 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
subItems.forEach { readerItem ->
when (readerItem) {
is ReaderPage -> {
if (pagedItems.last().isNotEmpty() && pagedItems.last().last()?.chapter?.chapter?.id != readerItem.chapter.chapter.id) {
if (pagedItems.last().isNotEmpty() &&
pagedItems.last().last()?.chapter?.chapter?.id != readerItem.chapter.chapter.id
) {
pagedItems.add(mutableListOf())
}
pagedItems.last().add(readerItem)
@ -298,9 +302,10 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
// Add a 'blank' page after each full page. It will be used when chunked to solo a page
items.add(itemIndex + 1, null)
if (
currentItem.fullPage && itemIndex > 0 &&
items[itemIndex - 1] != null &&
(itemIndex - 1) % 2 == 0
currentItem.fullPage &&
itemIndex > 0 &&
items[itemIndex - 1] != null &&
(itemIndex - 1) % 2 == 0
) {
// If a page is a full page, check if the previous page needs to be isolated
// we should check if it's an even or odd page, since even pages need shifting
@ -340,7 +345,8 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
// We will however shift to the first page of the new chapter if the last page we were are
// on is not in the new chapter that has loaded
val newPage = when {
oldCurrent?.first is ReaderPage && (oldCurrent.first as ReaderPage).chapter != currentChapter &&
oldCurrent?.first is ReaderPage &&
(oldCurrent.first as ReaderPage).chapter != currentChapter &&
(oldCurrent.second as? ChapterTransition)?.from != currentChapter ->
subItems.find { it is ReaderPage && it.chapter == currentChapter }
useSecondPage -> oldCurrent?.second ?: oldCurrent?.first
@ -349,7 +355,10 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
val index = when (newPage) {
is ChapterTransition -> {
val filteredPages = joinedItems.filter { it.first is ReaderPage && (it.first as ReaderPage).chapter == newPage.to }
val filteredPages = joinedItems.filter {
it.first is ReaderPage &&
(it.first as ReaderPage).chapter == newPage.to
}
val page = if (newPage is ChapterTransition.Next) {
filteredPages.minByOrNull { (it.first as ReaderPage).index }?.first
} else {

View File

@ -89,7 +89,7 @@ class WebtoonConfig(
readerPreferences.webtoonDisableZoomOut()
.register(
{ zoomOutDisabled = it },
{ zoomPropertyChangedListener?.invoke(it) }
{ zoomPropertyChangedListener?.invoke(it) },
)
readerPreferences.webtoonDoubleTapZoomEnabled()

View File

@ -199,7 +199,9 @@ class WebtoonPageHolder(
ReaderPageImageView.Config(
zoomDuration = viewer.config.doubleTapAnimDuration,
minimumScaleType = SubsamplingScaleImageView.SCALE_TYPE_FIT_WIDTH,
cropBorders = (viewer.config.imageCropBorders && viewer.isContinuous) || (viewer.config.continuousCropBorders && !viewer.isContinuous),
cropBorders =
(viewer.config.imageCropBorders && viewer.isContinuous) ||
(viewer.config.continuousCropBorders && !viewer.isContinuous),
),
)
removeErrorLayout()

View File

@ -182,7 +182,11 @@ class WebtoonRecyclerView @JvmOverloads constructor(
setScaleRate(currentScale)
layoutParams.height = if (currentScale < 1) { (originalHeight / currentScale).toInt() } else { originalHeight }
layoutParams.height = if (currentScale < 1) {
(originalHeight / currentScale).toInt()
} else {
originalHeight
}
halfHeight = layoutParams.height / 2
if (currentScale != DEFAULT_RATE) {

Some files were not shown because too many files have changed in this diff Show More