diff --git a/.github/ISSUE_TEMPLATE/report_issue.yml b/.github/ISSUE_TEMPLATE/report_issue.yml index 8fac5785f5..0835f86cb6 100644 --- a/.github/ISSUE_TEMPLATE/report_issue.yml +++ b/.github/ISSUE_TEMPLATE/report_issue.yml @@ -53,7 +53,7 @@ body: label: Mihon version description: You can find your Mihon version in **More → About**. placeholder: | - Example: "0.16.3" + Example: "0.16.4" validations: required: true @@ -96,7 +96,7 @@ body: required: true - label: I have gone through the [FAQ](https://mihon.app/docs/faq/general) and [troubleshooting guide](https://mihon.app/docs/guides/troubleshooting/). required: true - - label: I have updated the app to version **[0.16.3](https://github.com/mihonapp/mihon/releases/latest)**. + - label: I have updated the app to version **[0.16.4](https://github.com/mihonapp/mihon/releases/latest)**. required: true - label: I have updated all installed extensions. required: true diff --git a/.github/ISSUE_TEMPLATE/request_feature.yml b/.github/ISSUE_TEMPLATE/request_feature.yml index 2c6d324c9a..48071fc913 100644 --- a/.github/ISSUE_TEMPLATE/request_feature.yml +++ b/.github/ISSUE_TEMPLATE/request_feature.yml @@ -31,7 +31,7 @@ body: required: true - label: I have written a short but informative title. required: true - - label: I have updated the app to version **[0.16.3](https://github.com/mihonapp/mihon/releases/latest)**. + - label: I have updated the app to version **[0.16.4](https://github.com/mihonapp/mihon/releases/latest)**. required: true - label: I will fill out all of the requested information in this form. required: true diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 867e8f851c..8b63752a09 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,8 +22,8 @@ android { defaultConfig { applicationId = "app.mihon" - versionCode = 4 - versionName = "0.16.3" + versionCode = 6 + versionName = "0.16.4" buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"") @@ -284,7 +284,7 @@ tasks { "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi", "-opt-in=androidx.compose.animation.ExperimentalAnimationApi", "-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi", - "-opt-in=coil.annotation.ExperimentalCoilApi", + "-opt-in=coil3.annotation.ExperimentalCoilApi", "-opt-in=com.google.accompanist.permissions.ExperimentalPermissionsApi", "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", "-opt-in=kotlinx.coroutines.FlowPreview", @@ -304,6 +304,12 @@ tasks { project.layout.buildDirectory.dir("compose_metrics").get().asFile.absolutePath, ) } + + // https://developer.android.com/jetpack/androidx/releases/compose-compiler#1.5.9 + kotlinOptions.freeCompilerArgs += listOf( + "-P", + "plugin:androidx.compose.compiler.plugins.kotlin:nonSkippingGroupOptimization=true", + ) } } diff --git a/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt b/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt index 1b9dd62c8f..ebd2ad3e92 100644 --- a/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt +++ b/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt @@ -95,7 +95,13 @@ fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean { /** * Creates a ComicInfo instance based on the manga and chapter metadata. */ -fun getComicInfo(manga: Manga, chapter: Chapter, chapterUrl: String, categories: List?) = ComicInfo( +fun getComicInfo( + manga: Manga, + chapter: Chapter, + urls: List, + categories: List?, + sourceName: String, +) = ComicInfo( title = ComicInfo.Title(chapter.name), series = ComicInfo.Series(manga.title), number = chapter.chapterNumber.takeIf { it >= 0 }?.let { @@ -105,7 +111,7 @@ fun getComicInfo(manga: Manga, chapter: Chapter, chapterUrl: String, categories: ComicInfo.Number(it.toString()) } }, - web = ComicInfo.Web(chapterUrl), + web = ComicInfo.Web(urls.joinToString(" ")), summary = manga.description?.let { ComicInfo.Summary(it) }, writer = manga.author?.let { ComicInfo.Writer(it) }, penciller = manga.artist?.let { ComicInfo.Penciller(it) }, @@ -115,6 +121,7 @@ fun getComicInfo(manga: Manga, chapter: Chapter, chapterUrl: String, categories: ComicInfoPublishingStatus.toComicInfoValue(manga.status), ), categories = categories?.let { ComicInfo.CategoriesTachiyomi(it.joinToString()) }, + source = ComicInfo.SourceMihon(sourceName), inker = null, colorist = null, letterer = null, diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt b/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt index a942d5f948..fdf24ec4e9 100644 --- a/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt +++ b/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt @@ -21,7 +21,7 @@ class TrackChapter( private val delayedTrackingStore: DelayedTrackingStore, ) { - suspend fun await(context: Context, mangaId: Long, chapterNumber: Double) { + suspend fun await(context: Context, mangaId: Long, chapterNumber: Double, setupJobOnFailure: Boolean = true) { withNonCancellableContext { val tracks = getTracks.await(mangaId) if (tracks.isEmpty()) return@withNonCancellableContext @@ -43,7 +43,9 @@ class TrackChapter( delayedTrackingStore.remove(track.id) } catch (e: Exception) { delayedTrackingStore.add(track.id, chapterNumber) - DelayedTrackingUpdateJob.setupTask(context) + if (setupJobOnFailure) { + DelayedTrackingUpdateJob.setupTask(context) + } throw e } } diff --git a/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt b/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt index 1f4e246dc9..50589ae9d6 100644 --- a/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt +++ b/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt @@ -45,7 +45,7 @@ class DelayedTrackingUpdateJob(private val context: Context, workerParams: Worke logcat(LogPriority.DEBUG) { "Updating delayed track item: ${track.mangaId}, last chapter read: ${track.lastChapterRead}" } - trackChapter.await(context, track.mangaId, track.lastChapterRead) + trackChapter.await(context, track.mangaId, track.lastChapterRead, setupJobOnFailure = false) } } diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseIcons.kt b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseIcons.kt index b4710fc407..950b55192b 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseIcons.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseIcons.kt @@ -25,7 +25,7 @@ import androidx.compose.ui.res.imageResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.core.graphics.drawable.toBitmap -import coil.compose.AsyncImage +import coil3.compose.AsyncImage import eu.kanade.domain.source.model.icon import eu.kanade.presentation.util.rememberResourceBitmapPainter import eu.kanade.tachiyomi.R diff --git a/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt b/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt index dfba8cd612..4ef2e97713 100644 --- a/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material.icons.Icons @@ -29,7 +30,6 @@ import androidx.compose.ui.util.fastForEachIndexed import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.launch import tachiyomi.i18n.MR -import tachiyomi.presentation.core.components.HorizontalPager import tachiyomi.presentation.core.components.material.TabText import tachiyomi.presentation.core.i18n.stringResource @@ -78,9 +78,8 @@ fun TabbedDialog( modifier = Modifier.animateContentSize(), state = pagerState, verticalAlignment = Alignment.Top, - ) { page -> - content(page) - } + pageContent = { page -> content(page) } + ) } } } diff --git a/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt b/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt index 9dae3de14f..efb1e2e049 100644 --- a/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.calculateEndPadding import androidx.compose.foundation.layout.calculateStartPadding import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material3.MaterialTheme import androidx.compose.material3.PrimaryTabRow @@ -24,7 +25,6 @@ import dev.icerock.moko.resources.StringResource import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.launch -import tachiyomi.presentation.core.components.HorizontalPager import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.TabText import tachiyomi.presentation.core.i18n.stringResource diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt b/app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt index db7af58752..6487ab39f6 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll @@ -22,7 +23,6 @@ import eu.kanade.tachiyomi.ui.library.LibraryItem import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibraryManga import tachiyomi.i18n.MR -import tachiyomi.presentation.core.components.HorizontalPager import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.util.plus diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt index 50ddadf0c6..b1a1474ecc 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt @@ -2,7 +2,6 @@ package eu.kanade.presentation.manga.components import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.combinedClickable -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -24,8 +23,9 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.composed import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.hapticfeedback.HapticFeedback import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.painterResource @@ -91,6 +91,7 @@ private fun NotDownloadedIndicator( .size(IconButtonTokens.StateLayerSize) .commonClickable( enabled = enabled, + hapticFeedback = LocalHapticFeedback.current, onLongClick = { onClick(ChapterDownloadAction.START_NOW) }, onClick = { onClick(ChapterDownloadAction.START) }, ) @@ -120,6 +121,7 @@ private fun DownloadingIndicator( .size(IconButtonTokens.StateLayerSize) .commonClickable( enabled = enabled, + hapticFeedback = LocalHapticFeedback.current, onLongClick = { onClick(ChapterDownloadAction.CANCEL) }, onClick = { isMenuExpanded = true }, ), @@ -136,6 +138,8 @@ private fun DownloadingIndicator( modifier = IndicatorModifier, color = strokeColor, strokeWidth = IndicatorStrokeWidth, + trackColor = Color.Transparent, + strokeCap = StrokeCap.Butt, ) } else { val animatedProgress by animateFloatAsState( @@ -153,6 +157,9 @@ private fun DownloadingIndicator( modifier = IndicatorModifier, color = strokeColor, strokeWidth = IndicatorSize / 2, + trackColor = Color.Transparent, + strokeCap = StrokeCap.Butt, + gapSize = 0.dp, ) } DropdownMenu(expanded = isMenuExpanded, onDismissRequest = { isMenuExpanded = false }) { @@ -192,6 +199,7 @@ private fun DownloadedIndicator( .size(IconButtonTokens.StateLayerSize) .commonClickable( enabled = enabled, + hapticFeedback = LocalHapticFeedback.current, onLongClick = { isMenuExpanded = true }, onClick = { isMenuExpanded = true }, ), @@ -226,6 +234,7 @@ private fun ErrorIndicator( .size(IconButtonTokens.StateLayerSize) .commonClickable( enabled = enabled, + hapticFeedback = LocalHapticFeedback.current, onLongClick = { onClick(ChapterDownloadAction.START) }, onClick = { onClick(ChapterDownloadAction.START) }, ), @@ -242,26 +251,23 @@ private fun ErrorIndicator( private fun Modifier.commonClickable( enabled: Boolean, + hapticFeedback: HapticFeedback, onLongClick: () -> Unit, onClick: () -> Unit, -) = composed { - val haptic = LocalHapticFeedback.current - - Modifier.combinedClickable( - enabled = enabled, - onLongClick = { - onLongClick() - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - }, - onClick = onClick, - role = Role.Button, - interactionSource = remember { MutableInteractionSource() }, - indication = ripple( - bounded = false, - radius = IconButtonTokens.StateLayerSize / 2, - ), - ) -} +) = this.combinedClickable( + enabled = enabled, + onLongClick = { + onLongClick() + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + }, + onClick = onClick, + role = Role.Button, + interactionSource = null, + indication = ripple( + bounded = false, + radius = IconButtonTokens.StateLayerSize / 2, + ), +) private val IndicatorSize = 26.dp private val IndicatorPadding = 2.dp diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt index 9c9f07c1a4..12720957eb 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt @@ -24,36 +24,27 @@ import androidx.compose.material3.ProvideTextStyle import androidx.compose.material3.Text import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.platform.LocalHapticFeedback -import androidx.compose.ui.platform.LocalViewConfiguration -import androidx.compose.ui.platform.ViewConfiguration import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import eu.kanade.tachiyomi.data.download.model.Download import me.saket.swipe.SwipeableActionsBox -import me.saket.swipe.rememberSwipeableActionsState 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.i18n.stringResource import tachiyomi.presentation.core.util.selectedBackground -import kotlin.math.absoluteValue @Composable fun MangaChapterListItem( @@ -75,142 +66,117 @@ fun MangaChapterListItem( onChapterSwipe: (LibraryPreferences.ChapterSwipeAction) -> Unit, modifier: Modifier = Modifier, ) { - val haptic = LocalHapticFeedback.current - val density = LocalDensity.current - val textAlpha = if (read) ReadItemAlpha else 1f val textSubtitleAlpha = if (read) ReadItemAlpha else SecondaryItemAlpha - // Increase touch slop of swipe action to reduce accidental trigger - val configuration = LocalViewConfiguration.current - CompositionLocalProvider( - LocalViewConfiguration provides object : ViewConfiguration by configuration { - override val touchSlop: Float = configuration.touchSlop * 3f - }, - ) { - val start = getSwipeAction( - action = chapterSwipeStartAction, - read = read, - bookmark = bookmark, - downloadState = downloadStateProvider(), - background = MaterialTheme.colorScheme.primaryContainer, - onSwipe = { onChapterSwipe(chapterSwipeStartAction) }, - ) - val end = getSwipeAction( - action = chapterSwipeEndAction, - read = read, - bookmark = bookmark, - downloadState = downloadStateProvider(), - background = MaterialTheme.colorScheme.primaryContainer, - onSwipe = { onChapterSwipe(chapterSwipeEndAction) }, - ) - - val swipeableActionsState = rememberSwipeableActionsState() - LaunchedEffect(Unit) { - // Haptic effect when swipe over threshold - val swipeActionThresholdPx = with(density) { swipeActionThreshold.toPx() } - snapshotFlow { swipeableActionsState.offset.value.absoluteValue > swipeActionThresholdPx } - .collect { if (it) haptic.performHapticFeedback(HapticFeedbackType.LongPress) } - } + val start = getSwipeAction( + action = chapterSwipeStartAction, + read = read, + bookmark = bookmark, + downloadState = downloadStateProvider(), + background = MaterialTheme.colorScheme.primaryContainer, + onSwipe = { onChapterSwipe(chapterSwipeStartAction) }, + ) + val end = getSwipeAction( + action = chapterSwipeEndAction, + read = read, + bookmark = bookmark, + downloadState = downloadStateProvider(), + background = MaterialTheme.colorScheme.primaryContainer, + onSwipe = { onChapterSwipe(chapterSwipeEndAction) }, + ) - SwipeableActionsBox( - modifier = Modifier.clipToBounds(), - state = swipeableActionsState, - startActions = listOfNotNull(start), - endActions = listOfNotNull(end), - swipeThreshold = swipeActionThreshold, - backgroundUntilSwipeThreshold = MaterialTheme.colorScheme.surfaceContainerLowest, + SwipeableActionsBox( + modifier = Modifier.clipToBounds(), + startActions = listOfNotNull(start), + endActions = listOfNotNull(end), + swipeThreshold = swipeActionThreshold, + backgroundUntilSwipeThreshold = MaterialTheme.colorScheme.surfaceContainerLowest, + ) { + Row( + modifier = modifier + .selectedBackground(selected) + .combinedClickable( + onClick = onClick, + onLongClick = onLongClick, + ) + .padding(start = 16.dp, top = 12.dp, end = 8.dp, bottom = 12.dp), ) { - Row( - modifier = modifier - .selectedBackground(selected) - .combinedClickable( - onClick = onClick, - onLongClick = onLongClick, - ) - .padding(start = 16.dp, top = 12.dp, end = 8.dp, bottom = 12.dp), + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(6.dp), ) { - Column( - modifier = Modifier.weight(1f), - verticalArrangement = Arrangement.spacedBy(6.dp), + Row( + horizontalArrangement = Arrangement.spacedBy(2.dp), + verticalAlignment = Alignment.CenterVertically, ) { - Row( - horizontalArrangement = Arrangement.spacedBy(2.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - var textHeight by remember { mutableIntStateOf(0) } - if (!read) { - Icon( - imageVector = Icons.Filled.Circle, - contentDescription = stringResource(MR.strings.unread), - modifier = Modifier - .height(8.dp) - .padding(end = 4.dp), - tint = MaterialTheme.colorScheme.primary, + var textHeight by remember { mutableIntStateOf(0) } + if (!read) { + Icon( + imageVector = Icons.Filled.Circle, + contentDescription = stringResource(MR.strings.unread), + modifier = Modifier + .height(8.dp) + .padding(end = 4.dp), + tint = MaterialTheme.colorScheme.primary, + ) + } + if (bookmark) { + Icon( + imageVector = Icons.Filled.Bookmark, + contentDescription = stringResource(MR.strings.action_filter_bookmarked), + modifier = Modifier + .sizeIn(maxHeight = with(LocalDensity.current) { textHeight.toDp() - 2.dp }), + tint = MaterialTheme.colorScheme.primary, + ) + } + Text( + text = title, + style = MaterialTheme.typography.bodyMedium, + color = LocalContentColor.current.copy(alpha = textAlpha), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + onTextLayout = { textHeight = it.size.height }, + ) + } + + Row(modifier = Modifier.alpha(textSubtitleAlpha)) { + ProvideTextStyle(value = MaterialTheme.typography.bodySmall) { + if (date != null) { + Text( + text = date, + maxLines = 1, + overflow = TextOverflow.Ellipsis, ) + if (readProgress != null || scanlator != null) DotSeparatorText() } - if (bookmark) { - Icon( - imageVector = Icons.Filled.Bookmark, - contentDescription = stringResource(MR.strings.action_filter_bookmarked), - modifier = Modifier - .sizeIn(maxHeight = with(LocalDensity.current) { textHeight.toDp() - 2.dp }), - tint = MaterialTheme.colorScheme.primary, + if (readProgress != null) { + Text( + text = readProgress, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + color = LocalContentColor.current.copy(alpha = ReadItemAlpha), ) + if (scanlator != null) DotSeparatorText() } - Text( - text = title, - style = MaterialTheme.typography.bodyMedium, - color = LocalContentColor.current.copy(alpha = textAlpha), - maxLines = 1, - overflow = TextOverflow.Ellipsis, - onTextLayout = { textHeight = it.size.height }, - ) - } - - Row { - ProvideTextStyle( - value = MaterialTheme.typography.bodyMedium.copy( - fontSize = 12.sp, - color = LocalContentColor.current.copy(alpha = textSubtitleAlpha), - ), - ) { - if (date != null) { - Text( - text = date, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - if (readProgress != null || scanlator != null) DotSeparatorText() - } - if (readProgress != null) { - Text( - text = readProgress, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - color = LocalContentColor.current.copy(alpha = ReadItemAlpha), - ) - if (scanlator != null) DotSeparatorText() - } - if (scanlator != null) { - Text( - text = scanlator, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - } + if (scanlator != null) { + Text( + text = scanlator, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) } } } - - ChapterDownloadIndicator( - enabled = downloadIndicatorEnabled, - modifier = Modifier.padding(start = 4.dp), - downloadStateProvider = downloadStateProvider, - downloadProgressProvider = downloadProgressProvider, - onClick = { onDownloadClick?.invoke(it) }, - ) } + + ChapterDownloadIndicator( + enabled = downloadIndicatorEnabled, + modifier = Modifier.padding(start = 4.dp), + downloadStateProvider = downloadStateProvider, + downloadProgressProvider = downloadProgressProvider, + onClick = { onDownloadClick?.invoke(it) }, + ) } } } diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCover.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCover.kt index cfb4f5fcb1..4fb1cf87ba 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCover.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCover.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.painter.ColorPainter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.semantics.Role -import coil.compose.AsyncImage +import coil3.compose.AsyncImage import eu.kanade.presentation.util.rememberResourceBitmapPainter import eu.kanade.tachiyomi.R diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt index a71d086743..a70511326c 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt @@ -37,10 +37,10 @@ import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import androidx.core.view.updatePadding -import coil.imageLoader -import coil.request.CachePolicy -import coil.request.ImageRequest -import coil.size.Size +import coil3.imageLoader +import coil3.request.CachePolicy +import coil3.request.ImageRequest +import coil3.size.Size import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.components.DropdownMenu @@ -168,7 +168,9 @@ fun MangaCoverDialog( .data(coverDataProvider()) .size(Size.ORIGINAL) .memoryCachePolicy(CachePolicy.DISABLED) - .target { drawable -> + .target { image -> + val drawable = image.asDrawable(view.context.resources) + // Copy bitmap in case it came from memory cache // Because SSIV needs to thoroughly read the image val copy = (drawable as? BitmapDrawable)?.let { diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt index 0ad66a9a8b..ac4946c990 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt @@ -73,7 +73,7 @@ import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import coil.compose.AsyncImage +import coil3.compose.AsyncImage import eu.kanade.presentation.components.DropdownMenu import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.model.SManga diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt index bc60037e9f..a0a2e8f97d 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt @@ -341,7 +341,10 @@ object SettingsReaderScreen : SearchableSettings { Preference.PreferenceItem.SwitchPreference( pref = readerPreferences.webtoonDoubleTapZoomEnabled(), title = stringResource(MR.strings.pref_double_tap_zoom), - enabled = true, + ), + Preference.PreferenceItem.SwitchPreference( + pref = readerPreferences.webtoonDisableZoomOut(), + title = stringResource(MR.strings.pref_webtoon_disable_zoom_out), ), ), ) diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt index ee2eb854f7..fb3e16a93f 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt @@ -197,6 +197,10 @@ private fun ColumnScope.WebtoonViewerSettings(screenModel: ReaderSettingsScreenM label = stringResource(MR.strings.pref_double_tap_zoom), pref = screenModel.preferences.webtoonDoubleTapZoomEnabled(), ) + CheckboxItem( + label = stringResource(MR.strings.pref_webtoon_disable_zoom_out), + pref = screenModel.preferences.webtoonDisableZoomOut(), + ) } @Composable diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index dfe5dad416..6c29060c1f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -15,12 +15,14 @@ import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner import androidx.lifecycle.lifecycleScope -import coil.ImageLoader -import coil.ImageLoaderFactory -import coil.decode.GifDecoder -import coil.decode.ImageDecoderDecoder -import coil.disk.DiskCache -import coil.util.DebugLogger +import coil3.ImageLoader +import coil3.SingletonImageLoader +import coil3.disk.DiskCache +import coil3.disk.directory +import coil3.network.okhttp.OkHttpNetworkFetcherFactory +import coil3.request.allowRgb565 +import coil3.request.crossfade +import coil3.util.DebugLogger import eu.kanade.domain.DomainModule import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.ui.UiPreferences @@ -58,7 +60,7 @@ import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy import java.security.Security -class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory { +class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factory { private val basePreferences: BasePreferences by injectLazy() private val networkPreferences: NetworkPreferences by injectLazy() @@ -131,24 +133,19 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory { } } - override fun newImageLoader(): ImageLoader { + override fun newImageLoader(context: Context): ImageLoader { return ImageLoader.Builder(this).apply { - val callFactoryInit = { Injekt.get().client } - val diskCacheInit = { CoilDiskCache.get(this@App) } + val callFactoryLazy = lazy { Injekt.get().client } + val diskCacheLazy = lazy { CoilDiskCache.get(this@App) } components { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - add(ImageDecoderDecoder.Factory()) - } else { - add(GifDecoder.Factory()) - } + add(OkHttpNetworkFetcherFactory(callFactoryLazy::value)) add(TachiyomiImageDecoder.Factory()) - add(MangaCoverFetcher.MangaFactory(lazy(callFactoryInit), lazy(diskCacheInit))) - add(MangaCoverFetcher.MangaCoverFactory(lazy(callFactoryInit), lazy(diskCacheInit))) + add(MangaCoverFetcher.MangaFactory(callFactoryLazy, diskCacheLazy)) + add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy, diskCacheLazy)) add(MangaKeyer()) add(MangaCoverKeyer()) } - callFactory(callFactoryInit) - diskCache(diskCacheInit) + diskCache(diskCacheLazy::value) crossfade((300 * this@App.animatorDurationScale).toInt()) allowRgb565(DeviceUtil.isLowRamDevice(this@App)) if (networkPreferences.verboseLogging().get()) logger(DebugLogger()) @@ -156,7 +153,6 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory { // Coil spawns a new thread for every image load by default fetcherDispatcher(Dispatchers.IO.limitedParallelism(8)) decoderDispatcher(Dispatchers.IO.limitedParallelism(2)) - transformationDispatcher(Dispatchers.IO.limitedParallelism(2)) }.build() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/MangaBackupCreator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/MangaBackupCreator.kt index e29a6e938d..24a069aeb8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/MangaBackupCreator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/MangaBackupCreator.kt @@ -98,5 +98,6 @@ private fun Manga.toBackupManga() = updateStrategy = this.updateStrategy, lastModifiedAt = this.lastModifiedAt, favoriteModifiedAt = this.favoriteModifiedAt, - notes = notes, + version = this.version, + notes = this.notes, ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt index 567ca372cd..d729efe165 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt @@ -4,6 +4,7 @@ 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 @@ -21,6 +22,7 @@ data class BackupChapter( @ProtoNumber(9) var chapterNumber: Float = 0F, @ProtoNumber(10) var sourceOrder: Long = 0, @ProtoNumber(11) var lastModifiedAt: Long = 0, + @ProtoNumber(12) var version: Long = 0, ) { fun toChapterImpl(): Chapter { return Chapter.create().copy( @@ -35,6 +37,7 @@ data class BackupChapter( dateUpload = this@BackupChapter.dateUpload, sourceOrder = this@BackupChapter.sourceOrder, lastModifiedAt = this@BackupChapter.lastModifiedAt, + version = this@BackupChapter.version, ) } } @@ -53,6 +56,8 @@ val backupChapterMapper = { dateFetch: Long, dateUpload: Long, lastModifiedAt: Long, + version: Long, + _: Long, -> BackupChapter( url = url, @@ -66,5 +71,6 @@ val backupChapterMapper = { dateUpload = dateUpload, sourceOrder = sourceOrder, lastModifiedAt = lastModifiedAt, + version = version, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt index e0c5674a7a..7021a1ea45 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt @@ -5,7 +5,10 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import tachiyomi.domain.manga.model.Manga -@Suppress("DEPRECATION") +@Suppress( + "DEPRECATION", + "MagicNumber", +) @Serializable data class BackupManga( // in 1.x some of these values have different names @@ -40,7 +43,8 @@ data class BackupManga( @ProtoNumber(107) var favoriteModifiedAt: Long? = null, // Mihon values start here @ProtoNumber(108) var excludedScanlators: List = emptyList(), - @ProtoNumber(109) var notes: String? = null, + @ProtoNumber(109) var version: Long = 0, + @ProtoNumber(110) var notes: String? = null, ) { fun getMangaImpl(): Manga { return Manga.create().copy( @@ -60,6 +64,7 @@ data class BackupManga( updateStrategy = this@BackupManga.updateStrategy, lastModifiedAt = this@BackupManga.lastModifiedAt, favoriteModifiedAt = this@BackupManga.favoriteModifiedAt, + version = this@BackupManga.version, notes = this@BackupManga.notes, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt index 6ff39fb737..dcb0fe822c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt @@ -83,7 +83,7 @@ class MangaRestorer( } private suspend fun restoreExistingManga(manga: Manga, dbManga: Manga): Manga { - return if (manga.lastModifiedAt > dbManga.lastModifiedAt) { + return if (manga.version > dbManga.version) { updateManga(dbManga.copyFrom(manga).copy(id = dbManga.id)) } else { updateManga(manga.copyFrom(dbManga).copy(id = dbManga.id)) @@ -100,6 +100,7 @@ class MangaRestorer( thumbnailUrl = newer.thumbnailUrl, status = newer.status, initialized = this.initialized || newer.initialized, + version = newer.version, ) } @@ -126,6 +127,8 @@ class MangaRestorer( dateAdded = manga.dateAdded, mangaId = manga.id, updateStrategy = manga.updateStrategy.let(UpdateStrategyColumnAdapter::encode), + version = manga.version, + isSyncing = 1, notes = manga.notes, ) } @@ -138,6 +141,7 @@ class MangaRestorer( return manga.copy( initialized = manga.description != null, id = insertManga(manga), + version = manga.version, ) } @@ -184,7 +188,7 @@ class MangaRestorer( } private fun Chapter.forComparison() = - this.copy(id = 0L, mangaId = 0L, dateFetch = 0L, dateUpload = 0L, lastModifiedAt = 0L) + this.copy(id = 0L, mangaId = 0L, dateFetch = 0L, dateUpload = 0L, lastModifiedAt = 0L, version = 0L) private suspend fun insertNewChapters(chapters: List) { handler.await(true) { @@ -201,6 +205,7 @@ class MangaRestorer( chapter.sourceOrder, chapter.dateFetch, chapter.dateUpload, + chapter.version, ) } } @@ -222,6 +227,8 @@ class MangaRestorer( dateFetch = null, dateUpload = null, chapterId = chapter.id, + version = chapter.version, + isSyncing = 0, ) } } @@ -254,6 +261,7 @@ class MangaRestorer( coverLastModified = manga.coverLastModified, dateAdded = manga.dateAdded, updateStrategy = manga.updateStrategy, + version = manga.version, ) mangasQueries.selectLastInsertedRowId() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt index 978f65bc99..0556d9d1f1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt @@ -1,19 +1,19 @@ package eu.kanade.tachiyomi.data.coil import androidx.core.net.toUri -import coil.ImageLoader -import coil.decode.DataSource -import coil.decode.ImageSource -import coil.disk.DiskCache -import coil.fetch.FetchResult -import coil.fetch.Fetcher -import coil.fetch.SourceResult -import coil.network.HttpException -import coil.request.Options -import coil.request.Parameters +import coil3.Extras +import coil3.ImageLoader +import coil3.decode.DataSource +import coil3.decode.ImageSource +import coil3.disk.DiskCache +import coil3.fetch.FetchResult +import coil3.fetch.Fetcher +import coil3.fetch.SourceFetchResult +import coil3.getOrDefault +import coil3.request.Options import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.data.cache.CoverCache -import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher.Companion.USE_CUSTOM_COVER +import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher.Companion.USE_CUSTOM_COVER_KEY import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.source.online.HttpSource import logcat.LogPriority @@ -22,6 +22,7 @@ import okhttp3.Call import okhttp3.Request import okhttp3.Response import okhttp3.internal.http.HTTP_NOT_MODIFIED +import okio.FileSystem import okio.Path.Companion.toOkioPath import okio.Source import okio.buffer @@ -33,6 +34,7 @@ import tachiyomi.domain.manga.model.MangaCover import tachiyomi.domain.source.service.SourceManager import uy.kohesive.injekt.injectLazy import java.io.File +import java.io.IOException /** * A [Fetcher] that fetches cover image for [Manga] object. @@ -42,7 +44,7 @@ import java.io.File * handled by Coil's [DiskCache]. * * Available request parameter: - * - [USE_CUSTOM_COVER]: Use custom cover if set by user, default is true + * - [USE_CUSTOM_COVER_KEY]: Use custom cover if set by user, default is true */ class MangaCoverFetcher( private val url: String?, @@ -61,7 +63,7 @@ class MangaCoverFetcher( override suspend fun fetch(): FetchResult { // Use custom cover if exists - val useCustomCover = options.parameters.value(USE_CUSTOM_COVER) ?: true + val useCustomCover = options.extras.getOrDefault(USE_CUSTOM_COVER_KEY) if (useCustomCover) { val customCoverFile = customCoverFileLazy.value if (customCoverFile.exists()) { @@ -80,8 +82,12 @@ class MangaCoverFetcher( } private fun fileLoader(file: File): FetchResult { - return SourceResult( - source = ImageSource(file = file.toOkioPath(), diskCacheKey = diskCacheKey), + return SourceFetchResult( + source = ImageSource( + file = file.toOkioPath(), + fileSystem = FileSystem.SYSTEM, + diskCacheKey = diskCacheKey + ), mimeType = "image/*", dataSource = DataSource.DISK, ) @@ -92,8 +98,8 @@ class MangaCoverFetcher( .openInputStream() .source() .buffer() - return SourceResult( - source = ImageSource(source = source, context = options.context), + return SourceFetchResult( + source = ImageSource(source = source, fileSystem = FileSystem.SYSTEM), mimeType = "image/*", dataSource = DataSource.DISK, ) @@ -121,7 +127,7 @@ class MangaCoverFetcher( } // Read from snapshot - return SourceResult( + return SourceFetchResult( source = snapshot.toImageSource(), mimeType = "image/*", dataSource = DataSource.DISK, @@ -141,7 +147,7 @@ class MangaCoverFetcher( // Read from disk cache snapshot = writeToDiskCache(response) if (snapshot != null) { - return SourceResult( + return SourceFetchResult( source = snapshot.toImageSource(), mimeType = "image/*", dataSource = DataSource.NETWORK, @@ -149,8 +155,8 @@ class MangaCoverFetcher( } // Read from response if cache is unused or unusable - return SourceResult( - source = ImageSource(source = responseBody.source(), context = options.context), + return SourceFetchResult( + source = ImageSource(source = responseBody.source(), fileSystem = FileSystem.SYSTEM), mimeType = "image/*", dataSource = if (response.cacheResponse != null) DataSource.DISK else DataSource.NETWORK, ) @@ -169,17 +175,20 @@ class MangaCoverFetcher( val response = client.newCall(newRequest()).await() if (!response.isSuccessful && response.code != HTTP_NOT_MODIFIED) { response.close() - throw HttpException(response) + throw IOException(response.message) } return response } private fun newRequest(): Request { - val request = Request.Builder() - .url(url!!) - .headers(sourceLazy.value?.headers ?: options.headers) - // Support attaching custom data to the network request. - .tag(Parameters::class.java, options.parameters) + val request = Request.Builder().apply { + url(url!!) + + val sourceHeaders = sourceLazy.value?.headers + if (sourceHeaders != null) { + headers(sourceHeaders) + } + } when { options.networkCachePolicy.readEnabled -> { @@ -264,7 +273,12 @@ class MangaCoverFetcher( } private fun DiskCache.Snapshot.toImageSource(): ImageSource { - return ImageSource(file = data, diskCacheKey = diskCacheKey, closeable = this) + return ImageSource( + file = data, + fileSystem = FileSystem.SYSTEM, + diskCacheKey = diskCacheKey, + closeable = this, + ) } private fun getResourceType(cover: String?): Type? { @@ -330,7 +344,7 @@ class MangaCoverFetcher( } companion object { - const val USE_CUSTOM_COVER = "use_custom_cover" + val USE_CUSTOM_COVER_KEY = Extras.Key(true) private val CACHE_CONTROL_NO_STORE = CacheControl.Builder().noStore().build() private val CACHE_CONTROL_NO_NETWORK_NO_CACHE = CacheControl.Builder().noCache().onlyIfCached().build() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverKeyer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverKeyer.kt index bc85b22f47..56e0291aa2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverKeyer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverKeyer.kt @@ -1,7 +1,7 @@ package eu.kanade.tachiyomi.data.coil -import coil.key.Keyer -import coil.request.Options +import coil3.key.Keyer +import coil3.request.Options import eu.kanade.domain.manga.model.hasCustomCover import eu.kanade.tachiyomi.data.cache.CoverCache import tachiyomi.domain.manga.model.MangaCover diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt index acbda8cc57..3f6c139062 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt @@ -1,13 +1,13 @@ package eu.kanade.tachiyomi.data.coil -import androidx.core.graphics.drawable.toDrawable -import coil.ImageLoader -import coil.decode.DecodeResult -import coil.decode.Decoder -import coil.decode.ImageDecoderDecoder -import coil.decode.ImageSource -import coil.fetch.SourceResult -import coil.request.Options +import coil3.ImageLoader +import coil3.asCoilImage +import coil3.decode.DecodeResult +import coil3.decode.Decoder +import coil3.decode.ImageSource +import coil3.fetch.SourceFetchResult +import coil3.request.Options +import coil3.request.allowRgb565 import okio.BufferedSource import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.decoder.ImageDecoder @@ -30,14 +30,14 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti check(bitmap != null) { "Failed to decode image" } return DecodeResult( - drawable = bitmap.toDrawable(options.context.resources), + image = bitmap.asCoilImage(), isSampled = false, ) } class Factory : Decoder.Factory { - override fun create(result: SourceResult, options: Options, imageLoader: ImageLoader): Decoder? { + override fun create(result: SourceFetchResult, options: Options, imageLoader: ImageLoader): Decoder? { if (!isApplicable(result.source.source())) return null return TachiyomiImageDecoder(result.source, options) } @@ -52,7 +52,7 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti } } - override fun equals(other: Any?) = other is ImageDecoderDecoder.Factory + override fun equals(other: Any?) = other is Factory override fun hashCode() = javaClass.hashCode() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Chapter.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Chapter.kt index 4ff50483ec..f913680846 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Chapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Chapter.kt @@ -21,6 +21,8 @@ interface Chapter : SChapter, Serializable { var source_order: Int var last_modified: Long + + var version: Long } fun Chapter.toDomainChapter(): DomainChapter? { @@ -39,5 +41,6 @@ fun Chapter.toDomainChapter(): DomainChapter? { chapterNumber = chapter_number.toDouble(), scanlator = scanlator, lastModifiedAt = last_modified, + version = version, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/ChapterImpl.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/ChapterImpl.kt index 58ba41dec4..a92dd56df5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/ChapterImpl.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/ChapterImpl.kt @@ -28,6 +28,8 @@ class ChapterImpl : Chapter { override var last_modified: Long = 0 + override var version: Long = 0 + override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || javaClass != other.javaClass) return false diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index facbe605a9..7c8238d5a7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -54,6 +54,7 @@ import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.download.service.DownloadPreferences import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.service.SourceManager +import tachiyomi.domain.track.interactor.GetTracks import tachiyomi.i18n.MR import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -78,6 +79,7 @@ class Downloader( private val downloadPreferences: DownloadPreferences = Injekt.get(), private val xml: XML = Injekt.get(), private val getCategories: GetCategories = Injekt.get(), + private val getTracks: GetTracks = Injekt.get(), ) { /** @@ -626,9 +628,22 @@ class Downloader( chapter: Chapter, source: HttpSource, ) { - val chapterUrl = source.getChapterUrl(chapter.toSChapter()) val categories = getCategories.await(manga.id).map { it.name.trim() }.takeUnless { it.isEmpty() } - val comicInfo = getComicInfo(manga, chapter, chapterUrl, categories) + val urls = getTracks.await(manga.id) + .mapNotNull { track -> + track.remoteUrl.takeUnless { url -> url.isBlank() }?.trim() + } + .plus(source.getChapterUrl(chapter.toSChapter()).trim()) + .distinct() + + val comicInfo = getComicInfo( + manga, + chapter, + urls, + categories, + source.name + ) + // Remove the old file dir.findFile(COMIC_INFO_FILE, true)?.delete() dir.createFile(COMIC_INFO_FILE)!!.openOutputStream().use { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt index 4d13dc724f..e18bf76c40 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt @@ -9,9 +9,10 @@ import android.graphics.BitmapFactory import android.net.Uri import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat -import coil.imageLoader -import coil.request.ImageRequest -import coil.transform.CircleCropTransformation +import coil3.imageLoader +import coil3.request.ImageRequest +import coil3.request.transformations +import coil3.transform.CircleCropTransformation import eu.kanade.presentation.util.formatChapterNumber import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.core.security.SecurityPreferences @@ -294,7 +295,7 @@ class LibraryUpdateNotifier( .transformations(CircleCropTransformation()) .size(NOTIF_ICON_SIZE) .build() - val drawable = context.imageLoader.execute(request).drawable + val drawable = context.imageLoader.execute(request).image?.asDrawable(context.resources) return drawable?.getBitmapOrNull() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt index b56a10e462..d67c2cabec 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.data.track.myanimelist -import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.network.parseAs import kotlinx.serialization.json.Json import okhttp3.Interceptor @@ -32,7 +31,8 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList) : Interceptor // Add the authorization header to the original request val authRequest = originalRequest.newBuilder() .addHeader("Authorization", "Bearer ${oauth!!.access_token}") - .header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})") + // TODO(antsy): Add back custom user agent when they stop blocking us for no apparent reason + // .header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})") .build() return chain.proceed(authRequest) diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt index c85611acfc..24a8cb377c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.extension.installer import android.app.Service import android.content.pm.PackageManager +import android.os.Process import eu.kanade.tachiyomi.extension.model.InstallStep import eu.kanade.tachiyomi.util.system.getUriSize import eu.kanade.tachiyomi.util.system.toast @@ -49,7 +50,8 @@ class ShizukuInstaller(private val service: Service) : Installer(service) { try { val size = service.getUriSize(entry.uri) ?: throw IllegalStateException() service.contentResolver.openInputStream(entry.uri)!!.use { - val createCommand = "pm install-create --user current -r -i ${service.packageName} -S $size" + val userId = Process.myUserHandle().hashCode() + val createCommand = "pm install-create --user $userId -r -i ${service.packageName} -S $size" val createResult = exec(createCommand) sessionId = SESSION_ID_REGEX.find(createResult.out)?.value ?: throw RuntimeException("Failed to create install session") diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt index 2d469f2602..0468c45e56 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt @@ -6,7 +6,6 @@ import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.os.Build import androidx.core.content.pm.PackageInfoCompat -import dalvik.system.PathClassLoader import eu.kanade.domain.extension.interactor.TrustExtension import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.tachiyomi.extension.model.Extension @@ -16,6 +15,7 @@ import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.SourceFactory import eu.kanade.tachiyomi.util.lang.Hash import eu.kanade.tachiyomi.util.storage.copyAndSetReadOnlyTo +import eu.kanade.tachiyomi.util.system.ChildFirstPathClassLoader import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.runBlocking @@ -272,7 +272,7 @@ internal object ExtensionLoader { } val classLoader = try { - PathClassLoader(appInfo.sourceDir, null, context.classLoader) + ChildFirstPathClassLoader(appInfo.sourceDir, null, context.classLoader) } catch (e: Exception) { logcat(LogPriority.ERROR, e) { "Extension load error: $extName ($pkgName)" } return LoadResult.Error diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt index 7b0cdcf725..c0c2b555f3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt @@ -28,7 +28,6 @@ import tachiyomi.domain.history.interactor.RemoveHistory import tachiyomi.domain.history.model.HistoryWithRelations import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.time.LocalDate class HistoryScreenModel( private val getHistory: GetHistory = Injekt.get(), @@ -60,12 +59,10 @@ class HistoryScreenModel( private fun List.toHistoryUiModels(): List { return map { HistoryUiModel.Item(it) } .insertSeparators { before, after -> - val beforeDate = before?.item?.readAt?.time?.toLocalDate() ?: LocalDate.MIN - val afterDate = after?.item?.readAt?.time?.toLocalDate() ?: LocalDate.MIN + val beforeDate = before?.item?.readAt?.time?.toLocalDate() + val afterDate = after?.item?.readAt?.time?.toLocalDate() when { - beforeDate.isAfter(afterDate) - or afterDate.equals(LocalDate.MIN) - or beforeDate.equals(LocalDate.MIN) -> HistoryUiModel.Header(afterDate) + beforeDate != afterDate && afterDate != null -> HistoryUiModel.Header(afterDate) // Return null to avoid adding a separator between two items. else -> null } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaCoverScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaCoverScreenModel.kt index 3cbfa540be..75a98c5df2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaCoverScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaCoverScreenModel.kt @@ -5,9 +5,9 @@ import android.net.Uri import androidx.compose.material3.SnackbarHostState import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.screenModelScope -import coil.imageLoader -import coil.request.ImageRequest -import coil.size.Size +import coil3.imageLoader +import coil3.request.ImageRequest +import coil3.size.Size import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.saver.Image @@ -96,7 +96,7 @@ class MangaCoverScreenModel( .build() return withIOContext { - val result = context.imageLoader.execute(req).drawable + val result = context.imageLoader.execute(req).image?.asDrawable(context.resources) // TODO: Handle animated cover val bitmap = result?.getBitmapOrNull() ?: return@withIOContext null diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt index ab0146a0e0..373ed97a83 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt @@ -4,9 +4,9 @@ import android.content.Context import android.graphics.Bitmap import android.net.Uri import androidx.core.app.NotificationCompat -import coil.imageLoader -import coil.request.CachePolicy -import coil.request.ImageRequest +import coil3.imageLoader +import coil3.request.CachePolicy +import coil3.request.ImageRequest import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.notification.NotificationHandler import eu.kanade.tachiyomi.data.notification.NotificationReceiver @@ -37,7 +37,7 @@ class SaveImageNotifier(private val context: Context) { .memoryCachePolicy(CachePolicy.DISABLED) .size(720, 1280) .target( - onSuccess = { showCompleteNotification(uri, it.getBitmapOrNull()) }, + onSuccess = { showCompleteNotification(uri, it.asDrawable(context.resources).getBitmapOrNull()) }, onError = { onError(null) }, ) .build() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt index fadba25c49..89856bf226 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt @@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.ui.reader.loader import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder -import org.apache.commons.compress.archivers.zip.ZipFile +import mihon.core.common.extensions.toZipFile import tachiyomi.core.common.util.system.ImageUtil import java.nio.channels.SeekableByteChannel @@ -12,7 +12,7 @@ import java.nio.channels.SeekableByteChannel */ internal class ZipPageLoader(channel: SeekableByteChannel) : PageLoader() { - private val zip = ZipFile(channel) + private val zip = channel.toZipFile() override var isLocal: Boolean = true diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt index 6c079b836e..53e342e766 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt @@ -72,6 +72,8 @@ class ReaderPreferences( fun skipDupe() = preferenceStore.getBoolean("skip_dupe", false) + fun webtoonDisableZoomOut() = preferenceStore.getBoolean("webtoon_disable_zoom_out", false) + // endregion // region Split two page spread diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt index c60e404e71..fbb1302e82 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt @@ -18,10 +18,11 @@ import androidx.annotation.StyleRes import androidx.appcompat.widget.AppCompatImageView import androidx.core.os.postDelayed import androidx.core.view.isVisible -import coil.dispose -import coil.imageLoader -import coil.request.CachePolicy -import coil.request.ImageRequest +import coil3.dispose +import coil3.imageLoader +import coil3.request.CachePolicy +import coil3.request.ImageRequest +import coil3.request.crossfade import com.davemorrissey.labs.subscaleview.ImageSource import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_IN_OUT_QUAD @@ -348,7 +349,7 @@ open class ReaderPageImageView @JvmOverloads constructor( .diskCachePolicy(CachePolicy.DISABLED) .target( onSuccess = { result -> - setImageDrawable(result) + setImageDrawable(result.asDrawable(context.resources)) (result as? Animatable)?.start() isVisible = true this@ReaderPageImageView.onImageLoaded() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonConfig.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonConfig.kt index 28ad91a51d..f554788628 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonConfig.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonConfig.kt @@ -29,6 +29,11 @@ class WebtoonConfig( var imageCropBorders = false private set + var zoomOutDisabled = false + private set + + var zoomPropertyChangedListener: ((Boolean) -> Unit)? = null + var sidePadding = 0 private set @@ -74,6 +79,12 @@ class WebtoonConfig( { imagePropertyChangedListener?.invoke() }, ) + readerPreferences.webtoonDisableZoomOut() + .register( + { zoomOutDisabled = it }, + { zoomPropertyChangedListener?.invoke(it) } + ) + readerPreferences.webtoonDoubleTapZoomEnabled() .register( { doubleTapZoom = it }, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonFrame.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonFrame.kt index 572335b250..17aafc6fd1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonFrame.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonFrame.kt @@ -33,6 +33,12 @@ class WebtoonFrame(context: Context) : FrameLayout(context) { scaleDetector.isQuickScaleEnabled = value } + var zoomOutDisabled = false + set(value) { + field = value + recycler?.zoomOutDisabled = value + } + /** * Recycler view added in this frame. */ diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonRecyclerView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonRecyclerView.kt index d86619cdf2..20c18c6223 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonRecyclerView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonRecyclerView.kt @@ -33,6 +33,15 @@ class WebtoonRecyclerView @JvmOverloads constructor( private var firstVisibleItemPosition = 0 private var lastVisibleItemPosition = 0 private var currentScale = DEFAULT_RATE + var zoomOutDisabled = false + set(value) { + field = value + if (value && currentScale < DEFAULT_RATE) { + zoom(currentScale, DEFAULT_RATE, x, 0f, y, 0f) + } + } + private val minRate + get() = if (zoomOutDisabled) DEFAULT_RATE else MIN_RATE private val listener = GestureListener() private val detector = Detector() @@ -166,7 +175,7 @@ class WebtoonRecyclerView @JvmOverloads constructor( fun onScale(scaleFactor: Float) { currentScale *= scaleFactor currentScale = currentScale.coerceIn( - MIN_RATE, + minRate, MAX_SCALE_RATE, ) @@ -193,8 +202,8 @@ class WebtoonRecyclerView @JvmOverloads constructor( } fun onScaleEnd() { - if (scaleX < MIN_RATE) { - zoom(currentScale, MIN_RATE, x, 0f, y, 0f) + if (scaleX < minRate) { + zoom(currentScale, minRate, x, 0f, y, 0f) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt index da11e68ff4..1e370a8c7c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt @@ -152,6 +152,10 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr frame.doubleTapZoom = it } + config.zoomPropertyChangedListener = { + frame.zoomOutDisabled = it + } + config.navigationModeChangedListener = { val showOnStart = config.navigationOverlayOnStart || config.forceNavigationOverlay activity.binding.navigationOverlay.setNavigation(config.navigator, showOnStart) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt index 2b2c1c25a5..c5385d1f04 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt @@ -45,7 +45,6 @@ import tachiyomi.domain.updates.interactor.GetUpdates import tachiyomi.domain.updates.model.UpdatesWithRelations import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.time.LocalDate import java.time.ZonedDateTime class UpdatesScreenModel( @@ -374,12 +373,10 @@ class UpdatesScreenModel( return items .map { UpdatesUiModel.Item(it) } .insertSeparators { before, after -> - val beforeDate = before?.item?.update?.dateFetch?.toLocalDate() ?: LocalDate.MIN - val afterDate = after?.item?.update?.dateFetch?.toLocalDate() ?: LocalDate.MIN + val beforeDate = before?.item?.update?.dateFetch?.toLocalDate() + val afterDate = after?.item?.update?.dateFetch?.toLocalDate() when { - beforeDate.isAfter(afterDate) - or afterDate.equals(LocalDate.MIN) - or beforeDate.equals(LocalDate.MIN) -> UpdatesUiModel.Header(afterDate) + beforeDate != afterDate && afterDate != null -> UpdatesUiModel.Header(afterDate) // Return null to avoid adding a separator between two items. else -> null } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt index 9e61555ecb..8326fe2f82 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt @@ -13,6 +13,7 @@ import java.time.format.DateTimeFormatter import java.time.format.FormatStyle import java.time.temporal.ChronoUnit import java.util.Date +import kotlin.math.absoluteValue fun LocalDateTime.toDateTimestampString(dateTimeFormatter: DateTimeFormatter): String { val date = dateTimeFormatter.format(this) @@ -49,13 +50,20 @@ fun LocalDate.toRelativeString( val now = LocalDate.now() val difference = ChronoUnit.DAYS.between(this, now) return when { - difference < 0 -> difference.toString() + difference < -7 -> dateFormat.format(this) + difference < 0 -> context.pluralStringResource( + MR.plurals.upcoming_relative_time, + difference.toInt().absoluteValue, + difference.toInt().absoluteValue, + ) + difference < 1 -> context.stringResource(MR.strings.relative_time_today) difference < 7 -> context.pluralStringResource( MR.plurals.relative_time, difference.toInt(), difference.toInt(), ) + else -> dateFormat.format(this) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ChildFirstPathClassLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ChildFirstPathClassLoader.kt new file mode 100644 index 0000000000..b63dfd0328 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ChildFirstPathClassLoader.kt @@ -0,0 +1,86 @@ +package eu.kanade.tachiyomi.util.system + +import dalvik.system.PathClassLoader +import java.io.IOException +import java.io.InputStream +import java.net.URL +import java.util.Enumeration + +/** + * A parent-last class loader that will try in order: + * - the system class loader + * - the child class loader + * - the parent class loader. + */ +class ChildFirstPathClassLoader( + dexPath: String, + librarySearchPath: String?, + parent: ClassLoader +) : PathClassLoader(dexPath, librarySearchPath, parent) { + + private val systemClassLoader: ClassLoader? = getSystemClassLoader() + + override fun loadClass(name: String?, resolve: Boolean): Class<*> { + var c = findLoadedClass(name) + + if (c == null && systemClassLoader != null) { + try { + c = systemClassLoader.loadClass(name) + } catch (_: ClassNotFoundException) {} + } + + if (c == null) { + c = try { + findClass(name) + } catch (_: ClassNotFoundException) { + super.loadClass(name, resolve) + } + } + + if (resolve) { + resolveClass(c) + } + + return c + } + + override fun getResource(name: String?): URL? { + return systemClassLoader?.getResource(name) + ?: findResource(name) + ?: super.getResource(name) + } + + override fun getResources(name: String?): Enumeration { + val systemUrls = systemClassLoader?.getResources(name) + val localUrls = findResources(name) + val parentUrls = parent?.getResources(name) + val urls = buildList { + while (systemUrls?.hasMoreElements() == true) { + add(systemUrls.nextElement()) + } + + while (localUrls?.hasMoreElements() == true) { + add(localUrls.nextElement()) + } + + while (parentUrls?.hasMoreElements() == true) { + add(parentUrls.nextElement()) + } + } + + return object : Enumeration { + val iterator = urls.iterator() + + override fun hasMoreElements() = iterator.hasNext() + override fun nextElement() = iterator.next() + } + } + + override fun getResourceAsStream(name: String?): InputStream? { + return try { + getResource(name)?.openStream() + } catch (_: IOException) { + return null + } + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/DrawableExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/DrawableExtensions.kt index 2a09aeca96..2877768af1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/DrawableExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/DrawableExtensions.kt @@ -4,7 +4,7 @@ import android.graphics.Bitmap import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import androidx.core.graphics.drawable.toBitmap -import coil.drawable.ScaleDrawable +import coil3.gif.ScaleDrawable fun Drawable.getBitmapOrNull(): Bitmap? = when (this) { is BitmapDrawable -> bitmap diff --git a/core-metadata/src/main/java/tachiyomi/core/metadata/comicinfo/ComicInfo.kt b/core-metadata/src/main/java/tachiyomi/core/metadata/comicinfo/ComicInfo.kt index 681b4819b1..5d4d922771 100644 --- a/core-metadata/src/main/java/tachiyomi/core/metadata/comicinfo/ComicInfo.kt +++ b/core-metadata/src/main/java/tachiyomi/core/metadata/comicinfo/ComicInfo.kt @@ -27,6 +27,7 @@ fun SManga.getComicInfo() = ComicInfo( coverArtist = null, tags = null, categories = null, + source = null, ) fun SManga.copyFromComicInfo(comicInfo: ComicInfo) { @@ -81,6 +82,7 @@ data class ComicInfo( val web: Web?, val publishingStatus: PublishingStatusTachiyomi?, val categories: CategoriesTachiyomi?, + val source: SourceMihon?, ) { @XmlElement(false) @XmlSerialName("xmlns:xsd", "", "") @@ -154,6 +156,10 @@ data class ComicInfo( @Serializable @XmlSerialName("Categories", "http://www.w3.org/2001/XMLSchema", "ty") data class CategoriesTachiyomi(@XmlValue(true) val value: String = "") + + @Serializable + @XmlSerialName("SourceMihon", "http://www.w3.org/2001/XMLSchema", "mh") + data class SourceMihon(@XmlValue(true) val value: String = "") } enum class ComicInfoPublishingStatus( diff --git a/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt index f664c175b4..29cea58248 100644 --- a/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt @@ -1,7 +1,7 @@ package eu.kanade.tachiyomi.util.storage +import mihon.core.common.extensions.toZipFile import org.apache.commons.compress.archivers.zip.ZipArchiveEntry -import org.apache.commons.compress.archivers.zip.ZipFile import org.jsoup.Jsoup import org.jsoup.nodes.Document import java.io.Closeable @@ -17,7 +17,7 @@ class EpubFile(channel: SeekableByteChannel) : Closeable { /** * Zip file of this epub. */ - private val zip = ZipFile(channel) + private val zip = channel.toZipFile() /** * Path separator used by this epub. diff --git a/core/common/src/main/kotlin/mihon/core/common/extensions/SeekableByteChannel.kt b/core/common/src/main/kotlin/mihon/core/common/extensions/SeekableByteChannel.kt new file mode 100644 index 0000000000..69e2d7201f --- /dev/null +++ b/core/common/src/main/kotlin/mihon/core/common/extensions/SeekableByteChannel.kt @@ -0,0 +1,8 @@ +package mihon.core.common.extensions + +import org.apache.commons.compress.archivers.zip.ZipFile +import java.nio.channels.SeekableByteChannel + +fun SeekableByteChannel.toZipFile(): ZipFile { + return ZipFile.Builder().setSeekableByteChannel(this).get() +} diff --git a/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt b/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt index 1c13d2be7d..b01f4e8821 100644 --- a/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt @@ -551,7 +551,7 @@ object ImageUtil { imageStream: InputStream, resetAfterExtraction: Boolean = true, ): BitmapFactory.Options { - imageStream.mark(imageStream.available() + 1) + imageStream.mark(Int.MAX_VALUE) val imageBytes = imageStream.readBytes() val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } diff --git a/data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt index d2284b612e..099ccb0da9 100644 --- a/data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt @@ -29,6 +29,7 @@ class ChapterRepositoryImpl( chapter.sourceOrder, chapter.dateFetch, chapter.dateUpload, + chapter.version, ) val lastInsertId = chaptersQueries.selectLastInsertedRowId().executeAsOne() chapter.copy(id = lastInsertId) @@ -64,6 +65,8 @@ class ChapterRepositoryImpl( dateFetch = chapterUpdate.dateFetch, dateUpload = chapterUpdate.dateUpload, chapterId = chapterUpdate.id, + version = chapterUpdate.version, + isSyncing = 0, ) } } @@ -124,6 +127,7 @@ class ChapterRepositoryImpl( } } + @Suppress("LongParameterList") private fun mapChapter( id: Long, mangaId: Long, @@ -138,6 +142,9 @@ class ChapterRepositoryImpl( dateFetch: Long, dateUpload: Long, lastModifiedAt: Long, + version: Long, + @Suppress("UNUSED_PARAMETER") + isSyncing: Long, ): Chapter = Chapter( id = id, mangaId = mangaId, @@ -152,5 +159,6 @@ class ChapterRepositoryImpl( chapterNumber = chapterNumber, scanlator = scanlator, lastModifiedAt = lastModifiedAt, + version = version, ) } diff --git a/data/src/main/java/tachiyomi/data/manga/MangaMapper.kt b/data/src/main/java/tachiyomi/data/manga/MangaMapper.kt index fabed8cbc0..14da67da9f 100644 --- a/data/src/main/java/tachiyomi/data/manga/MangaMapper.kt +++ b/data/src/main/java/tachiyomi/data/manga/MangaMapper.kt @@ -5,6 +5,7 @@ import tachiyomi.domain.library.model.LibraryManga import tachiyomi.domain.manga.model.Manga object MangaMapper { + @Suppress("LongParameterList") fun mapManga( id: Long, source: Long, @@ -28,6 +29,9 @@ object MangaMapper { calculateInterval: Long, lastModifiedAt: Long, favoriteModifiedAt: Long?, + version: Long, + @Suppress("UNUSED_PARAMETER") + isSyncing: Long, notes: String?, ): Manga = Manga( id = id, @@ -52,9 +56,11 @@ object MangaMapper { initialized = initialized, lastModifiedAt = lastModifiedAt, favoriteModifiedAt = favoriteModifiedAt, + version = version, notes = notes, ) + @Suppress("LongParameterList") fun mapLibraryManga( id: Long, source: Long, @@ -78,6 +84,8 @@ object MangaMapper { calculateInterval: Long, lastModifiedAt: Long, favoriteModifiedAt: Long?, + version: Long, + isSyncing: Long, notes: String?, totalCount: Long, readCount: Double, @@ -110,6 +118,8 @@ object MangaMapper { calculateInterval, lastModifiedAt, favoriteModifiedAt, + version, + isSyncing, notes, ), category = category, diff --git a/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt index 3a0eb60daa..a5bef7f5f9 100644 --- a/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt @@ -106,6 +106,7 @@ class MangaRepositoryImpl( coverLastModified = manga.coverLastModified, dateAdded = manga.dateAdded, updateStrategy = manga.updateStrategy, + version = manga.version, ) mangasQueries.selectLastInsertedRowId() } @@ -155,6 +156,8 @@ class MangaRepositoryImpl( dateAdded = value.dateAdded, mangaId = value.id, updateStrategy = value.updateStrategy?.let(UpdateStrategyColumnAdapter::encode), + version = value.version, + isSyncing = 0, notes = value.notes ) } diff --git a/data/src/main/sqldelight/tachiyomi/data/chapters.sq b/data/src/main/sqldelight/tachiyomi/data/chapters.sq index 4c341793f4..c69de987d7 100644 --- a/data/src/main/sqldelight/tachiyomi/data/chapters.sq +++ b/data/src/main/sqldelight/tachiyomi/data/chapters.sq @@ -14,6 +14,8 @@ CREATE TABLE chapters( date_fetch INTEGER NOT NULL, date_upload INTEGER NOT NULL, last_modified_at INTEGER NOT NULL DEFAULT 0, + version INTEGER NOT NULL DEFAULT 0, + is_syncing INTEGER NOT NULL DEFAULT 0, FOREIGN KEY(manga_id) REFERENCES mangas (_id) ON DELETE CASCADE ); @@ -30,6 +32,22 @@ BEGIN WHERE _id = new._id; END; +CREATE TRIGGER update_chapter_and_manga_version AFTER UPDATE ON chapters +WHEN new.is_syncing = 0 AND ( + new.read != old.read OR + new.bookmark != old.bookmark OR + new.last_page_read != old.last_page_read +) +BEGIN + -- Update the chapter version + UPDATE chapters SET version = version + 1 + WHERE _id = new._id; + + -- Update the manga version + UPDATE mangas SET version = version + 1 + WHERE _id = new.manga_id AND (SELECT is_syncing FROM mangas WHERE _id = new.manga_id) = 0; +END; + getChapterById: SELECT * FROM chapters @@ -73,9 +91,14 @@ removeChaptersWithIds: DELETE FROM chapters WHERE _id IN :chapterIds; +resetIsSyncing: +UPDATE chapters +SET is_syncing = 0 +WHERE is_syncing = 1; + insert: -INSERT INTO chapters(manga_id, url, name, scanlator, read, bookmark, last_page_read, chapter_number, source_order, date_fetch, date_upload, last_modified_at) -VALUES (:mangaId, :url, :name, :scanlator, :read, :bookmark, :lastPageRead, :chapterNumber, :sourceOrder, :dateFetch, :dateUpload, 0); +INSERT INTO chapters(manga_id, url, name, scanlator, read, bookmark, last_page_read, chapter_number, source_order, date_fetch, date_upload, last_modified_at, version, is_syncing) +VALUES (:mangaId, :url, :name, :scanlator, :read, :bookmark, :lastPageRead, :chapterNumber, :sourceOrder, :dateFetch, :dateUpload, 0, :version, 0); update: UPDATE chapters @@ -89,7 +112,9 @@ SET manga_id = coalesce(:mangaId, manga_id), chapter_number = coalesce(:chapterNumber, chapter_number), source_order = coalesce(:sourceOrder, source_order), date_fetch = coalesce(:dateFetch, date_fetch), - date_upload = coalesce(:dateUpload, date_upload) + date_upload = coalesce(:dateUpload, date_upload), + version = coalesce(:version, version), + is_syncing = coalesce(:isSyncing, is_syncing) WHERE _id = :chapterId; selectLastInsertedRowId: diff --git a/data/src/main/sqldelight/tachiyomi/data/mangas.sq b/data/src/main/sqldelight/tachiyomi/data/mangas.sq index f13cfbf1bc..9eb9d70fdb 100644 --- a/data/src/main/sqldelight/tachiyomi/data/mangas.sq +++ b/data/src/main/sqldelight/tachiyomi/data/mangas.sq @@ -26,6 +26,8 @@ CREATE TABLE mangas( calculate_interval INTEGER DEFAULT 0 NOT NULL, last_modified_at INTEGER NOT NULL DEFAULT 0, favorite_modified_at INTEGER, + version INTEGER NOT NULL DEFAULT 0, + is_syncing INTEGER NOT NULL DEFAULT 0, notes TEXT ); @@ -49,6 +51,16 @@ BEGIN WHERE _id = new._id; END; +CREATE TRIGGER update_manga_version AFTER UPDATE ON mangas +BEGIN + UPDATE mangas SET version = version + 1 + WHERE _id = new._id AND new.is_syncing = 0 AND ( + new.url != old.url OR + new.description != old.description OR + new.favorite != old.favorite + ); +END; + getMangaById: SELECT * FROM mangas @@ -105,6 +117,11 @@ resetViewerFlags: UPDATE mangas SET viewer = 0; +resetIsSyncing: +UPDATE mangas +SET is_syncing = 0 +WHERE is_syncing = 1; + getSourceIdsWithNonLibraryManga: SELECT source, COUNT(*) AS manga_count FROM mangas @@ -117,8 +134,8 @@ WHERE favorite = 0 AND source IN :sourceIds; insert: -INSERT INTO mangas(source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added, update_strategy, calculate_interval, last_modified_at) -VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :dateAdded, :updateStrategy, :calculateInterval, 0); +INSERT INTO mangas(source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added, update_strategy, calculate_interval, last_modified_at, version) +VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :dateAdded, :updateStrategy, :calculateInterval, 0, :version); update: UPDATE mangas SET @@ -141,6 +158,8 @@ UPDATE mangas SET date_added = coalesce(:dateAdded, date_added), update_strategy = coalesce(:updateStrategy, update_strategy), calculate_interval = coalesce(:calculateInterval, calculate_interval), + version = coalesce(:version, version), + is_syncing = coalesce(:isSyncing, is_syncing), notes = coalesce(:notes, notes) WHERE _id = :mangaId; diff --git a/data/src/main/sqldelight/tachiyomi/data/mangas_categories.sq b/data/src/main/sqldelight/tachiyomi/data/mangas_categories.sq index b908e3f863..3d8c815c97 100644 --- a/data/src/main/sqldelight/tachiyomi/data/mangas_categories.sq +++ b/data/src/main/sqldelight/tachiyomi/data/mangas_categories.sq @@ -2,25 +2,22 @@ CREATE TABLE mangas_categories( _id INTEGER NOT NULL PRIMARY KEY, manga_id INTEGER NOT NULL, category_id INTEGER NOT NULL, - last_modified_at INTEGER NOT NULL DEFAULT 0, FOREIGN KEY(category_id) REFERENCES categories (_id) ON DELETE CASCADE, FOREIGN KEY(manga_id) REFERENCES mangas (_id) ON DELETE CASCADE ); -CREATE TRIGGER update_last_modified_at_mangas_categories -AFTER UPDATE ON mangas_categories -FOR EACH ROW +CREATE TRIGGER insert_manga_category_update_version AFTER INSERT ON mangas_categories BEGIN - UPDATE mangas_categories - SET last_modified_at = strftime('%s', 'now') - WHERE _id = new._id; + UPDATE mangas + SET version = version + 1 + WHERE _id = new.manga_id AND (SELECT is_syncing FROM mangas WHERE _id = new.manga_id) = 0; END; insert: -INSERT INTO mangas_categories(manga_id, category_id, last_modified_at) -VALUES (:mangaId, :categoryId, 0); +INSERT INTO mangas_categories(manga_id, category_id) +VALUES (:mangaId, :categoryId); deleteMangaCategoryByMangaId: DELETE FROM mangas_categories diff --git a/data/src/main/sqldelight/tachiyomi/migrations/2.sqm b/data/src/main/sqldelight/tachiyomi/migrations/2.sqm index 82ed296d72..1a05f72e44 100644 --- a/data/src/main/sqldelight/tachiyomi/migrations/2.sqm +++ b/data/src/main/sqldelight/tachiyomi/migrations/2.sqm @@ -1,3 +1,46 @@ --- Add notes column -ALTER TABLE mangas -ADD notes TEXT; +-- Mangas table +ALTER TABLE mangas ADD COLUMN version INTEGER NOT NULL DEFAULT 0; +ALTER TABLE mangas ADD COLUMN is_syncing INTEGER NOT NULL DEFAULT 0; + +-- Chapters table +ALTER TABLE chapters ADD COLUMN version INTEGER NOT NULL DEFAULT 0; +ALTER TABLE chapters ADD COLUMN is_syncing INTEGER NOT NULL DEFAULT 0; + +-- Mangas triggers +DROP TRIGGER IF EXISTS update_manga_version; +CREATE TRIGGER update_manga_version AFTER UPDATE ON mangas +BEGIN + UPDATE mangas SET version = version + 1 + WHERE _id = new._id AND new.is_syncing = 0 AND ( + new.url != old.url OR + new.description != old.description OR + new.favorite != old.favorite + ); +END; + +-- Chapters triggers +DROP TRIGGER IF EXISTS update_chapter_and_manga_version; +CREATE TRIGGER update_chapter_and_manga_version AFTER UPDATE ON chapters +WHEN new.is_syncing = 0 AND ( + new.read != old.read OR + new.bookmark != old.bookmark OR + new.last_page_read != old.last_page_read +) +BEGIN + -- Update the chapter version + UPDATE chapters SET version = version + 1 + WHERE _id = new._id; + + -- Update the manga version + UPDATE mangas SET version = version + 1 + WHERE _id = new.manga_id AND (SELECT is_syncing FROM mangas WHERE _id = new.manga_id) = 0; +END; + +-- manga_categories table +DROP TRIGGER IF EXISTS insert_manga_category_update_version; +CREATE TRIGGER insert_manga_category_update_version AFTER INSERT ON mangas_categories +BEGIN + UPDATE mangas + SET version = version + 1 + WHERE _id = new.manga_id AND (SELECT is_syncing FROM mangas WHERE _id = new.manga_id) = 0; +END; diff --git a/domain/src/main/java/tachiyomi/domain/chapter/model/Chapter.kt b/domain/src/main/java/tachiyomi/domain/chapter/model/Chapter.kt index 3a4a8c4a45..f993e0256e 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/model/Chapter.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/model/Chapter.kt @@ -14,6 +14,7 @@ data class Chapter( val chapterNumber: Double, val scanlator: String?, val lastModifiedAt: Long, + val version: Long, ) { val isRecognizedNumber: Boolean get() = chapterNumber >= 0f @@ -43,6 +44,7 @@ data class Chapter( chapterNumber = -1.0, scanlator = null, lastModifiedAt = 0, + version = 1, ) } } diff --git a/domain/src/main/java/tachiyomi/domain/chapter/model/ChapterUpdate.kt b/domain/src/main/java/tachiyomi/domain/chapter/model/ChapterUpdate.kt index 33d1d4fba5..5a9193dc68 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/model/ChapterUpdate.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/model/ChapterUpdate.kt @@ -13,6 +13,7 @@ data class ChapterUpdate( val dateUpload: Long? = null, val chapterNumber: Double? = null, val scanlator: String? = null, + val version: Long? = null, ) fun Chapter.toChapterUpdate(): ChapterUpdate { @@ -29,5 +30,6 @@ fun Chapter.toChapterUpdate(): ChapterUpdate { dateUpload, chapterNumber, scanlator, + version, ) } diff --git a/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt b/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt index 8519ded996..4e1db57556 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt @@ -29,6 +29,7 @@ data class Manga( val initialized: Boolean, val lastModifiedAt: Long, val favoriteModifiedAt: Long?, + val version: Long, val notes: String?, ) : Serializable { @@ -123,6 +124,7 @@ data class Manga( initialized = false, lastModifiedAt = 0L, favoriteModifiedAt = null, + version = 0L, notes = null, ) } diff --git a/domain/src/main/java/tachiyomi/domain/manga/model/MangaUpdate.kt b/domain/src/main/java/tachiyomi/domain/manga/model/MangaUpdate.kt index a3d41cf0d5..67882d848e 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/model/MangaUpdate.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/model/MangaUpdate.kt @@ -23,6 +23,7 @@ data class MangaUpdate( val thumbnailUrl: String? = null, val updateStrategy: UpdateStrategy? = null, val initialized: Boolean? = null, + val version: Long? = null, val notes: String? = null, ) @@ -48,6 +49,7 @@ fun Manga.toMangaUpdate(): MangaUpdate { thumbnailUrl = thumbnailUrl, updateStrategy = updateStrategy, initialized = initialized, + version = version, notes = notes, ) } diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 25f71a3a7a..26e6db019b 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -1,5 +1,5 @@ [versions] -agp_version = "8.2.2" +agp_version = "8.3.0" lifecycle_version = "2.7.0" paging_version = "3.2.1" @@ -28,7 +28,7 @@ paging-compose = { module = "androidx.paging:paging-compose", version.ref = "pag benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.3" test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha03" test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha03" -test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0-beta01" +test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0" [bundles] lifecycle = ["lifecycle-common", "lifecycle-process", "lifecycle-runtimektx"] diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 778c43c478..54a808a29b 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,7 +1,7 @@ [versions] -compiler = "1.5.8" -compose-bom = "2024.01.00-alpha03" -accompanist = "0.34.0" +compiler = "1.5.10" +compose-bom = "2024.02.00-alpha02" +accompanist = "0.35.0-alpha" [libraries] activity = "androidx.activity:activity-compose:1.8.2" diff --git a/gradle/kotlinx.versions.toml b/gradle/kotlinx.versions.toml index cc75dd895f..0cdec4d597 100644 --- a/gradle/kotlinx.versions.toml +++ b/gradle/kotlinx.versions.toml @@ -1,6 +1,6 @@ [versions] kotlin_version = "1.9.22" -serialization_version = "1.6.2" +serialization_version = "1.6.3" xml_serialization_version = "0.86.3" [libraries] @@ -9,7 +9,7 @@ gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = " immutables = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version = "0.3.7" } -coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version = "1.7.3" } +coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version = "1.8.0" } coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core" } coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android" } coroutines-guava = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-guava" } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8d7446300e..d55c51452f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,13 +8,13 @@ shizuku_version = "12.2.0" sqldelight = "2.0.0" sqlite = "2.4.0" voyager = "1.0.0" -detekt = "1.23.1" -detektCompose = "0.3.11" +detekt = "1.23.5" +detektCompose = "0.3.12" [libraries] desugar = "com.android.tools:desugar_jdk_libs:2.0.4" android-shortcut-gradle = "com.github.zellius:android-shortcut-gradle-plugin:0.1.2" -google-services-gradle = "com.google.gms:google-services:4.4.0" +google-services-gradle = "com.google.gms:google-services:4.4.1" rxjava = "io.reactivex:rxjava:1.3.8" @@ -22,7 +22,7 @@ okhttp-core = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp_ve okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp_version" } okhttp-brotli = { module = "com.squareup.okhttp3:okhttp-brotli", version.ref = "okhttp_version" } okhttp-dnsoverhttps = { module = "com.squareup.okhttp3:okhttp-dnsoverhttps", version.ref = "okhttp_version" } -okio = "com.squareup.okio:okio:3.7.0" +okio = "com.squareup.okio:okio:3.8.0" conscrypt-android = "org.conscrypt:conscrypt-android:2.5.2" @@ -32,7 +32,7 @@ jsoup = "org.jsoup:jsoup:1.17.2" disklrucache = "com.jakewharton:disklrucache:2.0.2" unifile = "com.github.tachiyomiorg:unifile:7c257e1c64" -common-compress = "org.apache.commons:commons-compress:1.25.0" +common-compress = "org.apache.commons:commons-compress:1.26.0" junrar = "com.github.junrar:junrar:7.5.5" sqlite-framework = { module = "androidx.sqlite:sqlite-framework", version.ref = "sqlite" } @@ -43,13 +43,14 @@ preferencektx = "androidx.preference:preference-ktx:1.2.1" injekt-core = "com.github.inorichi.injekt:injekt-core:65b0440" -coil-bom = { module = "io.coil-kt:coil-bom", version = "2.5.0" } -coil-core = { module = "io.coil-kt:coil" } -coil-gif = { module = "io.coil-kt:coil-gif" } -coil-compose = { module = "io.coil-kt:coil-compose" } +coil-bom = { module = "io.coil-kt.coil3:coil-bom", version = "3.0.0-alpha06" } +coil-core = { module = "io.coil-kt.coil3:coil" } +coil-gif = { module = "io.coil-kt.coil3:coil-gif" } +coil-compose = { module = "io.coil-kt.coil3:coil-compose" } +coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp" } subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:7e57335" -image-decoder = "com.github.tachiyomiorg:image-decoder:fbd6601290" +image-decoder = "com.github.tachiyomiorg:image-decoder:398d3c074f" natural-comparator = "com.github.gpanther:java-nat-sort:natural-comparator-1.1" @@ -63,14 +64,14 @@ directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0" insetter = "dev.chrisbanes.insetter:insetter:0.6.1" compose-materialmotion = "io.github.fornewid:material-motion-compose-core:1.2.0" -swipe = "me.saket.swipe:swipe:1.2.0" +swipe = "me.saket.swipe:swipe:1.3.0" moko-core = { module = "dev.icerock.moko:resources", version.ref = "moko" } moko-gradle = { module = "dev.icerock.moko:resources-generator", version.ref = "moko" } logcat = "com.squareup.logcat:logcat:0.1" -firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.5.0" +firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.5.1" aboutLibraries-gradle = { module = "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin", version.ref = "aboutlib_version" } aboutLibraries-compose = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "aboutlib_version" } @@ -87,9 +88,9 @@ sqldelight-android-paging = { module = "app.cash.sqldelight:androidx-paging3-ext sqldelight-dialects-sql = { module = "app.cash.sqldelight:sqlite-3-38-dialect", version.ref = "sqldelight" } sqldelight-gradle = { module = "app.cash.sqldelight:gradle-plugin", version.ref = "sqldelight" } -junit = "org.junit.jupiter:junit-jupiter:5.10.1" +junit = "org.junit.jupiter:junit-jupiter:5.10.2" kotest-assertions = "io.kotest:kotest-assertions-core:5.8.0" -mockk = "io.mockk:mockk:1.13.9" +mockk = "io.mockk:mockk:1.13.10" voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" } voyager-screenmodel = { module = "cafe.adriel.voyager:voyager-screenmodel", version.ref = "voyager" } @@ -105,9 +106,9 @@ archive = ["common-compress", "junrar"] okhttp = ["okhttp-core", "okhttp-logging", "okhttp-brotli", "okhttp-dnsoverhttps"] js-engine = ["quickjs-android"] sqlite = ["sqlite-framework", "sqlite-ktx", "sqlite-android"] -coil = ["coil-core", "coil-gif", "coil-compose"] +coil = ["coil-core", "coil-gif", "coil-compose", "coil-network-okhttp"] shizuku = ["shizuku-api", "shizuku-provider"] sqldelight = ["sqldelight-android-driver", "sqldelight-coroutines", "sqldelight-android-paging"] voyager = ["voyager-navigator", "voyager-screenmodel", "voyager-tab-navigator", "voyager-transitions"] richtext = ["richtext-commonmark", "richtext-m3"] -test = ["junit", "kotest-assertions", "mockk"] \ No newline at end of file +test = ["junit", "kotest-assertions", "mockk"] diff --git a/i18n/src/commonMain/resources/MR/base/plurals.xml b/i18n/src/commonMain/resources/MR/base/plurals.xml index 2f10b004c7..d9c958afc5 100644 --- a/i18n/src/commonMain/resources/MR/base/plurals.xml +++ b/i18n/src/commonMain/resources/MR/base/plurals.xml @@ -10,6 +10,11 @@ %1$d days ago + + Tomorrow + In %1$d days + + %d category %d categories diff --git a/i18n/src/commonMain/resources/MR/base/strings.xml b/i18n/src/commonMain/resources/MR/base/strings.xml index cead4c3465..9c3abb8535 100644 --- a/i18n/src/commonMain/resources/MR/base/strings.xml +++ b/i18n/src/commonMain/resources/MR/base/strings.xml @@ -456,6 +456,7 @@ High Low Lowest + Disable zoom out Delete chapters diff --git a/i18n/src/commonMain/resources/MR/cv/plurals.xml b/i18n/src/commonMain/resources/MR/cv/plurals.xml index b96d0872c5..21a2490606 100644 --- a/i18n/src/commonMain/resources/MR/cv/plurals.xml +++ b/i18n/src/commonMain/resources/MR/cv/plurals.xml @@ -5,28 +5,28 @@ %d пухмӑш - 1 минут хыҫҫӑн + %1$s минут хыҫҫӑн %1$s минут хыҫҫӑн - %1$s,%2$s йӑнӑшпа тӑвӑннӑ - %1$s, %2$s йӑнӑшпа тӑвӑннӑ + %1$s хушши %2$s йӑнӑшпа тӑвӑннӑ + %1$s хушши %2$s йӑнӑшпа тӑвӑннӑ Хушма валли ҫӗнетӳ пур %d хушма валли ҫӗнетӳ пур - %1$s сыпӑкӗсем - %1$s сыпӑкӗсем тата ытти %2$d + %1$s сыпӑкӗсем тата тепӗр 1 + %1$s сыпӑкӗсем тата тепӗр %2$d - 1 ҫӗнӗ сыпӑк + %1$d ҫӗнӗ сыпӑк %1$d ҫӗнӗ сыпӑк - Ҫӗнӗ сыпӑксем 1 хайлав валли тупӑннӑ - Ҫӗнӗ сыпӑксем %d хайлав валли тупӑннӑ + %d хайлав валли + %d хайлав валли %1$s сыпӑк @@ -37,8 +37,8 @@ %1$s йулчӗ - 1 сӑнану - %d сӑнану + %d йӗрлев + %d йӗрлев %d сыпӑк ҫук @@ -64,4 +64,8 @@ Тепӗр сыпӑк Тепӗр %d сыпӑк + + %d усрав + %d усрав + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/cv/strings.xml b/i18n/src/commonMain/resources/MR/cv/strings.xml index d10ebf33d4..617a009ed5 100644 --- a/i18n/src/commonMain/resources/MR/cv/strings.xml +++ b/i18n/src/commonMain/resources/MR/cv/strings.xml @@ -1,6 +1,6 @@ - Вулавӑшри серилӗхсем + Вулавӑшри хайлавсем Усӑ курнӑ: %1$s Куккисем катертнӗ Кукки тасат @@ -49,8 +49,8 @@ Ыкран сыхлавӗ Сыхлав тата вӑрттӑнлӑх Систерӳсене ӗнер - Ҫул-кун хармачӗ - Ҫутнӑ + Вӑхӑт хармачӗ + Тӗттӗм Сӳнтернӗ Тата Тийевсем @@ -72,8 +72,8 @@ Пӑрахӑҫла Ҫаклат Сӳнтер - Тийене сыпӑксем шучӗ - Йат-йыш + Тийенӗ сыпӑксен шучӗ + Йат йышӗ Кӑтарт Кӑтарту тытӑмӗ WebView-ра уҫ @@ -83,12 +83,12 @@ Катерт Ҫӗнӗрен Чар - Сыпӑксем пӑх - Хуплашка улӑштар - Пухмӑша хуш + Сыпӑксене пӑх + Хуплашкана улӑштар + Пухмӑша кӗрт Пухмӑш йатне улӑштар Пухмӑшсене улӑштар - Пухмӑш хуш + Пухмӑша хуш Хуш Умӗнхине вуланӑ пек паллӑ ту Улӑштар @@ -198,7 +198,7 @@ Шанчӑклӑ мар хушма Ним туман чух ҫаклатни %1$s-мӗш сыпӑк - Ҫаклатӑва уҫма пӳрне йӗрӗ ыйтни + Ҫаклатӑва уҫма пӳрне йӗрре ыйтни Аяккинчи чаку Сыпӑксем урлӑ каҫнине яланах кӑтарт Тӑрӑлӑх мар @@ -400,7 +400,7 @@ Янтӑв ту v%1$s верссиччен ҫӗнетнӗ Мӗн ҫӗнни - Темӑ + Темӗ Хушни вӑхӑчӗпе Тиск ҫинче вырӑн ҫитмен пирки сыпӑксем тийенеймерӗҫ \"%1$s\" пур ҫӗрте шыра @@ -436,7 +436,7 @@ NSFW (18+) ҫӑл куҫӗсем Файлсене суйламалли хушӑм тупӑнман Тархасшӑн MAL-а ҫӗнӗрен кӗр - Ҫӑл куҫсен тата хушмасен йат-йышӗнче кӑтартни + Ҫӑл куҫсен тата хушмасен йат йышӗнче кӑтартни Вулама вӗҫленӗ вӑхӑчӗ Вулама пуҫланӑ вӑхӑчӗ Хӗрри @@ -477,31 +477,31 @@ Йӑнӑшсене кӑтарт Ҫак серилӗх валли пурне те пӑрахӑҫла Хӑй-хальлӗн тийесе илни, малтанах тийени - Йулашки ҫӗнетӳ тӗрӗсленипе + Йулашки хут ҫӗнетӳ пуррине тӗрӗсленипе Йулнӑ сыпӑксемпе Ҫырав шучӗпе Пурне те катерт - «%s» пухмӑш катертесшӗнех-и\? - Пухмӑш катерт + «%s» пухмӑша катертесшӗнех-и? + Пухмӑша катерт Вырӑнти ҫӑл куҫран Асӑрхаттару Йатсӑр сетке - Серилӗхе пуҫламӑша куҫар + Хайлава пуҫламӑша куҫар Улшӑнӑва ҫирӗплетме есӗлӗхе ҫирӗплет - Ҫырава кӑтарт + Хайлава кӑтарт Чӗлхе Шыра… Хуп Тийеве халех пуҫла - InternalError: Хушма пӗлӗме пӑхма тӑвӑм-пулӑм кӗнекине пӑх + InternalError: Хушма хыпар-пӗлӳ пӑхма тӑвӑм-пулӑм кӗнекине пӑх Кӑтартӑну Ҫутнӑ Сӳнтернӗ - Темӑ, кун тата вӑхӑт хармачӗ + Темӗ, кун тата вӑхӑт хармачӗ Пухмӑшсем, пӗтӗмӗшле ҫӗнетӳ, сыпӑксене туртни Вулав тытӑмӗ, кӑтартӑнни, куҫӑм Йаланхилле - Ӑнсӑрт ҫырав уҫ + Ӑнсӑрт ҫырава уҫ Ҫӗр ҫырли тайккирийӗ Ҫур ҫӗр ӗнтрӗкӗ Пӗр йенлӗ ӳсӗм килӗштерӗвӗ, анлӑлатнӑ килӗштерӳ @@ -524,7 +524,7 @@ Чараксӑр тетел урлӑ ҫеҫ Серилӗхе пуҫламан Такку - Хуп-хура темӑ + Хуп-хура темӗ Ап чӗлхи Халь мар Кашни 3 кун @@ -533,8 +533,8 @@ Шутлавсем Пайлашу асне ӑт Хӑй тӗллӗн тата хӑй-хальлӗн йантӑлав - Пухмӑш ҫӗнет - Йӑнӑш кӗнекине тийесе йани, петтерей лайӑхлатни + Пухмӑша ҫӗнет + Йӑнӑш кӗнекине тийесе йани, петтерейе лайӑхлатни Вуламан сыпӑксен шутне «Ҫӗнӗлӗх» ыккун ҫинче кӑтартни Сӑна тӑрӑх пысӑклатни Нумай чӗлхеллӗ @@ -568,6 +568,37 @@ Shizuku-н хушма ларткӑча усӑ курма Shizuku ларт тата ҫут. Йурӗ %s уҫ - Серилӗхе вӗҫе куҫар + Хайлава вӗҫе куҫар Кӗртнӗ пухмӑшсенче пулнӑ пулсан та кӑларса пӑрахнӑ пухмӑшсенче пулнӑ серилӗхсем ҫӗнелмӗҫ. + Суйланӑ + Суйламан + Ҫӳлелле куҫ + Куҫаруҫӑ + Пӗлӗмсем тата усрав + Йӗрлевҫӗн хаклавӗ + Пултмӑшсене сас паллисен йӗркипе аласшӑн-им? + Кӳр + Йаланхилле тавӑр + Пуҫлама пӗлкӗч + Килӗрех! + Айтӑр темиҫе йапала ӗнерӗпӗр. Есӗ вӗсене йаланах кайран ӗнерӳсенче улӑштарма пултаратӑн. + Малалла + Пуҫла + Ирттер + Папка суйла + Папка суйламалла + Усрав пӗлкӗчӗ + Апсене лартма ирӗк + Ҫӑл куҫсен хушмисене лартма ирӗк парӗ. + Систерӳсем килме ирӗк + Вулавӑшри ҫӗнетӳсем пурри пирки тата ытти пирки пӗлесси. + Петтерейе хыҫра усӑ курма ирӗк + Тӑсӑлакан вулавӑш ҫӗнелни, тийени тата йантӑ ӑтава тавӑрни чарӑнасран хӑтӑлтарӗ. + Ирӗк пар + %s-ра ҫӗнни? Епӗр пуҫлав пӗлкӗчӗпе паллашма сӗнетпӗр. + Ҫур ҫӗр + Путмӑшсене ала + Кивӗ верссирен ҫӗнӗлетӗн те мӗн суламаллине пӗлместӗн? Нумайрах пӗлме усрав пӗлкӗчне кӗрсе пӑх. + %s ҫӗнӗрен лартатӑн? + «%1$s» вырӑнне «%2$s» \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/de/strings.xml b/i18n/src/commonMain/resources/MR/de/strings.xml index a402087fb0..e038287cdf 100644 --- a/i18n/src/commonMain/resources/MR/de/strings.xml +++ b/i18n/src/commonMain/resources/MR/de/strings.xml @@ -798,4 +798,5 @@ Open-Source-Repository Bald Zertifikatsbestätigungen zurücknehmen + Rauszoomen deaktivieren \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/eo/plurals.xml b/i18n/src/commonMain/resources/MR/eo/plurals.xml index 1daf79fc55..90b28e9259 100644 --- a/i18n/src/commonMain/resources/MR/eo/plurals.xml +++ b/i18n/src/commonMain/resources/MR/eo/plurals.xml @@ -1,7 +1,7 @@ - Post 1 minuto + Post %1$s minuto Post %1$s minutoj @@ -17,7 +17,7 @@ Ĉapitroj %1$s kaj %2$d pli - 1 nova ĉapitro + %1$d nova ĉapitro %1$d novaj ĉapitroj @@ -25,8 +25,8 @@ Por %d titoloj - Mankas 1 ĉapitron - Mankas %d ĉapitrojn + Preterpasas %d ĉapitron, aŭ ĝi mankas ĉe la fonto aŭ ĝi estis elfiltrita + Preterpasas %d ĉapitrojn, aŭ ili mankas ĉe la fonto aŭ ili estis elfiltritaj 1 ŝanĝspurilo @@ -60,4 +60,12 @@ Sekva nelegita ĉapitro Sekvaj %d nelegitaj ĉapitroj + + %d deponejo + %d deponejoj + + + Disponebla ĝisdatigo de etendaĵo + Disponeblaj ĝisdatigoj de %d etendaĵoj + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/eo/strings.xml b/i18n/src/commonMain/resources/MR/eo/strings.xml index 657166ba73..991249e0e8 100644 --- a/i18n/src/commonMain/resources/MR/eo/strings.xml +++ b/i18n/src/commonMain/resources/MR/eo/strings.xml @@ -481,4 +481,40 @@ Forigi ĉiojn Bonvenon! Preterpasi + Elektitaj + Ne elektitaj + Pli da opcioj + Forigi elŝutitaĵn + Datumoj kaj konservejo + Ĝisdatigi kategorion + Sekva + Dosierujo devas esti elektita + Eloso, dato & tempoformo + Apa ŝlosado, sekura ekrano + Malŝlosi %s + Malŝaltita + Tuta eraro: + Agordoj de fonto + Apa agordoj + Norda + Cunami + \"%1$s\" anstataŭ \"%2$s\" + Apa lingvo + Ĝisdatigi ĉiujn + Versio + Lingvo + Instalilo + Malmoderna + Libera: %1$s / Tuta: %2$s + Popularaj + Havantaj rezultoj + Nekonata titolo + Sen priskribo + Baldaŭ + Ĉu vi certas? + Ĵus + Neniam + %1$s eraro: %2$s + Neniu fonto trovita + Ups! \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/es/plurals.xml b/i18n/src/commonMain/resources/MR/es/plurals.xml index 31c76cb44c..08f01feea4 100644 --- a/i18n/src/commonMain/resources/MR/es/plurals.xml +++ b/i18n/src/commonMain/resources/MR/es/plurals.xml @@ -85,4 +85,9 @@ %d repositorios %d repositorios + + Mañana + Dentro de %1$d días + Dentro de %1$d días + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/es/strings.xml b/i18n/src/commonMain/resources/MR/es/strings.xml index 9a91634037..0aedfb7b40 100644 --- a/i18n/src/commonMain/resources/MR/es/strings.xml +++ b/i18n/src/commonMain/resources/MR/es/strings.xml @@ -214,7 +214,7 @@ ¿Quieres borrar los capítulos descargados? Pausado Seguimiento - Ya existe una categoría con este nombre + ¡Ya existe una categoría con este nombre! Categorías eliminadas ¿Añadir manga a la biblioteca\? Imagen guardada @@ -506,7 +506,7 @@ El formato del capítulo no es correcto No se ha encontrado el capítulo Desactivar el modo incógnito - Ofrece funciones mejoradas para ciertas fuentes. Se hace un seguimiento automático de los elementos al añadirlos a la biblioteca. + Ofrecen funciones mejoradas para ciertas fuentes. Se hace un seguimiento automático de los elementos al añadirlos a la biblioteca. Servicios de seguimiento mejorados Guía de seguimiento Automático @@ -669,7 +669,7 @@ %dh %dm En uso - ¿? + N/D En servicios de seguimiento Descargados Estadísticas @@ -681,7 +681,7 @@ Ver número de capítulos por leer en el icono de actualizaciones Se ha copiado al portapapeles Saltarse los capítulos repetidos - Está disponible, pero la fuente todavía no se ha instalado: %s + Están disponibles, pero las fuentes todavía no se han instalado: %s Ya tienes un elemento en la biblioteca con este mismo nombre. \n \n¿Seguro que quieres continuar\? @@ -742,7 +742,7 @@ Almacenamiento utilizado Puntuación del rastreador Aplicar - Volver a la configuración predeterminada + Restablecer vista Crear No se ha encontrado ningún equipo de traducción Equipo de traducción @@ -758,15 +758,15 @@ ¿Es la primera vez que instalas %s? Te recomendamos leer la guía de introducción. Comenzar Tienes que elegir una carpeta - Te damos la bienvenida + ¡Bienvenid@s! ¿No es la primera vez que instalas %s? Saltar Siguiente Lo primero de todo es dejar las cosas a tu gusto. Siempre puedes volver a cambiarlas más tarde en los ajustes. Todavía no has proporcionado ninguna carpeta - Selecciona una carpeta donde %1$s guardará los capítulos descargados, las copias de seguridad y otras cosas. + Seleccione una carpeta donde %1$s almacenará las descargas de capítulos, copias de seguridad, etc. \n -\nTe recomendamos que sea solo para %1$s. +\nSe recomienda una carpeta dedicada. \n \nCarpeta seleccionada: %2$s Permiso para instalar aplicaciones @@ -780,9 +780,9 @@ Toca aquí para conceder los permisos necesarios para instalar extensiones. Incluir datos privados, como las claves de inicio de sesión en plataformas de seguimiento Descripción completa del problema: - Se espera que se publiquen nuevos capítulos en torno a %1$s, el ciclo aproximado de comprobación entre números es de %2$s. + Se espera que el siguiente número salga en aproximadamente %1$s, la aplicación busca actualizaciones cada %2$s. Frecuencia de actualización personalizada: - El repositorio ya existe + ¡Este repositorio ya existe! Actualizaciones inteligentes La dirección URL del repositorio no parece ser correcta Añade más repositorios a Mihon; la dirección URL tiene que terminar en «index.min.json». @@ -798,4 +798,5 @@ Pronto Dejar de marcar todas la extensiones desconocidas como de confianza Abrir repositorio + Desactivar alejar \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/fil/plurals.xml b/i18n/src/commonMain/resources/MR/fil/plurals.xml index 04755d1256..4d10091a13 100644 --- a/i18n/src/commonMain/resources/MR/fil/plurals.xml +++ b/i18n/src/commonMain/resources/MR/fil/plurals.xml @@ -68,4 +68,8 @@ %d na repo %d na mga repo - + + Bukas + Sa loob ng %1$d araw + + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/fil/strings.xml b/i18n/src/commonMain/resources/MR/fil/strings.xml index b26aa0a818..8d1d80efab 100644 --- a/i18n/src/commonMain/resources/MR/fil/strings.xml +++ b/i18n/src/commonMain/resources/MR/fil/strings.xml @@ -184,8 +184,8 @@ \nSa pamamagitan ng pagtitiwala sa extension na ito, tinatanggap mo ang mga panganib na ito. Di-pinagkakatiwalaang extension I-uninstall - Tiwala - Hindi pinagkakatiwalaan + Itiwala + Di pinagkakatiwalaan Naka-install Ini-install Dina-download @@ -798,4 +798,5 @@ Malapit na Bawiin ang mga pinagkakatiwalaang hindi kilalang extension Open source na repo - + Patayin ang pag-zoom out + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/fr/plurals.xml b/i18n/src/commonMain/resources/MR/fr/plurals.xml index 9fc87129c0..21e4e5eab7 100644 --- a/i18n/src/commonMain/resources/MR/fr/plurals.xml +++ b/i18n/src/commonMain/resources/MR/fr/plurals.xml @@ -62,8 +62,8 @@ Chapitre suivant non lu - Les %d suivants non lus - Les %d suivants non lus + Les %d chapitres suivants non lus + Les %d chapitres suivants non lus Chapitre suivant diff --git a/i18n/src/commonMain/resources/MR/hr/plurals.xml b/i18n/src/commonMain/resources/MR/hr/plurals.xml index a404dab795..1bddc175f3 100644 --- a/i18n/src/commonMain/resources/MR/hr/plurals.xml +++ b/i18n/src/commonMain/resources/MR/hr/plurals.xml @@ -85,4 +85,9 @@ %d repozitorija %d repozitorija + + Sutra + Za %1$d dana + Za %1$d dana + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/hr/strings.xml b/i18n/src/commonMain/resources/MR/hr/strings.xml index 657095031e..b9ec5c2374 100644 --- a/i18n/src/commonMain/resources/MR/hr/strings.xml +++ b/i18n/src/commonMain/resources/MR/hr/strings.xml @@ -511,7 +511,7 @@ Invertirano Danas Potpuno crna tamna tema - Strawberry Daiquiri + Daiquiri jagoda Izgled Vodič za pokretanje Ovjeri za potvrditi promjenu @@ -798,4 +798,5 @@ Predviđa se da će nova poglavlja biti objavljena za oko %1$s, provjera svakih %2$s. Uskoro Dostupno: %1$s / Ukupno: %2$s + Deaktiviraj smanjivanje zumiranja \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/hu/plurals.xml b/i18n/src/commonMain/resources/MR/hu/plurals.xml index 535bbf3a16..ee1f7b9a8c 100644 --- a/i18n/src/commonMain/resources/MR/hu/plurals.xml +++ b/i18n/src/commonMain/resources/MR/hu/plurals.xml @@ -5,15 +5,15 @@ %d bővítményfrissítés érhető el - 1 perc után - %1$s percek után + %1$s perc múlva + %1$s perc múlva %d kategória - %d kategóriák + %d kategória - 1 fejezet + %1$s fejezet %1$s fejezet @@ -25,12 +25,12 @@ %1$d napja - %d tracker - %d trackerek + %d nyilvántartó + %d nyilvántartó - %d-nak/nek - %d-nak/nek + %d elemnek + %d elemeknek %d fejezet kihagyása, hiányzik a forrás, vagy ki lett szűrve @@ -54,14 +54,22 @@ Következő fejezet - Következő %d fejezetek + Következő %d fejezet - Hiányzó %1$s fejezet - Hiányzó %1$s fejezetek + %1$s fejezet hiányzik + %1$s fejezet hiányzik 1 nap %d nap + + Holnap + %1$d nap múlva + + + %d repo + %d repok + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/hu/strings.xml b/i18n/src/commonMain/resources/MR/hu/strings.xml index 3a9eff4151..8366439ff3 100644 --- a/i18n/src/commonMain/resources/MR/hu/strings.xml +++ b/i18n/src/commonMain/resources/MR/hu/strings.xml @@ -4,7 +4,7 @@ Kategóriák Könyvtár bejegyzések Fejezetek - Követés + Nyilvántartás Előzmények Beállítások Előzmények @@ -40,7 +40,7 @@ Összes Frissítések korlátozása Töltés közben - Csak a jelenleg is folyamatban lévőket frissítse + \"Befejezett\" állapotú elemek kihagyása Alapértelmezett kategória Mindig kérdezzen rá Teljes képernyő @@ -76,8 +76,8 @@ Olvasatlannak jelölés Előző olvasottnak jelölése Letöltés - A fejezet könyvjelzőzése - A fejezet eltávolítása a könyvjelzők közül + Fejezet könyvjelzőzése + Fejezet eltávolítása a könyvjelzők közül Törlés Könyvtár frissítése Szerkesztés @@ -130,8 +130,8 @@ Több Források Dátum formátum - Be - Ki + Sötét + Világos Könyvtár Frissít Előre @@ -175,7 +175,7 @@ Mindig Biztonság Értesítések kezelése - Rendszerbeállítás követése + Rendszer Téma Ugrás legalulra Ugrás legfelülre @@ -266,16 +266,14 @@ Felhasználónév Sütik törölve A változtatások érvénybe lépéséhez újra kell indítania az applikációt - Érvénytelen backup fájl + Érvénytelen biztonsági mentési fájl: Él Könyvtár frissítése Nem található fejezet Licenszelt - Ezt a kiterjesztést nem megbízható tanúsítvánnyal írták alá, és nem lett aktiválva. + Egy rosszindulatú bővítmény hozzáférhet a tárolt bejelentkezési adatokhoz, vagy tetszőleges kódot futtathat. \n -\nEgy rosszindulatú kiterjesztés elolvashatja a Mihonban tárolt bejelentkezési adatokat, vagy tetszőleges kódot futtathat. -\n -\nAz ebben a tanúsítványban való megbízással, elfogadja ezeket a kockázatokat. +\nHa megbízik ebben a bővítményben, elfogadja ezeket a kockázatokat. Bizalom Tiltott: %s Kedvelt: %s @@ -324,16 +322,16 @@ Kinézet Letöltés elkezdése most Helyi forrás - Követve + Nyilvántartva Hitelesítsd magad a változtatás elfogadásához Alapértelmezett Szolgáltatások - Útmutató a követéshez + Nyilvántartási útmutató Új fejezetek letöltése Automatikus letöltés Hátulról az ötödik fejezet Hátulról a negyedik befejezett fejezet - Utolsó előtti előtti befejezett fejezet + Harmadikként olvasott fejezet Utolsó előtti befejezett fejezet Kikapcsolva Tiltott kategóriák @@ -407,7 +405,7 @@ Figyelmeztetés A Shizuku nem fut Régi - Egyirányú szinkronizálás, hogy a fejezetben való előrehaladás frissítve legyen a követési szolgáltatásokban. Állíts be követést egyéni bejegyzésekre, a követési gomb lenyomásával. + Egyirányú szinkronizálás, hogy a fejezetben való előrehaladás frissítve legyen a követési szolgáltatásokban. Állíts be követést egyéni elemekre, a követés gomb lenyomásával. 3 naponta Csak Wi-Fi-n keresztül Ajánlott az automatikus biztonsági mentése. Ezen kívül máshol is legyenek másolatok. @@ -428,10 +426,10 @@ Globális keresés… Feltöltési dátum szerint Ez eltávolítja a fejezet olvasási dátumát. Biztos benne\? - Csak a befejezett fejezeteket() + Olvasatlan fejezet(ek)et tartalmazó elemek kihagyása Törölni akarja a %s kategóriát \? InternalError: Nézze meg a hibaüzenetet további információért - Csak nem-lemért hálózatokon + Csak korlátlan hálózatokon Mit tartalmazzon a biztonsági mentés\? Biztonsági mentés/helyreállítás nem biztos,hogy működik ha a MIUI Optimalizáció ki van kapcsolva. Helyreállítás folyamatban van @@ -457,16 +455,14 @@ Automatikusan nagyítsa a széles képeket Helyreállítás befejeződött %02d perc, %02d másodperc - Adatok visszaállítása a biztonsági mentésből. -\n -\nEzek után telepíteni kell a hiányzó bővítményeket és be kell jelentkezni a tracking szolgáltatásokba, hogy újra használhassa őket. + Előfordulhat, hogy telepítenie kell a hiányzó bővítményeket, majd később be kell jelentkeznie a nyilvántartási szolgáltatásokba a használatukhoz. Könyvtár fedők frissítése Hibaüzenetetek törlése Sorozat beállításainak visszaállítása Néhány gyártónak extra korlátozása van arra, hogy kikapcsolja a háttér folyamatokat. Ezen a web oldalon több információt találsz, hogy hogyan oldható meg. Olvasási előzmények megállítása Fedlap - Tartva + Szüneteltetve Kategóriák törölve Válasszon képet a fedlapnak Nem található új frissítés @@ -479,7 +475,7 @@ A forrás nem található Utolsó frissítés megtekintése Olvasatlanok száma - Bejegyzés mutatása + Elem mutatása Kizárólag fedlapok mutatása Hiányzó források: DNS a HTTPS mellett (DoH) @@ -502,14 +498,14 @@ Fej. %1$s - %2$s Kezdési útmutató Adatbázis törlése - Még nem lett elindítva + El nem kezdett elemek kihagyása Új fejezet Biztonsági mentés már folyamatban van Nincs telepítve Figyelmeztetés: a tömeges letöltések a források lelassulásához és/vagy a Mihon leállásához vezethetnek. Koppintson további információért. Koppintson további információért Nem sikerült a fedlap frissítése - Először adja a mangát a könyvtárhoz + Először adja az elemet a könyvtárhoz Átugorva, mivel tartalmaz olvasatlan fejezeteket Átugorva, mivel nincs olvasott fejezet Új hivatalos verzió érhető el. Koppintson további információért, hogy hogyan importáljon nem hivatalos F-Droid verziókról. @@ -530,7 +526,7 @@ Befejezett Nincs leírás Letöltés (%1$d/%2$d) - Tracking hozzáadása + Felvétel nyilvántartáshoz Elkezdett Válaszon beletartozó adatokat Fejezet beállításai frissültek @@ -544,11 +540,11 @@ Olvasó mód Befejezve: Jelenlegi: - Hát ez kínos... + Hát, ez kínos A nagy frissítések kárt okoznak a forrásoknak, és lassabb frissítésekhez, valamint megnövekedett akkumulátorhasználathoz vezethetnek. Koppintson további információért. %1$d frissítés sikertelen - Növeli a teljesítményt a nagy képek elválasztásával. - Nem bejelentkezett trackerek: + Javítja az olvasó teljesítményét + Nem bejelentkezett nyilvántartó: Inkognitó mód Inkognitó mód kikapcsolása Szűri a könyvtár összes tartalmát @@ -567,9 +563,9 @@ A forrás nem támogatott Nincs egyezés Ilyen nevű kategória már létezik! - Mangában lévő összes fejezet visszaállítása + Összes fejezet visszaállítása ebben az elemben Hiba a kép mentése közben - Tartva lista + Szüneteltettek listája Egyéni fedlap Következő: Előző: @@ -577,13 +573,13 @@ Nincs előző fejezet %1$s oldalt nem lehetett betölteni Oldalak betöltése… - Követ + Nyilvántart Nem sikerült a kiterjesztési lista megnyitása Kikapcsolva - Szolgáltatások, melyek fejlett funkciókat kínálnak. A bejegyzés automatikusan könyvtárhoz lesz adva és frissítve lesz. + Szolgáltatások, amelyek fejlett funkciókat kínálnak. Az elemek automatikusan könyvtárhoz lesznek adva és frissítve lesznek. Biztonsági mentés készítése Felhasználható az aktuális könyvtár visszaállítására - Biztonsági mentés visszaállítása + Biztonsági mentésből visszaállítás Könyvtár visszaállítása biztonsági mentésről Biztonsági mentések gyakorisága Biztonsági mentés helyreállítása @@ -597,7 +593,7 @@ Segít háttérbeli könyvtár frissítésben és a biztonsági mentésekben Akkumulátor optimalizálás már ki van kapcsolva Bő feljegyzés - Bő feljegyzések rendszer feljegyzésbe való kiírása (csökkenti az app teljesítményét) + Részletes naplók irása a rendszernaplóba (csökkenti az alkalmazás teljesítményét) Újdonságok Segíts fordítani Adatvédelmi irányelvek @@ -632,9 +628,9 @@ Összes Olvasott Átlagos értékelés - Véletlenszerű manga megnyitása + Véletlenszerű elem megnyitása Könyvtár utoljára frissítve: %s - Követők + Nyilvántartók Az olvasatlanok számának megjelenítése a Frissítések ikonon Olvasás folytatása gomb Kategóriák, globális frissítés, fejezet csúsztatás @@ -654,10 +650,10 @@ Másolás a vágólapra Kategória frissítése Vágólapra másolva - Van már ilyen nevű ... a könyvtáradban. + Van már egy ilyen nevű elem a könyvtáradban. \n -\nBiztos folytatni akarod\? - Ez eltávolítja az eddig kiválasztott kezdeti dátumot a/az %s +\nBiztos folytatni akarod? + Ez eltávolítja az eddig kiválasztott kezdeti dátumot a(z) %s szolgáltatásból F-Droid csomagok nem hivatalosan támogatottak. \nKattincs további információért. Befejezett olvasmányok @@ -665,10 +661,10 @@ Elérhető de a forrás nincs telepítve: %s Tárhely engedély nincs megadva Használt - %1$s error: %2$s + %1$s hiba: %2$s Biztos vagy benne\? Kategória üres - Ez eltávolítja az eddig kiválasztott befejezési dátumot a/az %s + Ez eltávolítja az eddig kiválasztott befejezési dátumot a(z) %s szolgáltatásból Olvasási idő Nem található bejegyzés ebben a kategóriában A/az \"%s\"-t elfogod távolítani a könyvtáradból @@ -686,15 +682,15 @@ Balra csúsztatási cselekmény Duplikált fejezetek átugrása Időköz állítása - Egyedi lekérési időköz + Egyedi frissítési időköz Következő várt frissítés Dupla koppintás a nagyításhoz Már könyvtári bejegyzések elrejtése - Követéshez bejelentkezés + Bejelentkezés nyilvántartóhoz Letöltött törlése Magas képek szétvágása %d soronként - Kivül az elvárt kijönési időszakon + Következő kiadás idejének megjóslása Széles oldalak forgatása az illeszkedéshez Jobbra csúsztatási cselekmény Forgatott széles oldalak megfordítása @@ -740,4 +736,67 @@ \"%1$s\", ahelyett hogy \"%2$s\" %s nem elérhető Szkennelő-fordítók tiltása + Alkalmazás + Bevezetési útmutató + Üdvözöljük! + Következő + Kezdjünk hozzá + Kihagyás + Válasszon ki egy mappát, ahol a(z) %1$s a fejezetek letöltéseit, biztonsági mentéseket és egyebeket tárolja. +\n +\nAjánlott egy külön mappa használata. +\n +\nKiválasztott mappa: %2$s + Válasszon ki egy mappát + Ki kell választani egy mappát + Régebbi verzióról frissít, és nem tudja, mit válasszon? További információkért olvassa el a tárolási útmutatót. + Tárolási útmutató + Alkalmazások telepítésének engedélyezése + A forrásbővítmények telepítéséhez. + Értesítési engedély + Értesüljön a könyvtári frissítésekről és egyéb információkról. + Háttérben lévő akkumulátor-használat + Kerülje el a hosszan tartó könyvtárfrissítések, letöltések és biztonsági mentések helyreállításának megszakítását. + Engedélyezés + Új a(z) %s az ön számára? Javasoljuk, hogy olvassa el a Kezdő útmutatót. + Újratelepíti a(z) %s? + Intelligens frissítés + A bővítmények telepítéséhez engedélyek szükségesek. Az engedélyezéshez koppintson ide. + Megbízható ismeretlen kiterjesztések visszavonása + Bővítmény tárolók + Nincsenek beállítva tárolók. + Tároló felvétele + Tároló URL + További tárolók hozzáadása a Mihonhoz. Ennek az URL-nek \"index.min.json\"-ra végződő URL-nek kell lennie. + Ez a tároló már létezik! + Tároló törlése + Érvénytelen tároló URL + Szeretné törölni a(z) \"%s\" tárolót? + Nyílt forráskódú tároló + Nincs tárolási hely beállítva + Létrehoz + Teljes hiba: + Kezdetnek állítsunk be néhány dolgot. Ezeket később bármikor módosíthatja a beállításokban. + Felület + Licencelt – Nincs megjeleníthető fejezet + Törlés a(z) %s szolgáltatásból + Ezzel leállítja a helyi nyilvántartást. + N/A + %d perc + %d mp. + %d nap + %d óra + Nyilvántartási pont + Nord + Érzékeny beállítások megadása (Pl. szolgáltatások bejelentkezési tokenei) + Letöltési index érvénytelenítése + Új fejezetek megjelenése %1$s múlva várható, ellenőrzési időköz: %2$s. + Leállítja a(z) %s nyilvántartását? + Nyilvántartott elemek + Kicsinyítés letiltása + Elérhető: %1$s / Összesen: %2$s + Letöltési index érvénytelenítve + Mind becslése + Hamarosan + Egyedi frissítési időköz: \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/in/plurals.xml b/i18n/src/commonMain/resources/MR/in/plurals.xml index ff54281380..cb31ac13d8 100644 --- a/i18n/src/commonMain/resources/MR/in/plurals.xml +++ b/i18n/src/commonMain/resources/MR/in/plurals.xml @@ -43,7 +43,7 @@ berikutnya %d chapter - Bab %1$s yang hilang + %1$s bab hilang %d hari @@ -51,4 +51,7 @@ %d repo + + Dalam %1$d hari + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/in/strings.xml b/i18n/src/commonMain/resources/MR/in/strings.xml index 9859b62c0e..6b7bf30fbd 100644 --- a/i18n/src/commonMain/resources/MR/in/strings.xml +++ b/i18n/src/commonMain/resources/MR/in/strings.xml @@ -492,7 +492,7 @@ Gagal menyimpan sampul Sampul disimpan Sampul - Nonaktif + Matikan Aktif Panduan pelacakan Pengaturan per-kategori untuk urutan @@ -798,4 +798,5 @@ Segera Cabut izin ekstensi tidak dikenal yang tepercaya Repo sumber terbuka + Menonaktifkan zoom out \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ja/strings.xml b/i18n/src/commonMain/resources/MR/ja/strings.xml index 9c7429d80d..9ddf503d25 100644 --- a/i18n/src/commonMain/resources/MR/ja/strings.xml +++ b/i18n/src/commonMain/resources/MR/ja/strings.xml @@ -785,7 +785,7 @@ リポジトリを削除 リポジトリ「%s」を削除してもよろしいですか? ストレージ ガイド - トラッカーログイン情報などの機密性の高い情報を含みます + トラッカーログイン情報などの機密性の高い情報を含む 間もなく 不明な拡張機能の信頼を取り消す 拡張機能リポジトリ @@ -798,4 +798,5 @@ リポジトリURL Nord スマート・アップデート + ズームアウトを無効にする \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ne/strings.xml b/i18n/src/commonMain/resources/MR/ne/strings.xml index 955f018de0..aa98d1a159 100644 --- a/i18n/src/commonMain/resources/MR/ne/strings.xml +++ b/i18n/src/commonMain/resources/MR/ne/strings.xml @@ -111,7 +111,7 @@ उल्टो चयन गर्नुहोस् राखिएको मिती अध्याय ल्याएको मिति - सबैभन्दा नयाँ अध्याय + नवीनतम अध्याय पुस्तकालय अपडेट गर्दा नयाँ आवरण र विवरणहरूको लागि जाँच गर्नुहोस् मेटाडेटा स्वतः रिफ्रेस गर्नुहोस् \"समाप्त\" स्थिति भएको @@ -409,7 +409,7 @@ अन्य पुस्तकालयबाट ट्याबहरू - सबैभन्दा नयाँ + नवीनतम द्वारा अर्डर गर्नुहोस् मिति %1$s मा लगइन गर्नुहोस् @@ -802,4 +802,5 @@ सिर्जना गर्नुहोस् पूर्ण त्रुटि: संवेदनशील सेटिङहरू समावेश गर्नुहोस् (जस्तै, ट्र्याकर लगइन टोकनहरू) + जूम आउट असक्षम गर्नुहोस् \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/nl/plurals.xml b/i18n/src/commonMain/resources/MR/nl/plurals.xml index 426f1662aa..a6aedc6dd7 100644 --- a/i18n/src/commonMain/resources/MR/nl/plurals.xml +++ b/i18n/src/commonMain/resources/MR/nl/plurals.xml @@ -64,4 +64,8 @@ %1$s hoofdstuk mist %1$s hoofdstukken missen + + Morgen + Over %1$d dagen + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/nl/strings.xml b/i18n/src/commonMain/resources/MR/nl/strings.xml index 2a19fa1aa7..24f2e69c79 100644 --- a/i18n/src/commonMain/resources/MR/nl/strings.xml +++ b/i18n/src/commonMain/resources/MR/nl/strings.xml @@ -744,4 +744,13 @@ App-instellingen Relatieve tijdstempels \"%1$s\" in plaats van \"%2$s\" + Geselecteerd + Niet geselecteerd + Meer opties + Navigeer omhoog + Scanlator + Toepassen + Naar standaard terugkeren + Onboarding gids + Welkom! \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/pl/plurals.xml b/i18n/src/commonMain/resources/MR/pl/plurals.xml index 02c3f4116b..da0820219b 100644 --- a/i18n/src/commonMain/resources/MR/pl/plurals.xml +++ b/i18n/src/commonMain/resources/MR/pl/plurals.xml @@ -102,4 +102,10 @@ repos repos + + Jutro + Za %1$d dni + Za %1$d dni + Za %1$d dni + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/pl/strings.xml b/i18n/src/commonMain/resources/MR/pl/strings.xml index f079535ed2..b86212168d 100644 --- a/i18n/src/commonMain/resources/MR/pl/strings.xml +++ b/i18n/src/commonMain/resources/MR/pl/strings.xml @@ -728,7 +728,7 @@ Przenieś serię na dół Nigdy Wybierz folder - Następna aktualizacja spodziewana za około %1$s, sprawdzanie co %2$s + Następna aktualizacja spodziewana za około %1$s, sprawdzanie co %2$s. Folder musi być wybrany Uprawnienia powiadomień Sortuj kategorie @@ -802,4 +802,5 @@ Licencjonowany - Brak rozdziałów Pominięto, ponieważ nie spodziewano się dzisiaj żadnej publikacji Wyklucz skanlatorów + Wyłącz oddalenie \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ro/plurals.xml b/i18n/src/commonMain/resources/MR/ro/plurals.xml index 2bda6def53..17e794c1c1 100644 --- a/i18n/src/commonMain/resources/MR/ro/plurals.xml +++ b/i18n/src/commonMain/resources/MR/ro/plurals.xml @@ -3,7 +3,7 @@ După %1$s minut După %1$s minute - După %1$s minute + După %1$s de minute %1$d capitol nou @@ -33,7 +33,7 @@ %d categorie %d categorii - %d categorii + %d de categorii Gata în %1$s cu eroarea %2$s @@ -48,7 +48,7 @@ %d tracker %d trackere - %d trackere + %d de trackere Omiterea %d capitol, fie că sursa lipsește, fie că a fost filtrată @@ -58,12 +58,12 @@ Ieri Acum %1$d zile - Acum %1$d zile + Acum %1$d de zile Următorul capitol necitit Următoarele %d capitole necitite - Următoarele %d capitole necitite + Următoarele %d de capitole necitite Următorul capitol @@ -80,4 +80,9 @@ %d zile %d zile + + %d repozitoriu + %d repozitorii + %d de repozitorii + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ro/strings.xml b/i18n/src/commonMain/resources/MR/ro/strings.xml index 8e7e8d7cb3..5bc15ead10 100644 --- a/i18n/src/commonMain/resources/MR/ro/strings.xml +++ b/i18n/src/commonMain/resources/MR/ro/strings.xml @@ -753,4 +753,50 @@ Înregistrări monitorizate Exclude scanlator Glisare capitol + Ghid de îmbarcare + Bine ați venit! + Următorul + Omite + Selectează un folder + Un folder trebuie selectat + Actualizați de la o versiune mai veche și nu sunteți sigur ce să selectați? Pentru mai multe informații, consultați ghidul de stocare. + Ghid de stocare + Permisiunea de a instala aplicații + Pentru a instala extensii sursă. + Utilizarea bateriei în fundal + Evitați întreruperile actualizărilor bibliotecii, descărcărilor, și restaurărilor de lungă durată. + Acordă + Reinstalare %s? + Actualizare inteligentă + Repozitoriu extensii + Nu aveți nici un repozitoriu configurat. + Adăugați repozitoriu + Adăugați repozitorii adiționale pentru Mihon. Acestea ar trebuii să fie URL-uri care se încheie cu \"index.min.json\". + Doriți să ștergeți repozitoriul \"%s\"? + Repozitoriu open source + Nici o locație pentru stocare nu a fost setată + Eroarea completă: + Includeți setări sensibile (ex. tokeni de urmărire al logări) + Disponibil: %1$s / Total: %2$s + Estimează la fiecare + Configurează actualizări la fiecare + Se preconizează că vor fi lansate noi capitole în aproximativ %1$s, verifică la fiecare %2$s. + În curând + Frecvența personalizată de actualizare: + Să configurăm câteva lucruri mai întâi. Le puteți schimba oricând în setări și mai târziu. + Începe + Selectează un folder unde %1$s va păstra capitolele descărcate, copiile de rezervă, și altele. +\n +\nSe recomanda folosirea unui folder dedicat. +\n +\nFolder-ul selectat: %2$s + Permisiune de notificare + Primiți notificări pentru actualizările bibliotecii și altele. + Nou pe %s? Vă recomandăm să consultați ghidul introductiv. + Sunt necesare permisiuni pentru instalarea de extensii. Atingeți aici pentru a permite. + Revocați încrederea extensiilor necunoscute + URL repozitoriu + Acest repozitoriu există deja! + Șterge repozitoriu + URL repozitoriu invalid \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ru/plurals.xml b/i18n/src/commonMain/resources/MR/ru/plurals.xml index 7d85c6f3ff..a4f625ebea 100644 --- a/i18n/src/commonMain/resources/MR/ru/plurals.xml +++ b/i18n/src/commonMain/resources/MR/ru/plurals.xml @@ -102,4 +102,10 @@ %d репозиториев %d репозиториев + + Завтра + В течении %1$d дней + В течении %1$d дней + В течении %1$d дней + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ru/strings.xml b/i18n/src/commonMain/resources/MR/ru/strings.xml index 9ed0c48ba4..47a203ae61 100644 --- a/i18n/src/commonMain/resources/MR/ru/strings.xml +++ b/i18n/src/commonMain/resources/MR/ru/strings.xml @@ -798,4 +798,5 @@ Север Убрать доверие к ненадёжным расширениям Открыть репозиторий источника + Отключить уменьшение масштаба \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/sr/plurals.xml b/i18n/src/commonMain/resources/MR/sr/plurals.xml index 6a538ebc27..a997e61a43 100644 --- a/i18n/src/commonMain/resources/MR/sr/plurals.xml +++ b/i18n/src/commonMain/resources/MR/sr/plurals.xml @@ -41,9 +41,9 @@ Завршено у %1$s са %2$s грешака - %d трекер - %d трекера - %d трекера + %d пратилац + %d пратиоца + %d пратилаца %1$s поглавље @@ -85,4 +85,9 @@ %d репозиторија %d репозиторија + + За %1$d дан + За %1$d дана + За %1$d дана + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/sr/strings.xml b/i18n/src/commonMain/resources/MR/sr/strings.xml index b74ad34aa7..6eea421500 100644 --- a/i18n/src/commonMain/resources/MR/sr/strings.xml +++ b/i18n/src/commonMain/resources/MR/sr/strings.xml @@ -49,7 +49,7 @@ Уклони Настави Отвори у претраживачу - Режим дисплеја + Режим приказа Екран Збијена мрежа Листа @@ -62,7 +62,7 @@ Ресетуј Врати назад Отвори записник - Врати се + Врати Учитавање… Апликација није доступна Опште @@ -155,7 +155,7 @@ Четири пре прочитаног поглавља Пет пре прочитаног поглавља Преузми нова поглавља - Сервиси + Сервиси праћења Направи резервну копију Може се користити за враћање на тренутно стање колекције Врати се на резервну копију @@ -235,8 +235,8 @@ Последње поглавље Погледај поглавља Откажи све - Икључено - Укључено + Светла + Тамна По систему Управљај обавештењима Безбедност и приватност @@ -280,7 +280,7 @@ Погл. %1$s - %2$s Ажурирање колекције Учитавање страница није успело: %1$s - Утитавање страница… + Учитавање страница… Нема претходног поглавља Нема следећег поглавља Претходно: @@ -321,7 +321,7 @@ Последње коришћено Филтрира све наслове у колекцији Провери ажурирања - Sajt + Вебсајт Враћање је отказано Враћање на резервну копију није успело Враћање је већ у току @@ -395,7 +395,7 @@ Јин и јанг Потпуно црна тема NSFW - извори за одрасле (18+) - Показати у листи за изворе и додатке + Покажи у листи за изворе и додатке Начин читања Инсталер Аутоматско преузимање @@ -421,7 +421,7 @@ Картице Непознат статус Поређај по - Побољшани сервиси + Побољшани сервиси за праћење Неисправна датотека резервне копије Недостају извори: Резервна копија не садржи ниједан наслов. @@ -549,7 +549,7 @@ Историја је избрисана Да ли сте сигурни\? Цела историја ће бити изгубљена. Водич за миграцију извора - Упозорење: велики број преузимања може довести до успоравања извора и/или блокирања Mihon-ја. Додирни да сазнаш више. + Упозорење: велики број преузимања може довести до успоравања извора и/или блокирања Mihon-а. Додирни да сазнаш више. Велика ажурирања штете изворима и могу довести до споријег ажурирања и повећања потрошње батерије. Кликни да сазнаш више. Ажурирај све Ажурирања апликације @@ -571,7 +571,7 @@ Мрежа насловница Померај широке слике Аутоматски зумирај слике у пејзажном облику - Sačuvaj kao CBZ arhivu + Сачувај као CBZ архиву Обрнути портрет Издавање завршено Прескочено јер је наслов завршен @@ -588,7 +588,7 @@ Режим читања, дисплеј, навигација Аутоматско преузимање, преузимање унапред Једносмерна синхронизација напретка, побољшана синхронизација - Извори, екстензије, глобална претрага + Извори, додаци, глобална претрага Ручне и аутоматске резервне копије Преузми аутоматски током читања Копирано @@ -596,7 +596,7 @@ Присили апликацију да поново провери преузета поглавља Није инсталирано %s је наишао на неочекивану грешку. Предлажемо да поделите запис о прекиду програма на нашем каналу за подршку на Discord-у. - Widget није доступан када је омогућено закључавање апликације + Виџет није доступан када је омогућено закључавање апликације Прокажи број непрочитаних на икони ажурирања Листа жеља Доступно, али извор није преузет: %s @@ -633,11 +633,11 @@ Просечна оцена Прочитано Коришћено - %d дана - %d сати + %d д + %dч N/A - %d минута - %d секунди + %dмин + %dс Нема описа Дугме за наставак читања InternalError: Провери записнике о прекиду програма за даље информације @@ -655,7 +655,7 @@ Није могуће ресетовати подешавања читача Управо ћете уклонити \"%s\" из своје колекције Погледај твоје недавно ажуриране наслове у колекцији - Неважећи индекс преузимања + Поништи индекс преузимања Број непрочитаних Последња провера ажурирања Обриши категорију @@ -710,7 +710,7 @@ Има резултата Прескочено јер данас није очекивано издање Предвиди очекивано време изласка - Постави интервалу + Постави интервал Интервали Такође уклони из %s Запис праћења @@ -735,7 +735,7 @@ Паметно ажурирање Забљесни приликом листања Смањује артефакте на е-инк екранима - Репозиторије екстензија + Репозиторије додатака Место складиштења Користи се за аутоматске резервне копије, преузимаље поглавља и локални извор. Опозови поверење непознатим екстензијама @@ -761,7 +761,7 @@ Дозвола за инсталацију апликација Пресокочи Следеће - За инсталирање екстензија. + За инсталирање додатака. За примање обавештења о ажурурању библиотеке и др. Одобри Добро дошли! @@ -780,9 +780,27 @@ Норд Ажурураш са старије верзије и не знаш шта да изабереш? Потражи информације у водичу складиштења. Водич складиштења - За инсталацију екстензија потребне су дозволе. Додирни овде за одобрење + За инсталацију додатака потребне су дозволе. Додирни овде за одобрење Грешка: Ова репозиторија већ постоји Прилагођена учесталост ажурирања: Отвори репозиторију + Није одабрано + Одабрано + Више опција + Навигација према горе + Сортирање категорија + Да ли желиш да сортираш категорије по абецеди? + Премести серију на крај + Подешавања апликације + Подешавања извора + Последња аутоматска резервна копија:%s + Никада + Ажурирање библиотеке... (%s) + Онемогући умањивање + Створи + Коришћење складишта + Ни једна датотека није одабрана + Примени + Врати на подразумевано \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/tr/plurals.xml b/i18n/src/commonMain/resources/MR/tr/plurals.xml index 3a2a47d45d..7e40a5df65 100644 --- a/i18n/src/commonMain/resources/MR/tr/plurals.xml +++ b/i18n/src/commonMain/resources/MR/tr/plurals.xml @@ -25,8 +25,8 @@ %1$s kaldı - %d ulam - %d ulam + %d kategori + %d kategoriler %1$s içinde %2$s hatayla tamamlandı diff --git a/i18n/src/commonMain/resources/MR/tr/strings.xml b/i18n/src/commonMain/resources/MR/tr/strings.xml index d08383a88f..fdd88b422f 100644 --- a/i18n/src/commonMain/resources/MR/tr/strings.xml +++ b/i18n/src/commonMain/resources/MR/tr/strings.xml @@ -36,10 +36,10 @@ Kitaplığı güncelle Düzenle Ekle - Ulam ekle - Ulamları düzenle - Ulamı yeniden adlandır - Ulamları ayarla + Kategori ekle + Kategorileri düzenle + Kategoriyi yeniden adlandır + Kategorileri ayarla Kapağı düzenle Duraklat Önceki bölüm @@ -73,7 +73,7 @@ Izgara boyutu Dikey Yatay - Kendiliğinden güncellemeler + Otomatik güncellemeler Kapalı 6 saatte bir 12 saatte bir @@ -85,7 +85,7 @@ Şarj olurken \"Bitirilmiş\" durumda olan girdileri atla Okuyunca ilerlemeyi güncelle - Varsayılan ulam + Varsayılan kategori Her zaman sor Güncelle Kur @@ -106,15 +106,15 @@ Sayfa numarası göster Kenarları kırp Özel parlaklık - Özel renk süzgeci + Özel renk filtresi Ekranı açık tut Gezinme Ses düğmeleri - Ters ses düğmeleri + Ses düğmelerini tersine çevir Arka plan rengi - Ak - Kara - Öntanımlı okuma kipi + Beyaz + Siyah + Varsayılan okuma modu Sayfalanmış (soldan sağa) Sayfalanmış (soldan sağa) Sayfalanmış (dikey) @@ -125,8 +125,8 @@ Uzat Genişliği sığdır Yüksekliği sığdır - Özgün boyut - Uslu sığdırma + Orijinal boyut + Akıllı sığdırma Yakınlaştırma başlangıç konumu Kendiliğinden Sol @@ -135,7 +135,7 @@ Animasyon yok Normal Hızlı - Ön tanımlı döndürme + Varsayılan döndürme Bağımsız Kilitli dikey Kilitli yatay @@ -143,7 +143,7 @@ Y M A - Elle okundu olarak imlenince + Elle okundu olarak işaretlenince Okunduktan sonra kendiliğinden sil Devre dışı Son okunan bölüm @@ -182,7 +182,7 @@ Giriş Giriş yapıldı Bilinmeyen sorun - Ulam güncelleniyor + Kategori güncelleniyor Başka sonuç yok Yerel kaynak Diğer @@ -222,8 +222,8 @@ Durum Durum Tür - Bu adla bir ulam zaten var! - Ulamlar silindi + Bu adla bir kategori zaten var! + Kategoriler silindi Bu, bu bölümün okunma tarihini kaldıracak. Emin misiniz? Bu girdi için tüm bölümleri sıfırla Kitaplığa eklensin mi\? @@ -254,7 +254,7 @@ Yeni güncelleme yok Yakında okunan yok Kitaplığınız boş - Ulamınız yok. Kitaplığınızı düzenlemek için artı düğmesine basarak bir tane oluşturun. + Tanımlı kategoriniz yok. Kitaplığınızı düzenlemek için artı düğmesine basarak bir tane oluşturun. İndirici Hata Bölüm, beklenmeyen hata sonucu indirilemedi @@ -273,8 +273,8 @@ Uzun dokununca eylemleri göster WebView ile Aç 32-bit renk - Okundu olarak imli bölümleri atla - Renk filtresi karışım kipi + Okundu olarak işaretli bölümleri atla + Renk filtresi karışım modu Kaplama Çarpma Ekran @@ -325,7 +325,7 @@ Pil iyileştirme zaten devre dışı E-posta adresi Bölüm geçişini her zaman göster - Komut-Seçenek Dizelgesi + Menü En yeni En eski En üste taşı @@ -333,7 +333,7 @@ Uzantı güncellemeleri Kitaplık güncelleniyor Okunan - Süzgeçlenmiş bölümleri geç + Filtrelenmiş bölümleri geç Kaynaklar Tersini seç Aralıklı uzun şerit @@ -362,7 +362,7 @@ %02d dk, %02d sn Kitaplığınızdaki tüm girdileri süzer Gri - Renk şeritlerini azaltır, ancak başarımı etkileyebilir + Renk şeritlenmesini azaltır, ancak performansı etkileyebilir Okuma kipi Bu dizi için Aygıt ayarları açılamadı @@ -378,12 +378,12 @@ Geçiş yap Rahat ızgara Sekmeler - Ulam sekmelerini göster + Kategori sekmelerini göster Sayfa bulunamadı Tümünü devre dışı bırak Tümünü etkinleştir - Okuyucu açılınca, kısaca o anki kipi göster - Okuma kipini göster + Okuyucu açılınca, kısaca o anki modu göster + Okuma modunu göster Başlat Kaynak bulunamadı Devre dışı bırak @@ -401,18 +401,18 @@ Saklama alanı az olduğundan bölümler indirilemedi Eklendiği tarih \"%1$s\" için genel arama - Okuma kipi + Okuma modu Tema İğnelenmiş kaynağınız yok Bitirildi İlerleme Hatalar İzleyiciler giriş yapmadı: - İmli bölümleri silmeye izin ver + İşaretli bölümleri silmeye izin ver Bölümleri sil Bu uzantıdaki kaynaklar yetişkin (18+) içerik içerebilir 18+ - Bu, resmi olmayan veya yanlış imlenmiş uzantıların, uygulama içinde yetişkin (18+) içeriği göstermesini engellemez. + Bu, resmi olmayan veya yanlış işaretlenmiş uzantıların, uygulama içinde yetişkin (18+) içeriği göstermesini engellemez. Bölüm bulunamadı Öntanımlı bölüm ayarları güncellendi Öntanımlı olarak ayarla @@ -432,7 +432,7 @@ Önceki sayfa Kaynak geçiş yapma kılavuzu Yetişkin içerik (18+) kaynakları - Kaynaklar ve uzantılar dizelgesinde göster + Kaynaklar ve uzantılar listesinde göster Dosya seçme uygulaması bulunamadı Lütfen MAL\'ye tekrar giriş yapın Dokunma bölgeleri @@ -467,17 +467,17 @@ Bölümün alındığı tarih Hariç tutulan ulamlardaki girdiler, dahil edilen ulamlarda olsa bile indirilmeyecektir. Kendiliğinden indir - Hariç tutulan ulamlardaki girdiler, dahil edilen ulamlarda olsa bile güncellenmeyecektir. + Hariç tutulan kategorilerdeki girdiler, dahil edilen kategorilerde olsa bile güncellenmeyecektir. Ayrıntıları görmek için dokun Bu Android sürümü artık desteklenmiyor Panoya kopyalanamadı Yatay Dikey Döndürme - Girdilerin başlığına göre sıralaç oluşturur - Sayfaları ayrı sıralaçlara kaydet + Girdilerin başlığına göre dosya oluşturur + Sayfaları ayrı dosyalara kaydet Eylemler - Boz tonlama + Gri tonlama Gizli kipi devre dışı bırak Kendiliğinden Bu dizi için tümünü iptal et @@ -496,15 +496,15 @@ Kapak kaydedilemedi Kapak kaydedildi Kapak - Ulam kapsamında sıralama ayarları + Kategori bazlı sıralama ayarları İzleme kılavuzu - Henüz bir ulamınız yok. + Henüz bir kategoriniz yok. Şimdi indirmeye başla Bazı üreticilerin arka plan hizmetlerini durduran ek uygulama kısıtlamaları vardır. Bu web sitesinde durumun nasıl düzeltileceği hakkında daha fazla bilgi var. Yedekleme/geri yükleme, MIUI iyileştirmesi devre dışıysa düzgün çalışmayabilir. Belirli kaynaklar için gelişmiş özellikler sağlar. Girdiler, kitaplığınıza eklendiğinde kendiliğinden izlenir. Gelişmiş izleyiciler - Arı kara karanlık kip + Saf siyah karanlık mod Yotsuba Yin & Yang Tako @@ -521,14 +521,14 @@ Kaydırmada komut-seçenek dizelgesini gizleme duyarlılığı Ters Çevrilmiş Bugün - Deniz kökçesi & Türk kökçesi + Ördekbaşı & Turkuaz Değişikliği onaylamak için kimlik doğrula Varsayılan Görünüm İzle Başlangıç kılavuzu Tablet arayüzü - Hariç tutulan ulamlar + Hariç tutulan kategoriler Çeviriye yardım edin Uygulama bilgisi Uzantı kurucusu olarak kullanmak için Shizuku\'yu kurun ve başlatın. @@ -561,7 +561,7 @@ Kitaplık güncelleme hatalarının nasıl düzeltileceği konusunda yardım için bkz. %1$s Yalnızca kapağa ızgara Okunmayan bölümü olan girdileri atla - Başlamayan girdileri atla + Başlanmayan girdileri atla Dizi bitirildiği için atlandı Okunmamış bölümler olduğu için atlandı Hiçbir bölüm okunmadığı için atlandı @@ -606,8 +606,8 @@ Uygulama dili Açıklama yok Lavanta - Ulamı sil - \"%s\" ulamını silmek istiyor musunuz? + Kategoriyi sil + \"%s\" Kategorisini silmek istiyor musunuz? İçsel sorun: Daha çok bilgi için çökme günlüklerine bakın Öntanımlı kullanıcı aracısı dizgesi Öntanımlı kullanıcı aracısı dizgesini sıfırla @@ -632,7 +632,7 @@ Elle ve kendiliğinden yedeklemeler, depolama alanı Uygulama kilidi, güvenli ekran Çökme günlükleri dökümü, pil iyileştirmeleri - Ulamlar, genel güncelleme, bölüm kaydırma + Kategoriler, genel güncelleme, bölüm kaydırma Kaynaklar, uzantılar, genel arama Kendiliğinden indir, önceden indir Okuma kipi, görüntüleme, gezinme @@ -647,7 +647,7 @@ Şimdi İndirilenler denetleniyor Gelişigüzel girdi aç - Bu ulamda girdi bulunamadı + Bu kategoride girdi bulunamadı F-Droid derlemeleri resmi olarak desteklenmemektedir. \nDaha fazla bilgi edinmek için dokunun. Okumayı sürdür düğmesi @@ -677,11 +677,11 @@ %ddak %dsn Şimdi değil - Ulam boş + Kategori boş Güncellemeler simgesinde okunmayan sayısını göster Panoya kopyalandı Kullanılabilir ancak kaynak kurulu değil: %s - Yinelenen bölümleri atla + Tekrarlanan bölümleri atla Kitaplığınızda aynı ada sahip bir girdiniz var. \n \nYine de devam etmek istiyor musunuz\? @@ -689,7 +689,7 @@ *gerekli Zaten kitaplıkta bulunan girdileri gizle Panoya kopyala - Ulamı güncelle + Kategoriyi güncelle Uzun görselleri otomatik böl Kaplama Geniş sayfaları sığdırmak için döndür @@ -704,7 +704,7 @@ Özelleştirilmiş güncelleme aralığı Zaman aralıkları Sonraki beklenen güncelleme - Bir sonraki yayın zamanını tahmin et + Bir sonraki yayınlama zamanını tahmin et TAMAM Hepsini tahmin et %s izlemesi kaldırılsın mı\? @@ -726,21 +726,21 @@ Kitaplık eşleştiriliyor Aç: %s Diziyi en alta taşı - Ulamları sırala + Kategorileri sırala Kitaplık güncelleniyor… (%s) - Ulamları alfabetik sıralamak ister misiniz\? + Kategorileri alfabetik sıralamak ister misiniz? Seçilen dosya yok Kaynak ayarları Uygulama ayarları Göreli zaman damgaları \"%1$s\" yerine \"%2$s Asla - E-ink görüntülerinde gölgelenmeyi azaltır + E-ink ekranlarda gölgelenmeyi azaltır Uygula Varsayılana dön En son şu tarihte kendi yedekledi: %s Tareviriler bulunamadı - Tareviri + Scanlator Sayfa değişiminde ışık çak Depolama kullanımı İzleyici notu @@ -748,7 +748,7 @@ Tarevirileri hariç tut Oluştur Seçilmedi - Diğer seçenekler + Daha fazla seçenek Seçili Depolama yeri Kendiliğinden yedeklemeler, bölüm indirmeleri ve yerel kaynak için kullanılır. diff --git a/i18n/src/commonMain/resources/MR/uk/plurals.xml b/i18n/src/commonMain/resources/MR/uk/plurals.xml index ad45386773..362a57bd15 100644 --- a/i18n/src/commonMain/resources/MR/uk/plurals.xml +++ b/i18n/src/commonMain/resources/MR/uk/plurals.xml @@ -36,8 +36,8 @@ %d категорій - %1$s залишилось - %1$s залишилось + %1$s залишився + %1$s залишились %1$s залишилось %1$s залишилось @@ -67,15 +67,15 @@ Учора - %1$d днів тому + %1$d дні тому %1$d днів тому %1$d днів тому Наступний непрочитаний розділ Наступні %d непрочитані розділи - Наступні %d непрочитані розділи - Наступні %d непрочитані розділи + Наступні %d непрочитаних розділів + Наступні %d непрочитаних розділів Наступний розділ diff --git a/i18n/src/commonMain/resources/MR/zh-rCN/plurals.xml b/i18n/src/commonMain/resources/MR/zh-rCN/plurals.xml index 93ef735397..64caf1d6b2 100644 --- a/i18n/src/commonMain/resources/MR/zh-rCN/plurals.xml +++ b/i18n/src/commonMain/resources/MR/zh-rCN/plurals.xml @@ -51,4 +51,7 @@ %d 个仓库 + + %1$d 天后 + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml b/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml index c450be7690..ee40e55fcc 100644 --- a/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml +++ b/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml @@ -798,4 +798,5 @@ 即将更新 撤销已信任的未知插件 打开图源仓库 + 禁用缩小 \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/zh-rTW/plurals.xml b/i18n/src/commonMain/resources/MR/zh-rTW/plurals.xml index f3d9a6f0ba..ee3847fac8 100644 --- a/i18n/src/commonMain/resources/MR/zh-rTW/plurals.xml +++ b/i18n/src/commonMain/resources/MR/zh-rTW/plurals.xml @@ -51,4 +51,7 @@ %d 處儲存庫 + + %1$d 天後 + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml b/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml index 84b6803791..872706038a 100644 --- a/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml +++ b/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml @@ -191,7 +191,7 @@ 已清除快取,刪除了 %1$d 個檔案 清除時發生錯誤 已清除 Cookie - 協助我們修復錯誤,傳送的資料將不包含個人敏感訊息 + 協助我們修復錯誤,傳送的資料將不包含個人敏感資訊 登入 %1$s 使用者名稱 正在更新類別 @@ -460,7 +460,7 @@ 左邊 上一頁 下一頁 - 章節獲取日期 + 章節擷取日期 右邊 橫向 快顯選單 @@ -710,7 +710,7 @@ 下次預期更新 刊期 預計每個 - 預估出刊日 + 自訂預估出刊日 設定為每個 由於未臨出刊日,因此略過 有結果 @@ -798,4 +798,5 @@ 撤銷對不明擴充套件的信任 即將出刊 開啟來源儲存庫 + 停用縮小 \ No newline at end of file diff --git a/presentation-core/build.gradle.kts b/presentation-core/build.gradle.kts index cfae8ba2ad..e30c5869cd 100644 --- a/presentation-core/build.gradle.kts +++ b/presentation-core/build.gradle.kts @@ -52,7 +52,7 @@ tasks { "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi", "-opt-in=androidx.compose.animation.ExperimentalAnimationApi", "-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi", - "-opt-in=coil.annotation.ExperimentalCoilApi", + "-opt-in=coil3.annotation.ExperimentalCoilApi", "-opt-in=kotlinx.coroutines.FlowPreview", ) } diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/Pager.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/Pager.kt deleted file mode 100644 index fb3cbdf749..0000000000 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/Pager.kt +++ /dev/null @@ -1,56 +0,0 @@ -package tachiyomi.presentation.core.components - -import androidx.compose.foundation.gestures.Orientation -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.pager.PageSize -import androidx.compose.foundation.pager.PagerDefaults -import androidx.compose.foundation.pager.PagerScope -import androidx.compose.foundation.pager.PagerSnapDistance -import androidx.compose.foundation.pager.PagerState -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.input.nestedscroll.NestedScrollConnection -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp - -/** - * Horizontal Pager with custom SnapFlingBehavior for a more natural swipe feeling - */ -@Composable -fun HorizontalPager( - state: PagerState, - modifier: Modifier = Modifier, - contentPadding: PaddingValues = PaddingValues(0.dp), - pageSize: PageSize = PageSize.Fill, - beyondBoundsPageCount: Int = 0, - pageSpacing: Dp = 0.dp, - verticalAlignment: Alignment.Vertical = Alignment.CenterVertically, - userScrollEnabled: Boolean = true, - reverseLayout: Boolean = false, - key: ((index: Int) -> Any)? = null, - pageNestedScrollConnection: NestedScrollConnection = PagerDefaults.pageNestedScrollConnection( - state = state, - orientation = Orientation.Horizontal, - ), - pageContent: @Composable PagerScope.(page: Int) -> Unit, -) { - androidx.compose.foundation.pager.HorizontalPager( - state = state, - modifier = modifier, - contentPadding = contentPadding, - pageSize = pageSize, - outOfBoundsPageCount = beyondBoundsPageCount, - pageSpacing = pageSpacing, - verticalAlignment = verticalAlignment, - flingBehavior = PagerDefaults.flingBehavior( - state = state, - pagerSnapDistance = PagerSnapDistance.atMost(0), - ), - userScrollEnabled = userScrollEnabled, - reverseLayout = reverseLayout, - key = key, - pageNestedScrollConnection = pageNestedScrollConnection, - pageContent = pageContent, - ) -} diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt index e4977bfd3f..4fa7850a8e 100644 --- a/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt +++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt @@ -21,13 +21,15 @@ import androidx.glance.background import androidx.glance.layout.fillMaxSize import androidx.glance.layout.padding import androidx.glance.unit.ColorProvider -import coil.executeBlocking -import coil.imageLoader -import coil.request.CachePolicy -import coil.request.ImageRequest -import coil.size.Precision -import coil.size.Scale -import coil.transform.RoundedCornersTransformation +import coil3.annotation.ExperimentalCoilApi +import coil3.executeBlocking +import coil3.imageLoader +import coil3.request.CachePolicy +import coil3.request.ImageRequest +import coil3.request.transformations +import coil3.size.Precision +import coil3.size.Scale +import coil3.transform.RoundedCornersTransformation import eu.kanade.tachiyomi.core.security.SecurityPreferences import eu.kanade.tachiyomi.util.system.dpToPx import kotlinx.collections.immutable.ImmutableList @@ -105,6 +107,7 @@ abstract class BaseUpdatesGridGlanceWidget( } } + @OptIn(ExperimentalCoilApi::class) private suspend fun List.prepareData( rowCount: Int, columnCount: Int, @@ -140,7 +143,11 @@ abstract class BaseUpdatesGridGlanceWidget( } } .build() - Pair(updatesView.mangaId, context.imageLoader.executeBlocking(request).drawable?.toBitmap()) + val bitmap = context.imageLoader.executeBlocking(request) + .image + ?.asDrawable(context.resources) + ?.toBitmap() + Pair(updatesView.mangaId, bitmap) } .toImmutableList() } diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt index b1ae25236f..9e2aa84069 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt @@ -16,9 +16,9 @@ import kotlinx.coroutines.awaitAll import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromStream import logcat.LogPriority +import mihon.core.common.extensions.toZipFile import nl.adaptivity.xmlutil.AndroidXmlReader import nl.adaptivity.xmlutil.serialization.XML -import org.apache.commons.compress.archivers.zip.ZipFile import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE import tachiyomi.core.metadata.comicinfo.ComicInfo @@ -210,7 +210,7 @@ actual class LocalSource( for (chapter in chapterArchives) { when (Format.valueOf(chapter)) { is Format.Zip -> { - ZipFile(chapter.openReadOnlyChannel(context)).use { zip: ZipFile -> + chapter.openReadOnlyChannel(context).toZipFile().use { zip -> zip.getEntry(COMIC_INFO_FILE)?.let { comicInfoFile -> zip.getInputStream(comicInfoFile).buffered().use { stream -> return copyComicInfoFile(stream, folder) @@ -328,7 +328,7 @@ actual class LocalSource( entry?.let { coverManager.update(manga, it.openInputStream()) } } is Format.Zip -> { - ZipFile(format.file.openReadOnlyChannel(context)).use { zip -> + format.file.openReadOnlyChannel(context).toZipFile().use { zip -> val entry = zip.entries.toList() .sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) } .find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } }