diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml
index b58c2fb3fb..077eb0251e 100644
--- a/.github/workflows/build_pull_request.yml
+++ b/.github/workflows/build_pull_request.yml
@@ -28,10 +28,10 @@ jobs:
- name: Dependency Review
uses: actions/dependency-review-action@v3
- - name: Set up JDK 11
+ - name: Set up JDK
uses: actions/setup-java@v3
with:
- java-version: 11
+ java-version: 17
distribution: adopt
- name: Build app and run unit tests
diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml
index ef72155589..2a0ca39096 100644
--- a/.github/workflows/build_push.yml
+++ b/.github/workflows/build_push.yml
@@ -23,10 +23,10 @@ jobs:
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
- - name: Set up JDK 11
+ - name: Set up JDK
uses: actions/setup-java@v3
with:
- java-version: 11
+ java-version: 17
distribution: adopt
- name: Build app and run unit tests
diff --git a/.github/workflows/issue_moderator.yml b/.github/workflows/issue_moderator.yml
index 592e23b457..1bfe6239a2 100644
--- a/.github/workflows/issue_moderator.yml
+++ b/.github/workflows/issue_moderator.yml
@@ -11,9 +11,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Moderate issues
- uses: tachiyomiorg/issue-moderator-action@v1
+ uses: tachiyomiorg/issue-moderator-action@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
+ duplicate-label: Duplicate
+
auto-close-rules: |
[
{
@@ -27,3 +29,4 @@ jobs:
"message": "Requested information in the template was not filled out."
}
]
+ auto-close-ignore-label: do-not-autoclose
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index a64dfcb678..5bbf3fa2f6 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -23,8 +23,8 @@ android {
defaultConfig {
applicationId = "xyz.jmir.tachiyomi.mi"
- versionCode = 99
- versionName = "0.14.5"
+ versionCode = 102
+ versionName = "0.14.6"
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
@@ -102,7 +102,7 @@ android {
}
}
- packagingOptions {
+ packaging {
resources.excludes.addAll(listOf(
"META-INF/DEPENDENCIES",
"LICENSE.txt",
@@ -258,7 +258,7 @@ dependencies {
implementation(libs.bundles.shizuku)
// Tests
- testImplementation(libs.junit)
+ testImplementation(libs.bundles.test)
// For detecting memory leaks; see https://square.github.io/leakcanary/
// debugImplementation(libs.leakcanary.android)
diff --git a/app/proguard-android-optimize.txt b/app/proguard-android-optimize.txt
index 1a85da105a..9f53403165 100644
--- a/app/proguard-android-optimize.txt
+++ b/app/proguard-android-optimize.txt
@@ -1,4 +1,5 @@
-dontusemixedcaseclassnames
+-ignorewarnings
-verbose
-keepattributes *Annotation*
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 123a76360f..343be8d652 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -1,5 +1,8 @@
-dontobfuscate
+-keep,allowoptimization class eu.kanade.**
+-keep,allowoptimization class tachiyomi.**
+
# Keep common dependencies used in extensions
-keep,allowoptimization class androidx.preference.** { public protected *; }
-keep,allowoptimization class android.content.** { *; }
diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
index 57a9a3396f..dcbbdf1218 100644
--- a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -1,6 +1,6 @@
-
-
-
+
+
+
\ No newline at end of file
diff --git a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml
index 57a9a3396f..dcbbdf1218 100644
--- a/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ b/app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -1,6 +1,6 @@
-
-
-
+
+
+
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ca6c4e6cc0..56a084673a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -281,10 +281,6 @@
android:name=".data.updater.AppUpdateService"
android:exported="false" />
-
-
diff --git a/app/src/main/java/eu/kanade/core/util/CollectionUtils.kt b/app/src/main/java/eu/kanade/core/util/CollectionUtils.kt
index 07d5933ad0..5017631501 100644
--- a/app/src/main/java/eu/kanade/core/util/CollectionUtils.kt
+++ b/app/src/main/java/eu/kanade/core/util/CollectionUtils.kt
@@ -12,10 +12,10 @@ fun List.insertSeparators(
val newList = mutableListOf()
for (i in -1..lastIndex) {
val before = getOrNull(i)
- before?.let { newList.add(it) }
+ before?.let(newList::add)
val after = getOrNull(i + 1)
val separator = generator.invoke(before, after)
- separator?.let { newList.add(it) }
+ separator?.let(newList::add)
}
return newList
}
@@ -80,7 +80,7 @@ inline fun List.fastMapNotNull(transform: (T) -> R?): List {
contract { callsInPlace(transform) }
val destination = ArrayList()
fastForEach { element ->
- transform(element)?.let { destination.add(it) }
+ transform(element)?.let(destination::add)
}
return destination
}
diff --git a/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt b/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt
index 83f1eb358f..3cea7a5b97 100644
--- a/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt
+++ b/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt
@@ -3,6 +3,7 @@ package eu.kanade.domain.base
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
+import eu.kanade.tachiyomi.R
import tachiyomi.core.preference.PreferenceStore
class BasePreferences(
@@ -22,4 +23,10 @@ class BasePreferences(
fun acraEnabled() = preferenceStore.getBoolean("acra.enable", false)
fun deviceHasPip() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && context.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
+
+ enum class ExtensionInstaller(val titleResId: Int) {
+ LEGACY(R.string.ext_installer_legacy),
+ PACKAGEINSTALLER(R.string.ext_installer_packageinstaller),
+ SHIZUKU(R.string.ext_installer_shizuku),
+ }
}
diff --git a/app/src/main/java/eu/kanade/domain/base/ExtensionInstallerPreference.kt b/app/src/main/java/eu/kanade/domain/base/ExtensionInstallerPreference.kt
index a981bd0ff9..03c63dd2bb 100644
--- a/app/src/main/java/eu/kanade/domain/base/ExtensionInstallerPreference.kt
+++ b/app/src/main/java/eu/kanade/domain/base/ExtensionInstallerPreference.kt
@@ -1,7 +1,7 @@
package eu.kanade.domain.base
import android.content.Context
-import eu.kanade.tachiyomi.data.preference.PreferenceValues.ExtensionInstaller
+import eu.kanade.domain.base.BasePreferences.ExtensionInstaller
import eu.kanade.tachiyomi.util.system.hasMiuiPackageInstaller
import eu.kanade.tachiyomi.util.system.isShizukuInstalled
import kotlinx.coroutines.CoroutineScope
diff --git a/app/src/main/java/eu/kanade/domain/entries/anime/interactor/UpdateAnime.kt b/app/src/main/java/eu/kanade/domain/entries/anime/interactor/UpdateAnime.kt
index f5e84a5ece..4b9ebdd354 100644
--- a/app/src/main/java/eu/kanade/domain/entries/anime/interactor/UpdateAnime.kt
+++ b/app/src/main/java/eu/kanade/domain/entries/anime/interactor/UpdateAnime.kt
@@ -1,12 +1,12 @@
package eu.kanade.domain.entries.anime.interactor
import eu.kanade.domain.entries.anime.model.hasCustomCover
-import eu.kanade.domain.entries.anime.model.isLocal
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.data.cache.AnimeCoverCache
import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.domain.entries.anime.model.AnimeUpdate
import tachiyomi.domain.entries.anime.repository.AnimeRepository
+import tachiyomi.source.local.entries.anime.isLocal
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.Date
diff --git a/app/src/main/java/eu/kanade/domain/entries/anime/model/Anime.kt b/app/src/main/java/eu/kanade/domain/entries/anime/model/Anime.kt
index 8df0789f84..52f73c2699 100644
--- a/app/src/main/java/eu/kanade/domain/entries/anime/model/Anime.kt
+++ b/app/src/main/java/eu/kanade/domain/entries/anime/model/Anime.kt
@@ -5,7 +5,6 @@ import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.data.cache.AnimeCoverCache
import tachiyomi.domain.entries.TriStateFilter
import tachiyomi.domain.entries.anime.model.Anime
-import tachiyomi.source.local.entries.anime.LocalAnimeSource
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@@ -77,8 +76,6 @@ fun SAnime.toDomainAnime(sourceId: Long): Anime {
)
}
-fun Anime.isLocal(): Boolean = source == LocalAnimeSource.ID
-
fun Anime.hasCustomCover(coverCache: AnimeCoverCache = Injekt.get()): Boolean {
return coverCache.getCustomCoverFile(id).exists()
}
diff --git a/app/src/main/java/eu/kanade/domain/entries/manga/interactor/UpdateManga.kt b/app/src/main/java/eu/kanade/domain/entries/manga/interactor/UpdateManga.kt
index d9b27ed74b..8d159cfe01 100644
--- a/app/src/main/java/eu/kanade/domain/entries/manga/interactor/UpdateManga.kt
+++ b/app/src/main/java/eu/kanade/domain/entries/manga/interactor/UpdateManga.kt
@@ -1,12 +1,12 @@
package eu.kanade.domain.entries.manga.interactor
import eu.kanade.domain.entries.manga.model.hasCustomCover
-import eu.kanade.domain.entries.manga.model.isLocal
import eu.kanade.tachiyomi.data.cache.MangaCoverCache
import eu.kanade.tachiyomi.source.model.SManga
import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.domain.entries.manga.model.MangaUpdate
import tachiyomi.domain.entries.manga.repository.MangaRepository
+import tachiyomi.source.local.entries.manga.isLocal
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.Date
diff --git a/app/src/main/java/eu/kanade/domain/entries/manga/model/Manga.kt b/app/src/main/java/eu/kanade/domain/entries/manga/model/Manga.kt
index df638d71eb..92ace31224 100644
--- a/app/src/main/java/eu/kanade/domain/entries/manga/model/Manga.kt
+++ b/app/src/main/java/eu/kanade/domain/entries/manga/model/Manga.kt
@@ -10,7 +10,6 @@ import tachiyomi.core.metadata.comicinfo.ComicInfoPublishingStatus
import tachiyomi.domain.entries.TriStateFilter
import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.domain.items.chapter.model.Chapter
-import tachiyomi.source.local.entries.manga.LocalMangaSource
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@@ -89,8 +88,6 @@ fun SManga.toDomainManga(sourceId: Long): Manga {
)
}
-fun Manga.isLocal(): Boolean = source == LocalMangaSource.ID
-
fun Manga.hasCustomCover(coverCache: MangaCoverCache = Injekt.get()): Boolean {
return coverCache.getCustomCoverFile(id).exists()
}
diff --git a/app/src/main/java/eu/kanade/domain/items/chapter/interactor/SyncChaptersWithSource.kt b/app/src/main/java/eu/kanade/domain/items/chapter/interactor/SyncChaptersWithSource.kt
index b7cdef7a79..42802ae709 100644
--- a/app/src/main/java/eu/kanade/domain/items/chapter/interactor/SyncChaptersWithSource.kt
+++ b/app/src/main/java/eu/kanade/domain/items/chapter/interactor/SyncChaptersWithSource.kt
@@ -7,7 +7,6 @@ import eu.kanade.domain.items.chapter.model.toSChapter
import eu.kanade.tachiyomi.data.download.manga.MangaDownloadManager
import eu.kanade.tachiyomi.data.download.manga.MangaDownloadProvider
import eu.kanade.tachiyomi.source.MangaSource
-import eu.kanade.tachiyomi.source.manga.isLocal
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.online.HttpSource
import tachiyomi.data.items.chapter.ChapterSanitizer
@@ -20,6 +19,7 @@ import tachiyomi.domain.items.chapter.model.NoChaptersException
import tachiyomi.domain.items.chapter.model.toChapterUpdate
import tachiyomi.domain.items.chapter.repository.ChapterRepository
import tachiyomi.domain.items.chapter.service.ChapterRecognition
+import tachiyomi.source.local.entries.manga.isLocal
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.lang.Long.max
diff --git a/app/src/main/java/eu/kanade/domain/items/chapter/model/ChapterFilter.kt b/app/src/main/java/eu/kanade/domain/items/chapter/model/ChapterFilter.kt
index 315fa24185..06b3b3422a 100644
--- a/app/src/main/java/eu/kanade/domain/items/chapter/model/ChapterFilter.kt
+++ b/app/src/main/java/eu/kanade/domain/items/chapter/model/ChapterFilter.kt
@@ -1,13 +1,13 @@
package eu.kanade.domain.items.chapter.model
import eu.kanade.domain.entries.manga.model.downloadedFilter
-import eu.kanade.domain.entries.manga.model.isLocal
import eu.kanade.tachiyomi.data.download.manga.MangaDownloadManager
import eu.kanade.tachiyomi.ui.entries.manga.ChapterItem
import tachiyomi.domain.entries.applyFilter
import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.domain.items.chapter.model.Chapter
import tachiyomi.domain.items.chapter.service.getChapterSort
+import tachiyomi.source.local.entries.manga.isLocal
/**
* Applies the view filters to the list of chapters obtained from the database.
diff --git a/app/src/main/java/eu/kanade/domain/items/episode/interactor/SyncEpisodesWithSource.kt b/app/src/main/java/eu/kanade/domain/items/episode/interactor/SyncEpisodesWithSource.kt
index e415c7937f..9132080cf6 100644
--- a/app/src/main/java/eu/kanade/domain/items/episode/interactor/SyncEpisodesWithSource.kt
+++ b/app/src/main/java/eu/kanade/domain/items/episode/interactor/SyncEpisodesWithSource.kt
@@ -9,7 +9,6 @@ import eu.kanade.tachiyomi.animesource.model.SEpisode
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
import eu.kanade.tachiyomi.data.download.anime.AnimeDownloadManager
import eu.kanade.tachiyomi.data.download.anime.AnimeDownloadProvider
-import eu.kanade.tachiyomi.source.anime.isLocal
import tachiyomi.data.items.episode.EpisodeSanitizer
import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.domain.items.episode.interactor.GetEpisodeByAnimeId
@@ -20,6 +19,7 @@ import tachiyomi.domain.items.episode.model.NoEpisodesException
import tachiyomi.domain.items.episode.model.toEpisodeUpdate
import tachiyomi.domain.items.episode.repository.EpisodeRepository
import tachiyomi.domain.items.episode.service.EpisodeRecognition
+import tachiyomi.source.local.entries.anime.isLocal
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.lang.Long.max
diff --git a/app/src/main/java/eu/kanade/domain/items/episode/model/EpisodeFilter.kt b/app/src/main/java/eu/kanade/domain/items/episode/model/EpisodeFilter.kt
index 54ef7179c2..11d47e02fb 100644
--- a/app/src/main/java/eu/kanade/domain/items/episode/model/EpisodeFilter.kt
+++ b/app/src/main/java/eu/kanade/domain/items/episode/model/EpisodeFilter.kt
@@ -1,13 +1,13 @@
package eu.kanade.domain.items.episode.model
import eu.kanade.domain.entries.anime.model.downloadedFilter
-import eu.kanade.domain.entries.anime.model.isLocal
import eu.kanade.tachiyomi.data.download.anime.AnimeDownloadManager
import eu.kanade.tachiyomi.ui.entries.anime.EpisodeItem
import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.domain.entries.applyFilter
import tachiyomi.domain.items.episode.model.Episode
import tachiyomi.domain.items.episode.service.getEpisodeSort
+import tachiyomi.source.local.entries.anime.isLocal
/**
* Applies the view filters to the list of episodes obtained from the database.
diff --git a/app/src/main/java/eu/kanade/domain/track/anime/service/DelayedAnimeTrackingUpdateJob.kt b/app/src/main/java/eu/kanade/domain/track/anime/service/DelayedAnimeTrackingUpdateJob.kt
index 4d977fcfef..3343643dc6 100644
--- a/app/src/main/java/eu/kanade/domain/track/anime/service/DelayedAnimeTrackingUpdateJob.kt
+++ b/app/src/main/java/eu/kanade/domain/track/anime/service/DelayedAnimeTrackingUpdateJob.kt
@@ -7,11 +7,11 @@ import androidx.work.CoroutineWorker
import androidx.work.ExistingWorkPolicy
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
-import androidx.work.WorkManager
import androidx.work.WorkerParameters
import eu.kanade.domain.track.anime.model.toDbTrack
import eu.kanade.domain.track.anime.store.DelayedAnimeTrackingStore
import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.util.system.workManager
import logcat.LogPriority
import tachiyomi.core.util.lang.withIOContext
import tachiyomi.core.util.system.logcat
@@ -74,8 +74,7 @@ class DelayedAnimeTrackingUpdateJob(context: Context, workerParams: WorkerParame
.addTag(TAG)
.build()
- WorkManager.getInstance(context)
- .enqueueUniqueWork(TAG, ExistingWorkPolicy.REPLACE, request)
+ context.workManager.enqueueUniqueWork(TAG, ExistingWorkPolicy.REPLACE, request)
}
}
}
diff --git a/app/src/main/java/eu/kanade/domain/track/manga/service/DelayedMangaTrackingUpdateJob.kt b/app/src/main/java/eu/kanade/domain/track/manga/service/DelayedMangaTrackingUpdateJob.kt
index 242bd5b2c3..32fdcb8acf 100644
--- a/app/src/main/java/eu/kanade/domain/track/manga/service/DelayedMangaTrackingUpdateJob.kt
+++ b/app/src/main/java/eu/kanade/domain/track/manga/service/DelayedMangaTrackingUpdateJob.kt
@@ -7,11 +7,11 @@ import androidx.work.CoroutineWorker
import androidx.work.ExistingWorkPolicy
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
-import androidx.work.WorkManager
import androidx.work.WorkerParameters
import eu.kanade.domain.track.manga.model.toDbTrack
import eu.kanade.domain.track.manga.store.DelayedMangaTrackingStore
import eu.kanade.tachiyomi.data.track.TrackManager
+import eu.kanade.tachiyomi.util.system.workManager
import logcat.LogPriority
import tachiyomi.core.util.lang.withIOContext
import tachiyomi.core.util.system.logcat
@@ -74,8 +74,7 @@ class DelayedMangaTrackingUpdateJob(context: Context, workerParams: WorkerParame
.addTag(TAG)
.build()
- WorkManager.getInstance(context)
- .enqueueUniqueWork(TAG, ExistingWorkPolicy.REPLACE, request)
+ context.workManager.enqueueUniqueWork(TAG, ExistingWorkPolicy.REPLACE, request)
}
}
}
diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeSourcesScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeSourcesScreen.kt
index 4d6283c729..fc5ebc82d5 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeSourcesScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/anime/AnimeSourcesScreen.kt
@@ -29,6 +29,7 @@ import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.domain.source.anime.model.AnimeSource
import tachiyomi.domain.source.anime.model.Pin
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
+import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
import tachiyomi.presentation.core.screens.EmptyScreen
@@ -143,7 +144,7 @@ private fun AnimeSourcePinButton(
onClick: () -> Unit,
) {
val icon = if (isPinned) Icons.Filled.PushPin else Icons.Outlined.PushPin
- val tint = if (isPinned) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onBackground
+ val tint = if (isPinned) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onBackground.copy(alpha = SecondaryItemAlpha)
val description = if (isPinned) R.string.action_unpin else R.string.action_pin
IconButton(onClick = onClick) {
Icon(
diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/BrowseAnimeSourceScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/BrowseAnimeSourceScreen.kt
index 6bd23cda92..31dc7f7484 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/anime/BrowseAnimeSourceScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/anime/BrowseAnimeSourceScreen.kt
@@ -78,6 +78,7 @@ fun BrowseAnimeSourceContent(
if (animeList.itemCount <= 0 && errorState != null && errorState is LoadState.Error) {
EmptyScreen(
+ modifier = Modifier.padding(contentPadding),
message = getErrorMessage(errorState),
actions = if (source is LocalAnimeSource) {
listOf(
@@ -112,7 +113,9 @@ fun BrowseAnimeSourceContent(
}
if (animeList.itemCount == 0 && animeList.loadState.refresh is LoadState.Loading) {
- LoadingScreen()
+ LoadingScreen(
+ modifier = Modifier.padding(contentPadding),
+ )
return
}
diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/GlobalAnimeSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/GlobalAnimeSearchScreen.kt
index 426f0e909c..5b18bcd10b 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/anime/GlobalAnimeSearchScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/anime/GlobalAnimeSearchScreen.kt
@@ -29,7 +29,7 @@ fun GlobalAnimeSearchScreen(
navigateUp: () -> Unit,
onChangeSearchQuery: (String?) -> Unit,
onSearch: (String) -> Unit,
- getAnime: @Composable (AnimeCatalogueSource, Anime) -> State,
+ getAnime: @Composable (Anime) -> State,
onClickSource: (AnimeCatalogueSource) -> Unit,
onClickItem: (Anime) -> Unit,
onLongClickItem: (Anime) -> Unit,
@@ -62,7 +62,7 @@ fun GlobalAnimeSearchScreen(
private fun GlobalAnimeSearchContent(
items: Map,
contentPadding: PaddingValues,
- getAnime: @Composable (AnimeCatalogueSource, Anime) -> State,
+ getAnime: @Composable (Anime) -> State,
onClickSource: (AnimeCatalogueSource) -> Unit,
onClickItem: (Anime) -> Unit,
onLongClickItem: (Anime) -> Unit,
@@ -96,7 +96,7 @@ private fun GlobalAnimeSearchContent(
GlobalAnimeSearchCardRow(
titles = result.result,
- getAnime = { getAnime(source, it) },
+ getAnime = getAnime,
onClick = onClickItem,
onLongClick = onLongClickItem,
)
diff --git a/app/src/main/java/eu/kanade/presentation/browse/anime/MigrateAnimeSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/anime/MigrateAnimeSearchScreen.kt
index a6bcebc050..9829db2731 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/anime/MigrateAnimeSearchScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/anime/MigrateAnimeSearchScreen.kt
@@ -21,7 +21,7 @@ import tachiyomi.presentation.core.components.material.Scaffold
fun MigrateAnimeSearchScreen(
navigateUp: () -> Unit,
state: MigrateAnimeSearchState,
- getAnime: @Composable (AnimeCatalogueSource, Anime) -> State,
+ getAnime: @Composable (Anime) -> State,
onChangeSearchQuery: (String?) -> Unit,
onSearch: (String) -> Unit,
onClickSource: (AnimeCatalogueSource) -> Unit,
@@ -58,7 +58,7 @@ fun MigrateAnimeSearchContent(
sourceId: Long,
items: Map,
contentPadding: PaddingValues,
- getAnime: @Composable (AnimeCatalogueSource, Anime) -> State,
+ getAnime: @Composable (Anime) -> State,
onClickSource: (AnimeCatalogueSource) -> Unit,
onClickItem: (Anime) -> Unit,
onLongClickItem: (Anime) -> Unit,
@@ -85,7 +85,7 @@ fun MigrateAnimeSearchContent(
GlobalAnimeSearchCardRow(
titles = result.result,
- getAnime = { getAnime(source, it) },
+ getAnime = getAnime,
onClick = onClickItem,
onLongClick = onLongClickItem,
)
diff --git a/app/src/main/java/eu/kanade/presentation/browse/manga/BrowseMangaSourceScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/manga/BrowseMangaSourceScreen.kt
index f9fd9b4672..f591ba98d4 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/manga/BrowseMangaSourceScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/manga/BrowseMangaSourceScreen.kt
@@ -78,6 +78,7 @@ fun BrowseSourceContent(
if (mangaList.itemCount <= 0 && errorState != null && errorState is LoadState.Error) {
EmptyScreen(
+ modifier = Modifier.padding(contentPadding),
message = getErrorMessage(errorState),
actions = if (source is LocalMangaSource) {
listOf(
@@ -112,7 +113,9 @@ fun BrowseSourceContent(
}
if (mangaList.itemCount == 0 && mangaList.loadState.refresh is LoadState.Loading) {
- LoadingScreen()
+ LoadingScreen(
+ modifier = Modifier.padding(contentPadding),
+ )
return
}
diff --git a/app/src/main/java/eu/kanade/presentation/browse/manga/GlobalMangaSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/manga/GlobalMangaSearchScreen.kt
index 30308f6cea..701ff60886 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/manga/GlobalMangaSearchScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/manga/GlobalMangaSearchScreen.kt
@@ -29,7 +29,7 @@ fun GlobalMangaSearchScreen(
navigateUp: () -> Unit,
onChangeSearchQuery: (String?) -> Unit,
onSearch: (String) -> Unit,
- getManga: @Composable (CatalogueSource, Manga) -> State,
+ getManga: @Composable (Manga) -> State,
onClickSource: (CatalogueSource) -> Unit,
onClickItem: (Manga) -> Unit,
onLongClickItem: (Manga) -> Unit,
@@ -62,7 +62,7 @@ fun GlobalMangaSearchScreen(
private fun GlobalSearchContent(
items: Map,
contentPadding: PaddingValues,
- getManga: @Composable (CatalogueSource, Manga) -> State,
+ getManga: @Composable (Manga) -> State,
onClickSource: (CatalogueSource) -> Unit,
onClickItem: (Manga) -> Unit,
onLongClickItem: (Manga) -> Unit,
@@ -96,7 +96,7 @@ private fun GlobalSearchContent(
GlobalMangaSearchCardRow(
titles = result.result,
- getManga = { getManga(source, it) },
+ getManga = getManga,
onClick = onClickItem,
onLongClick = onLongClickItem,
)
diff --git a/app/src/main/java/eu/kanade/presentation/browse/manga/MangaSourcesScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/manga/MangaSourcesScreen.kt
index 8c4b5276e2..8d97d67559 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/manga/MangaSourcesScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/manga/MangaSourcesScreen.kt
@@ -29,6 +29,7 @@ import eu.kanade.tachiyomi.util.system.LocaleHelper
import tachiyomi.domain.source.manga.model.Pin
import tachiyomi.domain.source.manga.model.Source
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
+import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.components.material.topSmallPaddingValues
import tachiyomi.presentation.core.screens.EmptyScreen
@@ -143,7 +144,7 @@ private fun SourcePinButton(
onClick: () -> Unit,
) {
val icon = if (isPinned) Icons.Filled.PushPin else Icons.Outlined.PushPin
- val tint = if (isPinned) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onBackground
+ val tint = if (isPinned) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onBackground.copy(alpha = SecondaryItemAlpha)
val description = if (isPinned) R.string.action_unpin else R.string.action_pin
IconButton(onClick = onClick) {
Icon(
diff --git a/app/src/main/java/eu/kanade/presentation/browse/manga/MigrateMangaSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/manga/MigrateMangaSearchScreen.kt
index 0aa7d3c2bd..12db7ad92e 100644
--- a/app/src/main/java/eu/kanade/presentation/browse/manga/MigrateMangaSearchScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/browse/manga/MigrateMangaSearchScreen.kt
@@ -21,7 +21,7 @@ import tachiyomi.presentation.core.components.material.Scaffold
fun MigrateMangaSearchScreen(
navigateUp: () -> Unit,
state: MigrateMangaSearchState,
- getManga: @Composable (CatalogueSource, Manga) -> State,
+ getManga: @Composable (Manga) -> State,
onChangeSearchQuery: (String?) -> Unit,
onSearch: (String) -> Unit,
onClickSource: (CatalogueSource) -> Unit,
@@ -58,7 +58,7 @@ fun MigrateMangaSearchContent(
sourceId: Long,
items: Map,
contentPadding: PaddingValues,
- getManga: @Composable (CatalogueSource, Manga) -> State,
+ getManga: @Composable (Manga) -> State,
onClickSource: (CatalogueSource) -> Unit,
onClickItem: (Manga) -> Unit,
onLongClickItem: (Manga) -> Unit,
@@ -85,7 +85,7 @@ fun MigrateMangaSearchContent(
GlobalMangaSearchCardRow(
titles = result.result,
- getManga = { getManga(source, it) },
+ getManga = getManga,
onClick = onClickItem,
onLongClick = onLongClickItem,
)
diff --git a/app/src/main/java/eu/kanade/presentation/components/AdaptiveSheet.kt b/app/src/main/java/eu/kanade/presentation/components/AdaptiveSheet.kt
index ff2a8dbbd5..0746f62c28 100644
--- a/app/src/main/java/eu/kanade/presentation/components/AdaptiveSheet.kt
+++ b/app/src/main/java/eu/kanade/presentation/components/AdaptiveSheet.kt
@@ -14,6 +14,8 @@ import androidx.compose.foundation.layout.only
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Dialog
+import androidx.compose.ui.window.DialogProperties
import cafe.adriel.voyager.core.lifecycle.DisposableEffectIgnoringConfiguration
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.transitions.ScreenTransition
@@ -78,17 +80,25 @@ fun AdaptiveSheet(
content: @Composable (PaddingValues) -> Unit,
) {
val isTabletUi = isTabletUi()
- AdaptiveSheetImpl(
- isTabletUi = isTabletUi,
- tonalElevation = tonalElevation,
- enableSwipeDismiss = enableSwipeDismiss,
+ Dialog(
onDismissRequest = onDismissRequest,
+ properties = DialogProperties(
+ usePlatformDefaultWidth = false,
+ decorFitsSystemWindows = false,
+ ),
) {
- val contentPadding = if (isTabletUi) {
- PaddingValues()
- } else {
- WindowInsets.navigationBars.only(WindowInsetsSides.Bottom).asPaddingValues()
+ AdaptiveSheetImpl(
+ isTabletUi = isTabletUi,
+ tonalElevation = tonalElevation,
+ enableSwipeDismiss = enableSwipeDismiss,
+ onDismissRequest = onDismissRequest,
+ ) {
+ val contentPadding = if (isTabletUi) {
+ PaddingValues()
+ } else {
+ WindowInsets.navigationBars.only(WindowInsetsSides.Bottom).asPaddingValues()
+ }
+ content(contentPadding)
}
- content(contentPadding)
}
}
diff --git a/app/src/main/java/eu/kanade/presentation/components/AppBar.kt b/app/src/main/java/eu/kanade/presentation/components/AppBar.kt
index 7c8342559f..2aefa606a9 100644
--- a/app/src/main/java/eu/kanade/presentation/components/AppBar.kt
+++ b/app/src/main/java/eu/kanade/presentation/components/AppBar.kt
@@ -26,7 +26,6 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
@@ -50,8 +49,10 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.Pill
+import tachiyomi.presentation.core.util.clearFocusOnSoftKeyboardHide
import tachiyomi.presentation.core.util.runOnEnterKeyPressed
import tachiyomi.presentation.core.util.secondaryItemAlpha
+import tachiyomi.presentation.core.util.showSoftKeyboard
const val SEARCH_DEBOUNCE_MILLIS = 250L
@@ -259,7 +260,6 @@ fun SearchToolbar(
actionMode: Boolean = false,
) {
val focusRequester = remember { FocusRequester() }
- var searchClickCount by remember { mutableStateOf(0) }
AppBar(
titleContent = {
@@ -281,7 +281,9 @@ fun SearchToolbar(
modifier = Modifier
.fillMaxWidth()
.focusRequester(focusRequester)
- .runOnEnterKeyPressed(action = searchAndClearFocus),
+ .runOnEnterKeyPressed(action = searchAndClearFocus)
+ .showSoftKeyboard(remember { searchQuery.isEmpty() })
+ .clearFocusOnSoftKeyboardHide(),
textStyle = MaterialTheme.typography.titleMedium.copy(
color = MaterialTheme.colorScheme.onBackground,
fontWeight = FontWeight.Normal,
@@ -320,10 +322,7 @@ fun SearchToolbar(
navigateUp = if (searchQuery == null) navigateUp else onClickCloseSearch,
actions = {
key("search") {
- val onClick = {
- searchClickCount++
- onChangeSearchQuery("")
- }
+ val onClick = { onChangeSearchQuery("") }
if (!searchEnabled) {
// Don't show search action
@@ -332,7 +331,12 @@ fun SearchToolbar(
Icon(Icons.Outlined.Search, contentDescription = stringResource(R.string.action_search))
}
} else if (searchQuery.isNotEmpty()) {
- IconButton(onClick) {
+ IconButton(
+ onClick = {
+ onClick()
+ focusRequester.requestFocus()
+ },
+ ) {
Icon(Icons.Outlined.Close, contentDescription = stringResource(R.string.action_reset))
}
}
@@ -344,15 +348,6 @@ fun SearchToolbar(
scrollBehavior = scrollBehavior,
onCancelActionMode = cancelAction,
)
- LaunchedEffect(searchClickCount) {
- if (searchQuery == null) return@LaunchedEffect
- if (searchClickCount == 0 && searchQuery.isNotEmpty()) return@LaunchedEffect
- try {
- focusRequester.requestFocus()
- } catch (_: Throwable) {
- // TextField is gone
- }
- }
}
sealed interface AppBar {
diff --git a/app/src/main/java/eu/kanade/presentation/entries/ItemHeader.kt b/app/src/main/java/eu/kanade/presentation/entries/ItemHeader.kt
index 6e03286240..ebbaecec36 100644
--- a/app/src/main/java/eu/kanade/presentation/entries/ItemHeader.kt
+++ b/app/src/main/java/eu/kanade/presentation/entries/ItemHeader.kt
@@ -1,27 +1,30 @@
package eu.kanade.presentation.entries
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import eu.kanade.tachiyomi.R
+import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
@Composable
fun ItemHeader(
enabled: Boolean,
itemCount: Int?,
+ missingItemsCount: Int,
onClick: () -> Unit,
isManga: Boolean,
) {
- Row(
+ Column(
modifier = Modifier
.fillMaxWidth()
.clickable(
@@ -29,7 +32,7 @@ fun ItemHeader(
onClick = onClick,
)
.padding(horizontal = 16.dp, vertical = 4.dp),
- verticalAlignment = Alignment.CenterVertically,
+ verticalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(
text = if (itemCount == null) {
@@ -40,8 +43,24 @@ fun ItemHeader(
pluralStringResource(id = pluralCount, count = itemCount, itemCount)
},
style = MaterialTheme.typography.titleMedium,
- modifier = Modifier.weight(1f),
color = MaterialTheme.colorScheme.onBackground,
)
+
+ MissingItemsWarning(missingItemsCount)
+ }
+}
+
+@Composable
+private fun MissingItemsWarning(count: Int) {
+ if (count == 0) {
+ return
}
+
+ Text(
+ text = pluralStringResource(id = R.plurals.missing_items, count = count, count),
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.error.copy(alpha = SecondaryItemAlpha),
+ )
}
diff --git a/app/src/main/java/eu/kanade/presentation/entries/anime/AnimeScreen.kt b/app/src/main/java/eu/kanade/presentation/entries/anime/AnimeScreen.kt
index bba77206fd..a79bfa7069 100644
--- a/app/src/main/java/eu/kanade/presentation/entries/anime/AnimeScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/entries/anime/AnimeScreen.kt
@@ -73,6 +73,7 @@ import eu.kanade.tachiyomi.util.system.copyToClipboard
import kotlinx.coroutines.delay
import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.domain.items.episode.model.Episode
+import tachiyomi.domain.items.service.missingItemsCount
import tachiyomi.domain.source.anime.model.StubAnimeSource
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.TwoPanelBox
@@ -415,6 +416,7 @@ private fun AnimeScreenSmallImpl(
ItemHeader(
enabled = episodes.fastAll { !it.selected },
itemCount = episodes.size,
+ missingItemsCount = episodes.map { it.episode.episodeNumber }.missingItemsCount(),
onClick = onFilterClicked,
isManga = false,
)
@@ -658,6 +660,7 @@ fun AnimeScreenLargeImpl(
ItemHeader(
enabled = episodes.fastAll { !it.selected },
itemCount = episodes.size,
+ missingItemsCount = episodes.map { it.episode.episodeNumber }.missingItemsCount(),
onClick = onFilterButtonClicked,
isManga = false,
)
diff --git a/app/src/main/java/eu/kanade/presentation/entries/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/presentation/entries/manga/MangaScreen.kt
index 3c57de0128..fa4e9d73b4 100644
--- a/app/src/main/java/eu/kanade/presentation/entries/manga/MangaScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/entries/manga/MangaScreen.kt
@@ -68,6 +68,7 @@ import eu.kanade.tachiyomi.util.lang.toRelativeString
import eu.kanade.tachiyomi.util.system.copyToClipboard
import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.domain.items.chapter.model.Chapter
+import tachiyomi.domain.items.service.missingItemsCount
import tachiyomi.domain.source.manga.model.StubMangaSource
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.TwoPanelBox
@@ -397,6 +398,7 @@ private fun MangaScreenSmallImpl(
ItemHeader(
enabled = chapters.fastAll { !it.selected },
itemCount = chapters.size,
+ missingItemsCount = chapters.map { it.chapter.chapterNumber }.missingItemsCount(),
onClick = onFilterClicked,
isManga = true,
)
@@ -611,6 +613,7 @@ fun MangaScreenLargeImpl(
ItemHeader(
enabled = chapters.fastAll { !it.selected },
itemCount = chapters.size,
+ missingItemsCount = chapters.map { it.chapter.chapterNumber }.missingItemsCount(),
onClick = onFilterButtonClicked,
isManga = true,
)
diff --git a/app/src/main/java/eu/kanade/presentation/library/LibraryColumnsDialog.kt b/app/src/main/java/eu/kanade/presentation/library/LibraryColumnsDialog.kt
new file mode 100644
index 0000000000..f3ad0827ef
--- /dev/null
+++ b/app/src/main/java/eu/kanade/presentation/library/LibraryColumnsDialog.kt
@@ -0,0 +1,126 @@
+package eu.kanade.presentation.library
+
+import androidx.compose.foundation.layout.BoxWithConstraints
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.dp
+import eu.kanade.tachiyomi.R
+import tachiyomi.presentation.core.components.WheelPickerDefaults
+import tachiyomi.presentation.core.components.WheelTextPicker
+
+@Composable
+fun LibraryColumnsDialog(
+ initialPortrait: Int,
+ initialLandscape: Int,
+ onDismissRequest: () -> Unit,
+ onValueChanged: (portrait: Int, landscape: Int) -> Unit,
+) {
+ var portraitValue by rememberSaveable { mutableStateOf(initialPortrait) }
+ var landscapeValue by rememberSaveable { mutableStateOf(initialLandscape) }
+
+ AlertDialog(
+ onDismissRequest = onDismissRequest,
+ title = { Text(text = stringResource(R.string.pref_library_columns)) },
+ text = {
+ Column {
+ Row {
+ Text(
+ modifier = Modifier.weight(1f),
+ text = stringResource(R.string.portrait),
+ textAlign = TextAlign.Center,
+ maxLines = 1,
+ style = MaterialTheme.typography.labelMedium,
+ )
+ Text(
+ modifier = Modifier.weight(1f),
+ text = stringResource(R.string.landscape),
+ textAlign = TextAlign.Center,
+ maxLines = 1,
+ style = MaterialTheme.typography.labelMedium,
+ )
+ }
+ LibraryColumnsPicker(
+ modifier = Modifier.fillMaxWidth(),
+ portraitValue = portraitValue,
+ onPortraitChange = { portraitValue = it },
+ landscapeValue = landscapeValue,
+ onLandscapeChange = { landscapeValue = it },
+ )
+ }
+ },
+ dismissButton = {
+ TextButton(onClick = onDismissRequest) {
+ Text(text = stringResource(R.string.action_cancel))
+ }
+ },
+ confirmButton = {
+ TextButton(
+ enabled = portraitValue != initialPortrait || landscapeValue != initialLandscape,
+ onClick = { onValueChanged(portraitValue, landscapeValue) },
+ ) {
+ Text(text = stringResource(android.R.string.ok))
+ }
+ },
+ )
+}
+
+@Composable
+private fun LibraryColumnsPicker(
+ modifier: Modifier = Modifier,
+ portraitValue: Int,
+ onPortraitChange: (Int) -> Unit,
+ landscapeValue: Int,
+ onLandscapeChange: (Int) -> Unit,
+) {
+ BoxWithConstraints(
+ modifier = modifier,
+ contentAlignment = Alignment.Center,
+ ) {
+ WheelPickerDefaults.Background(size = DpSize(maxWidth, 128.dp))
+
+ val size = DpSize(width = maxWidth / 2, height = 128.dp)
+ Row {
+ val columns = (0..10).map { getColumnValue(value = it) }
+ WheelTextPicker(
+ startIndex = portraitValue,
+ items = columns,
+ size = size,
+ onSelectionChanged = onPortraitChange,
+ backgroundContent = null,
+ )
+ WheelTextPicker(
+ startIndex = landscapeValue,
+ items = columns,
+ size = size,
+ onSelectionChanged = onLandscapeChange,
+ backgroundContent = null,
+ )
+ }
+ }
+}
+
+@Composable
+@ReadOnlyComposable
+private fun getColumnValue(value: Int): String {
+ return if (value == 0) {
+ stringResource(R.string.label_default)
+ } else {
+ value.toString()
+ }
+}
diff --git a/app/src/main/java/eu/kanade/presentation/library/anime/AnimeLibrarySettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/library/anime/AnimeLibrarySettingsDialog.kt
index 4c075dcffe..91f19d3691 100644
--- a/app/src/main/java/eu/kanade/presentation/library/anime/AnimeLibrarySettingsDialog.kt
+++ b/app/src/main/java/eu/kanade/presentation/library/anime/AnimeLibrarySettingsDialog.kt
@@ -7,11 +7,15 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.components.TabbedDialog
import eu.kanade.presentation.components.TabbedDialogPaddings
import eu.kanade.presentation.components.TriStateItem
+import eu.kanade.presentation.library.LibraryColumnsDialog
import eu.kanade.presentation.util.collectAsState
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.library.anime.AnimeLibrarySettingsScreenModel
@@ -22,6 +26,7 @@ import tachiyomi.domain.library.anime.model.sort
import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.library.model.display
import tachiyomi.domain.library.service.LibraryPreferences
+import tachiyomi.presentation.core.components.BasicItem
import tachiyomi.presentation.core.components.CheckboxItem
import tachiyomi.presentation.core.components.HeadingItem
import tachiyomi.presentation.core.components.RadioItem
@@ -171,6 +176,23 @@ private fun ColumnScope.DisplayPage(
category: Category,
screenModel: AnimeLibrarySettingsScreenModel,
) {
+ val portraitColumns by screenModel.libraryPreferences.animePortraitColumns().collectAsState()
+ val landscapeColumns by screenModel.libraryPreferences.animeLandscapeColumns().collectAsState()
+
+ var showColumnsDialog by rememberSaveable { mutableStateOf(false) }
+ if (showColumnsDialog) {
+ LibraryColumnsDialog(
+ initialPortrait = portraitColumns,
+ initialLandscape = landscapeColumns,
+ onDismissRequest = { showColumnsDialog = false },
+ onValueChanged = { portrait, landscape ->
+ screenModel.libraryPreferences.animePortraitColumns().set(portrait)
+ screenModel.libraryPreferences.animeLandscapeColumns().set(landscape)
+ showColumnsDialog = false
+ },
+ )
+ }
+
HeadingItem(R.string.action_display_mode)
listOf(
R.string.action_display_grid to LibraryDisplayMode.CompactGrid,
@@ -185,7 +207,14 @@ private fun ColumnScope.DisplayPage(
)
}
- HeadingItem(R.string.complications_header)
+ if (category.display != LibraryDisplayMode.List) {
+ BasicItem(
+ label = stringResource(R.string.pref_library_columns),
+ onClick = { showColumnsDialog = true },
+ )
+ }
+
+ HeadingItem(R.string.overlay_header)
val downloadBadge by screenModel.libraryPreferences.downloadBadge().collectAsState()
CheckboxItem(
label = stringResource(R.string.action_display_download_badge_anime),
diff --git a/app/src/main/java/eu/kanade/presentation/library/manga/MangaLibrarySettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/library/manga/MangaLibrarySettingsDialog.kt
index ba911a5e5b..9b9843c6e0 100644
--- a/app/src/main/java/eu/kanade/presentation/library/manga/MangaLibrarySettingsDialog.kt
+++ b/app/src/main/java/eu/kanade/presentation/library/manga/MangaLibrarySettingsDialog.kt
@@ -7,11 +7,15 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.components.TabbedDialog
import eu.kanade.presentation.components.TabbedDialogPaddings
import eu.kanade.presentation.components.TriStateItem
+import eu.kanade.presentation.library.LibraryColumnsDialog
import eu.kanade.presentation.util.collectAsState
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.library.manga.MangaLibrarySettingsScreenModel
@@ -22,6 +26,7 @@ import tachiyomi.domain.library.manga.model.sort
import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.library.model.display
import tachiyomi.domain.library.service.LibraryPreferences
+import tachiyomi.presentation.core.components.BasicItem
import tachiyomi.presentation.core.components.CheckboxItem
import tachiyomi.presentation.core.components.HeadingItem
import tachiyomi.presentation.core.components.RadioItem
@@ -170,6 +175,23 @@ private fun ColumnScope.DisplayPage(
category: Category,
screenModel: MangaLibrarySettingsScreenModel,
) {
+ val portraitColumns by screenModel.libraryPreferences.mangaPortraitColumns().collectAsState()
+ val landscapeColumns by screenModel.libraryPreferences.mangaLandscapeColumns().collectAsState()
+
+ var showColumnsDialog by rememberSaveable { mutableStateOf(false) }
+ if (showColumnsDialog) {
+ LibraryColumnsDialog(
+ initialPortrait = portraitColumns,
+ initialLandscape = landscapeColumns,
+ onDismissRequest = { showColumnsDialog = false },
+ onValueChanged = { portrait, landscape ->
+ screenModel.libraryPreferences.mangaPortraitColumns().set(portrait)
+ screenModel.libraryPreferences.mangaLandscapeColumns().set(landscape)
+ showColumnsDialog = false
+ },
+ )
+ }
+
HeadingItem(R.string.action_display_mode)
listOf(
R.string.action_display_grid to LibraryDisplayMode.CompactGrid,
@@ -184,7 +206,14 @@ private fun ColumnScope.DisplayPage(
)
}
- HeadingItem(R.string.complications_header)
+ if (category.display != LibraryDisplayMode.List) {
+ BasicItem(
+ label = stringResource(R.string.pref_library_columns),
+ onClick = { showColumnsDialog = true },
+ )
+ }
+
+ HeadingItem(R.string.overlay_header)
val downloadBadge by screenModel.libraryPreferences.downloadBadge().collectAsState()
CheckboxItem(
label = stringResource(R.string.action_display_download_badge),
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/AboutScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/AboutScreen.kt
index ae1f6f5a81..f6f62eca9d 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/AboutScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/AboutScreen.kt
@@ -227,7 +227,7 @@ object AboutScreen : Screen() {
}
}
- private fun getFormattedBuildTime(): String {
+ internal fun getFormattedBuildTime(): String {
return try {
val inputDf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'", Locale.US)
inputDf.timeZone = TimeZone.getTimeZone("UTC")
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt
index 4c4bc44fb6..12109eef70 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAdvancedScreen.kt
@@ -26,6 +26,7 @@ import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.domain.base.BasePreferences
import eu.kanade.presentation.more.settings.Preference
+import eu.kanade.presentation.more.settings.screen.debug.DebugInfoScreen
import eu.kanade.presentation.util.collectAsState
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.cache.ChapterCache
@@ -33,7 +34,6 @@ import eu.kanade.tachiyomi.data.cache.EpisodeCache
import eu.kanade.tachiyomi.data.download.anime.AnimeDownloadCache
import eu.kanade.tachiyomi.data.download.manga.MangaDownloadCache
import eu.kanade.tachiyomi.data.library.manga.MangaLibraryUpdateJob
-import eu.kanade.tachiyomi.data.preference.PreferenceValues
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.NetworkPreferences
@@ -76,6 +76,8 @@ object SettingsAdvancedScreen : SearchableSettings {
override fun getPreferences(): List {
val scope = rememberCoroutineScope()
val context = LocalContext.current
+ val navigator = LocalNavigator.currentOrThrow
+
val basePreferences = remember { Injekt.get() }
val networkPreferences = remember { Injekt.get() }
@@ -104,6 +106,10 @@ object SettingsAdvancedScreen : SearchableSettings {
true
},
),
+ Preference.PreferenceItem.TextPreference(
+ title = stringResource(R.string.pref_debug_info),
+ onClick = { navigator.push(DebugInfoScreen) },
+ ),
getBackgroundActivityGroup(),
getDataGroup(),
getNetworkGroup(networkPreferences = networkPreferences),
@@ -116,7 +122,6 @@ object SettingsAdvancedScreen : SearchableSettings {
private fun getBackgroundActivityGroup(): Preference.PreferenceGroup {
val context = LocalContext.current
val uriHandler = LocalUriHandler.current
- val navigator = LocalNavigator.currentOrThrow
return Preference.PreferenceGroup(
title = stringResource(R.string.label_background_activity),
@@ -148,10 +153,6 @@ object SettingsAdvancedScreen : SearchableSettings {
subtitle = stringResource(R.string.about_dont_kill_my_app),
onClick = { uriHandler.openUri("https://dontkillmyapp.com/") },
),
- Preference.PreferenceItem.TextPreference(
- title = stringResource(R.string.pref_worker_info),
- onClick = { navigator.push(WorkerInfoScreen) },
- ),
),
)
}
@@ -386,7 +387,7 @@ object SettingsAdvancedScreen : SearchableSettings {
entries = extensionInstallerPref.entries
.associateWith { stringResource(it.titleResId) },
onValueChanged = {
- if (it == PreferenceValues.ExtensionInstaller.SHIZUKU &&
+ if (it == BasePreferences.ExtensionInstaller.SHIZUKU &&
!context.isShizukuInstalled
) {
shizukuMissing = true
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBackupScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBackupScreen.kt
index 0e5dc4d0f5..0e2f941461 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBackupScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBackupScreen.kt
@@ -41,23 +41,23 @@ import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.util.collectAsState
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.backup.BackupConst
-import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
+import eu.kanade.tachiyomi.data.backup.BackupCreateJob
import eu.kanade.tachiyomi.data.backup.BackupFileValidator
-import eu.kanade.tachiyomi.data.backup.BackupRestoreService
+import eu.kanade.tachiyomi.data.backup.BackupRestoreJob
import eu.kanade.tachiyomi.data.backup.models.Backup
-import eu.kanade.tachiyomi.data.preference.FLAG_CATEGORIES
-import eu.kanade.tachiyomi.data.preference.FLAG_CHAPTERS
-import eu.kanade.tachiyomi.data.preference.FLAG_EXTENSIONS
-import eu.kanade.tachiyomi.data.preference.FLAG_EXT_SETTINGS
-import eu.kanade.tachiyomi.data.preference.FLAG_HISTORY
-import eu.kanade.tachiyomi.data.preference.FLAG_SETTINGS
-import eu.kanade.tachiyomi.data.preference.FLAG_TRACK
import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.system.DeviceUtil
import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.launch
import tachiyomi.domain.backup.service.BackupPreferences
+import tachiyomi.domain.backup.service.FLAG_CATEGORIES
+import tachiyomi.domain.backup.service.FLAG_CHAPTERS
+import tachiyomi.domain.backup.service.FLAG_EXTENSIONS
+import tachiyomi.domain.backup.service.FLAG_EXT_SETTINGS
+import tachiyomi.domain.backup.service.FLAG_HISTORY
+import tachiyomi.domain.backup.service.FLAG_SETTINGS
+import tachiyomi.domain.backup.service.FLAG_TRACK
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
import tachiyomi.presentation.core.components.material.Divider
import tachiyomi.presentation.core.util.isScrolledToEnd
@@ -100,7 +100,7 @@ object SettingsBackupScreen : SearchableSettings {
Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
)
- BackupCreatorJob.startNow(context, it, flag)
+ BackupCreateJob.startNow(context, it, flag)
}
flag = 0
}
@@ -126,7 +126,7 @@ object SettingsBackupScreen : SearchableSettings {
subtitle = stringResource(R.string.pref_create_backup_summ),
onClick = {
scope.launch {
- if (!BackupCreatorJob.isManualJobRunning(context)) {
+ if (!BackupCreateJob.isManualJobRunning(context)) {
if (DeviceUtil.isMiui && DeviceUtil.isMiuiOptimizationDisabled()) {
context.toast(R.string.restore_miui_warning, Toast.LENGTH_LONG)
}
@@ -288,7 +288,7 @@ object SettingsBackupScreen : SearchableSettings {
confirmButton = {
TextButton(
onClick = {
- BackupRestoreService.start(context, err.uri)
+ BackupRestoreJob.start(context, err.uri)
onDismissRequest()
},
) {
@@ -318,7 +318,7 @@ object SettingsBackupScreen : SearchableSettings {
}
if (results.missingSources.isEmpty() && results.missingTrackers.isEmpty()) {
- BackupRestoreService.start(context, it)
+ BackupRestoreJob.start(context, it)
return@rememberLauncherForActivityResult
}
@@ -330,7 +330,7 @@ object SettingsBackupScreen : SearchableSettings {
title = stringResource(R.string.pref_restore_backup),
subtitle = stringResource(R.string.pref_restore_backup_summ),
onClick = {
- if (!BackupRestoreService.isRunning(context)) {
+ if (!BackupRestoreJob.isRunning(context)) {
if (DeviceUtil.isMiui && DeviceUtil.isMiuiOptimizationDisabled()) {
context.toast(R.string.restore_miui_warning, Toast.LENGTH_LONG)
}
@@ -381,7 +381,7 @@ object SettingsBackupScreen : SearchableSettings {
168 to stringResource(R.string.update_weekly),
),
onValueChanged = {
- BackupCreatorJob.setupTask(context, it)
+ BackupCreateJob.setupTask(context, it)
true
},
),
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt
index bb19ebb98f..4f905a5506 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDownloadScreen.kt
@@ -61,6 +61,11 @@ object SettingsDownloadScreen : SearchableSettings {
pref = downloadPreferences.saveChaptersAsCBZ(),
title = stringResource(R.string.save_chapter_as_cbz),
),
+ Preference.PreferenceItem.SwitchPreference(
+ pref = downloadPreferences.splitTallImages(),
+ title = stringResource(R.string.split_tall_images),
+ subtitle = stringResource(R.string.split_tall_images_summary),
+ ),
Preference.PreferenceItem.ListPreference(
pref = downloadPreferences.numberOfDownloads(),
title = stringResource(R.string.pref_download_slots),
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt
index c79487d6cf..1638793a44 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsLibraryScreen.kt
@@ -1,14 +1,6 @@
package eu.kanade.presentation.more.settings.screen
import androidx.annotation.StringRes
-import androidx.compose.foundation.layout.BoxWithConstraints
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.collectAsState
@@ -18,14 +10,9 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.unit.DpSize
-import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastMap
import androidx.core.content.ContextCompat
import cafe.adriel.voyager.navigator.LocalNavigator
@@ -53,8 +40,6 @@ import tachiyomi.domain.library.service.LibraryPreferences.Companion.DEVICE_ONLY
import tachiyomi.domain.library.service.LibraryPreferences.Companion.ENTRY_HAS_UNVIEWED
import tachiyomi.domain.library.service.LibraryPreferences.Companion.ENTRY_NON_COMPLETED
import tachiyomi.domain.library.service.LibraryPreferences.Companion.ENTRY_NON_VIEWED
-import tachiyomi.presentation.core.components.WheelPicker
-import tachiyomi.presentation.core.components.WheelPickerDefaults
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@@ -74,69 +59,11 @@ object SettingsLibraryScreen : SearchableSettings {
val libraryPreferences = remember { Injekt.get() }
return mutableListOf(
- getDisplayGroup(libraryPreferences),
getCategoriesGroup(LocalNavigator.currentOrThrow, allCategories, allAnimeCategories, libraryPreferences),
getGlobalUpdateGroup(allCategories, allAnimeCategories, libraryPreferences),
)
}
- @Composable
- private fun getDisplayGroup(libraryPreferences: LibraryPreferences): Preference.PreferenceGroup {
- val scope = rememberCoroutineScope()
-
- val animePortraitColumns by libraryPreferences.animePortraitColumns().stateIn(scope).collectAsState()
- val mangaPortraitColumns by libraryPreferences.mangaPortraitColumns().stateIn(scope).collectAsState()
- val animeLandscapeColumns by libraryPreferences.animeLandscapeColumns().stateIn(scope).collectAsState()
- val mangaLandscapeColumns by libraryPreferences.mangaLandscapeColumns().stateIn(scope).collectAsState()
-
- var showAnimeDialog by rememberSaveable { mutableStateOf(false) }
- var showDialog by rememberSaveable { mutableStateOf(false) }
-
- if (showAnimeDialog) {
- LibraryColumnsDialog(
- initialPortrait = animePortraitColumns,
- initialLandscape = animeLandscapeColumns,
- onDismissRequest = { showAnimeDialog = false },
- onValueChanged = { portrait, landscape ->
- libraryPreferences.animePortraitColumns().set(portrait)
- libraryPreferences.animeLandscapeColumns().set(landscape)
- showAnimeDialog = false
- },
- )
- }
-
- if (showDialog) {
- LibraryColumnsDialog(
- initialPortrait = mangaPortraitColumns,
- initialLandscape = mangaLandscapeColumns,
- onDismissRequest = { showDialog = false },
- onValueChanged = { portrait, landscape ->
- libraryPreferences.mangaPortraitColumns().set(portrait)
- libraryPreferences.mangaLandscapeColumns().set(landscape)
- showDialog = false
- },
- )
- }
-
- return Preference.PreferenceGroup(
- title = stringResource(R.string.pref_category_display),
- preferenceItems = listOf(
- Preference.PreferenceItem.TextPreference(
- title = stringResource(R.string.pref_library_anime_columns),
- subtitle = "${stringResource(R.string.portrait)}: ${getColumnValue(animePortraitColumns)}, " +
- "${stringResource(R.string.landscape)}: ${getColumnValue(animeLandscapeColumns)}",
- onClick = { showAnimeDialog = true },
- ),
- Preference.PreferenceItem.TextPreference(
- title = stringResource(R.string.pref_library_manga_columns),
- subtitle = "${stringResource(R.string.portrait)}: ${getColumnValue(mangaPortraitColumns)}, " +
- "${stringResource(R.string.landscape)}: ${getColumnValue(mangaLandscapeColumns)}",
- onClick = { showDialog = true },
- ),
- ),
- )
- }
-
@Composable
private fun getCategoriesGroup(
navigator: Navigator,
@@ -349,108 +276,4 @@ object SettingsLibraryScreen : SearchableSettings {
),
)
}
-
- @Composable
- private fun LibraryColumnsDialog(
- initialPortrait: Int,
- initialLandscape: Int,
- onDismissRequest: () -> Unit,
- onValueChanged: (portrait: Int, landscape: Int) -> Unit,
- ) {
- var portraitValue by rememberSaveable { mutableStateOf(initialPortrait) }
- var landscapeValue by rememberSaveable { mutableStateOf(initialLandscape) }
-
- AlertDialog(
- onDismissRequest = onDismissRequest,
- title = { Text(text = stringResource(R.string.pref_library_columns)) },
- text = {
- Column {
- Row {
- Text(
- modifier = Modifier.weight(1f),
- text = stringResource(R.string.portrait),
- textAlign = TextAlign.Center,
- maxLines = 1,
- style = MaterialTheme.typography.labelMedium,
- )
- Text(
- modifier = Modifier.weight(1f),
- text = stringResource(R.string.landscape),
- textAlign = TextAlign.Center,
- maxLines = 1,
- style = MaterialTheme.typography.labelMedium,
- )
- }
- LibraryColumnsPicker(
- modifier = Modifier.fillMaxWidth(),
- portraitValue = portraitValue,
- onPortraitChange = { portraitValue = it },
- landscapeValue = landscapeValue,
- onLandscapeChange = { landscapeValue = it },
- )
- }
- },
- dismissButton = {
- TextButton(onClick = onDismissRequest) {
- Text(text = stringResource(R.string.action_cancel))
- }
- },
- confirmButton = {
- TextButton(
- enabled = portraitValue != initialPortrait || landscapeValue != initialLandscape,
- onClick = { onValueChanged(portraitValue, landscapeValue) },
- ) {
- Text(text = stringResource(android.R.string.ok))
- }
- },
- )
- }
-
- @Composable
- private fun LibraryColumnsPicker(
- modifier: Modifier = Modifier,
- portraitValue: Int,
- onPortraitChange: (Int) -> Unit,
- landscapeValue: Int,
- onLandscapeChange: (Int) -> Unit,
- ) {
- BoxWithConstraints(
- modifier = modifier,
- contentAlignment = Alignment.Center,
- ) {
- WheelPickerDefaults.Background(size = DpSize(maxWidth, maxHeight))
-
- val size = DpSize(width = maxWidth / 2, height = 128.dp)
- Row {
- WheelPicker(
- size = size,
- count = 11,
- startIndex = portraitValue,
- onSelectionChanged = onPortraitChange,
- backgroundContent = null,
- ) { index ->
- WheelPickerDefaults.Item(text = getColumnValue(value = index))
- }
- WheelPicker(
- size = size,
- count = 11,
- startIndex = landscapeValue,
- onSelectionChanged = onLandscapeChange,
- backgroundContent = null,
- ) { index ->
- WheelPickerDefaults.Item(text = getColumnValue(value = index))
- }
- }
- }
- }
-
- @Composable
- @ReadOnlyComposable
- private fun getColumnValue(value: Int): String {
- return if (value == 0) {
- stringResource(R.string.label_default)
- } else {
- value.toString()
- }
- }
}
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsPlayerScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsPlayerScreen.kt
index cb5409c658..daa7400001 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsPlayerScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsPlayerScreen.kt
@@ -357,7 +357,7 @@ object SettingsPlayerScreen : SearchableSettings {
content = {
WheelTextPicker(
modifier = Modifier.align(Alignment.Center),
- texts = remember { 1..255 }.map {
+ items = remember { 1..255 }.map {
stringResource(
R.string.seconds_short,
it,
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 033de10587..b49f0dcd7e 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
@@ -12,8 +12,6 @@ import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.util.collectAsState
import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.data.preference.PreferenceValues.ReaderHideThreshold
-import eu.kanade.tachiyomi.data.preference.PreferenceValues.TappingInvertMode
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
@@ -156,10 +154,12 @@ object SettingsReaderScreen : SearchableSettings {
val navModePref = readerPreferences.navigationModePager()
val imageScaleTypePref = readerPreferences.imageScaleType()
val dualPageSplitPref = readerPreferences.dualPageSplitPaged()
+ val rotateToFitPref = readerPreferences.dualPageRotateToFit()
val navMode by navModePref.collectAsState()
val imageScaleType by imageScaleTypePref.collectAsState()
val dualPageSplit by dualPageSplitPref.collectAsState()
+ val rotateToFit by rotateToFitPref.collectAsState()
return Preference.PreferenceGroup(
title = stringResource(R.string.pager_viewer),
@@ -175,10 +175,10 @@ object SettingsReaderScreen : SearchableSettings {
pref = readerPreferences.pagerNavInverted(),
title = stringResource(R.string.pref_read_with_tapping_inverted),
entries = mapOf(
- TappingInvertMode.NONE to stringResource(R.string.none),
- TappingInvertMode.HORIZONTAL to stringResource(R.string.tapping_inverted_horizontal),
- TappingInvertMode.VERTICAL to stringResource(R.string.tapping_inverted_vertical),
- TappingInvertMode.BOTH to stringResource(R.string.tapping_inverted_both),
+ ReaderPreferences.TappingInvertMode.NONE to stringResource(R.string.none),
+ ReaderPreferences.TappingInvertMode.HORIZONTAL to stringResource(R.string.tapping_inverted_horizontal),
+ ReaderPreferences.TappingInvertMode.VERTICAL to stringResource(R.string.tapping_inverted_vertical),
+ ReaderPreferences.TappingInvertMode.BOTH to stringResource(R.string.tapping_inverted_both),
),
enabled = navMode != 5,
),
@@ -222,6 +222,10 @@ object SettingsReaderScreen : SearchableSettings {
Preference.PreferenceItem.SwitchPreference(
pref = dualPageSplitPref,
title = stringResource(R.string.pref_dual_page_split),
+ onValueChanged = {
+ rotateToFitPref.set(false)
+ true
+ },
),
Preference.PreferenceItem.SwitchPreference(
pref = readerPreferences.dualPageInvertPaged(),
@@ -229,6 +233,19 @@ object SettingsReaderScreen : SearchableSettings {
subtitle = stringResource(R.string.pref_dual_page_invert_summary),
enabled = dualPageSplit,
),
+ Preference.PreferenceItem.SwitchPreference(
+ pref = rotateToFitPref,
+ title = stringResource(R.string.pref_page_rotate),
+ onValueChanged = {
+ dualPageSplitPref.set(false)
+ true
+ },
+ ),
+ Preference.PreferenceItem.SwitchPreference(
+ pref = readerPreferences.dualPageRotateToFitInvert(),
+ title = stringResource(R.string.pref_page_rotate_invert),
+ enabled = rotateToFit,
+ ),
),
)
}
@@ -255,10 +272,10 @@ object SettingsReaderScreen : SearchableSettings {
pref = readerPreferences.webtoonNavInverted(),
title = stringResource(R.string.pref_read_with_tapping_inverted),
entries = mapOf(
- TappingInvertMode.NONE to stringResource(R.string.none),
- TappingInvertMode.HORIZONTAL to stringResource(R.string.tapping_inverted_horizontal),
- TappingInvertMode.VERTICAL to stringResource(R.string.tapping_inverted_vertical),
- TappingInvertMode.BOTH to stringResource(R.string.tapping_inverted_both),
+ ReaderPreferences.TappingInvertMode.NONE to stringResource(R.string.none),
+ ReaderPreferences.TappingInvertMode.HORIZONTAL to stringResource(R.string.tapping_inverted_horizontal),
+ ReaderPreferences.TappingInvertMode.VERTICAL to stringResource(R.string.tapping_inverted_vertical),
+ ReaderPreferences.TappingInvertMode.BOTH to stringResource(R.string.tapping_inverted_both),
),
enabled = navMode != 5,
),
@@ -278,10 +295,10 @@ object SettingsReaderScreen : SearchableSettings {
pref = readerPreferences.readerHideThreshold(),
title = stringResource(R.string.pref_hide_threshold),
entries = mapOf(
- ReaderHideThreshold.HIGHEST to stringResource(R.string.pref_highest),
- ReaderHideThreshold.HIGH to stringResource(R.string.pref_high),
- ReaderHideThreshold.LOW to stringResource(R.string.pref_low),
- ReaderHideThreshold.LOWEST to stringResource(R.string.pref_lowest),
+ ReaderPreferences.ReaderHideThreshold.HIGHEST to stringResource(R.string.pref_highest),
+ ReaderPreferences.ReaderHideThreshold.HIGH to stringResource(R.string.pref_high),
+ ReaderPreferences.ReaderHideThreshold.LOW to stringResource(R.string.pref_low),
+ ReaderPreferences.ReaderHideThreshold.LOWEST to stringResource(R.string.pref_lowest),
),
),
Preference.PreferenceItem.SwitchPreference(
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/DebugInfoScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/DebugInfoScreen.kt
new file mode 100644
index 0000000000..5128cf5e24
--- /dev/null
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/DebugInfoScreen.kt
@@ -0,0 +1,132 @@
+package eu.kanade.presentation.more.settings.screen.debug
+
+import android.os.Build
+import android.webkit.WebView
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.produceState
+import androidx.compose.ui.platform.LocalContext
+import androidx.profileinstaller.ProfileVerifier
+import cafe.adriel.voyager.navigator.LocalNavigator
+import cafe.adriel.voyager.navigator.currentOrThrow
+import eu.kanade.presentation.more.settings.Preference
+import eu.kanade.presentation.more.settings.PreferenceScaffold
+import eu.kanade.presentation.more.settings.screen.AboutScreen
+import eu.kanade.presentation.util.Screen
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.util.system.DeviceUtil
+import kotlinx.coroutines.guava.await
+
+object DebugInfoScreen : Screen() {
+ @Composable
+ override fun Content() {
+ val navigator = LocalNavigator.currentOrThrow
+ PreferenceScaffold(
+ titleRes = R.string.pref_debug_info,
+ onBackPressed = navigator::pop,
+ itemsProvider = {
+ listOf(
+ Preference.PreferenceItem.TextPreference(
+ title = WorkerInfoScreen.title,
+ onClick = { navigator.push(WorkerInfoScreen) },
+ ),
+ getAppInfoGroup(),
+ getDeviceInfoGroup(),
+ )
+ },
+ )
+ }
+
+ @Composable
+ private fun getAppInfoGroup(): Preference.PreferenceGroup {
+ return Preference.PreferenceGroup(
+ title = "App info",
+ preferenceItems = listOf(
+ Preference.PreferenceItem.TextPreference(
+ title = "Version",
+ subtitle = AboutScreen.getVersionName(false),
+ ),
+ Preference.PreferenceItem.TextPreference(
+ title = "Build time",
+ subtitle = AboutScreen.getFormattedBuildTime(),
+ ),
+ getProfileVerifierPreference(),
+ Preference.PreferenceItem.TextPreference(
+ title = "WebView version",
+ subtitle = getWebViewVersion(),
+ ),
+ ),
+ )
+ }
+
+ @Composable
+ @ReadOnlyComposable
+ private fun getWebViewVersion(): String {
+ val webView = WebView.getCurrentWebViewPackage() ?: return "how did you get here?"
+ val pm = LocalContext.current.packageManager
+ val label = webView.applicationInfo.loadLabel(pm)
+ val version = webView.versionName
+ return "$label $version"
+ }
+
+ @Composable
+ private fun getProfileVerifierPreference(): Preference.PreferenceItem.TextPreference {
+ val status by produceState(initialValue = "-") {
+ val result = ProfileVerifier.getCompilationStatusAsync().await().profileInstallResultCode
+ value = when (result) {
+ ProfileVerifier.CompilationStatus.RESULT_CODE_NO_PROFILE -> "No profile installed"
+ ProfileVerifier.CompilationStatus.RESULT_CODE_COMPILED_WITH_PROFILE -> "Compiled"
+ ProfileVerifier.CompilationStatus.RESULT_CODE_COMPILED_WITH_PROFILE_NON_MATCHING -> "Compiled non-matching"
+ ProfileVerifier.CompilationStatus.RESULT_CODE_ERROR_CACHE_FILE_EXISTS_BUT_CANNOT_BE_READ,
+ ProfileVerifier.CompilationStatus.RESULT_CODE_ERROR_CANT_WRITE_PROFILE_VERIFICATION_RESULT_CACHE_FILE,
+ ProfileVerifier.CompilationStatus.RESULT_CODE_ERROR_PACKAGE_NAME_DOES_NOT_EXIST,
+ -> "Error $result"
+ ProfileVerifier.CompilationStatus.RESULT_CODE_ERROR_UNSUPPORTED_API_VERSION -> "Not supported"
+ ProfileVerifier.CompilationStatus.RESULT_CODE_PROFILE_ENQUEUED_FOR_COMPILATION -> "Pending compilation"
+ else -> "Unknown code $result"
+ }
+ }
+ return Preference.PreferenceItem.TextPreference(
+ title = "Profile compilation status",
+ subtitle = status,
+ )
+ }
+
+ private fun getDeviceInfoGroup(): Preference.PreferenceGroup {
+ val items = mutableListOf(
+ Preference.PreferenceItem.TextPreference(
+ title = "Model",
+ subtitle = "${Build.MANUFACTURER} ${Build.MODEL} (${Build.DEVICE})",
+ ),
+ )
+ if (DeviceUtil.oneUiVersion != null) {
+ items += Preference.PreferenceItem.TextPreference(
+ title = "OneUI version",
+ subtitle = "${DeviceUtil.oneUiVersion}",
+ )
+ } else if (DeviceUtil.miuiMajorVersion != null) {
+ items += Preference.PreferenceItem.TextPreference(
+ title = "MIUI version",
+ subtitle = "${DeviceUtil.miuiMajorVersion}",
+ )
+ }
+
+ val androidVersion = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ Build.VERSION.RELEASE_OR_PREVIEW_DISPLAY
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ Build.VERSION.RELEASE_OR_CODENAME
+ } else {
+ Build.VERSION.RELEASE
+ }
+ items += Preference.PreferenceItem.TextPreference(
+ title = "Android version",
+ subtitle = "$androidVersion (${Build.DISPLAY})",
+ )
+
+ return Preference.PreferenceGroup(
+ title = "Device info",
+ preferenceItems = items,
+ )
+ }
+}
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/WorkerInfoScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt
similarity index 78%
rename from app/src/main/java/eu/kanade/presentation/more/settings/screen/WorkerInfoScreen.kt
rename to app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt
index b09ee8d072..4decc0e1dc 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/WorkerInfoScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt
@@ -1,4 +1,4 @@
-package eu.kanade.presentation.more.settings.screen
+package eu.kanade.presentation.more.settings.screen.debug
import android.content.Context
import androidx.compose.foundation.horizontalScroll
@@ -11,43 +11,39 @@ import androidx.compose.material.icons.filled.ContentCopy
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.SnackbarHost
-import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastForEach
import androidx.lifecycle.asFlow
import androidx.work.WorkInfo
-import androidx.work.WorkManager
import androidx.work.WorkQuery
import cafe.adriel.voyager.core.model.ScreenModel
-import cafe.adriel.voyager.core.model.coroutineScope
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import eu.kanade.presentation.util.Screen
-import eu.kanade.tachiyomi.R
+import eu.kanade.presentation.util.ioCoroutineScope
+import eu.kanade.tachiyomi.util.system.copyToClipboard
+import eu.kanade.tachiyomi.util.system.workManager
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
import tachiyomi.presentation.core.components.LazyColumn
import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.util.plus
object WorkerInfoScreen : Screen() {
+ const val title = "Worker info"
+
@Composable
override fun Content() {
val context = LocalContext.current
@@ -59,24 +55,19 @@ object WorkerInfoScreen : Screen() {
val finished by screenModel.finished.collectAsState()
val running by screenModel.running.collectAsState()
- val snackbarHostState = remember { SnackbarHostState() }
- val scope = rememberCoroutineScope()
-
Scaffold(
topBar = {
TopAppBar(
- title = { Text(text = stringResource(R.string.pref_worker_info)) },
+ title = { Text(text = title) },
navigationIcon = {
IconButton(onClick = navigator::pop) {
Icon(imageVector = Icons.Default.ArrowBack, contentDescription = null)
}
},
actions = {
- val copiedString = stringResource(R.string.copied_to_clipboard_plain)
IconButton(
onClick = {
- clipboardManager.setText(AnnotatedString(enqueued + finished + running))
- scope.launch { snackbarHostState.showSnackbar(copiedString) }
+ context.copyToClipboard(title, enqueued + finished + running)
},
) {
Icon(imageVector = Icons.Default.ContentCopy, contentDescription = null)
@@ -85,7 +76,6 @@ object WorkerInfoScreen : Screen() {
scrollBehavior = it,
)
},
- snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
) { contentPadding ->
LazyColumn(
contentPadding = contentPadding + PaddingValues(horizontal = 16.dp),
@@ -122,31 +112,31 @@ object WorkerInfoScreen : Screen() {
}
private class Model(context: Context) : ScreenModel {
- private val workManager = WorkManager.getInstance(context)
+ private val workManager = context.workManager
val finished = workManager
.getWorkInfosLiveData(WorkQuery.fromStates(WorkInfo.State.SUCCEEDED, WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
.asFlow()
.map(::constructString)
- .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), "")
+ .stateIn(ioCoroutineScope, SharingStarted.WhileSubscribed(), "")
val running = workManager
.getWorkInfosLiveData(WorkQuery.fromStates(WorkInfo.State.RUNNING))
.asFlow()
.map(::constructString)
- .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), "")
+ .stateIn(ioCoroutineScope, SharingStarted.WhileSubscribed(), "")
val enqueued = workManager
.getWorkInfosLiveData(WorkQuery.fromStates(WorkInfo.State.ENQUEUED))
.asFlow()
.map(::constructString)
- .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), "")
+ .stateIn(ioCoroutineScope, SharingStarted.WhileSubscribed(), "")
private fun constructString(list: List) = buildString {
if (list.isEmpty()) {
appendLine("-")
} else {
- list.forEach { workInfo ->
+ list.fastForEach { workInfo ->
appendLine("Id: ${workInfo.id}")
appendLine("Tags:")
workInfo.tags.forEach {
diff --git a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogSelector.kt b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogSelector.kt
index b3e6c79b11..3855b395fb 100644
--- a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogSelector.kt
+++ b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogSelector.kt
@@ -32,6 +32,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import eu.kanade.tachiyomi.R
import tachiyomi.presentation.core.components.ScrollbarLazyColumn
+import tachiyomi.presentation.core.components.WheelNumberPicker
import tachiyomi.presentation.core.components.WheelTextPicker
import tachiyomi.presentation.core.components.material.AlertDialogContent
import tachiyomi.presentation.core.components.material.Divider
@@ -100,10 +101,10 @@ fun TrackItemSelector(
BaseSelector(
title = stringResource(titleText),
content = {
- WheelTextPicker(
+ WheelNumberPicker(
modifier = Modifier.align(Alignment.Center),
startIndex = selection,
- texts = range.map { "$it" },
+ items = range.toList(),
onSelectionChanged = { onSelectionChange(it) },
)
},
@@ -126,7 +127,7 @@ fun TrackScoreSelector(
WheelTextPicker(
modifier = Modifier.align(Alignment.Center),
startIndex = selections.indexOf(selection).coerceAtLeast(0),
- texts = selections,
+ items = selections,
onSelectionChanged = { onSelectionChange(selections[it]) },
)
},
@@ -149,12 +150,14 @@ fun TrackDateSelector(
)
AlertDialogContent(
modifier = Modifier.windowInsetsPadding(WindowInsets.systemBars),
+ title = { Text(text = title) },
content = {
Column {
DatePicker(
state = pickerState,
- title = { Text(text = title) },
dateValidator = dateValidator,
+ title = null,
+ headline = null,
showModeToggle = false,
)
Row(
diff --git a/app/src/main/java/eu/kanade/presentation/util/Navigator.kt b/app/src/main/java/eu/kanade/presentation/util/Navigator.kt
index fe89516d64..f9eaa93df6 100644
--- a/app/src/main/java/eu/kanade/presentation/util/Navigator.kt
+++ b/app/src/main/java/eu/kanade/presentation/util/Navigator.kt
@@ -3,12 +3,20 @@ package eu.kanade.presentation.util
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ProvidableCompositionLocal
import androidx.compose.runtime.staticCompositionLocalOf
+import cafe.adriel.voyager.core.model.ScreenModel
+import cafe.adriel.voyager.core.model.ScreenModelStore
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.core.screen.ScreenKey
import cafe.adriel.voyager.core.screen.uniqueScreenKey
import cafe.adriel.voyager.core.stack.StackEvent
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.transitions.ScreenTransition
+import kotlinx.coroutines.CoroutineName
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.plus
import soup.compose.material.motion.animation.materialSharedAxisX
import soup.compose.material.motion.animation.rememberSlideDistance
@@ -28,6 +36,18 @@ abstract class Screen : Screen {
override val key: ScreenKey = uniqueScreenKey
}
+/**
+ * A variant of ScreenModel.coroutineScope except with the IO dispatcher instead of the
+ * main dispatcher.
+ */
+val ScreenModel.ioCoroutineScope: CoroutineScope
+ get() = ScreenModelStore.getOrPutDependency(
+ screenModel = this,
+ name = "ScreenModelIoCoroutineScope",
+ factory = { key -> CoroutineScope(Dispatchers.IO + SupervisorJob()) + CoroutineName(key) },
+ onDispose = { scope -> scope.cancel() },
+ )
+
interface AssistContentScreen {
fun onProvideAssistUrl(): String?
}
diff --git a/app/src/main/java/eu/kanade/presentation/util/Resources.kt b/app/src/main/java/eu/kanade/presentation/util/Resources.kt
index 44d9968b94..5b7bc9948d 100644
--- a/app/src/main/java/eu/kanade/presentation/util/Resources.kt
+++ b/app/src/main/java/eu/kanade/presentation/util/Resources.kt
@@ -11,11 +11,11 @@ import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toBitmap
/**
- * Create a BitmapPainter from an drawable resource.
- *
- * > Only use this if [androidx.compose.ui.res.painterResource] doesn't work.
+ * Create a BitmapPainter from a drawable resource.
+ * Use this only if [androidx.compose.ui.res.painterResource] doesn't work.
*
* @param id the resource identifier
+ *
* @return the bitmap associated with the resource
*/
@Composable
diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt
index c34fa42d31..5b754c0573 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/App.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt
@@ -41,8 +41,8 @@ import eu.kanade.tachiyomi.network.NetworkPreferences
import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate
import eu.kanade.tachiyomi.util.system.WebViewUtil
import eu.kanade.tachiyomi.util.system.animatorDurationScale
-import eu.kanade.tachiyomi.util.system.notification
-import eu.kanade.tachiyomi.util.system.notificationManager
+import eu.kanade.tachiyomi.util.system.cancelNotification
+import eu.kanade.tachiyomi.util.system.notify
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -96,7 +96,10 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
.onEach { enabled ->
if (enabled) {
disableIncognitoReceiver.register()
- val notification = notification(Notifications.CHANNEL_INCOGNITO_MODE) {
+ notify(
+ Notifications.ID_INCOGNITO_MODE,
+ Notifications.CHANNEL_INCOGNITO_MODE,
+ ) {
setContentTitle(getString(R.string.pref_incognito_mode))
setContentText(getString(R.string.notification_incognito_text))
setSmallIcon(R.drawable.ic_glasses_24dp)
@@ -110,10 +113,9 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
)
setContentIntent(pendingIntent)
}
- notificationManager.notify(Notifications.ID_INCOGNITO_MODE, notification)
} else {
disableIncognitoReceiver.unregister()
- notificationManager.cancel(Notifications.ID_INCOGNITO_MODE)
+ cancelNotification(Notifications.ID_INCOGNITO_MODE)
}
}
.launchIn(ProcessLifecycleOwner.get().lifecycleScope)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt
index 70b61ae093..f612c661ab 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt
@@ -3,15 +3,13 @@ package eu.kanade.tachiyomi
import android.content.Context
import androidx.core.content.edit
import androidx.preference.PreferenceManager
-import androidx.work.WorkManager
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.domain.ui.UiPreferences
import eu.kanade.tachiyomi.core.security.SecurityPreferences
-import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
+import eu.kanade.tachiyomi.data.backup.BackupCreateJob
import eu.kanade.tachiyomi.data.library.anime.AnimeLibraryUpdateJob
import eu.kanade.tachiyomi.data.library.manga.MangaLibraryUpdateJob
-import eu.kanade.tachiyomi.data.preference.PreferenceValues
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.network.NetworkPreferences
import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE
@@ -21,7 +19,9 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.util.preference.minusAssign
import eu.kanade.tachiyomi.util.preference.plusAssign
import eu.kanade.tachiyomi.util.system.DeviceUtil
+import eu.kanade.tachiyomi.util.system.isReleaseBuildType
import eu.kanade.tachiyomi.util.system.toast
+import eu.kanade.tachiyomi.util.system.workManager
import tachiyomi.core.preference.PreferenceStore
import tachiyomi.core.preference.getEnum
import tachiyomi.domain.backup.service.BackupPreferences
@@ -61,7 +61,7 @@ object Migrations {
// Always set up background tasks to ensure they're running
MangaLibraryUpdateJob.setupTask(context)
AnimeLibraryUpdateJob.setupTask(context)
- BackupCreatorJob.setupTask(context)
+ BackupCreateJob.setupTask(context)
// Fresh install
if (oldVersion == 0) {
@@ -103,7 +103,7 @@ object Migrations {
if (oldVersion < 43) {
// Restore jobs after migrating from Evernote's job scheduler to WorkManager.
MangaLibraryUpdateJob.setupTask(context)
- BackupCreatorJob.setupTask(context)
+ BackupCreateJob.setupTask(context)
}
if (oldVersion < 44) {
// Reset sorting preference if using removed sort by source
@@ -282,12 +282,12 @@ object Migrations {
if (oldSecureScreen) {
securityPreferences.secureScreen().set(SecurityPreferences.SecureScreenMode.ALWAYS)
}
- if (DeviceUtil.isMiui && basePreferences.extensionInstaller().get() == PreferenceValues.ExtensionInstaller.PACKAGEINSTALLER) {
- basePreferences.extensionInstaller().set(PreferenceValues.ExtensionInstaller.LEGACY)
+ if (DeviceUtil.isMiui && basePreferences.extensionInstaller().get() == BasePreferences.ExtensionInstaller.PACKAGEINSTALLER) {
+ basePreferences.extensionInstaller().set(BasePreferences.ExtensionInstaller.LEGACY)
}
}
if (oldVersion < 76) {
- BackupCreatorJob.setupTask(context)
+ BackupCreateJob.setupTask(context)
}
if (oldVersion < 77) {
val oldReaderTap = prefs.getBoolean("reader_tap", false)
@@ -331,7 +331,7 @@ object Migrations {
}
if (backupPreferences.backupInterval().get() == 0) {
backupPreferences.backupInterval().set(12)
- BackupCreatorJob.setupTask(context)
+ BackupCreateJob.setupTask(context)
}
}
if (oldVersion < 85) {
@@ -416,8 +416,8 @@ object Migrations {
}
if (oldVersion < 97) {
// Removed background jobs
- WorkManager.getInstance(context).cancelAllWorkByTag("UpdateChecker")
- WorkManager.getInstance(context).cancelAllWorkByTag("ExtensionUpdate")
+ context.workManager.cancelAllWorkByTag("UpdateChecker")
+ context.workManager.cancelAllWorkByTag("ExtensionUpdate")
prefs.edit {
remove("automatic_ext_updates")
}
@@ -447,6 +447,16 @@ object Migrations {
}
}
}
+ if (oldVersion < 100) {
+ BackupCreateJob.setupTask(context)
+ }
+ if (oldVersion < 102) {
+ // This was accidentally visible from the reader settings sheet, but should always
+ // be disabled in release builds.
+ if (isReleaseBuildType) {
+ readerPreferences.longStripSplitWebtoon().set(false)
+ }
+ }
return true
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreatorJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreateJob.kt
similarity index 77%
rename from app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreatorJob.kt
rename to app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreateJob.kt
index 13d63cb9fa..752e0384bf 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreatorJob.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreateJob.kt
@@ -9,13 +9,13 @@ import androidx.work.ExistingWorkPolicy
import androidx.work.ForegroundInfo
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.PeriodicWorkRequestBuilder
-import androidx.work.WorkInfo
-import androidx.work.WorkManager
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.data.notification.Notifications
-import eu.kanade.tachiyomi.util.system.notificationManager
+import eu.kanade.tachiyomi.util.system.cancelNotification
+import eu.kanade.tachiyomi.util.system.isRunning
+import eu.kanade.tachiyomi.util.system.workManager
import logcat.LogPriority
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.backup.service.BackupPreferences
@@ -23,7 +23,7 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.concurrent.TimeUnit
-class BackupCreatorJob(private val context: Context, workerParams: WorkerParameters) :
+class BackupCreateJob(private val context: Context, workerParams: WorkerParameters) :
CoroutineWorker(context, workerParams) {
private val notifier = BackupNotifier(context)
@@ -41,7 +41,6 @@ class BackupCreatorJob(private val context: Context, workerParams: WorkerParamet
logcat(LogPriority.ERROR, e) { "Not allowed to run on foreground service" }
}
- context.notificationManager.notify(Notifications.ID_BACKUP_PROGRESS, notifier.showBackupProgress().build())
return try {
val location = BackupManager(context).createBackup(uri, flags, isAutoBackup)
if (!isAutoBackup) notifier.showBackupComplete(UniFile.fromUri(context, location.toUri()))
@@ -51,18 +50,20 @@ class BackupCreatorJob(private val context: Context, workerParams: WorkerParamet
if (!isAutoBackup) notifier.showBackupError(e.message)
Result.failure()
} finally {
- context.notificationManager.cancel(Notifications.ID_BACKUP_PROGRESS)
+ context.cancelNotification(Notifications.ID_BACKUP_PROGRESS)
}
}
override suspend fun getForegroundInfo(): ForegroundInfo {
- return ForegroundInfo(Notifications.ID_BACKUP_PROGRESS, notifier.showBackupProgress().build())
+ return ForegroundInfo(
+ Notifications.ID_BACKUP_PROGRESS,
+ notifier.showBackupProgress().build(),
+ )
}
companion object {
fun isManualJobRunning(context: Context): Boolean {
- val list = WorkManager.getInstance(context).getWorkInfosByTag(TAG_MANUAL).get()
- return list.find { it.state == WorkInfo.State.RUNNING } != null
+ return context.workManager.isRunning(TAG_MANUAL)
}
fun setupTask(context: Context, prefInterval: Int? = null, prefFlags: Int? = null) {
@@ -71,9 +72,8 @@ class BackupCreatorJob(private val context: Context, workerParams: WorkerParamet
val flags = prefFlags ?: backupPreferences.backupFlags().get().sumOf { s ->
s.toInt(16)
}
- val workManager = WorkManager.getInstance(context)
if (interval > 0) {
- val request = PeriodicWorkRequestBuilder(
+ val request = PeriodicWorkRequestBuilder(
interval.toLong(),
TimeUnit.HOURS,
10,
@@ -88,9 +88,9 @@ class BackupCreatorJob(private val context: Context, workerParams: WorkerParamet
)
.build()
- workManager.enqueueUniquePeriodicWork(TAG_AUTO, ExistingPeriodicWorkPolicy.UPDATE, request)
+ context.workManager.enqueueUniquePeriodicWork(TAG_AUTO, ExistingPeriodicWorkPolicy.UPDATE, request)
} else {
- workManager.cancelUniqueWork(TAG_AUTO)
+ context.workManager.cancelUniqueWork(TAG_AUTO)
}
}
@@ -100,11 +100,11 @@ class BackupCreatorJob(private val context: Context, workerParams: WorkerParamet
LOCATION_URI_KEY to uri.toString(),
BACKUP_FLAGS_KEY to flags,
)
- val request = OneTimeWorkRequestBuilder()
+ val request = OneTimeWorkRequestBuilder()
.addTag(TAG_MANUAL)
.setInputData(inputData)
.build()
- WorkManager.getInstance(context).enqueueUniqueWork(TAG_MANUAL, ExistingWorkPolicy.KEEP, request)
+ context.workManager.enqueueUniqueWork(TAG_MANUAL, ExistingWorkPolicy.KEEP, request)
}
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt
index dc172cae7c..fb4a548888 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupManager.kt
@@ -193,7 +193,7 @@ class BackupManager(
.map(Manga::source)
.distinct()
.map(mangaSourceManager::getOrStub)
- .map { BackupSource.copyFrom(it) }
+ .map(BackupSource::copyFrom)
.toList()
}
@@ -203,7 +203,7 @@ class BackupManager(
.map(Anime::source)
.distinct()
.map(animeSourceManager::getOrStub)
- .map { BackupAnimeSource.copyFrom(it) }
+ .map(BackupAnimeSource::copyFrom)
.toList()
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupNotifier.kt
index f7ef75d9d6..092e88bc7e 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupNotifier.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupNotifier.kt
@@ -9,8 +9,9 @@ import eu.kanade.tachiyomi.core.security.SecurityPreferences
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.storage.getUriCompat
+import eu.kanade.tachiyomi.util.system.cancelNotification
import eu.kanade.tachiyomi.util.system.notificationBuilder
-import eu.kanade.tachiyomi.util.system.notificationManager
+import eu.kanade.tachiyomi.util.system.notify
import uy.kohesive.injekt.injectLazy
import java.io.File
import java.util.concurrent.TimeUnit
@@ -34,7 +35,7 @@ class BackupNotifier(private val context: Context) {
}
private fun NotificationCompat.Builder.show(id: Int) {
- context.notificationManager.notify(id, build())
+ context.notify(id, build())
}
fun showBackupProgress(): NotificationCompat.Builder {
@@ -50,7 +51,7 @@ class BackupNotifier(private val context: Context) {
}
fun showBackupError(error: String?) {
- context.notificationManager.cancel(Notifications.ID_BACKUP_PROGRESS)
+ context.cancelNotification(Notifications.ID_BACKUP_PROGRESS)
with(completeNotificationBuilder) {
setContentTitle(context.getString(R.string.creating_backup_error))
@@ -61,15 +62,13 @@ class BackupNotifier(private val context: Context) {
}
fun showBackupComplete(unifile: UniFile) {
- context.notificationManager.cancel(Notifications.ID_BACKUP_PROGRESS)
+ context.cancelNotification(Notifications.ID_BACKUP_PROGRESS)
with(completeNotificationBuilder) {
setContentTitle(context.getString(R.string.backup_created))
setContentText(unifile.filePath ?: unifile.name)
- // Clear old actions if they exist
clearActions()
-
addAction(
R.drawable.ic_share_24dp,
context.getString(R.string.action_share),
@@ -91,12 +90,10 @@ class BackupNotifier(private val context: Context) {
setProgress(maxAmount, progress, false)
setOnlyAlertOnce(true)
- // Clear old actions if they exist
clearActions()
-
addAction(
R.drawable.ic_close_24dp,
- context.getString(R.string.action_stop),
+ context.getString(R.string.action_cancel),
NotificationReceiver.cancelRestorePendingBroadcast(context, Notifications.ID_RESTORE_PROGRESS),
)
}
@@ -107,7 +104,7 @@ class BackupNotifier(private val context: Context) {
}
fun showRestoreError(error: String?) {
- context.notificationManager.cancel(Notifications.ID_RESTORE_PROGRESS)
+ context.cancelNotification(Notifications.ID_RESTORE_PROGRESS)
with(completeNotificationBuilder) {
setContentTitle(context.getString(R.string.restoring_backup_error))
@@ -118,7 +115,7 @@ class BackupNotifier(private val context: Context) {
}
fun showRestoreComplete(time: Long, errorCount: Int, path: String?, file: String?) {
- context.notificationManager.cancel(Notifications.ID_RESTORE_PROGRESS)
+ context.cancelNotification(Notifications.ID_RESTORE_PROGRESS)
val timeString = context.getString(
R.string.restore_duration,
@@ -132,9 +129,7 @@ class BackupNotifier(private val context: Context) {
setContentTitle(context.getString(R.string.restore_completed))
setContentText(context.resources.getQuantityString(R.plurals.restore_completed_message, errorCount, timeString, errorCount))
- // Clear old actions if they exist
clearActions()
-
if (errorCount > 0 && !path.isNullOrEmpty() && !file.isNullOrEmpty()) {
val destFile = File(path, file)
val uri = destFile.getUriCompat(context)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreJob.kt
new file mode 100644
index 0000000000..72d23e48a3
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreJob.kt
@@ -0,0 +1,85 @@
+package eu.kanade.tachiyomi.data.backup
+
+import android.content.Context
+import android.net.Uri
+import androidx.core.net.toUri
+import androidx.work.CoroutineWorker
+import androidx.work.ExistingWorkPolicy
+import androidx.work.ForegroundInfo
+import androidx.work.OneTimeWorkRequestBuilder
+import androidx.work.WorkerParameters
+import androidx.work.workDataOf
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.data.notification.Notifications
+import eu.kanade.tachiyomi.util.system.cancelNotification
+import eu.kanade.tachiyomi.util.system.isRunning
+import eu.kanade.tachiyomi.util.system.workManager
+import kotlinx.coroutines.CancellationException
+import logcat.LogPriority
+import tachiyomi.core.util.system.logcat
+
+class BackupRestoreJob(private val context: Context, workerParams: WorkerParameters) :
+ CoroutineWorker(context, workerParams) {
+
+ private val notifier = BackupNotifier(context)
+
+ override suspend fun doWork(): Result {
+ val uri = inputData.getString(LOCATION_URI_KEY)?.toUri()
+ ?: return Result.failure()
+
+ try {
+ setForeground(getForegroundInfo())
+ } catch (e: IllegalStateException) {
+ logcat(LogPriority.ERROR, e) { "Not allowed to run on foreground service" }
+ }
+
+ return try {
+ val restorer = BackupRestorer(context, notifier)
+ restorer.restoreBackup(uri)
+ Result.success()
+ } catch (e: Exception) {
+ if (e is CancellationException) {
+ notifier.showRestoreError(context.getString(R.string.restoring_backup_canceled))
+ Result.success()
+ } else {
+ logcat(LogPriority.ERROR, e)
+ notifier.showRestoreError(e.message)
+ Result.failure()
+ }
+ } finally {
+ context.cancelNotification(Notifications.ID_RESTORE_PROGRESS)
+ }
+ }
+
+ override suspend fun getForegroundInfo(): ForegroundInfo {
+ return ForegroundInfo(
+ Notifications.ID_RESTORE_PROGRESS,
+ notifier.showRestoreProgress().build(),
+ )
+ }
+
+ companion object {
+ fun isRunning(context: Context): Boolean {
+ return context.workManager.isRunning(TAG)
+ }
+
+ fun start(context: Context, uri: Uri) {
+ val inputData = workDataOf(
+ LOCATION_URI_KEY to uri.toString(),
+ )
+ val request = OneTimeWorkRequestBuilder()
+ .addTag(TAG)
+ .setInputData(inputData)
+ .build()
+ context.workManager.enqueueUniqueWork(TAG, ExistingWorkPolicy.KEEP, request)
+ }
+
+ fun stop(context: Context) {
+ context.workManager.cancelUniqueWork(TAG)
+ }
+ }
+}
+
+private const val TAG = "BackupRestore"
+
+private const val LOCATION_URI_KEY = "location_uri" // String
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreService.kt
deleted file mode 100644
index 28b796586a..0000000000
--- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestoreService.kt
+++ /dev/null
@@ -1,141 +0,0 @@
-package eu.kanade.tachiyomi.data.backup
-
-import android.app.Service
-import android.content.Context
-import android.content.Intent
-import android.net.Uri
-import android.os.IBinder
-import android.os.PowerManager
-import androidx.core.content.ContextCompat
-import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.data.notification.Notifications
-import eu.kanade.tachiyomi.util.system.acquireWakeLock
-import eu.kanade.tachiyomi.util.system.getParcelableExtraCompat
-import eu.kanade.tachiyomi.util.system.isServiceRunning
-import kotlinx.coroutines.CoroutineExceptionHandler
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.cancel
-import kotlinx.coroutines.launch
-import logcat.LogPriority
-import tachiyomi.core.util.system.logcat
-
-/**
- * Restores backup.
- */
-class BackupRestoreService : Service() {
-
- companion object {
-
- /**
- * Returns the status of the service.
- *
- * @param context the application context.
- * @return true if the service is running, false otherwise.
- */
- fun isRunning(context: Context): Boolean =
- context.isServiceRunning(BackupRestoreService::class.java)
-
- /**
- * Starts a service to restore a backup from Json
- *
- * @param context context of application
- * @param uri path of Uri
- */
- fun start(context: Context, uri: Uri) {
- if (isRunning(context)) return
-
- val intent = Intent(context, BackupRestoreService::class.java).apply {
- putExtra(BackupConst.EXTRA_URI, uri)
- }
- ContextCompat.startForegroundService(context, intent)
- }
-
- /**
- * Stops the service.
- *
- * @param context the application context.
- */
- fun stop(context: Context) {
- context.stopService(Intent(context, BackupRestoreService::class.java))
-
- BackupNotifier(context).showRestoreError(context.getString(R.string.restoring_backup_canceled))
- }
- }
-
- /**
- * Wake lock that will be held until the service is destroyed.
- */
- private lateinit var wakeLock: PowerManager.WakeLock
-
- private lateinit var scope: CoroutineScope
- private var restorer: BackupRestorer? = null
- private lateinit var notifier: BackupNotifier
-
- override fun onCreate() {
- scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
- notifier = BackupNotifier(this)
- wakeLock = acquireWakeLock(javaClass.name)
-
- startForeground(Notifications.ID_RESTORE_PROGRESS, notifier.showRestoreProgress().build())
- }
-
- override fun stopService(name: Intent?): Boolean {
- destroyJob()
- return super.stopService(name)
- }
-
- override fun onDestroy() {
- destroyJob()
- }
-
- private fun destroyJob() {
- restorer?.job?.cancel()
- scope.cancel()
- if (wakeLock.isHeld) {
- wakeLock.release()
- }
- }
-
- /**
- * This method needs to be implemented, but it's not used/needed.
- */
- override fun onBind(intent: Intent): IBinder? = null
-
- /**
- * Method called when the service receives an intent.
- *
- * @param intent the start intent from.
- * @param flags the flags of the command.
- * @param startId the start id of this command.
- * @return the start value of the command.
- */
- override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- val uri = intent?.getParcelableExtraCompat(BackupConst.EXTRA_URI) ?: return START_NOT_STICKY
-
- // Cancel any previous job if needed.
- restorer?.job?.cancel()
-
- restorer = BackupRestorer(this, notifier)
-
- val handler = CoroutineExceptionHandler { _, exception ->
- logcat(LogPriority.ERROR, exception)
- restorer?.writeErrorLog()
-
- notifier.showRestoreError(exception.message)
- stopSelf(startId)
- }
- val job = scope.launch(handler) {
- if (restorer?.restoreBackup(uri) == false) {
- notifier.showRestoreError(getString(R.string.restoring_backup_canceled))
- }
- }
- job.invokeOnCompletion {
- stopSelf(startId)
- }
- restorer?.job = job
-
- return START_NOT_STICKY
- }
-}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt
index e5e3efe2a2..20d88d4196 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt
@@ -25,7 +25,8 @@ import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue
import eu.kanade.tachiyomi.util.BackupUtil
import eu.kanade.tachiyomi.util.storage.getUriCompat
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
-import kotlinx.coroutines.Job
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.isActive
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.domain.entries.manga.model.Manga
@@ -43,8 +44,6 @@ class BackupRestorer(
private val notifier: BackupNotifier,
) {
- var job: Job? = null
-
private var backupManager = BackupManager(context)
private var restoreAmount = 0
@@ -76,7 +75,7 @@ class BackupRestorer(
return true
}
- fun writeErrorLog(): File {
+ private fun writeErrorLog(): File {
try {
if (errors.isNotEmpty()) {
val file = context.createFileInCacheDir("aniyomi_restore.txt")
@@ -117,41 +116,42 @@ class BackupRestorer(
val backupAnimeMaps = backup.backupBrokenAnimeSources.map { BackupAnimeSource(it.name, it.sourceId) } + backup.backupAnimeSources
animeSourceMapping = backupAnimeMaps.associate { it.sourceId to it.name }
- // Restore individual manga
- backup.backupManga.forEach {
- if (job?.isActive != true) {
- return false
+ return coroutineScope {
+ // Restore individual manga
+ backup.backupManga.forEach {
+ if (!isActive) {
+ return@coroutineScope false
+ }
+
+ restoreManga(it, backup.backupCategories)
}
- restoreManga(it, backup.backupCategories)
- }
+ backup.backupAnime.forEach {
+ if (!isActive) {
+ return@coroutineScope false
+ }
- backup.backupAnime.forEach {
- if (job?.isActive != true) {
- return false
+ restoreAnime(it, backup.backupAnimeCategories)
}
- restoreAnime(it, backup.backupAnimeCategories)
- }
-
- // TODO: optionally trigger online library + tracker update
+ if (backup.backupPreferences.isNotEmpty()) {
+ restorePreferences(
+ backup.backupPreferences,
+ PreferenceManager.getDefaultSharedPreferences(context),
+ )
+ }
- if (backup.backupPreferences.isNotEmpty()) {
- restorePreferences(
- backup.backupPreferences,
- PreferenceManager.getDefaultSharedPreferences(context),
- )
- }
+ if (backup.backupExtensionPreferences.isNotEmpty()) {
+ restoreExtensionPreferences(backup.backupExtensionPreferences)
+ }
- if (backup.backupExtensionPreferences.isNotEmpty()) {
- restoreExtensionPreferences(backup.backupExtensionPreferences)
- }
+ if (backup.backupExtensions.isNotEmpty()) {
+ restoreExtensions(backup.backupExtensions)
+ }
- if (backup.backupExtensions.isNotEmpty()) {
- restoreExtensions(backup.backupExtensions)
+ // TODO: optionally trigger online library + tracker update
+ true
}
-
- return true
}
private suspend fun restoreCategories(backupCategories: List) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadManager.kt
index 0453480306..59e8099cd5 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadManager.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadManager.kt
@@ -90,7 +90,7 @@ class AnimeDownloadManager(
* @param episodeId the episode to check.
*/
fun getQueuedDownloadOrNull(episodeId: Long): AnimeDownload? {
- return queueState.value.find { it: AnimeDownload -> it.episode.id == episodeId }
+ return queueState.value.find { it.episode.id == episodeId }
}
fun startDownloadNow(episodeId: Long?) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadNotifier.kt
index 6bc773a6f3..f4ed0c4dad 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadNotifier.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadNotifier.kt
@@ -12,8 +12,9 @@ import eu.kanade.tachiyomi.data.notification.NotificationHandler
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.lang.chop
+import eu.kanade.tachiyomi.util.system.cancelNotification
import eu.kanade.tachiyomi.util.system.notificationBuilder
-import eu.kanade.tachiyomi.util.system.notificationManager
+import eu.kanade.tachiyomi.util.system.notify
import uy.kohesive.injekt.injectLazy
import java.util.regex.Pattern
@@ -61,7 +62,7 @@ internal class AnimeDownloadNotifier(private val context: Context) {
* @param id the id of the notification.
*/
private fun NotificationCompat.Builder.show(id: Int) {
- context.notificationManager.notify(id, build())
+ context.notify(id, build())
}
/**
@@ -70,7 +71,7 @@ internal class AnimeDownloadNotifier(private val context: Context) {
*/
fun dismissProgress(download: AnimeDownload) {
val notificationId = notificationIdMap[download.episode.id] ?: return
- context.notificationManager.cancel(notificationId)
+ context.cancelNotification(notificationId)
notificationIdMap.remove(download.episode.id)
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadProvider.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadProvider.kt
index fa3f97ecad..2ef9324d35 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadProvider.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadProvider.kt
@@ -138,14 +138,26 @@ class AnimeDownloadProvider(
* @param episodeScanlator scanlator of the episode to query
*/
fun getEpisodeDirName(episodeName: String, episodeScanlator: String?): String {
+ val newEpisodeName = sanitizeEpisodeName(episodeName)
return DiskUtil.buildValidFilename(
when {
- episodeScanlator.isNullOrBlank().not() -> "${episodeScanlator}_$episodeName"
- else -> episodeName
+ episodeScanlator.isNullOrBlank().not() -> "${episodeScanlator}_$newEpisodeName"
+ else -> newEpisodeName
},
)
}
+ /**
+ * Return the new name for the episode (in case it's empty or blank)
+ *
+ * @param episodeName the name of the episode
+ */
+ private fun sanitizeEpisodeName(episodeName: String): String {
+ return episodeName.ifBlank {
+ "Episode"
+ }
+ }
+
/**
* Returns the episode directory name for an episode.
*
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadService.kt
index a5ff3e863d..d78f3e6244 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadService.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloadService.kt
@@ -15,7 +15,7 @@ import eu.kanade.tachiyomi.util.system.acquireWakeLock
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
import eu.kanade.tachiyomi.util.system.isOnline
import eu.kanade.tachiyomi.util.system.isServiceRunning
-import eu.kanade.tachiyomi.util.system.notification
+import eu.kanade.tachiyomi.util.system.notificationBuilder
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -152,8 +152,8 @@ class AnimeDownloadService : Service() {
}
private fun getPlaceholderNotification(): Notification {
- return notification(Notifications.CHANNEL_DOWNLOADER_PROGRESS) {
+ return notificationBuilder(Notifications.CHANNEL_DOWNLOADER_PROGRESS) {
setContentTitle(getString(R.string.download_notifier_downloader_title))
- }
+ }.build()
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloader.kt
index 708bd17d9f..1e20f55dda 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloader.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/anime/AnimeDownloader.kt
@@ -62,11 +62,6 @@ import java.util.concurrent.TimeUnit
*
* The queue manipulation must be done in one thread (currently the main thread) to avoid unexpected
* behavior, but it's safe to read it from multiple threads.
- *
- * @param context the application context.
- * @param provider the downloads directory provider.
- * @param cache the downloads cache, used to add the downloads to the cache after their completion.
- * @param sourceManager the source manager.
*/
class AnimeDownloader(
private val context: Context,
@@ -85,7 +80,7 @@ class AnimeDownloader(
/**
* Queue where active downloads are kept.
*/
- val _queueState = MutableStateFlow>(emptyList())
+ private val _queueState = MutableStateFlow>(emptyList())
val queueState = _queueState.asStateFlow()
/**
@@ -146,7 +141,7 @@ class AnimeDownloader(
initializeSubscription()
- val pending = queueState.value.filter { it: AnimeDownload -> it.status != AnimeDownload.State.DOWNLOADED }
+ val pending = queueState.value.filter { it.status != AnimeDownload.State.DOWNLOADED }
pending.forEach { if (it.status != AnimeDownload.State.QUEUE) it.status = AnimeDownload.State.QUEUE }
isPaused = false
@@ -290,7 +285,7 @@ class AnimeDownloader(
// Runs in main thread (synchronization needed).
val episodesToQueue = episodesWithoutDir.await()
// Filter out those already enqueued.
- .filter { episode -> queueState.value.none { it: AnimeDownload -> it.episode.id == episode.id } }
+ .filter { episode -> queueState.value.none { it.episode.id == episode.id } }
// Create a download for each one.
.map { AnimeDownload(source, anime, it, changeDownloader, video) }
@@ -765,7 +760,7 @@ class AnimeDownloader(
* Returns true if all the queued downloads are in DOWNLOADED or ERROR state.
*/
private fun areAllAnimeDownloadsFinished(): Boolean {
- return queueState.value.none { it: AnimeDownload -> it.status.value <= AnimeDownload.State.DOWNLOADING.value }
+ return queueState.value.none { it.status.value <= AnimeDownload.State.DOWNLOADING.value }
}
private val progressSubject = PublishSubject.create()
@@ -780,7 +775,7 @@ class AnimeDownloader(
video?.progressSubject = subject
}
- fun addAllToQueue(downloads: List) {
+ private fun addAllToQueue(downloads: List) {
_queueState.update {
downloads.forEach { download ->
download.progressSubject = progressSubject
@@ -792,7 +787,7 @@ class AnimeDownloader(
}
}
- fun removeFromQueue(download: AnimeDownload) {
+ private fun removeFromQueue(download: AnimeDownload) {
_queueState.update {
store.remove(download)
download.progressSubject = null
@@ -814,7 +809,7 @@ class AnimeDownloader(
queueState.value.filter { it.anime.id == anime.id }.forEach { removeFromQueue(it) }
}
- fun _clearQueue() {
+ private fun _clearQueue() {
_queueState.update {
it.forEach { download ->
download.progressSubject = null
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadManager.kt
index 1ec8689ebb..4d04be8c22 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadManager.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadManager.kt
@@ -89,7 +89,7 @@ class MangaDownloadManager(
* @param chapterId the chapter to check.
*/
fun getQueuedDownloadOrNull(chapterId: Long): MangaDownload? {
- return queueState.value.find { it: MangaDownload -> it.chapter.id == chapterId }
+ return queueState.value.find { it.chapter.id == chapterId }
}
fun startDownloadNow(chapterId: Long?) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadNotifier.kt
index 08b286ebc0..22befe3003 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadNotifier.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadNotifier.kt
@@ -11,8 +11,9 @@ import eu.kanade.tachiyomi.data.notification.NotificationHandler
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.lang.chop
+import eu.kanade.tachiyomi.util.system.cancelNotification
import eu.kanade.tachiyomi.util.system.notificationBuilder
-import eu.kanade.tachiyomi.util.system.notificationManager
+import eu.kanade.tachiyomi.util.system.notify
import uy.kohesive.injekt.injectLazy
import java.util.regex.Pattern
@@ -50,7 +51,7 @@ internal class MangaDownloadNotifier(private val context: Context) {
* @param id the id of the notification.
*/
private fun NotificationCompat.Builder.show(id: Int) {
- context.notificationManager.notify(id, build())
+ context.notify(id, build())
}
/**
@@ -58,7 +59,7 @@ internal class MangaDownloadNotifier(private val context: Context) {
* those can only be dismissed by the user.
*/
fun dismissProgress() {
- context.notificationManager.cancel(Notifications.ID_DOWNLOAD_CHAPTER_PROGRESS)
+ context.cancelNotification(Notifications.ID_DOWNLOAD_CHAPTER_PROGRESS)
}
/**
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadProvider.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadProvider.kt
index 19f90a45a1..f9b12f82ca 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadProvider.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadProvider.kt
@@ -138,14 +138,26 @@ class MangaDownloadProvider(
* @param chapterScanlator scanlator of the chapter to query
*/
fun getChapterDirName(chapterName: String, chapterScanlator: String?): String {
+ val newChapterName = sanitizeChapterName(chapterName)
return DiskUtil.buildValidFilename(
when {
- chapterScanlator.isNullOrBlank().not() -> "${chapterScanlator}_$chapterName"
- else -> chapterName
+ chapterScanlator.isNullOrBlank().not() -> "${chapterScanlator}_$newChapterName"
+ else -> newChapterName
},
)
}
+ /**
+ * Return the new name for the chapter (in case it's empty or blank)
+ *
+ * @param chapterName the name of the chapter
+ */
+ private fun sanitizeChapterName(chapterName: String): String {
+ return chapterName.ifBlank {
+ "Chapter"
+ }
+ }
+
fun isChapterDirNameChanged(oldChapter: Chapter, newChapter: Chapter): Boolean {
return oldChapter.name != newChapter.name ||
oldChapter.scanlator?.takeIf { it.isNotBlank() } != newChapter.scanlator?.takeIf { it.isNotBlank() }
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadService.kt
index 36a43679ff..58042c1c0c 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadService.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloadService.kt
@@ -14,7 +14,7 @@ import eu.kanade.tachiyomi.util.system.acquireWakeLock
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
import eu.kanade.tachiyomi.util.system.isOnline
import eu.kanade.tachiyomi.util.system.isServiceRunning
-import eu.kanade.tachiyomi.util.system.notification
+import eu.kanade.tachiyomi.util.system.notificationBuilder
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -143,8 +143,8 @@ class MangaDownloadService : Service() {
}
private fun getPlaceholderNotification(): Notification {
- return notification(Notifications.CHANNEL_DOWNLOADER_PROGRESS) {
+ return notificationBuilder(Notifications.CHANNEL_DOWNLOADER_PROGRESS) {
setContentTitle(getString(R.string.download_notifier_downloader_title))
- }
+ }.build()
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloader.kt
index 2f3a69d5c1..9baa850bd0 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloader.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/manga/MangaDownloader.kt
@@ -66,11 +66,6 @@ import java.util.zip.ZipOutputStream
*
* The queue manipulation must be done in one thread (currently the main thread) to avoid unexpected
* behavior, but it's safe to read it from multiple threads.
- *
- * @param context the application context.
- * @param provider the downloads directory provider.
- * @param cache the downloads cache, used to add the downloads to the cache after their completion.
- * @param sourceManager the source manager.
*/
class MangaDownloader(
private val context: Context,
@@ -90,7 +85,7 @@ class MangaDownloader(
/**
* Queue where active downloads are kept.
*/
- val _queueState = MutableStateFlow>(emptyList())
+ private val _queueState = MutableStateFlow>(emptyList())
val queueState = _queueState.asStateFlow()
/**
@@ -140,7 +135,7 @@ class MangaDownloader(
initializeSubscription()
- val pending = queueState.value.filter { it: MangaDownload -> it.status != MangaDownload.State.DOWNLOADED }
+ val pending = queueState.value.filter { it.status != MangaDownload.State.DOWNLOADED }
pending.forEach { if (it.status != MangaDownload.State.QUEUE) it.status = MangaDownload.State.QUEUE }
isPaused = false
@@ -266,7 +261,7 @@ class MangaDownloader(
// Runs in main thread (synchronization needed).
val chaptersToQueue = chaptersWithoutDir.await()
// Filter out those already enqueued.
- .filter { chapter -> queueState.value.none { it: MangaDownload -> it.chapter.id == chapter.id } }
+ .filter { chapter -> queueState.value.none { it.chapter.id == chapter.id } }
// Create a download for each one.
.map { MangaDownload(source, manga, it) }
@@ -499,6 +494,8 @@ class MangaDownloader(
}
private fun splitTallImageIfNeeded(page: Page, tmpDir: UniFile) {
+ if (!downloadPreferences.splitTallImages().get()) return
+
try {
val filenamePrefix = String.format("%03d", page.number)
val imageFile = tmpDir.listFiles()?.firstOrNull { it.name.orEmpty().startsWith(filenamePrefix) }
@@ -638,10 +635,10 @@ class MangaDownloader(
* Returns true if all the queued downloads are in DOWNLOADED or ERROR state.
*/
private fun areAllDownloadsFinished(): Boolean {
- return queueState.value.none { it: MangaDownload -> it.status.value <= MangaDownload.State.DOWNLOADING.value }
+ return queueState.value.none { it.status.value <= MangaDownload.State.DOWNLOADING.value }
}
- fun addAllToQueue(downloads: List) {
+ private fun addAllToQueue(downloads: List) {
_queueState.update {
downloads.forEach { download ->
download.status = MangaDownload.State.QUEUE
@@ -651,7 +648,7 @@ class MangaDownloader(
}
}
- fun removeFromQueue(download: MangaDownload) {
+ private fun removeFromQueue(download: MangaDownload) {
_queueState.update {
store.remove(download)
if (download.status == MangaDownload.State.DOWNLOADING || download.status == MangaDownload.State.QUEUE) {
@@ -671,7 +668,7 @@ class MangaDownloader(
queueState.value.filter { it.manga.id == manga.id }.forEach { removeFromQueue(it) }
}
- fun _clearQueue() {
+ private fun _clearQueue() {
_queueState.update {
it.forEach { download ->
if (download.status == MangaDownload.State.DOWNLOADING || download.status == MangaDownload.State.QUEUE) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateJob.kt
index f0e4258339..1eed2ad892 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateJob.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateJob.kt
@@ -11,7 +11,6 @@ import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkInfo
-import androidx.work.WorkManager
import androidx.work.WorkQuery
import androidx.work.WorkerParameters
import androidx.work.workDataOf
@@ -37,8 +36,9 @@ import eu.kanade.tachiyomi.util.shouldDownloadNewEpisodes
import eu.kanade.tachiyomi.util.storage.getUriCompat
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
+import eu.kanade.tachiyomi.util.system.isRunning
+import eu.kanade.tachiyomi.util.system.workManager
import kotlinx.coroutines.CancellationException
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
@@ -47,7 +47,6 @@ import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
-import kotlinx.coroutines.withContext
import logcat.LogPriority
import tachiyomi.core.preference.getAndSet
import tachiyomi.core.util.lang.withIOContext
@@ -116,13 +115,7 @@ class AnimeLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
}
// Find a running manual worker. If exists, try again later
- val otherRunningWorker = withContext(Dispatchers.IO) {
- WorkManager.getInstance(context)
- .getWorkInfosByTag(WORK_NAME_MANUAL)
- .get()
- .find { it.state == WorkInfo.State.RUNNING }
- }
- if (otherRunningWorker != null) {
+ if (context.workManager.isRunning(WORK_NAME_MANUAL)) {
return Result.retry()
}
}
@@ -167,7 +160,10 @@ class AnimeLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
override suspend fun getForegroundInfo(): ForegroundInfo {
val notifier = AnimeLibraryUpdateNotifier(context)
- return ForegroundInfo(Notifications.ID_LIBRARY_PROGRESS, notifier.progressNotificationBuilder.build())
+ return ForegroundInfo(
+ Notifications.ID_LIBRARY_PROGRESS,
+ notifier.progressNotificationBuilder.build(),
+ )
}
/**
@@ -539,7 +535,7 @@ class AnimeLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
private const val KEY_TARGET = "target"
fun cancelAllWorks(context: Context) {
- WorkManager.getInstance(context).cancelAllWorkByTag(TAG)
+ context.workManager.cancelAllWorkByTag(TAG)
}
fun setupTask(
@@ -568,9 +564,9 @@ class AnimeLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
.setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.MINUTES)
.build()
- WorkManager.getInstance(context).enqueueUniquePeriodicWork(WORK_NAME_AUTO, ExistingPeriodicWorkPolicy.UPDATE, request)
+ context.workManager.enqueueUniquePeriodicWork(WORK_NAME_AUTO, ExistingPeriodicWorkPolicy.UPDATE, request)
} else {
- WorkManager.getInstance(context).cancelUniqueWork(WORK_NAME_AUTO)
+ context.workManager.cancelUniqueWork(WORK_NAME_AUTO)
}
}
fun startNow(
@@ -578,9 +574,8 @@ class AnimeLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
category: Category? = null,
target: Target = Target.EPISODES,
): Boolean {
- val wm = WorkManager.getInstance(context)
- val infos = wm.getWorkInfosByTag(TAG).get()
- if (infos.find { it.state == WorkInfo.State.RUNNING } != null) {
+ val wm = context.workManager
+ if (wm.isRunning(TAG)) {
// Already running either as a scheduled or manual job
return false
}
@@ -600,7 +595,7 @@ class AnimeLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
}
fun stop(context: Context) {
- val wm = WorkManager.getInstance(context)
+ val wm = context.workManager
val workQuery = WorkQuery.Builder.fromTags(listOf(TAG))
.addStates(listOf(WorkInfo.State.RUNNING))
.build()
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateNotifier.kt
index 53680d2fc0..00d04dd3dc 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateNotifier.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/anime/AnimeLibraryUpdateNotifier.kt
@@ -9,6 +9,7 @@ import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
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
@@ -21,9 +22,9 @@ import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.util.lang.chop
-import eu.kanade.tachiyomi.util.system.notification
+import eu.kanade.tachiyomi.util.system.cancelNotification
import eu.kanade.tachiyomi.util.system.notificationBuilder
-import eu.kanade.tachiyomi.util.system.notificationManager
+import eu.kanade.tachiyomi.util.system.notify
import tachiyomi.core.util.lang.launchUI
import tachiyomi.domain.entries.anime.model.Anime
import tachiyomi.domain.items.episode.model.Episode
@@ -82,7 +83,7 @@ class AnimeLibraryUpdateNotifier(private val context: Context) {
.setStyle(NotificationCompat.BigTextStyle().bigText(updatingText))
}
- context.notificationManager.notify(
+ context.notify(
Notifications.ID_LIBRARY_PROGRESS,
progressNotificationBuilder
.setProgress(total, current, false)
@@ -91,18 +92,16 @@ class AnimeLibraryUpdateNotifier(private val context: Context) {
}
fun showQueueSizeWarningNotification() {
- val notificationBuilder = context.notificationBuilder(Notifications.CHANNEL_LIBRARY_PROGRESS) {
+ context.notify(
+ Notifications.ID_LIBRARY_SIZE_WARNING,
+ Notifications.CHANNEL_LIBRARY_PROGRESS,
+ ) {
setContentTitle(context.getString(R.string.label_warning))
setStyle(NotificationCompat.BigTextStyle().bigText(context.getString(R.string.notification_size_warning)))
setSmallIcon(R.drawable.ic_warning_white_24dp)
setTimeoutAfter(AnimeDownloader.WARNING_NOTIF_TIMEOUT_MS)
setContentIntent(NotificationHandler.openUrl(context, HELP_WARNING_URL))
}
-
- context.notificationManager.notify(
- Notifications.ID_LIBRARY_SIZE_WARNING,
- notificationBuilder.build(),
- )
}
/**
@@ -116,17 +115,16 @@ class AnimeLibraryUpdateNotifier(private val context: Context) {
return
}
- context.notificationManager.notify(
+ context.notify(
Notifications.ID_LIBRARY_ERROR,
- context.notificationBuilder(Notifications.CHANNEL_LIBRARY_ERROR) {
- setContentTitle(context.resources.getString(R.string.notification_update_error, failed))
- setContentText(context.getString(R.string.action_show_errors))
- setSmallIcon(R.drawable.ic_ani)
+ Notifications.CHANNEL_LIBRARY_ERROR,
+ ) {
+ setContentTitle(context.resources.getString(R.string.notification_update_error, failed))
+ setContentText(context.getString(R.string.action_show_errors))
+ setSmallIcon(R.drawable.ic_ani)
- setContentIntent(NotificationReceiver.openErrorLogPendingActivity(context, uri))
- }
- .build(),
- )
+ setContentIntent(NotificationReceiver.openErrorLogPendingActivity(context, uri))
+ }
}
/**
@@ -139,16 +137,15 @@ class AnimeLibraryUpdateNotifier(private val context: Context) {
return
}
- context.notificationManager.notify(
+ context.notify(
Notifications.ID_LIBRARY_SKIPPED,
- context.notificationBuilder(Notifications.CHANNEL_LIBRARY_SKIPPED) {
- setContentTitle(context.resources.getString(R.string.notification_update_skipped, skipped))
- setContentText(context.getString(R.string.learn_more))
- setSmallIcon(R.drawable.ic_ani)
- setContentIntent(NotificationHandler.openUrl(context, HELP_SKIPPED_ANIME_URL))
- }
- .build(),
- )
+ Notifications.CHANNEL_LIBRARY_SKIPPED,
+ ) {
+ setContentTitle(context.resources.getString(R.string.notification_update_skipped, skipped))
+ setContentText(context.getString(R.string.learn_more))
+ setSmallIcon(R.drawable.ic_ani)
+ setContentIntent(NotificationHandler.openUrl(context, HELP_SKIPPED_ANIME_URL))
+ }
}
/**
@@ -158,58 +155,54 @@ class AnimeLibraryUpdateNotifier(private val context: Context) {
*/
fun showUpdateNotifications(updates: List>>) {
// Parent group notification
- context.notificationManager.notify(
- Notifications.ID_NEW_CHAPTERS,
- context.notification(Notifications.CHANNEL_NEW_CHAPTERS_EPISODES) {
- setContentTitle(context.getString(R.string.notification_new_chapters))
- if (updates.size == 1 && !preferences.hideNotificationContent().get()) {
- setContentText(updates.first().first.title.chop(NOTIF_ANIME_TITLE_MAX_LEN))
- } else {
- setContentText(
- context.resources.getQuantityString(
- R.plurals.notification_new_chapters_summary,
- updates.size,
- updates.size,
+ context.notify(
+ Notifications.ID_NEW_EPISODES,
+ Notifications.CHANNEL_NEW_CHAPTERS_EPISODES,
+ ) {
+ setContentTitle(context.getString(R.string.notification_new_episodes))
+ if (updates.size == 1 && !preferences.hideNotificationContent().get()) {
+ setContentText(updates.first().first.title.chop(NOTIF_ANIME_TITLE_MAX_LEN))
+ } else {
+ setContentText(context.resources.getQuantityString(R.plurals.notification_new_episodes_summary, updates.size, updates.size))
+
+ if (!preferences.hideNotificationContent().get()) {
+ setStyle(
+ NotificationCompat.BigTextStyle().bigText(
+ updates.joinToString("\n") {
+ it.first.title.chop(NOTIF_ANIME_TITLE_MAX_LEN)
+ },
),
)
-
- if (!preferences.hideNotificationContent().get()) {
- setStyle(
- NotificationCompat.BigTextStyle().bigText(
- updates.joinToString("\n") {
- it.first.title.chop(NOTIF_ANIME_TITLE_MAX_LEN)
- },
- ),
- )
- }
}
+ }
- setSmallIcon(R.drawable.ic_ani)
- setLargeIcon(notificationBitmap)
+ setSmallIcon(R.drawable.ic_ani)
+ setLargeIcon(notificationBitmap)
- setGroup(Notifications.GROUP_NEW_EPISODES)
- setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
- setGroupSummary(true)
- priority = NotificationCompat.PRIORITY_HIGH
+ setGroup(Notifications.GROUP_NEW_EPISODES)
+ setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
+ setGroupSummary(true)
+ priority = NotificationCompat.PRIORITY_HIGH
- setContentIntent(getNotificationIntent())
- setAutoCancel(true)
- },
- )
+ setContentIntent(getNotificationIntent())
+ setAutoCancel(true)
+ }
// Per-anime notification
if (!preferences.hideNotificationContent().get()) {
launchUI {
- updates.forEach { (anime, episodes) ->
- context.notificationManager.notify(anime.id.hashCode(), createNewEpisodesNotification(anime, episodes))
- }
+ context.notify(
+ updates.map { (anime, episodes) ->
+ NotificationManagerCompat.NotificationWithIdAndTag(anime.id.hashCode(), createNewEpisodesNotification(anime, episodes))
+ },
+ )
}
}
}
private suspend fun createNewEpisodesNotification(anime: Anime, episodes: Array): Notification {
val icon = getAnimeIcon(anime)
- return context.notification(Notifications.CHANNEL_NEW_CHAPTERS_EPISODES) {
+ return context.notificationBuilder(Notifications.CHANNEL_NEW_CHAPTERS_EPISODES) {
setContentTitle(anime.title)
val description = getNewEpisodesDescription(episodes)
@@ -265,14 +258,14 @@ class AnimeLibraryUpdateNotifier(private val context: Context) {
),
)
}
- }
+ }.build()
}
/**
* Cancels the progress notification.
*/
fun cancelProgressNotification() {
- context.notificationManager.cancel(Notifications.ID_LIBRARY_PROGRESS)
+ context.cancelNotification(Notifications.ID_LIBRARY_PROGRESS)
}
private suspend fun getAnimeIcon(anime: Anime): Bitmap? {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateJob.kt
index f473b40186..e297854d93 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateJob.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateJob.kt
@@ -11,7 +11,6 @@ import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkInfo
-import androidx.work.WorkManager
import androidx.work.WorkQuery
import androidx.work.WorkerParameters
import androidx.work.workDataOf
@@ -37,8 +36,9 @@ import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
import eu.kanade.tachiyomi.util.storage.getUriCompat
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
import eu.kanade.tachiyomi.util.system.isConnectedToWifi
+import eu.kanade.tachiyomi.util.system.isRunning
+import eu.kanade.tachiyomi.util.system.workManager
import kotlinx.coroutines.CancellationException
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
@@ -47,7 +47,6 @@ import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
-import kotlinx.coroutines.withContext
import logcat.LogPriority
import tachiyomi.core.preference.getAndSet
import tachiyomi.core.util.lang.withIOContext
@@ -116,13 +115,7 @@ class MangaLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
}
// Find a running manual worker. If exists, try again later
- val otherRunningWorker = withContext(Dispatchers.IO) {
- WorkManager.getInstance(context)
- .getWorkInfosByTag(WORK_NAME_MANUAL)
- .get()
- .find { it.state == WorkInfo.State.RUNNING }
- }
- if (otherRunningWorker != null) {
+ if (context.workManager.isRunning(WORK_NAME_MANUAL)) {
return Result.retry()
}
}
@@ -167,7 +160,10 @@ class MangaLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
override suspend fun getForegroundInfo(): ForegroundInfo {
val notifier = MangaLibraryUpdateNotifier(context)
- return ForegroundInfo(Notifications.ID_LIBRARY_PROGRESS, notifier.progressNotificationBuilder.build())
+ return ForegroundInfo(
+ Notifications.ID_LIBRARY_PROGRESS,
+ notifier.progressNotificationBuilder.build(),
+ )
}
/**
@@ -538,7 +534,7 @@ class MangaLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
private const val KEY_TARGET = "target"
fun cancelAllWorks(context: Context) {
- WorkManager.getInstance(context).cancelAllWorkByTag(TAG)
+ context.workManager.cancelAllWorkByTag(TAG)
}
fun setupTask(
@@ -567,9 +563,9 @@ class MangaLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
.setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.MINUTES)
.build()
- WorkManager.getInstance(context).enqueueUniquePeriodicWork(WORK_NAME_AUTO, ExistingPeriodicWorkPolicy.UPDATE, request)
+ context.workManager.enqueueUniquePeriodicWork(WORK_NAME_AUTO, ExistingPeriodicWorkPolicy.UPDATE, request)
} else {
- WorkManager.getInstance(context).cancelUniqueWork(WORK_NAME_AUTO)
+ context.workManager.cancelUniqueWork(WORK_NAME_AUTO)
}
}
@@ -578,9 +574,8 @@ class MangaLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
category: Category? = null,
target: Target = Target.CHAPTERS,
): Boolean {
- val wm = WorkManager.getInstance(context)
- val infos = wm.getWorkInfosByTag(TAG).get()
- if (infos.find { it.state == WorkInfo.State.RUNNING } != null) {
+ val wm = context.workManager
+ if (wm.isRunning(TAG)) {
// Already running either as a scheduled or manual job
return false
}
@@ -600,7 +595,7 @@ class MangaLibraryUpdateJob(private val context: Context, workerParams: WorkerPa
}
fun stop(context: Context) {
- val wm = WorkManager.getInstance(context)
+ val wm = context.workManager
val workQuery = WorkQuery.Builder.fromTags(listOf(TAG))
.addStates(listOf(WorkInfo.State.RUNNING))
.build()
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateNotifier.kt
index 356dfd9615..9e58b550c1 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateNotifier.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/manga/MangaLibraryUpdateNotifier.kt
@@ -9,6 +9,7 @@ import android.graphics.BitmapFactory
import android.graphics.drawable.BitmapDrawable
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
@@ -21,9 +22,9 @@ import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.util.lang.chop
-import eu.kanade.tachiyomi.util.system.notification
+import eu.kanade.tachiyomi.util.system.cancelNotification
import eu.kanade.tachiyomi.util.system.notificationBuilder
-import eu.kanade.tachiyomi.util.system.notificationManager
+import eu.kanade.tachiyomi.util.system.notify
import tachiyomi.core.util.lang.launchUI
import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.domain.items.chapter.model.Chapter
@@ -82,7 +83,7 @@ class MangaLibraryUpdateNotifier(private val context: Context) {
.setStyle(NotificationCompat.BigTextStyle().bigText(updatingText))
}
- context.notificationManager.notify(
+ context.notify(
Notifications.ID_LIBRARY_PROGRESS,
progressNotificationBuilder
.setProgress(total, current, false)
@@ -91,7 +92,10 @@ class MangaLibraryUpdateNotifier(private val context: Context) {
}
fun showQueueSizeWarningNotification() {
- val notificationBuilder = context.notificationBuilder(Notifications.CHANNEL_LIBRARY_PROGRESS) {
+ context.notify(
+ Notifications.ID_LIBRARY_SIZE_WARNING,
+ Notifications.CHANNEL_LIBRARY_PROGRESS,
+ ) {
setContentTitle(context.getString(R.string.label_warning))
setStyle(
NotificationCompat.BigTextStyle()
@@ -101,11 +105,6 @@ class MangaLibraryUpdateNotifier(private val context: Context) {
setTimeoutAfter(MangaDownloader.WARNING_NOTIF_TIMEOUT_MS)
setContentIntent(NotificationHandler.openUrl(context, HELP_WARNING_URL))
}
-
- context.notificationManager.notify(
- Notifications.ID_LIBRARY_SIZE_WARNING,
- notificationBuilder.build(),
- )
}
/**
@@ -119,17 +118,16 @@ class MangaLibraryUpdateNotifier(private val context: Context) {
return
}
- context.notificationManager.notify(
+ context.notify(
Notifications.ID_LIBRARY_ERROR,
- context.notificationBuilder(Notifications.CHANNEL_LIBRARY_ERROR) {
- setContentTitle(context.resources.getString(R.string.notification_update_error, failed))
- setContentText(context.getString(R.string.action_show_errors))
- setSmallIcon(R.drawable.ic_ani)
+ Notifications.CHANNEL_LIBRARY_ERROR,
+ ) {
+ setContentTitle(context.resources.getString(R.string.notification_update_error, failed))
+ setContentText(context.getString(R.string.action_show_errors))
+ setSmallIcon(R.drawable.ic_ani)
- setContentIntent(NotificationReceiver.openErrorLogPendingActivity(context, uri))
- }
- .build(),
- )
+ setContentIntent(NotificationReceiver.openErrorLogPendingActivity(context, uri))
+ }
}
/**
@@ -142,16 +140,15 @@ class MangaLibraryUpdateNotifier(private val context: Context) {
return
}
- context.notificationManager.notify(
+ context.notify(
Notifications.ID_LIBRARY_SKIPPED,
- context.notificationBuilder(Notifications.CHANNEL_LIBRARY_SKIPPED) {
- setContentTitle(context.resources.getString(R.string.notification_update_skipped, skipped))
- setContentText(context.getString(R.string.learn_more))
- setSmallIcon(R.drawable.ic_ani)
- setContentIntent(NotificationHandler.openUrl(context, HELP_SKIPPED_MANGA_URL))
- }
- .build(),
- )
+ Notifications.CHANNEL_LIBRARY_SKIPPED,
+ ) {
+ setContentTitle(context.resources.getString(R.string.notification_update_skipped, skipped))
+ setContentText(context.getString(R.string.learn_more))
+ setSmallIcon(R.drawable.ic_ani)
+ setContentIntent(NotificationHandler.openUrl(context, HELP_SKIPPED_MANGA_URL))
+ }
}
/**
@@ -161,58 +158,54 @@ class MangaLibraryUpdateNotifier(private val context: Context) {
*/
fun showUpdateNotifications(updates: List>>) {
// Parent group notification
- context.notificationManager.notify(
+ context.notify(
Notifications.ID_NEW_CHAPTERS,
- context.notification(Notifications.CHANNEL_NEW_CHAPTERS_EPISODES) {
- setContentTitle(context.getString(R.string.notification_new_chapters))
- if (updates.size == 1 && !preferences.hideNotificationContent().get()) {
- setContentText(updates.first().first.title.chop(NOTIF_MANGA_TITLE_MAX_LEN))
- } else {
- setContentText(
- context.resources.getQuantityString(
- R.plurals.notification_new_chapters_summary,
- updates.size,
- updates.size,
+ Notifications.CHANNEL_NEW_CHAPTERS_EPISODES,
+ ) {
+ setContentTitle(context.getString(R.string.notification_new_chapters))
+ if (updates.size == 1 && !preferences.hideNotificationContent().get()) {
+ setContentText(updates.first().first.title.chop(NOTIF_MANGA_TITLE_MAX_LEN))
+ } else {
+ setContentText(context.resources.getQuantityString(R.plurals.notification_new_chapters_summary, updates.size, updates.size))
+
+ if (!preferences.hideNotificationContent().get()) {
+ setStyle(
+ NotificationCompat.BigTextStyle().bigText(
+ updates.joinToString("\n") {
+ it.first.title.chop(NOTIF_MANGA_TITLE_MAX_LEN)
+ },
),
)
-
- if (!preferences.hideNotificationContent().get()) {
- setStyle(
- NotificationCompat.BigTextStyle().bigText(
- updates.joinToString("\n") {
- it.first.title.chop(NOTIF_MANGA_TITLE_MAX_LEN)
- },
- ),
- )
- }
}
+ }
- setSmallIcon(R.drawable.ic_ani)
- setLargeIcon(notificationBitmap)
+ setSmallIcon(R.drawable.ic_ani)
+ setLargeIcon(notificationBitmap)
- setGroup(Notifications.GROUP_NEW_CHAPTERS)
- setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
- setGroupSummary(true)
- priority = NotificationCompat.PRIORITY_HIGH
+ setGroup(Notifications.GROUP_NEW_CHAPTERS)
+ setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
+ setGroupSummary(true)
+ priority = NotificationCompat.PRIORITY_HIGH
- setContentIntent(getNotificationIntent())
- setAutoCancel(true)
- },
- )
+ setContentIntent(getNotificationIntent())
+ setAutoCancel(true)
+ }
// Per-manga notification
if (!preferences.hideNotificationContent().get()) {
launchUI {
- updates.forEach { (manga, chapters) ->
- context.notificationManager.notify(manga.id.hashCode(), createNewChaptersNotification(manga, chapters))
- }
+ context.notify(
+ updates.map { (manga, chapters) ->
+ NotificationManagerCompat.NotificationWithIdAndTag(manga.id.hashCode(), createNewChaptersNotification(manga, chapters))
+ },
+ )
}
}
}
private suspend fun createNewChaptersNotification(manga: Manga, chapters: Array): Notification {
val icon = getMangaIcon(manga)
- return context.notification(Notifications.CHANNEL_NEW_CHAPTERS_EPISODES) {
+ return context.notificationBuilder(Notifications.CHANNEL_NEW_CHAPTERS_EPISODES) {
setContentTitle(manga.title)
val description = getNewChaptersDescription(chapters)
@@ -274,14 +267,14 @@ class MangaLibraryUpdateNotifier(private val context: Context) {
),
)
}
- }
+ }.build()
}
/**
* Cancels the progress notification.
*/
fun cancelProgressNotification() {
- context.notificationManager.cancel(Notifications.ID_LIBRARY_PROGRESS)
+ context.cancelNotification(Notifications.ID_LIBRARY_PROGRESS)
}
private suspend fun getMangaIcon(manga: Manga): Bitmap? {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt
index 6518eee1d3..a21e801ac1 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt
@@ -6,11 +6,10 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
-import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.core.Constants
-import eu.kanade.tachiyomi.data.backup.BackupRestoreService
+import eu.kanade.tachiyomi.data.backup.BackupRestoreJob
import eu.kanade.tachiyomi.data.download.anime.AnimeDownloadManager
import eu.kanade.tachiyomi.data.download.manga.MangaDownloadManager
import eu.kanade.tachiyomi.data.library.anime.AnimeLibraryUpdateJob
@@ -21,6 +20,7 @@ import eu.kanade.tachiyomi.ui.player.PlayerActivity
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.storage.getUriCompat
+import eu.kanade.tachiyomi.util.system.cancelNotification
import eu.kanade.tachiyomi.util.system.getParcelableExtraCompat
import eu.kanade.tachiyomi.util.system.notificationManager
import eu.kanade.tachiyomi.util.system.toShareIntent
@@ -101,10 +101,7 @@ class NotificationReceiver : BroadcastReceiver() {
"application/x-protobuf+gzip",
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1),
)
- ACTION_CANCEL_RESTORE -> cancelRestore(
- context,
- intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1),
- )
+ ACTION_CANCEL_RESTORE -> cancelRestore(context)
// Cancel library update and dismiss notification
ACTION_CANCEL_LIBRARY_UPDATE -> cancelLibraryUpdate(context)
ACTION_CANCEL_ANIMELIB_UPDATE -> cancelAnimelibUpdate(context)
@@ -174,14 +171,6 @@ class NotificationReceiver : BroadcastReceiver() {
downloadEpisodes(urls, animeId)
}
}
- // Share crash dump file
- ACTION_SHARE_CRASH_LOG ->
- shareFile(
- context,
- intent.getParcelableExtraCompat(EXTRA_URI)!!,
- "text/plain",
- intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1),
- )
}
}
@@ -191,7 +180,7 @@ class NotificationReceiver : BroadcastReceiver() {
* @param notificationId the id of the notification
*/
private fun dismissNotification(context: Context, notificationId: Int) {
- context.notificationManager.cancel(notificationId)
+ context.cancelNotification(notificationId)
context.notificationManager.cancelAll()
}
@@ -279,11 +268,9 @@ class NotificationReceiver : BroadcastReceiver() {
* Method called when user wants to stop a backup restore job.
*
* @param context context of application
- * @param notificationId id of notification
*/
- private fun cancelRestore(context: Context, notificationId: Int) {
- BackupRestoreService.stop(context)
- ContextCompat.getMainExecutor(context).execute { dismissNotification(context, notificationId) }
+ private fun cancelRestore(context: Context) {
+ BackupRestoreJob.stop(context)
}
/**
@@ -403,8 +390,6 @@ class NotificationReceiver : BroadcastReceiver() {
private const val ACTION_SHARE_BACKUP = "$ID.$NAME.SEND_BACKUP"
- private const val ACTION_SHARE_CRASH_LOG = "$ID.$NAME.SEND_CRASH_LOG"
-
private const val ACTION_CANCEL_RESTORE = "$ID.$NAME.CANCEL_RESTORE"
private const val ACTION_CANCEL_LIBRARY_UPDATE = "$ID.$NAME.CANCEL_LIBRARY_UPDATE"
@@ -559,13 +544,13 @@ class NotificationReceiver : BroadcastReceiver() {
}
if (notifications.size == 2) {
- context.notificationManager.cancel(groupId)
+ context.cancelNotification(groupId)
return
}
}
}
- context.notificationManager.cancel(notificationId)
+ context.cancelNotification(notificationId)
}
/**
@@ -843,23 +828,6 @@ class NotificationReceiver : BroadcastReceiver() {
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE)
}
- /**
- * Returns [PendingIntent] that starts a share activity for a crash log dump file.
- *
- * @param context context of application
- * @param uri uri of file
- * @param notificationId id of notification
- * @return [PendingIntent]
- */
- internal fun shareCrashLogPendingBroadcast(context: Context, uri: Uri, notificationId: Int): PendingIntent {
- val intent = Intent(context, NotificationReceiver::class.java).apply {
- action = ACTION_SHARE_CRASH_LOG
- putExtra(EXTRA_URI, uri)
- putExtra(EXTRA_NOTIFICATION_ID, notificationId)
- }
- return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
- }
-
/**
* Returns [PendingIntent] that cancels a backup restore job.
*
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt b/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt
index 5c4c4bc588..c820bd0015 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/notification/Notifications.kt
@@ -62,12 +62,6 @@ object Notifications {
const val ID_BACKUP_COMPLETE = -502
const val ID_RESTORE_COMPLETE = -504
- /**
- * Notification channel used for crash log file sharing.
- */
- const val CHANNEL_CRASH_LOGS = "crash_logs_channel"
- const val ID_CRASH_LOGS = -601
-
/**
* Notification channel used for Incognito Mode
*/
@@ -93,6 +87,7 @@ object Notifications {
"library_progress_channel",
"updates_ext_channel",
"downloader_cache_renewal",
+ "crash_logs_channel",
)
/**
@@ -102,12 +97,12 @@ object Notifications {
* @param context The application context.
*/
fun createChannels(context: Context) {
- val notificationService = NotificationManagerCompat.from(context)
+ val notificationManager = NotificationManagerCompat.from(context)
// Delete old notification channels
- deprecatedChannels.forEach(notificationService::deleteNotificationChannel)
+ deprecatedChannels.forEach(notificationManager::deleteNotificationChannel)
- notificationService.createNotificationChannelGroupsCompat(
+ notificationManager.createNotificationChannelGroupsCompat(
listOf(
buildNotificationChannelGroup(GROUP_BACKUP_RESTORE) {
setName(context.getString(R.string.label_backup))
@@ -124,7 +119,7 @@ object Notifications {
),
)
- notificationService.createNotificationChannelsCompat(
+ notificationManager.createNotificationChannelsCompat(
listOf(
buildNotificationChannel(CHANNEL_COMMON, IMPORTANCE_LOW) {
setName(context.getString(R.string.channel_common))
@@ -168,9 +163,6 @@ object Notifications {
setShowBadge(false)
setSound(null, null)
},
- buildNotificationChannel(CHANNEL_CRASH_LOGS, IMPORTANCE_HIGH) {
- setName(context.getString(R.string.channel_crash_logs))
- },
buildNotificationChannel(CHANNEL_INCOGNITO_MODE, IMPORTANCE_LOW) {
setName(context.getString(R.string.pref_incognito_mode))
},
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt
deleted file mode 100644
index 667c2061fc..0000000000
--- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package eu.kanade.tachiyomi.data.preference
-
-import eu.kanade.tachiyomi.R
-
-const val FLAG_CATEGORIES = "1"
-const val FLAG_CHAPTERS = "2"
-const val FLAG_HISTORY = "4"
-const val FLAG_TRACK = "8"
-const val FLAG_SETTINGS = "10"
-const val FLAG_EXT_SETTINGS = "20"
-const val FLAG_EXTENSIONS = "40"
-
-/**
- * This class stores the values for the preferences in the application.
- */
-object PreferenceValues {
-
- enum class TappingInvertMode(val shouldInvertHorizontal: Boolean = false, val shouldInvertVertical: Boolean = false) {
- NONE,
- HORIZONTAL(shouldInvertHorizontal = true),
- VERTICAL(shouldInvertVertical = true),
- BOTH(shouldInvertHorizontal = true, shouldInvertVertical = true),
- }
-
- enum class ReaderHideThreshold(val threshold: Int) {
- HIGHEST(5),
- HIGH(13),
- LOW(31),
- LOWEST(47),
- }
-
- enum class ExtensionInstaller(val titleResId: Int) {
- LEGACY(R.string.ext_installer_legacy),
- PACKAGEINSTALLER(R.string.ext_installer_packageinstaller),
- SHIZUKU(R.string.ext_installer_shizuku),
- }
-}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt
index e142b52e95..f660a2b004 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt
@@ -277,7 +277,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
private const val clientId = "1aaf4cf232372708e98b5abc813d795b539c5a916dbbfe9ac61bf02a360832cc"
private const val clientSecret = "229942c742dd4cde803125d17d64501d91c0b12e14cb1e5120184d77d67024c0"
- private const val baseUrl = "https://shikimori.one"
+ private const val baseUrl = "https://shikimori.me"
private const val apiUrl = "$baseUrl/api"
private const val oauthUrl = "$baseUrl/oauth/token"
private const val loginUrl = "$baseUrl/oauth/authorize"
diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt
index 9b4181b1a3..9a31b1e96e 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateNotifier.kt
@@ -12,7 +12,7 @@ import eu.kanade.tachiyomi.data.notification.NotificationHandler
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.system.notificationBuilder
-import eu.kanade.tachiyomi.util.system.notificationManager
+import eu.kanade.tachiyomi.util.system.notify
internal class AppUpdateNotifier(private val context: Context) {
@@ -24,7 +24,7 @@ internal class AppUpdateNotifier(private val context: Context) {
* @param id id of the notification channel.
*/
private fun NotificationCompat.Builder.show(id: Int = Notifications.ID_APP_UPDATER) {
- context.notificationManager.notify(id, build())
+ context.notify(id, build())
}
@SuppressLint("LaunchActivityFromNotification")
diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionUpdateNotifier.kt
index 9cc7dd724b..2aefe50506 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionUpdateNotifier.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionUpdateNotifier.kt
@@ -5,29 +5,28 @@ import androidx.core.app.NotificationCompat
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.notification.Notifications
-import eu.kanade.tachiyomi.util.system.notification
-import eu.kanade.tachiyomi.util.system.notificationManager
+import eu.kanade.tachiyomi.util.system.notify
class ExtensionUpdateNotifier(private val context: Context) {
fun promptUpdates(names: List) {
- context.notificationManager.notify(
+ context.notify(
Notifications.ID_UPDATES_TO_EXTS,
- context.notification(Notifications.CHANNEL_EXTENSIONS_UPDATE) {
- setContentTitle(
- context.resources.getQuantityString(
- R.plurals.update_check_notification_ext_updates,
- names.size,
- names.size,
- ),
- )
- val extNames = names.joinToString(", ")
- setContentText(extNames)
- setStyle(NotificationCompat.BigTextStyle().bigText(extNames))
- setSmallIcon(R.drawable.ic_extension_24dp)
- setContentIntent(NotificationReceiver.openExtensionsPendingActivity(context))
- setAutoCancel(true)
- },
- )
+ Notifications.CHANNEL_EXTENSIONS_UPDATE,
+ ) {
+ setContentTitle(
+ context.resources.getQuantityString(
+ R.plurals.update_check_notification_ext_updates,
+ names.size,
+ names.size,
+ ),
+ )
+ val extNames = names.joinToString(", ")
+ setContentText(extNames)
+ setStyle(NotificationCompat.BigTextStyle().bigText(extNames))
+ setSmallIcon(R.drawable.ic_extension_24dp)
+ setContentIntent(NotificationReceiver.openExtensionsPendingActivity(context))
+ setAutoCancel(true)
+ }
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/anime/util/AnimeExtensionInstallService.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/anime/util/AnimeExtensionInstallService.kt
index f08af91d9c..000f4f35bd 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/extension/anime/util/AnimeExtensionInstallService.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/extension/anime/util/AnimeExtensionInstallService.kt
@@ -5,9 +5,9 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.IBinder
+import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.Notifications
-import eu.kanade.tachiyomi.data.preference.PreferenceValues
import eu.kanade.tachiyomi.extension.anime.installer.InstallerAnime
import eu.kanade.tachiyomi.extension.anime.installer.PackageInstallerInstallerAnime
import eu.kanade.tachiyomi.extension.anime.installer.ShizukuInstallerAnime
@@ -36,7 +36,7 @@ class AnimeExtensionInstallService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val uri = intent?.data
val id = intent?.getLongExtra(EXTRA_DOWNLOAD_ID, -1)?.takeIf { it != -1L }
- val installerUsed = intent?.getSerializableExtraCompat(
+ val installerUsed = intent?.getSerializableExtraCompat(
EXTRA_INSTALLER,
)
if (uri == null || id == null || installerUsed == null) {
@@ -46,8 +46,8 @@ class AnimeExtensionInstallService : Service() {
if (installer == null) {
installer = when (installerUsed) {
- PreferenceValues.ExtensionInstaller.PACKAGEINSTALLER -> PackageInstallerInstallerAnime(this)
- PreferenceValues.ExtensionInstaller.SHIZUKU -> ShizukuInstallerAnime(this)
+ BasePreferences.ExtensionInstaller.PACKAGEINSTALLER -> PackageInstallerInstallerAnime(this)
+ BasePreferences.ExtensionInstaller.SHIZUKU -> ShizukuInstallerAnime(this)
else -> {
logcat(LogPriority.ERROR) { "Not implemented for installer $installerUsed" }
stopSelf()
@@ -73,7 +73,7 @@ class AnimeExtensionInstallService : Service() {
context: Context,
downloadId: Long,
uri: Uri,
- installer: PreferenceValues.ExtensionInstaller,
+ installer: BasePreferences.ExtensionInstaller,
): Intent {
return Intent(context, AnimeExtensionInstallService::class.java)
.setDataAndType(uri, AnimeExtensionInstaller.APK_MIME)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/anime/util/AnimeExtensionInstaller.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/anime/util/AnimeExtensionInstaller.kt
index b1610d87aa..fc555129c6 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/extension/anime/util/AnimeExtensionInstaller.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/extension/anime/util/AnimeExtensionInstaller.kt
@@ -12,7 +12,6 @@ import androidx.core.content.getSystemService
import androidx.core.net.toUri
import com.jakewharton.rxrelay.PublishRelay
import eu.kanade.domain.base.BasePreferences
-import eu.kanade.tachiyomi.data.preference.PreferenceValues
import eu.kanade.tachiyomi.extension.InstallStep
import eu.kanade.tachiyomi.extension.anime.installer.InstallerAnime
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
@@ -134,7 +133,7 @@ internal class AnimeExtensionInstaller(private val context: Context) {
*/
fun installApk(downloadId: Long, uri: Uri) {
when (val installer = extensionInstaller.get()) {
- PreferenceValues.ExtensionInstaller.LEGACY -> {
+ BasePreferences.ExtensionInstaller.LEGACY -> {
val intent = Intent(context, AnimeExtensionInstallActivity::class.java)
.setDataAndType(uri, APK_MIME)
.putExtra(EXTRA_DOWNLOAD_ID, downloadId)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/manga/util/MangaExtensionInstallService.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/manga/util/MangaExtensionInstallService.kt
index 4568a8f2b1..e3c0bc0b60 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/extension/manga/util/MangaExtensionInstallService.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/extension/manga/util/MangaExtensionInstallService.kt
@@ -5,9 +5,9 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.IBinder
+import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.Notifications
-import eu.kanade.tachiyomi.data.preference.PreferenceValues
import eu.kanade.tachiyomi.extension.manga.installer.InstallerManga
import eu.kanade.tachiyomi.extension.manga.installer.PackageInstallerInstallerManga
import eu.kanade.tachiyomi.extension.manga.installer.ShizukuInstallerManga
@@ -36,7 +36,7 @@ class MangaExtensionInstallService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
val uri = intent?.data
val id = intent?.getLongExtra(EXTRA_DOWNLOAD_ID, -1)?.takeIf { it != -1L }
- val installerUsed = intent?.getSerializableExtraCompat(
+ val installerUsed = intent?.getSerializableExtraCompat(
EXTRA_INSTALLER,
)
if (uri == null || id == null || installerUsed == null) {
@@ -46,8 +46,8 @@ class MangaExtensionInstallService : Service() {
if (installer == null) {
installer = when (installerUsed) {
- PreferenceValues.ExtensionInstaller.PACKAGEINSTALLER -> PackageInstallerInstallerManga(this)
- PreferenceValues.ExtensionInstaller.SHIZUKU -> ShizukuInstallerManga(this)
+ BasePreferences.ExtensionInstaller.PACKAGEINSTALLER -> PackageInstallerInstallerManga(this)
+ BasePreferences.ExtensionInstaller.SHIZUKU -> ShizukuInstallerManga(this)
else -> {
logcat(LogPriority.ERROR) { "Not implemented for installer $installerUsed" }
stopSelf()
@@ -73,7 +73,7 @@ class MangaExtensionInstallService : Service() {
context: Context,
downloadId: Long,
uri: Uri,
- installer: PreferenceValues.ExtensionInstaller,
+ installer: BasePreferences.ExtensionInstaller,
): Intent {
return Intent(context, MangaExtensionInstallService::class.java)
.setDataAndType(uri, MangaExtensionInstaller.APK_MIME)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/manga/util/MangaExtensionInstaller.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/manga/util/MangaExtensionInstaller.kt
index e95f3eca67..88b2b42201 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/extension/manga/util/MangaExtensionInstaller.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/extension/manga/util/MangaExtensionInstaller.kt
@@ -12,7 +12,6 @@ import androidx.core.content.getSystemService
import androidx.core.net.toUri
import com.jakewharton.rxrelay.PublishRelay
import eu.kanade.domain.base.BasePreferences
-import eu.kanade.tachiyomi.data.preference.PreferenceValues
import eu.kanade.tachiyomi.extension.InstallStep
import eu.kanade.tachiyomi.extension.manga.installer.InstallerManga
import eu.kanade.tachiyomi.extension.manga.model.MangaExtension
@@ -134,7 +133,7 @@ internal class MangaExtensionInstaller(private val context: Context) {
*/
fun installApk(downloadId: Long, uri: Uri) {
when (val installer = extensionInstaller.get()) {
- PreferenceValues.ExtensionInstaller.LEGACY -> {
+ BasePreferences.ExtensionInstaller.LEGACY -> {
val intent = Intent(context, MangaExtensionInstallActivity::class.java)
.setDataAndType(uri, APK_MIME)
.putExtra(EXTRA_DOWNLOAD_ID, downloadId)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/anime/AnimeSourceExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/source/anime/AnimeSourceExtensions.kt
index 138e66cbdc..a359d76566 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/source/anime/AnimeSourceExtensions.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/source/anime/AnimeSourceExtensions.kt
@@ -6,7 +6,7 @@ import eu.kanade.tachiyomi.animesource.AnimeSource
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
import tachiyomi.domain.source.anime.model.AnimeSourceData
import tachiyomi.domain.source.anime.model.StubAnimeSource
-import tachiyomi.source.local.entries.anime.LocalAnimeSource
+import tachiyomi.source.local.entries.anime.isLocal
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@@ -31,6 +31,4 @@ fun AnimeSource.getNameForAnimeInfo(): String {
}
}
-fun AnimeSource.isLocal(): Boolean = id == LocalAnimeSource.ID
-
fun AnimeSource.isLocalOrStub(): Boolean = isLocal() || this is StubAnimeSource
diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/manga/MangaSourceExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/source/manga/MangaSourceExtensions.kt
index c8d0794f61..afd17ceaa5 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/source/manga/MangaSourceExtensions.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/source/manga/MangaSourceExtensions.kt
@@ -6,7 +6,7 @@ import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
import eu.kanade.tachiyomi.source.MangaSource
import tachiyomi.domain.source.manga.model.MangaSourceData
import tachiyomi.domain.source.manga.model.StubMangaSource
-import tachiyomi.source.local.entries.manga.LocalMangaSource
+import tachiyomi.source.local.entries.manga.isLocal
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@@ -31,6 +31,4 @@ fun MangaSource.getNameForMangaInfo(): String {
}
}
-fun MangaSource.isLocal(): Boolean = id == LocalMangaSource.ID
-
fun MangaSource.isLocalOrStub(): Boolean = isLocal() || this is StubMangaSource
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/SecureActivityDelegate.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/SecureActivityDelegate.kt
index 178a8704f4..02589cec98 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/SecureActivityDelegate.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/SecureActivityDelegate.kt
@@ -97,7 +97,7 @@ class SecureActivityDelegateImpl : SecureActivityDelegate, DefaultLifecycleObser
secureScreen == SecurityPreferences.SecureScreenMode.ALWAYS ||
secureScreen == SecurityPreferences.SecureScreenMode.INCOGNITO && incognitoMode
}
- .onEach { activity.window.setSecureScreen(it) }
+ .onEach(activity.window::setSecureScreen)
.launchIn(activity.lifecycleScope)
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/ThemingDelegate.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/ThemingDelegate.kt
index bcf3441a76..0463229369 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/ThemingDelegate.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/delegate/ThemingDelegate.kt
@@ -74,6 +74,6 @@ class ThemingDelegateImpl : ThemingDelegate {
override fun applyAppTheme(activity: Activity) {
val uiPreferences = Injekt.get()
ThemingDelegate.getThemeResIds(uiPreferences.appTheme().get(), uiPreferences.themeDarkAmoled().get())
- .forEach { activity.setTheme(it) }
+ .forEach(activity::setTheme)
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/extension/AnimeExtensionFilterScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/extension/AnimeExtensionFilterScreen.kt
index 2985a8c673..06180285f8 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/extension/AnimeExtensionFilterScreen.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/extension/AnimeExtensionFilterScreen.kt
@@ -34,7 +34,7 @@ class AnimeExtensionFilterScreen : Screen() {
AnimeExtensionFilterScreen(
navigateUp = navigator::pop,
state = successState,
- onClickToggle = { screenModel.toggle(it) },
+ onClickToggle = screenModel::toggle,
)
LaunchedEffect(Unit) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/extension/AnimeExtensionsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/extension/AnimeExtensionsScreenModel.kt
index 8288643043..851bac08f0 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/extension/AnimeExtensionsScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/extension/AnimeExtensionsScreenModel.kt
@@ -142,7 +142,7 @@ class AnimeExtensionsScreenModel(
else -> it.extension
}
}
- .forEach { updateExtension(it) }
+ .forEach(::updateExtension)
}
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/extension/details/AnimeExtensionDetailsScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/extension/details/AnimeExtensionDetailsScreen.kt
index cdb961217b..1e48494c79 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/extension/details/AnimeExtensionDetailsScreen.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/extension/details/AnimeExtensionDetailsScreen.kt
@@ -40,9 +40,9 @@ data class AnimeExtensionDetailsScreen(
onClickReadme = { uriHandler.openUri(screenModel.getReadmeUrl()) },
onClickEnableAll = { screenModel.toggleSources(true) },
onClickDisableAll = { screenModel.toggleSources(false) },
- onClickClearCookies = { screenModel.clearCookies() },
- onClickUninstall = { screenModel.uninstallExtension() },
- onClickSource = { screenModel.toggleSource(it) },
+ onClickClearCookies = screenModel::clearCookies,
+ onClickUninstall = screenModel::uninstallExtension,
+ onClickSource = screenModel::toggleSource,
)
LaunchedEffect(Unit) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/AnimeSourceSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/AnimeSourceSearchScreen.kt
index 1ebdcea514..3238f45468 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/AnimeSourceSearchScreen.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/AnimeSourceSearchScreen.kt
@@ -58,7 +58,7 @@ data class AnimeSourceSearchScreen(
searchQuery = state.toolbarQuery ?: "",
onChangeSearchQuery = screenModel::setToolbarQuery,
onClickCloseSearch = navigator::pop,
- onSearch = { screenModel.search(it) },
+ onSearch = screenModel::search,
scrollBehavior = scrollBehavior,
)
},
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/MigrateAnimeDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/MigrateAnimeDialog.kt
index e1e3b79bfe..64592181fb 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/MigrateAnimeDialog.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/MigrateAnimeDialog.kt
@@ -2,7 +2,9 @@ package eu.kanade.tachiyomi.ui.browse.anime.migration.search
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
@@ -23,6 +25,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEachIndexed
import cafe.adriel.voyager.core.model.StateScreenModel
import eu.kanade.domain.entries.anime.interactor.UpdateAnime
@@ -112,7 +115,9 @@ internal fun MigrateAnimeDialog(
}
},
confirmButton = {
- Row {
+ FlowRow(
+ horizontalArrangement = Arrangement.spacedBy(4.dp),
+ ) {
TextButton(
onClick = {
onClickTitle()
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/MigrateAnimeSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/MigrateAnimeSearchScreen.kt
index 1abb2eb6bd..f04b83a56b 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/MigrateAnimeSearchScreen.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/migration/search/MigrateAnimeSearchScreen.kt
@@ -22,9 +22,7 @@ class MigrateAnimeSearchScreen(private val animeId: Long) : Screen() {
MigrateAnimeSearchScreen(
navigateUp = navigator::pop,
state = state,
- getAnime = { source, anime ->
- screenModel.getAnime(source = source, initialAnime = anime)
- },
+ getAnime = { screenModel.getAnime(it) },
onChangeSearchQuery = screenModel::updateSearchQuery,
onSearch = screenModel::search,
onClickSource = {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/BrowseAnimeSourceScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/BrowseAnimeSourceScreen.kt
index b8f559fe62..ecfcbf93e1 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/BrowseAnimeSourceScreen.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/BrowseAnimeSourceScreen.kt
@@ -126,7 +126,7 @@ data class BrowseAnimeSourceScreen(
onWebViewClick = onWebViewClick,
onHelpClick = onHelpClick,
onSettingsClick = { navigator.push(SourcePreferencesScreen(sourceId)) },
- onSearch = { screenModel.search(it) },
+ onSearch = screenModel::search,
)
Row(
@@ -235,15 +235,9 @@ data class BrowseAnimeSourceScreen(
SourceFilterAnimeDialog(
onDismissRequest = onDismissRequest,
filters = state.filters,
- onReset = {
- screenModel.resetFilters()
- },
- onFilter = {
- screenModel.search(filters = state.filters)
- },
- onUpdate = {
- screenModel.setFilters(it)
- },
+ onReset = screenModel::resetFilters,
+ onFilter = { screenModel.search(filters = state.filters) },
+ onUpdate = screenModel::setFilters,
)
}
is BrowseAnimeSourceScreenModel.Dialog.AddDuplicateAnime -> {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/BrowseAnimeSourceScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/BrowseAnimeSourceScreenModel.kt
index 554605ff63..bc8627e320 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/BrowseAnimeSourceScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/browse/BrowseAnimeSourceScreenModel.kt
@@ -15,12 +15,11 @@ import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.core.preference.asState
import eu.kanade.domain.entries.anime.interactor.UpdateAnime
-import eu.kanade.domain.entries.anime.model.copyFrom
import eu.kanade.domain.entries.anime.model.toDomainAnime
-import eu.kanade.domain.entries.anime.model.toSAnime
import eu.kanade.domain.items.episode.interactor.SyncEpisodesWithTrackServiceTwoWay
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.domain.track.anime.model.toDomainTrack
+import eu.kanade.presentation.util.ioCoroutineScope
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
import eu.kanade.tachiyomi.animesource.model.AnimeFilterList
import eu.kanade.tachiyomi.data.cache.AnimeCoverCache
@@ -36,7 +35,6 @@ import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
@@ -44,12 +42,11 @@ import logcat.LogPriority
import tachiyomi.core.preference.CheckboxState
import tachiyomi.core.preference.mapAsCheckboxState
import tachiyomi.core.util.lang.launchIO
-import tachiyomi.core.util.lang.withIOContext
-import tachiyomi.core.util.lang.withNonCancellableContext
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.category.anime.interactor.GetAnimeCategories
import tachiyomi.domain.category.anime.interactor.SetAnimeCategories
import tachiyomi.domain.category.model.Category
+import tachiyomi.domain.entries.anime.interactor.GetAnime
import tachiyomi.domain.entries.anime.interactor.GetDuplicateLibraryAnime
import tachiyomi.domain.entries.anime.interactor.NetworkToLocalAnime
import tachiyomi.domain.entries.anime.model.Anime
@@ -78,6 +75,7 @@ class BrowseAnimeSourceScreenModel(
private val getEpisodeByAnimeId: GetEpisodeByAnimeId = Injekt.get(),
private val setAnimeCategories: SetAnimeCategories = Injekt.get(),
private val setAnimeDefaultEpisodeFlags: SetAnimeDefaultEpisodeFlags = Injekt.get(),
+ private val getAnime: GetAnime = Injekt.get(),
private val networkToLocalAnime: NetworkToLocalAnime = Injekt.get(),
private val updateAnime: UpdateAnime = Injekt.get(),
private val insertTrack: InsertAnimeTrack = Injekt.get(),
@@ -121,23 +119,21 @@ class BrowseAnimeSourceScreenModel(
) {
getRemoteAnime.subscribe(sourceId, listing.query ?: "", listing.filters)
}.flow.map { pagingData ->
- pagingData
- .map {
- flow {
- val localAnime = withIOContext { networkToLocalAnime.await(it.toDomainAnime(sourceId)) }
- emit(localAnime)
+ pagingData.map {
+ networkToLocalAnime.await(it.toDomainAnime(sourceId))
+ .let { localAnime ->
+ getAnime.subscribe(localAnime.url, localAnime.source)
}
- .filterNotNull()
- .filter {
- !sourcePreferences.hideInAnimeLibraryItems().get() || !it.favorite
- }
- .onEach(::initializeAnime)
- .stateIn(coroutineScope)
- }
+ .filterNotNull()
+ .filter { localAnime ->
+ !sourcePreferences.hideInAnimeLibraryItems().get() || !localAnime.favorite
+ }
+ .stateIn(ioCoroutineScope)
+ }
}
- .cachedIn(coroutineScope)
+ .cachedIn(ioCoroutineScope)
}
- .stateIn(coroutineScope, SharingStarted.Lazily, emptyFlow())
+ .stateIn(ioCoroutineScope, SharingStarted.Lazily, emptyFlow())
fun getColumnsPreference(orientation: Int): GridCells {
val isLandscape = orientation == Configuration.ORIENTATION_LANDSCAPE
@@ -230,26 +226,6 @@ class BrowseAnimeSourceScreenModel(
}
}
- /**
- * Initialize an anime.
- *
- * @return anime to initialize.
- */
- private suspend fun initializeAnime(anime: Anime) {
- if (anime.thumbnailUrl != null || anime.initialized) return
- withNonCancellableContext {
- try {
- val networkAnime = source.getAnimeDetails(anime.toSAnime())
- val updatedAnime = anime.copyFrom(networkAnime)
- .copy(initialized = true)
-
- updateAnime.await(updatedAnime.toAnimeUpdate())
- } catch (e: Exception) {
- logcat(LogPriority.ERROR, e)
- }
- }
- }
-
/**
* Adds or removes an anime from the library.
*
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/globalsearch/AnimeSearchScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/globalsearch/AnimeSearchScreenModel.kt
index 5b9d5a37c3..a69fb910f5 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/globalsearch/AnimeSearchScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/globalsearch/AnimeSearchScreenModel.kt
@@ -4,12 +4,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.produceState
import cafe.adriel.voyager.core.model.StateScreenModel
-import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.domain.entries.anime.interactor.UpdateAnime
-import eu.kanade.domain.entries.anime.model.copyFrom
import eu.kanade.domain.entries.anime.model.toDomainAnime
-import eu.kanade.domain.entries.anime.model.toSAnime
import eu.kanade.domain.source.service.SourcePreferences
+import eu.kanade.presentation.util.ioCoroutineScope
import eu.kanade.tachiyomi.animesource.AnimeCatalogueSource
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
import kotlinx.coroutines.asCoroutineDispatcher
@@ -18,15 +16,10 @@ import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-import logcat.LogPriority
import tachiyomi.core.util.lang.awaitSingle
-import tachiyomi.core.util.lang.withIOContext
-import tachiyomi.core.util.lang.withNonCancellableContext
-import tachiyomi.core.util.system.logcat
import tachiyomi.domain.entries.anime.interactor.GetAnime
import tachiyomi.domain.entries.anime.interactor.NetworkToLocalAnime
import tachiyomi.domain.entries.anime.model.Anime
-import tachiyomi.domain.entries.anime.model.toAnimeUpdate
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.concurrent.Executors
@@ -57,43 +50,19 @@ abstract class AnimeSearchScreenModel(
}
@Composable
- fun getAnime(source: AnimeCatalogueSource, initialAnime: Anime): State {
+ fun getAnime(initialAnime: Anime): State {
return produceState(initialValue = initialAnime) {
getAnime.subscribe(initialAnime.url, initialAnime.source)
.collectLatest { anime ->
if (anime == null) return@collectLatest
- withIOContext {
- initializeAnime(source, anime)
- }
value = anime
}
}
}
- /**
- * Initialize a anime.
- *
- * @param source to interact with
- * @param anime to initialize.
- */
- private suspend fun initializeAnime(source: AnimeCatalogueSource, anime: Anime) {
- if (anime.thumbnailUrl != null || anime.initialized) return
- withNonCancellableContext {
- try {
- val networkAnime = source.getAnimeDetails(anime.toSAnime())
- val updatedAnime = anime.copyFrom(networkAnime)
- .copy(initialized = true)
-
- updateAnime.await(updatedAnime.toAnimeUpdate())
- } catch (e: Exception) {
- logcat(LogPriority.ERROR, e)
- }
- }
- }
-
abstract fun getEnabledSources(): List
- fun getSelectedSources(): List {
+ private fun getSelectedSources(): List {
val filter = extensionFilter
val enabledSources = getEnabledSources()
@@ -124,7 +93,7 @@ abstract class AnimeSearchScreenModel(
abstract fun getItems(): Map
- fun getAndUpdateItems(function: (Map) -> Map) {
+ private fun getAndUpdateItems(function: (Map) -> Map) {
updateItems(function(getItems()))
}
@@ -136,7 +105,7 @@ abstract class AnimeSearchScreenModel(
val initialItems = getSelectedSources().associateWith { AnimeSearchItemResult.Loading }
updateItems(initialItems)
- coroutineScope.launch {
+ ioCoroutineScope.launch {
sources
.map { source ->
async {
@@ -145,10 +114,8 @@ abstract class AnimeSearchScreenModel(
source.fetchSearchAnime(1, query, source.getFilterList()).awaitSingle()
}
- val titles = withIOContext {
- page.animes.map {
- networkToLocalAnime.await(it.toDomainAnime(source.id))
- }
+ val titles = page.animes.map {
+ networkToLocalAnime.await(it.toDomainAnime(source.id))
}
getAndUpdateItems { items ->
@@ -159,7 +126,7 @@ abstract class AnimeSearchScreenModel(
} catch (e: Exception) {
getAndUpdateItems { items ->
val mutableMap = items.toMutableMap()
- mutableMap[source] = AnimeSearchItemResult.Error(throwable = e)
+ mutableMap[source] = AnimeSearchItemResult.Error(e)
mutableMap.toSortedMap(sortComparator(mutableMap))
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/globalsearch/GlobalAnimeSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/globalsearch/GlobalAnimeSearchScreen.kt
index 8014b30837..3f211d35e1 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/globalsearch/GlobalAnimeSearchScreen.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/anime/source/globalsearch/GlobalAnimeSearchScreen.kt
@@ -1,8 +1,12 @@
package eu.kanade.tachiyomi.ui.browse.anime.source.globalsearch
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
@@ -10,10 +14,11 @@ import eu.kanade.presentation.browse.anime.GlobalAnimeSearchScreen
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.ui.browse.anime.source.browse.BrowseAnimeSourceScreen
import eu.kanade.tachiyomi.ui.entries.anime.AnimeScreen
+import tachiyomi.presentation.core.screens.LoadingScreen
class GlobalAnimeSearchScreen(
val searchQuery: String = "",
- val extensionFilter: String = "",
+ private val extensionFilter: String = "",
) : Screen() {
@Composable
@@ -27,26 +32,44 @@ class GlobalAnimeSearchScreen(
)
}
val state by screenModel.state.collectAsState()
+ var showSingleLoadingScreen by remember {
+ mutableStateOf(searchQuery.isNotEmpty() && extensionFilter.isNotEmpty() && state.total == 1)
+ }
+
+ if (showSingleLoadingScreen) {
+ LoadingScreen()
- GlobalAnimeSearchScreen(
- state = state,
- navigateUp = navigator::pop,
- onChangeSearchQuery = screenModel::updateSearchQuery,
- onSearch = screenModel::search,
- getAnime = { source, anime ->
- screenModel.getAnime(
- source = source,
- initialAnime = anime,
- )
- },
- onClickSource = {
- if (!screenModel.incognitoMode.get()) {
- screenModel.lastUsedSourceId.set(it.id)
+ LaunchedEffect(state.items) {
+ when (val result = state.items.values.singleOrNull()) {
+ AnimeSearchItemResult.Loading -> return@LaunchedEffect
+ is AnimeSearchItemResult.Success -> {
+ val anime = result.result.singleOrNull()
+ if (anime != null) {
+ navigator.replace(AnimeScreen(anime.id, true))
+ } else {
+ // Backoff to result screen
+ showSingleLoadingScreen = false
+ }
+ }
+ else -> showSingleLoadingScreen = false
}
- navigator.push(BrowseAnimeSourceScreen(it.id, state.searchQuery))
- },
- onClickItem = { navigator.push(AnimeScreen(it.id, true)) },
- onLongClickItem = { navigator.push(AnimeScreen(it.id, true)) },
- )
+ }
+ } else {
+ GlobalAnimeSearchScreen(
+ state = state,
+ navigateUp = navigator::pop,
+ onChangeSearchQuery = screenModel::updateSearchQuery,
+ onSearch = screenModel::search,
+ getAnime = { screenModel.getAnime(it) },
+ onClickSource = {
+ if (!screenModel.incognitoMode.get()) {
+ screenModel.lastUsedSourceId.set(it.id)
+ }
+ navigator.push(BrowseAnimeSourceScreen(it.id, state.searchQuery))
+ },
+ onClickItem = { navigator.push(AnimeScreen(it.id, true)) },
+ onLongClickItem = { navigator.push(AnimeScreen(it.id, true)) },
+ )
+ }
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/MangaExtensionFilterScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/MangaExtensionFilterScreen.kt
index dce3acd254..ef21c39c44 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/MangaExtensionFilterScreen.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/MangaExtensionFilterScreen.kt
@@ -34,7 +34,7 @@ class MangaExtensionFilterScreen : Screen() {
MangaExtensionFilterScreen(
navigateUp = navigator::pop,
state = successState,
- onClickToggle = { screenModel.toggle(it) },
+ onClickToggle = screenModel::toggle,
)
LaunchedEffect(Unit) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/MangaExtensionsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/MangaExtensionsScreenModel.kt
index 105b81007a..94736ab69e 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/MangaExtensionsScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/MangaExtensionsScreenModel.kt
@@ -144,7 +144,7 @@ class MangaExtensionsScreenModel(
else -> it.extension
}
}
- .forEach { updateExtension(it) }
+ .forEach(::updateExtension)
}
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/details/MangaExtensionDetailsScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/details/MangaExtensionDetailsScreen.kt
index 800be34723..5fc1ac1cfd 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/details/MangaExtensionDetailsScreen.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/extension/details/MangaExtensionDetailsScreen.kt
@@ -40,9 +40,9 @@ data class MangaExtensionDetailsScreen(
onClickReadme = { uriHandler.openUri(screenModel.getReadmeUrl()) },
onClickEnableAll = { screenModel.toggleSources(true) },
onClickDisableAll = { screenModel.toggleSources(false) },
- onClickClearCookies = { screenModel.clearCookies() },
- onClickUninstall = { screenModel.uninstallExtension() },
- onClickSource = { screenModel.toggleSource(it) },
+ onClickClearCookies = screenModel::clearCookies,
+ onClickUninstall = screenModel::uninstallExtension,
+ onClickSource = screenModel::toggleSource,
)
LaunchedEffect(Unit) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MangaSourceSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MangaSourceSearchScreen.kt
index c97b1f8338..2896e323a5 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MangaSourceSearchScreen.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MangaSourceSearchScreen.kt
@@ -58,7 +58,7 @@ data class MangaSourceSearchScreen(
searchQuery = state.toolbarQuery ?: "",
onChangeSearchQuery = screenModel::setToolbarQuery,
onClickCloseSearch = navigator::pop,
- onSearch = { screenModel.search(it) },
+ onSearch = screenModel::search,
scrollBehavior = scrollBehavior,
)
},
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MigrateMangaDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MigrateMangaDialog.kt
index 3e6aa43c53..73e84908a7 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MigrateMangaDialog.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MigrateMangaDialog.kt
@@ -2,7 +2,9 @@ package eu.kanade.tachiyomi.ui.browse.manga.migration.search
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
@@ -23,6 +25,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEachIndexed
import cafe.adriel.voyager.core.model.StateScreenModel
import eu.kanade.domain.entries.manga.interactor.UpdateManga
@@ -111,7 +114,9 @@ internal fun MigrateMangaDialog(
}
},
confirmButton = {
- Row {
+ FlowRow(
+ horizontalArrangement = Arrangement.spacedBy(4.dp),
+ ) {
TextButton(
onClick = {
onClickTitle()
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MigrateMangaSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MigrateMangaSearchScreen.kt
index 9d6de70c5c..153d17dffb 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MigrateMangaSearchScreen.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/migration/search/MigrateMangaSearchScreen.kt
@@ -22,9 +22,7 @@ class MigrateSearchScreen(private val mangaId: Long) : Screen() {
MigrateMangaSearchScreen(
navigateUp = navigator::pop,
state = state,
- getManga = { source, manga ->
- screenModel.getManga(source = source, initialManga = manga)
- },
+ getManga = { screenModel.getManga(it) },
onChangeSearchQuery = screenModel::updateSearchQuery,
onSearch = screenModel::search,
onClickSource = {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/BrowseMangaSourceScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/BrowseMangaSourceScreen.kt
index b55df06009..b5e6304b80 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/BrowseMangaSourceScreen.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/BrowseMangaSourceScreen.kt
@@ -126,7 +126,7 @@ data class BrowseMangaSourceScreen(
onWebViewClick = onWebViewClick,
onHelpClick = onHelpClick,
onSettingsClick = { navigator.push(MangaSourcePreferencesScreen(sourceId)) },
- onSearch = { screenModel.search(it) },
+ onSearch = screenModel::search,
)
Row(
@@ -235,15 +235,9 @@ data class BrowseMangaSourceScreen(
SourceFilterMangaDialog(
onDismissRequest = onDismissRequest,
filters = state.filters,
- onReset = {
- screenModel.resetFilters()
- },
- onFilter = {
- screenModel.search(filters = state.filters)
- },
- onUpdate = {
- screenModel.setFilters(it)
- },
+ onReset = screenModel::resetFilters,
+ onFilter = { screenModel.search(filters = state.filters) },
+ onUpdate = screenModel::setFilters,
)
}
is BrowseMangaSourceScreenModel.Dialog.AddDuplicateManga -> {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/BrowseMangaSourceScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/BrowseMangaSourceScreenModel.kt
index 79be335626..a19b2c9b09 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/BrowseMangaSourceScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/browse/BrowseMangaSourceScreenModel.kt
@@ -15,12 +15,11 @@ import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.core.preference.asState
import eu.kanade.domain.entries.manga.interactor.UpdateManga
-import eu.kanade.domain.entries.manga.model.copyFrom
import eu.kanade.domain.entries.manga.model.toDomainManga
-import eu.kanade.domain.entries.manga.model.toSManga
import eu.kanade.domain.items.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
import eu.kanade.domain.source.service.SourcePreferences
import eu.kanade.domain.track.manga.model.toDomainTrack
+import eu.kanade.presentation.util.ioCoroutineScope
import eu.kanade.tachiyomi.data.cache.MangaCoverCache
import eu.kanade.tachiyomi.data.track.EnhancedMangaTrackService
import eu.kanade.tachiyomi.data.track.TrackManager
@@ -36,7 +35,6 @@ import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
@@ -44,13 +42,12 @@ import logcat.LogPriority
import tachiyomi.core.preference.CheckboxState
import tachiyomi.core.preference.mapAsCheckboxState
import tachiyomi.core.util.lang.launchIO
-import tachiyomi.core.util.lang.withIOContext
-import tachiyomi.core.util.lang.withNonCancellableContext
import tachiyomi.core.util.system.logcat
import tachiyomi.domain.category.manga.interactor.GetMangaCategories
import tachiyomi.domain.category.manga.interactor.SetMangaCategories
import tachiyomi.domain.category.model.Category
import tachiyomi.domain.entries.manga.interactor.GetDuplicateLibraryManga
+import tachiyomi.domain.entries.manga.interactor.GetManga
import tachiyomi.domain.entries.manga.interactor.NetworkToLocalManga
import tachiyomi.domain.entries.manga.model.Manga
import tachiyomi.domain.entries.manga.model.toMangaUpdate
@@ -78,6 +75,7 @@ class BrowseMangaSourceScreenModel(
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
private val setMangaCategories: SetMangaCategories = Injekt.get(),
private val setMangaDefaultChapterFlags: SetMangaDefaultChapterFlags = Injekt.get(),
+ private val getManga: GetManga = Injekt.get(),
private val networkToLocalManga: NetworkToLocalManga = Injekt.get(),
private val updateManga: UpdateManga = Injekt.get(),
private val insertTrack: InsertMangaTrack = Injekt.get(),
@@ -121,23 +119,21 @@ class BrowseMangaSourceScreenModel(
) {
getRemoteManga.subscribe(sourceId, listing.query ?: "", listing.filters)
}.flow.map { pagingData ->
- pagingData
- .map {
- flow {
- val localManga = withIOContext { networkToLocalManga.await(it.toDomainManga(sourceId)) }
- emit(localManga)
+ pagingData.map {
+ networkToLocalManga.await(it.toDomainManga(sourceId))
+ .let { localManga ->
+ getManga.subscribe(localManga.url, localManga.source)
}
- .filterNotNull()
- .filter {
- !sourcePreferences.hideInMangaLibraryItems().get() || !it.favorite
- }
- .onEach(::initializeManga)
- .stateIn(coroutineScope)
- }
+ .filterNotNull()
+ .filter { localManga ->
+ !sourcePreferences.hideInMangaLibraryItems().get() || !localManga.favorite
+ }
+ .stateIn(ioCoroutineScope)
+ }
}
- .cachedIn(coroutineScope)
+ .cachedIn(ioCoroutineScope)
}
- .stateIn(coroutineScope, SharingStarted.Lazily, emptyFlow())
+ .stateIn(ioCoroutineScope, SharingStarted.Lazily, emptyFlow())
fun getColumnsPreference(orientation: Int): GridCells {
val isLandscape = orientation == Configuration.ORIENTATION_LANDSCAPE
@@ -231,26 +227,6 @@ class BrowseMangaSourceScreenModel(
}
}
- /**
- * Initialize a manga.
- *
- * @param manga to initialize.
- */
- private suspend fun initializeManga(manga: Manga) {
- if (manga.thumbnailUrl != null || manga.initialized) return
- withNonCancellableContext {
- try {
- val networkManga = source.getMangaDetails(manga.toSManga())
- val updatedManga = manga.copyFrom(networkManga)
- .copy(initialized = true)
-
- updateManga.await(updatedManga.toMangaUpdate())
- } catch (e: Exception) {
- logcat(LogPriority.ERROR, e)
- }
- }
- }
-
/**
* Adds or removes a manga from the library.
*
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/GlobalMangaSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/GlobalMangaSearchScreen.kt
index 7836dd65e9..1570652a51 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/GlobalMangaSearchScreen.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/GlobalMangaSearchScreen.kt
@@ -1,8 +1,12 @@
package eu.kanade.tachiyomi.ui.browse.manga.source.globalsearch
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import cafe.adriel.voyager.core.model.rememberScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
@@ -10,10 +14,11 @@ import eu.kanade.presentation.browse.manga.GlobalMangaSearchScreen
import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.ui.browse.manga.source.browse.BrowseMangaSourceScreen
import eu.kanade.tachiyomi.ui.entries.manga.MangaScreen
+import tachiyomi.presentation.core.screens.LoadingScreen
class GlobalMangaSearchScreen(
val searchQuery: String = "",
- val extensionFilter: String = "",
+ private val extensionFilter: String = "",
) : Screen() {
@Composable
@@ -27,26 +32,44 @@ class GlobalMangaSearchScreen(
)
}
val state by screenModel.state.collectAsState()
+ var showSingleLoadingScreen by remember {
+ mutableStateOf(searchQuery.isNotEmpty() && extensionFilter.isNotEmpty() && state.total == 1)
+ }
+
+ if (showSingleLoadingScreen) {
+ LoadingScreen()
- GlobalMangaSearchScreen(
- state = state,
- navigateUp = navigator::pop,
- onChangeSearchQuery = screenModel::updateSearchQuery,
- onSearch = screenModel::search,
- getManga = { source, manga ->
- screenModel.getManga(
- source = source,
- initialManga = manga,
- )
- },
- onClickSource = {
- if (!screenModel.incognitoMode.get()) {
- screenModel.lastUsedSourceId.set(it.id)
+ LaunchedEffect(state.items) {
+ when (val result = state.items.values.singleOrNull()) {
+ MangaSearchItemResult.Loading -> return@LaunchedEffect
+ is MangaSearchItemResult.Success -> {
+ val manga = result.result.singleOrNull()
+ if (manga != null) {
+ navigator.replace(MangaScreen(manga.id, true))
+ } else {
+ // Backoff to result screen
+ showSingleLoadingScreen = false
+ }
+ }
+ else -> showSingleLoadingScreen = false
}
- navigator.push(BrowseMangaSourceScreen(it.id, state.searchQuery))
- },
- onClickItem = { navigator.push(MangaScreen(it.id, true)) },
- onLongClickItem = { navigator.push(MangaScreen(it.id, true)) },
- )
+ }
+ } else {
+ GlobalMangaSearchScreen(
+ state = state,
+ navigateUp = navigator::pop,
+ onChangeSearchQuery = screenModel::updateSearchQuery,
+ onSearch = screenModel::search,
+ getManga = { screenModel.getManga(it) },
+ onClickSource = {
+ if (!screenModel.incognitoMode.get()) {
+ screenModel.lastUsedSourceId.set(it.id)
+ }
+ navigator.push(BrowseMangaSourceScreen(it.id, state.searchQuery))
+ },
+ onClickItem = { navigator.push(MangaScreen(it.id, true)) },
+ onLongClickItem = { navigator.push(MangaScreen(it.id, true)) },
+ )
+ }
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/MangaSearchScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/MangaSearchScreenModel.kt
index b5a19805c8..ef7373e563 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/MangaSearchScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/manga/source/globalsearch/MangaSearchScreenModel.kt
@@ -4,12 +4,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.produceState
import cafe.adriel.voyager.core.model.StateScreenModel
-import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.domain.entries.manga.interactor.UpdateManga
-import eu.kanade.domain.entries.manga.model.copyFrom
import eu.kanade.domain.entries.manga.model.toDomainManga
-import eu.kanade.domain.entries.manga.model.toSManga
import eu.kanade.domain.source.service.SourcePreferences
+import eu.kanade.presentation.util.ioCoroutineScope
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
import eu.kanade.tachiyomi.source.CatalogueSource
import kotlinx.coroutines.asCoroutineDispatcher
@@ -18,15 +16,10 @@ import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-import logcat.LogPriority
import tachiyomi.core.util.lang.awaitSingle
-import tachiyomi.core.util.lang.withIOContext
-import tachiyomi.core.util.lang.withNonCancellableContext
-import tachiyomi.core.util.system.logcat
import tachiyomi.domain.entries.manga.interactor.GetManga
import tachiyomi.domain.entries.manga.interactor.NetworkToLocalManga
import tachiyomi.domain.entries.manga.model.Manga
-import tachiyomi.domain.entries.manga.model.toMangaUpdate
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.concurrent.Executors
@@ -57,43 +50,19 @@ abstract class MangaSearchScreenModel(
}
@Composable
- fun getManga(source: CatalogueSource, initialManga: Manga): State {
+ fun getManga(initialManga: Manga): State {
return produceState(initialValue = initialManga) {
getManga.subscribe(initialManga.url, initialManga.source)
.collectLatest { manga ->
if (manga == null) return@collectLatest
- withIOContext {
- initializeManga(source, manga)
- }
value = manga
}
}
}
- /**
- * Initialize a manga.
- *
- * @param source to interact with
- * @param manga to initialize.
- */
- private suspend fun initializeManga(source: CatalogueSource, manga: Manga) {
- if (manga.thumbnailUrl != null || manga.initialized) return
- withNonCancellableContext {
- try {
- val networkManga = source.getMangaDetails(manga.toSManga())
- val updatedManga = manga.copyFrom(networkManga)
- .copy(initialized = true)
-
- updateManga.await(updatedManga.toMangaUpdate())
- } catch (e: Exception) {
- logcat(LogPriority.ERROR, e)
- }
- }
- }
-
abstract fun getEnabledSources(): List
- fun getSelectedSources(): List {
+ private fun getSelectedSources(): List {
val filter = extensionFilter
val enabledSources = getEnabledSources()
@@ -124,7 +93,7 @@ abstract class MangaSearchScreenModel(
abstract fun getItems(): Map
- fun getAndUpdateItems(function: (Map) -> Map) {
+ private fun getAndUpdateItems(function: (Map) -> Map) {
updateItems(function(getItems()))
}
@@ -136,7 +105,7 @@ abstract class MangaSearchScreenModel(
val initialItems = getSelectedSources().associateWith { MangaSearchItemResult.Loading }
updateItems(initialItems)
- coroutineScope.launch {
+ ioCoroutineScope.launch {
sources
.map { source ->
async {
@@ -145,10 +114,8 @@ abstract class MangaSearchScreenModel(
source.fetchSearchManga(1, query, source.getFilterList()).awaitSingle()
}
- val titles = withIOContext {
- page.mangas.map {
- networkToLocalManga.await(it.toDomainManga(source.id))
- }
+ val titles = page.mangas.map {
+ networkToLocalManga.await(it.toDomainManga(source.id))
}
getAndUpdateItems { items ->
@@ -159,7 +126,7 @@ abstract class MangaSearchScreenModel(
} catch (e: Exception) {
getAndUpdateItems { items ->
val mutableMap = items.toMutableMap()
- mutableMap[source] = MangaSearchItemResult.Error(throwable = e)
+ mutableMap[source] = MangaSearchItemResult.Error(e)
mutableMap.toSortedMap(sortComparator(mutableMap))
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/entries/anime/AnimeScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/entries/anime/AnimeScreenModel.kt
index 22bfce6e8e..f6caa932a4 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/entries/anime/AnimeScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/entries/anime/AnimeScreenModel.kt
@@ -13,7 +13,6 @@ import eu.kanade.core.util.addOrRemove
import eu.kanade.domain.entries.anime.interactor.SetAnimeViewerFlags
import eu.kanade.domain.entries.anime.interactor.UpdateAnime
import eu.kanade.domain.entries.anime.model.downloadedFilter
-import eu.kanade.domain.entries.anime.model.isLocal
import eu.kanade.domain.entries.anime.model.toSAnime
import eu.kanade.domain.items.episode.interactor.SetSeenStatus
import eu.kanade.domain.items.episode.interactor.SyncEpisodesWithSource
@@ -75,6 +74,7 @@ import tachiyomi.domain.items.episode.service.getEpisodeSort
import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.source.anime.service.AnimeSourceManager
import tachiyomi.domain.track.anime.interactor.GetAnimeTracks
+import tachiyomi.source.local.entries.anime.isLocal
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.text.DecimalFormat
@@ -982,6 +982,14 @@ class AnimeInfoScreenModel(
}
}
+ private val Throwable.snackbarMessage: String
+ get() = when (val className = this::class.simpleName) {
+ null -> message ?: ""
+ "SourceNotInstalledException" -> context.getString(R.string.loader_not_implemented_error)
+ "Exception", "HttpException", "IOException" -> message ?: className
+ else -> "$className: $message"
+ }
+
fun showAnimeSkipIntroDialog() {
mutableState.update { state ->
when (state) {
@@ -1066,10 +1074,3 @@ val episodeDecimalFormat = DecimalFormat(
DecimalFormatSymbols()
.apply { decimalSeparator = '.' },
)
-
-private val Throwable.snackbarMessage: String
- get() = when (val className = this::class.simpleName) {
- null -> message ?: ""
- "Exception", "HttpException", "IOException", "SourceNotInstalledException" -> message ?: className
- else -> "$className: $message"
- }
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/entries/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/entries/manga/MangaScreenModel.kt
index 2ba4d6bd3f..71ba06edb8 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/entries/manga/MangaScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/entries/manga/MangaScreenModel.kt
@@ -12,7 +12,6 @@ import eu.kanade.core.preference.asState
import eu.kanade.core.util.addOrRemove
import eu.kanade.domain.entries.manga.interactor.UpdateManga
import eu.kanade.domain.entries.manga.model.downloadedFilter
-import eu.kanade.domain.entries.manga.model.isLocal
import eu.kanade.domain.entries.manga.model.toSManga
import eu.kanade.domain.items.chapter.interactor.SetReadStatus
import eu.kanade.domain.items.chapter.interactor.SyncChaptersWithSource
@@ -72,6 +71,7 @@ import tachiyomi.domain.items.chapter.service.getChapterSort
import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.source.manga.service.MangaSourceManager
import tachiyomi.domain.track.manga.interactor.GetMangaTracks
+import tachiyomi.source.local.entries.manga.isLocal
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.text.DecimalFormat
@@ -972,6 +972,14 @@ class MangaInfoScreenModel(
}
}
}
+
+ private val Throwable.snackbarMessage: String
+ get() = when (val className = this::class.simpleName) {
+ null -> message ?: ""
+ "SourceNotInstalledException" -> context.getString(R.string.loader_not_implemented_error)
+ "Exception", "HttpException", "IOException" -> message ?: className
+ else -> "$className: $message"
+ }
}
sealed class MangaScreenState {
@@ -1032,10 +1040,3 @@ val chapterDecimalFormat = DecimalFormat(
DecimalFormatSymbols()
.apply { decimalSeparator = '.' },
)
-
-private val Throwable.snackbarMessage: String
- get() = when (val className = this::class.simpleName) {
- null -> message ?: ""
- "Exception", "HttpException", "IOException", "SourceNotInstalledException" -> message ?: className
- else -> "$className: $message"
- }
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryScreenModel.kt
index 92f69849b2..c71bc725d8 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryScreenModel.kt
@@ -16,7 +16,6 @@ import eu.kanade.core.util.fastMapNotNull
import eu.kanade.core.util.fastPartition
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.entries.anime.interactor.UpdateAnime
-import eu.kanade.domain.entries.anime.model.isLocal
import eu.kanade.domain.items.episode.interactor.SetSeenStatus
import eu.kanade.presentation.components.SEARCH_DEBOUNCE_MILLIS
import eu.kanade.presentation.entries.DownloadAction
@@ -62,6 +61,7 @@ import tachiyomi.domain.library.anime.model.sort
import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.source.anime.service.AnimeSourceManager
import tachiyomi.domain.track.anime.interactor.GetTracksPerAnime
+import tachiyomi.source.local.entries.anime.isLocal
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.text.Collator
@@ -424,7 +424,6 @@ class AnimeLibraryScreenModel(
DownloadAction.NEXT_10_ITEMS -> downloadUnseenEpisodes(animes, 10)
DownloadAction.NEXT_25_ITEMS -> downloadUnseenEpisodes(animes, 25)
DownloadAction.UNVIEWED_ITEMS -> downloadUnseenEpisodes(animes, null)
- else -> {}
}
clearSelection()
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryTab.kt
index fe1dc0194e..d3ab376560 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryTab.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/anime/AnimeLibraryTab.kt
@@ -31,7 +31,6 @@ import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.currentOrThrow
import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
import cafe.adriel.voyager.navigator.tab.TabOptions
-import eu.kanade.domain.entries.anime.model.isLocal
import eu.kanade.presentation.category.ChangeCategoryDialog
import eu.kanade.presentation.entries.LibraryBottomActionMenu
import eu.kanade.presentation.library.DeleteLibraryEntryDialog
@@ -64,6 +63,7 @@ import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.EmptyScreenAction
import tachiyomi.presentation.core.screens.LoadingScreen
+import tachiyomi.source.local.entries.anime.isLocal
import uy.kohesive.injekt.injectLazy
object AnimeLibraryTab : Tab {
@@ -150,7 +150,7 @@ object AnimeLibraryTab : Tab {
onClickUnselectAll = screenModel::clearSelection,
onClickSelectAll = { screenModel.selectAll(screenModel.activeCategoryIndex) },
onClickInvertSelection = { screenModel.invertSelection(screenModel.activeCategoryIndex) },
- onClickFilter = { screenModel.showSettingsDialog() },
+ onClickFilter = screenModel::showSettingsDialog,
onClickRefresh = { onClickRefresh(state.categories[screenModel.activeCategoryIndex]) },
onClickGlobalUpdate = { onClickRefresh(null) },
onClickOpenRandomEntry = {
@@ -216,7 +216,7 @@ object AnimeLibraryTab : Tab {
}
Unit
}.takeIf { state.showAnimeContinueButton },
- onToggleSelection = { screenModel.toggleSelection(it) },
+ onToggleSelection = screenModel::toggleSelection,
onToggleRangeSelection = {
screenModel.toggleRangeSelection(it)
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
@@ -276,7 +276,7 @@ object AnimeLibraryTab : Tab {
}
LaunchedEffect(state.selectionMode, state.dialog) {
- HomeScreen.showBottomNav(!state.selectionMode && state.dialog !is AnimeLibraryScreenModel.Dialog.SettingsSheet)
+ HomeScreen.showBottomNav(!state.selectionMode)
}
LaunchedEffect(state.isLoading) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryScreenModel.kt
index 8e1d99c75b..19483eabd8 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryScreenModel.kt
@@ -16,7 +16,6 @@ import eu.kanade.core.util.fastMapNotNull
import eu.kanade.core.util.fastPartition
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.entries.manga.interactor.UpdateManga
-import eu.kanade.domain.entries.manga.model.isLocal
import eu.kanade.domain.items.chapter.interactor.SetReadStatus
import eu.kanade.presentation.components.SEARCH_DEBOUNCE_MILLIS
import eu.kanade.presentation.entries.DownloadAction
@@ -62,6 +61,7 @@ import tachiyomi.domain.library.manga.model.sort
import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.source.manga.service.MangaSourceManager
import tachiyomi.domain.track.manga.interactor.GetTracksPerManga
+import tachiyomi.source.local.entries.manga.isLocal
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.text.Collator
@@ -418,7 +418,6 @@ class MangaLibraryScreenModel(
DownloadAction.NEXT_10_ITEMS -> downloadUnreadChapters(mangas, 10)
DownloadAction.NEXT_25_ITEMS -> downloadUnreadChapters(mangas, 25)
DownloadAction.UNVIEWED_ITEMS -> downloadUnreadChapters(mangas, null)
- else -> {}
}
clearSelection()
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryTab.kt
index e2f95759a4..6d7bda0f26 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryTab.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/manga/MangaLibraryTab.kt
@@ -30,7 +30,6 @@ import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.navigator.currentOrThrow
import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
import cafe.adriel.voyager.navigator.tab.TabOptions
-import eu.kanade.domain.entries.manga.model.isLocal
import eu.kanade.presentation.category.ChangeCategoryDialog
import eu.kanade.presentation.entries.LibraryBottomActionMenu
import eu.kanade.presentation.library.DeleteLibraryEntryDialog
@@ -60,6 +59,7 @@ import tachiyomi.presentation.core.components.material.Scaffold
import tachiyomi.presentation.core.screens.EmptyScreen
import tachiyomi.presentation.core.screens.EmptyScreenAction
import tachiyomi.presentation.core.screens.LoadingScreen
+import tachiyomi.source.local.entries.manga.isLocal
import uy.kohesive.injekt.injectLazy
object MangaLibraryTab : Tab {
@@ -132,7 +132,7 @@ object MangaLibraryTab : Tab {
onClickUnselectAll = screenModel::clearSelection,
onClickSelectAll = { screenModel.selectAll(screenModel.activeCategoryIndex) },
onClickInvertSelection = { screenModel.invertSelection(screenModel.activeCategoryIndex) },
- onClickFilter = { screenModel.showSettingsDialog() },
+ onClickFilter = screenModel::showSettingsDialog,
onClickRefresh = { onClickRefresh(state.categories[screenModel.activeCategoryIndex]) },
onClickGlobalUpdate = { onClickRefresh(null) },
onClickOpenRandomEntry = {
@@ -203,7 +203,7 @@ object MangaLibraryTab : Tab {
}
Unit
}.takeIf { state.showMangaContinueButton },
- onToggleSelection = { screenModel.toggleSelection(it) },
+ onToggleSelection = screenModel::toggleSelection,
onToggleRangeSelection = {
screenModel.toggleRangeSelection(it)
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
@@ -263,7 +263,7 @@ object MangaLibraryTab : Tab {
}
LaunchedEffect(state.selectionMode, state.dialog) {
- HomeScreen.showBottomNav(!state.selectionMode && state.dialog !is MangaLibraryScreenModel.Dialog.SettingsSheet)
+ HomeScreen.showBottomNav(!state.selectionMode)
}
LaunchedEffect(state.isLoading) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/player/PlayerViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/player/PlayerViewModel.kt
index 5677e03936..5568402370 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/player/PlayerViewModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/player/PlayerViewModel.kt
@@ -8,7 +8,6 @@ import androidx.lifecycle.viewModelScope
import eu.kanade.core.util.asFlow
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.entries.anime.interactor.SetAnimeViewerFlags
-import eu.kanade.domain.entries.anime.model.isLocal
import eu.kanade.domain.items.episode.model.toDbEpisode
import eu.kanade.domain.track.anime.model.toDbTrack
import eu.kanade.domain.track.anime.service.DelayedAnimeTrackingUpdateJob
@@ -74,6 +73,7 @@ import tachiyomi.domain.items.episode.service.getEpisodeSort
import tachiyomi.domain.source.anime.service.AnimeSourceManager
import tachiyomi.domain.track.anime.interactor.GetAnimeTracks
import tachiyomi.domain.track.anime.interactor.InsertAnimeTrack
+import tachiyomi.source.local.entries.anime.isLocal
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.io.InputStream
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/player/settings/dialogs/SkipIntroLengthDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/player/settings/dialogs/SkipIntroLengthDialog.kt
index 7a1036d1c5..f9e9798865 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/player/settings/dialogs/SkipIntroLengthDialog.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/player/settings/dialogs/SkipIntroLengthDialog.kt
@@ -47,7 +47,7 @@ fun SkipIntroLengthDialog(
content = {
WheelTextPicker(
modifier = Modifier.align(Alignment.Center),
- texts = remember { 1..255 }.map { stringResource(R.string.seconds_short, it) },
+ items = remember { 1..255 }.map { stringResource(R.string.seconds_short, it) },
onSelectionChanged = { newLength = it + 1 },
startIndex = if (currentSkipIntroLength > 0) {
currentSkipIntroLength - 1
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ChapterTransition.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ChapterTransition.kt
new file mode 100644
index 0000000000..8a2f23cb91
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ChapterTransition.kt
@@ -0,0 +1,170 @@
+package eu.kanade.tachiyomi.ui.reader
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.FlowRow
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.OfflinePin
+import androidx.compose.material.icons.outlined.Warning
+import androidx.compose.material3.Icon
+import androidx.compose.material3.LocalContentColor
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.ProvideTextStyle
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.pluralStringResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.data.database.models.manga.Chapter
+import eu.kanade.tachiyomi.data.database.models.manga.toDomainChapter
+import eu.kanade.tachiyomi.data.download.manga.MangaDownloadManager
+import eu.kanade.tachiyomi.ui.reader.loader.DownloadPageLoader
+import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition
+import tachiyomi.domain.entries.manga.model.Manga
+import tachiyomi.domain.items.service.calculateChapterGap
+import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
+
+@Composable
+fun ChapterTransition(
+ transition: ChapterTransition,
+ downloadManager: MangaDownloadManager,
+ manga: Manga?,
+) {
+ manga ?: return
+
+ val currChapter = transition.from.chapter
+ val currChapterDownloaded = transition.from.pageLoader is DownloadPageLoader
+
+ val goingToChapter = transition.to?.chapter
+ val goingToChapterDownloaded = if (goingToChapter != null) {
+ downloadManager.isChapterDownloaded(
+ goingToChapter.name,
+ goingToChapter.scanlator,
+ manga.title,
+ manga.source,
+ skipCache = true,
+ )
+ } else {
+ false
+ }
+
+ ProvideTextStyle(MaterialTheme.typography.bodyMedium) {
+ when (transition) {
+ is ChapterTransition.Prev -> {
+ TransitionText(
+ topLabel = stringResource(R.string.transition_previous),
+ topChapter = goingToChapter,
+ topChapterDownloaded = goingToChapterDownloaded,
+ bottomLabel = stringResource(R.string.transition_current),
+ bottomChapter = currChapter,
+ bottomChapterDownloaded = currChapterDownloaded,
+ fallbackLabel = stringResource(R.string.transition_no_previous),
+ chapterGap = calculateChapterGap(currChapter.toDomainChapter(), goingToChapter?.toDomainChapter()),
+ )
+ }
+ is ChapterTransition.Next -> {
+ TransitionText(
+ topLabel = stringResource(R.string.transition_finished),
+ topChapter = currChapter,
+ topChapterDownloaded = currChapterDownloaded,
+ bottomLabel = stringResource(R.string.transition_next),
+ bottomChapter = goingToChapter,
+ bottomChapterDownloaded = goingToChapterDownloaded,
+ fallbackLabel = stringResource(R.string.transition_no_next),
+ chapterGap = calculateChapterGap(goingToChapter?.toDomainChapter(), currChapter.toDomainChapter()),
+ )
+ }
+ }
+ }
+}
+
+@Composable
+private fun TransitionText(
+ topLabel: String,
+ topChapter: Chapter? = null,
+ topChapterDownloaded: Boolean,
+ bottomLabel: String,
+ bottomChapter: Chapter? = null,
+ bottomChapterDownloaded: Boolean,
+ fallbackLabel: String,
+ chapterGap: Int,
+) {
+ val hasTopChapter = topChapter != null
+ val hasBottomChapter = bottomChapter != null
+
+ Column {
+ Text(
+ text = if (hasTopChapter) topLabel else fallbackLabel,
+ fontWeight = FontWeight.Bold,
+ textAlign = if (hasTopChapter) TextAlign.Start else TextAlign.Center,
+ )
+ topChapter?.let { ChapterText(chapter = it, downloaded = topChapterDownloaded) }
+
+ Spacer(Modifier.height(16.dp))
+
+ if (chapterGap > 0) {
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Icon(
+ imageVector = Icons.Outlined.Warning,
+ tint = MaterialTheme.colorScheme.error,
+ contentDescription = null,
+ )
+
+ Text(text = pluralStringResource(R.plurals.missing_chapters_warning, count = chapterGap, chapterGap))
+ }
+
+ Spacer(Modifier.height(16.dp))
+ }
+
+ Text(
+ text = if (hasBottomChapter) bottomLabel else fallbackLabel,
+ fontWeight = FontWeight.Bold,
+ textAlign = if (hasBottomChapter) TextAlign.Start else TextAlign.Center,
+ )
+ bottomChapter?.let { ChapterText(chapter = it, downloaded = bottomChapterDownloaded) }
+ }
+}
+
+@Composable
+private fun ColumnScope.ChapterText(
+ chapter: Chapter,
+ downloaded: Boolean,
+) {
+ FlowRow(
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ if (downloaded) {
+ Icon(
+ imageVector = Icons.Outlined.OfflinePin,
+ contentDescription = stringResource(R.string.label_downloaded),
+ )
+
+ Spacer(Modifier.width(8.dp))
+ }
+
+ Text(chapter.name)
+ }
+
+ chapter.scanlator?.let {
+ ProvideTextStyle(
+ MaterialTheme.typography.bodyMedium.copy(
+ color = LocalContentColor.current.copy(alpha = SecondaryItemAlpha),
+ ),
+ ) {
+ Text(it)
+ }
+ }
+}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt
index a21fd4f097..244e2819f0 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt
@@ -99,10 +99,6 @@ import uy.kohesive.injekt.injectLazy
import kotlin.math.abs
import kotlin.math.max
-/**
- * Activity containing the reader of Tachiyomi. This activity is mostly a container of the
- * viewers, to which calls from the presenter or UI events are delegated.
- */
class ReaderActivity : BaseActivity() {
companion object {
@@ -662,7 +658,7 @@ class ReaderActivity : BaseActivity() {
* Called from the presenter when a manga is ready. Used to instantiate the appropriate viewer
* and the toolbar title.
*/
- fun setManga(manga: Manga) {
+ private fun setManga(manga: Manga) {
val prevViewer = viewer
val viewerMode = ReadingModeType.fromPreference(viewModel.getMangaReadingMode(resolveDefault = false))
@@ -777,7 +773,7 @@ class ReaderActivity : BaseActivity() {
* Called from the presenter if the initial load couldn't load the pages of the chapter. In
* this case the activity is closed and a toast is shown to the user.
*/
- fun setInitialChapterError(error: Throwable) {
+ private fun setInitialChapterError(error: Throwable) {
logcat(LogPriority.ERROR, error)
finish()
toast(error.message)
@@ -952,7 +948,7 @@ class ReaderActivity : BaseActivity() {
* cover to the presenter.
*/
fun setAsCover(page: ReaderPage) {
- viewModel.setAsCover(this, page)
+ viewModel.setAsCover(page)
}
/**
@@ -1040,21 +1036,21 @@ class ReaderActivity : BaseActivity() {
.launchIn(lifecycleScope)
readerPreferences.showPageNumber().changes()
- .onEach { setPageNumberVisibility(it) }
+ .onEach(::setPageNumberVisibility)
.launchIn(lifecycleScope)
readerPreferences.trueColor().changes()
- .onEach { setTrueColor(it) }
+ .onEach(::setTrueColor)
.launchIn(lifecycleScope)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
readerPreferences.cutoutShort().changes()
- .onEach { setCutoutShort(it) }
+ .onEach(::setCutoutShort)
.launchIn(lifecycleScope)
}
readerPreferences.keepScreenOn().changes()
- .onEach { setKeepScreenOn(it) }
+ .onEach(::setKeepScreenOn)
.launchIn(lifecycleScope)
readerPreferences.customBrightness().changes()
@@ -1062,7 +1058,7 @@ class ReaderActivity : BaseActivity() {
.launchIn(lifecycleScope)
readerPreferences.colorFilter().changes()
- .onEach { setColorFilter(it) }
+ .onEach(::setCustomBrightness)
.launchIn(lifecycleScope)
readerPreferences.colorFilterMode().changes()
@@ -1139,7 +1135,7 @@ class ReaderActivity : BaseActivity() {
if (enabled) {
readerPreferences.customBrightnessValue().changes()
.sample(100)
- .onEach { setCustomBrightnessValue(it) }
+ .onEach(::setCustomBrightnessValue)
.launchIn(lifecycleScope)
} else {
setCustomBrightnessValue(0)
@@ -1153,7 +1149,7 @@ class ReaderActivity : BaseActivity() {
if (enabled) {
readerPreferences.colorFilterValue().changes()
.sample(100)
- .onEach { setColorFilterValue(it) }
+ .onEach(::setColorFilterValue)
.launchIn(lifecycleScope)
} else {
binding.colorOverlay.isVisible = false
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
index 2d851261f9..cd156c36db 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
@@ -1,14 +1,12 @@
package eu.kanade.tachiyomi.ui.reader
import android.app.Application
-import android.content.Context
import android.net.Uri
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import eu.kanade.domain.base.BasePreferences
import eu.kanade.domain.entries.manga.interactor.SetMangaViewerFlags
-import eu.kanade.domain.entries.manga.model.isLocal
import eu.kanade.domain.entries.manga.model.orientationType
import eu.kanade.domain.entries.manga.model.readingModeType
import eu.kanade.domain.items.chapter.model.toDbChapter
@@ -37,6 +35,7 @@ import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
+import eu.kanade.tachiyomi.util.chapter.removeDuplicates
import eu.kanade.tachiyomi.util.editCover
import eu.kanade.tachiyomi.util.lang.byteSize
import eu.kanade.tachiyomi.util.lang.takeBytes
@@ -78,6 +77,7 @@ import tachiyomi.domain.items.chapter.service.getChapterSort
import tachiyomi.domain.source.manga.service.MangaSourceManager
import tachiyomi.domain.track.manga.interactor.GetMangaTracks
import tachiyomi.domain.track.manga.interactor.InsertMangaTrack
+import tachiyomi.source.local.entries.manga.isLocal
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.Date
@@ -176,12 +176,7 @@ class ReaderViewModel(
else -> chapters
}.run {
if (readerPreferences.skipDupe().get()) {
- groupBy { it.chapterNumber }
- .map { (_, chapters) ->
- chapters.find { it.id == selectedChapter.id }
- ?: chapters.find { it.scanlator == selectedChapter.scanlator }
- ?: chapters.first()
- }
+ removeDuplicates(selectedChapter)
} else {
this
}
@@ -201,17 +196,6 @@ class ReaderViewModel(
private val incognitoMode = preferences.incognitoMode().get()
- override fun onCleared() {
- val currentChapters = state.value.viewerChapters
- if (currentChapters != null) {
- currentChapters.unref()
- saveReadingProgress(currentChapters.currChapter)
- chapterToDownload?.let {
- downloadManager.addDownloadsToStartOfQueue(listOf(it))
- }
- }
- }
-
init {
// To save state
state.map { it.viewerChapters?.currChapter }
@@ -226,6 +210,17 @@ class ReaderViewModel(
.launchIn(viewModelScope)
}
+ override fun onCleared() {
+ val currentChapters = state.value.viewerChapters
+ if (currentChapters != null) {
+ currentChapters.unref()
+ saveReadingProgress(currentChapters.currChapter)
+ chapterToDownload?.let {
+ downloadManager.addDownloadsToStartOfQueue(listOf(it))
+ }
+ }
+ }
+
/**
* Called when the user pressed the back button and is going to leave the reader. Used to
* trigger deletion of the downloaded chapters.
@@ -338,10 +333,11 @@ class ReaderViewModel(
}
/**
- * Called when the user is going to load the prev/next chapter through the menu button.
+ * Called when the user is going to load the prev/next chapter through the toolbar buttons.
*/
private suspend fun loadAdjacent(chapter: ReaderChapter) {
val loader = loader ?: return
+ saveCurrentChapterReadingProgress()
logcat { "Loading adjacent ${chapter.chapter.url}" }
@@ -456,8 +452,13 @@ class ReaderViewModel(
)
if (!isNextChapterDownloaded) return@launchIO
- val chaptersToDownload = getNextChapters.await(manga.id, nextChapter.id!!)
- .take(amount)
+ val chaptersToDownload = getNextChapters.await(manga.id, nextChapter.id!!).run {
+ if (readerPreferences.skipDupe().get()) {
+ removeDuplicates(nextChapter.toDomainChapter()!!)
+ } else {
+ this
+ }
+ }.take(amount)
downloadManager.downloadChapters(
manga,
chaptersToDownload,
@@ -767,7 +768,7 @@ class ReaderViewModel(
/**
* Sets the image of this [page] as cover and notifies the UI of the result.
*/
- fun setAsCover(context: Context, page: ReaderPage) {
+ fun setAsCover(page: ReaderPage) {
if (page.status != Page.State.READY) return
val manga = manga ?: return
val stream = page.stream ?: return
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 46d6a63a60..24a6441a1f 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
@@ -12,8 +12,9 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.NotificationHandler
import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.data.notification.Notifications
+import eu.kanade.tachiyomi.util.system.cancelNotification
import eu.kanade.tachiyomi.util.system.notificationBuilder
-import eu.kanade.tachiyomi.util.system.notificationManager
+import eu.kanade.tachiyomi.util.system.notify
/**
* Class used to show BigPictureStyle notifications
@@ -49,7 +50,7 @@ class SaveImageNotifier(private val context: Context) {
* Clears the notification message.
*/
fun onClear() {
- context.notificationManager.cancel(notificationId)
+ context.cancelNotification(notificationId)
}
/**
@@ -97,6 +98,6 @@ class SaveImageNotifier(private val context: Context) {
private fun updateNotification() {
// Displays the progress bar on notification
- context.notificationManager.notify(notificationId, notificationBuilder.build())
+ context.notify(notificationId, notificationBuilder.build())
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderColorFilterSettings.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderColorFilterSettings.kt
index 2695ce198b..7f76dbb764 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderColorFilterSettings.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderColorFilterSettings.kt
@@ -33,7 +33,7 @@ class ReaderColorFilterSettings @JvmOverloads constructor(context: Context, attr
addView(binding.root)
readerPreferences.colorFilter().changes()
- .onEach { setColorFilter(it) }
+ .onEach(::setColorFilter)
.launchIn((context as ReaderActivity).lifecycleScope)
readerPreferences.colorFilterMode().changes()
@@ -41,7 +41,7 @@ class ReaderColorFilterSettings @JvmOverloads constructor(context: Context, attr
.launchIn(context.lifecycleScope)
readerPreferences.customBrightness().changes()
- .onEach { setCustomBrightness(it) }
+ .onEach(::setCustomBrightness)
.launchIn(context.lifecycleScope)
// Get color and update values
@@ -141,7 +141,7 @@ class ReaderColorFilterSettings @JvmOverloads constructor(context: Context, attr
if (enabled) {
readerPreferences.customBrightnessValue().changes()
.sample(100)
- .onEach { setCustomBrightnessValue(it) }
+ .onEach(::setCustomBrightnessValue)
.launchIn((context as ReaderActivity).lifecycleScope)
} else {
setCustomBrightnessValue(0, true)
@@ -169,7 +169,7 @@ class ReaderColorFilterSettings @JvmOverloads constructor(context: Context, attr
if (enabled) {
readerPreferences.colorFilterValue().changes()
.sample(100)
- .onEach { setColorFilterValue(it) }
+ .onEach(::setColorFilterValue)
.launchIn((context as ReaderActivity).lifecycleScope)
}
setColorFilterSeekBar(enabled)
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderGeneralSettings.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderGeneralSettings.kt
index 742c1b8f21..50beac220c 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderGeneralSettings.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderGeneralSettings.kt
@@ -9,9 +9,9 @@ import androidx.lifecycle.lifecycleScope
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.ReaderGeneralSettingsBinding
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
-import eu.kanade.tachiyomi.util.preference.asHotFlow
import eu.kanade.tachiyomi.util.preference.bindToPreference
import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
import uy.kohesive.injekt.injectLazy
/**
@@ -37,8 +37,8 @@ class ReaderGeneralSettings @JvmOverloads constructor(context: Context, attrs: A
binding.backgroundColor.bindToIntPreference(readerPreferences.readerTheme(), R.array.reader_themes_values)
binding.showPageNumber.bindToPreference(readerPreferences.showPageNumber())
binding.fullscreen.bindToPreference(readerPreferences.fullscreen())
- readerPreferences.fullscreen()
- .asHotFlow {
+ readerPreferences.fullscreen().changes()
+ .onEach {
// If the preference is explicitly disabled, that means the setting was configured since there is a cutout
binding.cutoutShort.isVisible = it && ((context as ReaderActivity).hasCutout || !readerPreferences.cutoutShort().get())
binding.cutoutShort.bindToPreference(readerPreferences.cutoutShort())
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 855327c043..7c8ad1f729 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
@@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.ui.reader.setting
-import eu.kanade.tachiyomi.data.preference.PreferenceValues
import eu.kanade.tachiyomi.util.system.isReleaseBuildType
import tachiyomi.core.preference.PreferenceStore
import tachiyomi.core.preference.getEnum
@@ -19,6 +18,7 @@ class ReaderPreferences(
fun showReadingMode() = preferenceStore.getBoolean("pref_show_reading_mode", true)
+ // TODO: default this to true if reader long strip ever goes stable
fun trueColor() = preferenceStore.getBoolean("pref_true_color_key", false)
fun fullscreen() = preferenceStore.getBoolean("fullscreen", true)
@@ -54,7 +54,7 @@ class ReaderPreferences(
fun webtoonSidePadding() = preferenceStore.getInt("webtoon_side_padding", 0)
- fun readerHideThreshold() = preferenceStore.getEnum("reader_hide_threshold", PreferenceValues.ReaderHideThreshold.LOW)
+ fun readerHideThreshold() = preferenceStore.getEnum("reader_hide_threshold", ReaderHideThreshold.LOW)
fun folderPerManga() = preferenceStore.getBoolean("create_folder_per_manga", false)
@@ -76,6 +76,10 @@ class ReaderPreferences(
fun dualPageInvertWebtoon() = preferenceStore.getBoolean("pref_dual_page_invert_webtoon", false)
+ fun dualPageRotateToFit() = preferenceStore.getBoolean("pref_dual_page_rotate", false)
+
+ fun dualPageRotateToFitInvert() = preferenceStore.getBoolean("pref_dual_page_rotate_invert", false)
+
// endregion
// region Color filter
@@ -108,13 +112,27 @@ class ReaderPreferences(
fun navigationModeWebtoon() = preferenceStore.getInt("reader_navigation_mode_webtoon", 0)
- fun pagerNavInverted() = preferenceStore.getEnum("reader_tapping_inverted", PreferenceValues.TappingInvertMode.NONE)
+ fun pagerNavInverted() = preferenceStore.getEnum("reader_tapping_inverted", TappingInvertMode.NONE)
- fun webtoonNavInverted() = preferenceStore.getEnum("reader_tapping_inverted_webtoon", PreferenceValues.TappingInvertMode.NONE)
+ fun webtoonNavInverted() = preferenceStore.getEnum("reader_tapping_inverted_webtoon", TappingInvertMode.NONE)
fun showNavigationOverlayNewUser() = preferenceStore.getBoolean("reader_navigation_overlay_new_user", true)
fun showNavigationOverlayOnStart() = preferenceStore.getBoolean("reader_navigation_overlay_on_start", false)
// endregion
+
+ enum class TappingInvertMode(val shouldInvertHorizontal: Boolean = false, val shouldInvertVertical: Boolean = false) {
+ NONE,
+ HORIZONTAL(shouldInvertHorizontal = true),
+ VERTICAL(shouldInvertVertical = true),
+ BOTH(shouldInvertHorizontal = true, shouldInvertVertical = true),
+ }
+
+ enum class ReaderHideThreshold(val threshold: Int) {
+ HIGHEST(5),
+ HIGH(13),
+ LOW(31),
+ LOWEST(47),
+ }
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt
index 24dd81f9ab..0b0859ea08 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderReadingModeSettings.kt
@@ -9,14 +9,14 @@ import androidx.lifecycle.lifecycleScope
import eu.kanade.domain.entries.manga.model.orientationType
import eu.kanade.domain.entries.manga.model.readingModeType
import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.data.preference.PreferenceValues
import eu.kanade.tachiyomi.databinding.ReaderReadingModeSettingsBinding
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerViewer
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
-import eu.kanade.tachiyomi.util.preference.asHotFlow
import eu.kanade.tachiyomi.util.preference.bindToPreference
+import eu.kanade.tachiyomi.util.system.isReleaseBuildType
import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
import uy.kohesive.injekt.injectLazy
/**
@@ -71,12 +71,12 @@ class ReaderReadingModeSettings @JvmOverloads constructor(context: Context, attr
binding.webtoonPrefsGroup.root.isVisible = false
binding.pagerPrefsGroup.root.isVisible = true
- binding.pagerPrefsGroup.tappingInverted.bindToPreference(readerPreferences.pagerNavInverted(), PreferenceValues.TappingInvertMode::class.java)
+ binding.pagerPrefsGroup.tappingInverted.bindToPreference(readerPreferences.pagerNavInverted(), ReaderPreferences.TappingInvertMode::class.java)
binding.pagerPrefsGroup.navigatePan.bindToPreference(readerPreferences.navigateToPan())
binding.pagerPrefsGroup.pagerNav.bindToPreference(readerPreferences.navigationModePager())
- readerPreferences.navigationModePager()
- .asHotFlow {
+ readerPreferences.navigationModePager().changes()
+ .onEach {
val isTappingEnabled = it != 5
binding.pagerPrefsGroup.tappingInverted.isVisible = isTappingEnabled
binding.pagerPrefsGroup.navigatePan.isVisible = isTappingEnabled
@@ -84,8 +84,8 @@ class ReaderReadingModeSettings @JvmOverloads constructor(context: Context, attr
.launchIn((context as ReaderActivity).lifecycleScope)
// Makes so that landscape zoom gets hidden away when image scale type is not fit screen
binding.pagerPrefsGroup.scaleType.bindToPreference(readerPreferences.imageScaleType(), 1)
- readerPreferences.imageScaleType()
- .asHotFlow { binding.pagerPrefsGroup.landscapeZoom.isVisible = it == 1 }
+ readerPreferences.imageScaleType().changes()
+ .onEach { binding.pagerPrefsGroup.landscapeZoom.isVisible = it == 1 }
.launchIn((context as ReaderActivity).lifecycleScope)
binding.pagerPrefsGroup.landscapeZoom.bindToPreference(readerPreferences.landscapeZoom())
@@ -93,11 +93,26 @@ class ReaderReadingModeSettings @JvmOverloads constructor(context: Context, attr
binding.pagerPrefsGroup.cropBorders.bindToPreference(readerPreferences.cropBorders())
binding.pagerPrefsGroup.dualPageSplit.bindToPreference(readerPreferences.dualPageSplitPaged())
- // Makes it so that dual page invert gets hidden away when dual page split is turned off
- readerPreferences.dualPageSplitPaged()
- .asHotFlow { binding.pagerPrefsGroup.dualPageInvert.isVisible = it }
+ readerPreferences.dualPageSplitPaged().changes()
+ .onEach {
+ binding.pagerPrefsGroup.dualPageInvert.isVisible = it
+ if (it) {
+ binding.pagerPrefsGroup.dualPageRotateToFit.isChecked = false
+ }
+ }
.launchIn((context as ReaderActivity).lifecycleScope)
binding.pagerPrefsGroup.dualPageInvert.bindToPreference(readerPreferences.dualPageInvertPaged())
+
+ binding.pagerPrefsGroup.dualPageRotateToFit.bindToPreference(readerPreferences.dualPageRotateToFit())
+ readerPreferences.dualPageRotateToFit().changes()
+ .onEach {
+ binding.pagerPrefsGroup.dualPageRotateToFitInvert.isVisible = it
+ if (it) {
+ binding.pagerPrefsGroup.dualPageSplit.isChecked = false
+ }
+ }
+ .launchIn((context as ReaderActivity).lifecycleScope)
+ binding.pagerPrefsGroup.dualPageRotateToFitInvert.bindToPreference(readerPreferences.dualPageRotateToFitInvert())
}
/**
@@ -107,21 +122,22 @@ class ReaderReadingModeSettings @JvmOverloads constructor(context: Context, attr
binding.pagerPrefsGroup.root.isVisible = false
binding.webtoonPrefsGroup.root.isVisible = true
- binding.webtoonPrefsGroup.tappingInverted.bindToPreference(readerPreferences.webtoonNavInverted(), PreferenceValues.TappingInvertMode::class.java)
+ binding.webtoonPrefsGroup.tappingInverted.bindToPreference(readerPreferences.webtoonNavInverted(), ReaderPreferences.TappingInvertMode::class.java)
binding.webtoonPrefsGroup.webtoonNav.bindToPreference(readerPreferences.navigationModeWebtoon())
- readerPreferences.navigationModeWebtoon()
- .asHotFlow { binding.webtoonPrefsGroup.tappingInverted.isVisible = it != 5 }
+ readerPreferences.navigationModeWebtoon().changes()
+ .onEach { binding.webtoonPrefsGroup.tappingInverted.isVisible = it != 5 }
.launchIn((context as ReaderActivity).lifecycleScope)
binding.webtoonPrefsGroup.cropBordersWebtoon.bindToPreference(readerPreferences.cropBordersWebtoon())
binding.webtoonPrefsGroup.webtoonSidePadding.bindToIntPreference(readerPreferences.webtoonSidePadding(), R.array.webtoon_side_padding_values)
binding.webtoonPrefsGroup.dualPageSplit.bindToPreference(readerPreferences.dualPageSplitWebtoon())
// Makes it so that dual page invert gets hidden away when dual page split is turned off
- readerPreferences.dualPageSplitWebtoon()
- .asHotFlow { binding.webtoonPrefsGroup.dualPageInvert.isVisible = it }
+ readerPreferences.dualPageSplitWebtoon().changes()
+ .onEach { binding.webtoonPrefsGroup.dualPageInvert.isVisible = it }
.launchIn((context as ReaderActivity).lifecycleScope)
binding.webtoonPrefsGroup.dualPageInvert.bindToPreference(readerPreferences.dualPageInvertWebtoon())
+ binding.webtoonPrefsGroup.longStripSplit.isVisible = !isReleaseBuildType
binding.webtoonPrefsGroup.longStripSplit.bindToPreference(readerPreferences.longStripSplitWebtoon())
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsSheet.kt
index 14386d3c55..0f684736a8 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsSheet.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsSheet.kt
@@ -2,20 +2,27 @@ package eu.kanade.tachiyomi.ui.reader.setting
import android.animation.ValueAnimator
import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
import com.google.android.material.tabs.TabLayout
import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.databinding.CommonTabbedSheetBinding
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
+import eu.kanade.tachiyomi.widget.ViewPagerAdapter
import eu.kanade.tachiyomi.widget.listener.SimpleTabSelectedListener
-import eu.kanade.tachiyomi.widget.sheet.TabbedBottomSheetDialog
+import eu.kanade.tachiyomi.widget.sheet.BaseBottomSheetDialog
class ReaderSettingsSheet(
private val activity: ReaderActivity,
private val showColorFilterSettings: Boolean = false,
-) : TabbedBottomSheetDialog(activity) {
+) : BaseBottomSheetDialog(activity) {
- private val readingModeSettings = ReaderReadingModeSettings(activity)
- private val generalSettings = ReaderGeneralSettings(activity)
- private val colorFilterSettings = ReaderColorFilterSettings(activity)
+ private val tabs = listOf(
+ ReaderReadingModeSettings(activity) to R.string.pref_category_reading_mode,
+ ReaderGeneralSettings(activity) to R.string.pref_category_general,
+ ReaderColorFilterSettings(activity) to R.string.custom_filter,
+ )
private val backgroundDimAnimator by lazy {
val sheetBackgroundDim = window?.attributes?.dimAmount ?: 0.25f
@@ -27,13 +34,26 @@ class ReaderSettingsSheet(
}
}
+ private lateinit var binding: CommonTabbedSheetBinding
+
+ override fun createView(inflater: LayoutInflater): View {
+ binding = CommonTabbedSheetBinding.inflate(activity.layoutInflater)
+
+ val adapter = Adapter()
+ binding.pager.offscreenPageLimit = 2
+ binding.pager.adapter = adapter
+ binding.tabs.setupWithViewPager(binding.pager)
+
+ return binding.root
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
behavior.isFitToContents = false
behavior.halfExpandedRatio = 0.25f
- val filterTabIndex = getTabViews().indexOf(colorFilterSettings)
+ val filterTabIndex = tabs.indexOfFirst { it.first is ReaderColorFilterSettings }
binding.tabs.addOnTabSelectedListener(
object : SimpleTabSelectedListener() {
override fun onTabSelected(tab: TabLayout.Tab?) {
@@ -63,15 +83,18 @@ class ReaderSettingsSheet(
}
}
- override fun getTabViews() = listOf(
- readingModeSettings,
- generalSettings,
- colorFilterSettings,
- )
+ private inner class Adapter : ViewPagerAdapter() {
- override fun getTabTitles() = listOf(
- R.string.pref_category_reading_mode,
- R.string.pref_category_general,
- R.string.custom_filter,
- )
+ override fun createView(container: ViewGroup, position: Int): View {
+ return tabs[position].first
+ }
+
+ override fun getCount(): Int {
+ return tabs.size
+ }
+
+ override fun getPageTitle(position: Int): CharSequence {
+ return activity.resources!!.getString(tabs[position].second)
+ }
+ }
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/MissingChapters.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/MissingChapters.kt
index cbea70e6fc..a2948127ec 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/MissingChapters.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/MissingChapters.kt
@@ -2,45 +2,11 @@ package eu.kanade.tachiyomi.ui.reader.viewer
import eu.kanade.tachiyomi.data.database.models.manga.toDomainChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
-import tachiyomi.domain.items.chapter.model.Chapter
-import kotlin.math.floor
+import tachiyomi.domain.items.service.calculateChapterGap as domainCalculateChapterGap
-private val pattern = Regex("""\d+""")
-
-fun hasMissingChapters(higherReaderChapter: ReaderChapter?, lowerReaderChapter: ReaderChapter?): Boolean {
- if (higherReaderChapter == null || lowerReaderChapter == null) return false
- return hasMissingChapters(higherReaderChapter.chapter.toDomainChapter(), lowerReaderChapter.chapter.toDomainChapter())
-}
-
-fun hasMissingChapters(higherChapter: Chapter?, lowerChapter: Chapter?): Boolean {
- if (higherChapter == null || lowerChapter == null) return false
- // Check if name contains a number that is potential chapter number
- if (!pattern.containsMatchIn(higherChapter.name) || !pattern.containsMatchIn(lowerChapter.name)) return false
- // Check if potential chapter number was recognized as chapter number
- if (!higherChapter.isRecognizedNumber || !lowerChapter.isRecognizedNumber) return false
- return hasMissingChapters(higherChapter.chapterNumber, lowerChapter.chapterNumber)
-}
-
-fun hasMissingChapters(higherChapterNumber: Float, lowerChapterNumber: Float): Boolean {
- if (higherChapterNumber < 0f || lowerChapterNumber < 0f) return false
- return calculateChapterDifference(higherChapterNumber, lowerChapterNumber) > 0f
-}
-
-fun calculateChapterDifference(higherReaderChapter: ReaderChapter?, lowerReaderChapter: ReaderChapter?): Float {
- if (higherReaderChapter == null || lowerReaderChapter == null) return 0f
- return calculateChapterDifference(higherReaderChapter.chapter.toDomainChapter(), lowerReaderChapter.chapter.toDomainChapter())
-}
-
-fun calculateChapterDifference(higherChapter: Chapter?, lowerChapter: Chapter?): Float {
- if (higherChapter == null || lowerChapter == null) return 0f
- // Check if name contains a number that is potential chapter number
- if (!pattern.containsMatchIn(higherChapter.name) || !pattern.containsMatchIn(lowerChapter.name)) return 0f
- // Check if potential chapter number was recognized as chapter number
- if (!higherChapter.isRecognizedNumber || !lowerChapter.isRecognizedNumber) return 0f
- return calculateChapterDifference(higherChapter.chapterNumber, lowerChapter.chapterNumber)
-}
-
-fun calculateChapterDifference(higherChapterNumber: Float, lowerChapterNumber: Float): Float {
- if (higherChapterNumber < 0f || lowerChapterNumber < 0f) return 0f
- return floor(higherChapterNumber) - floor(lowerChapterNumber) - 1f
+fun calculateChapterGap(higherReaderChapter: ReaderChapter?, lowerReaderChapter: ReaderChapter?): Int {
+ return domainCalculateChapterGap(
+ higherReaderChapter?.chapter?.toDomainChapter(),
+ lowerReaderChapter?.chapter?.toDomainChapter(),
+ )
}
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 49ad3fb1d1..f010e9c365 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
@@ -115,7 +115,7 @@ open class ReaderPageImageView @JvmOverloads constructor(
val point = when (config!!.zoomStartPosition) {
ZoomStartPosition.LEFT -> if (forward) PointF(0F, 0F) else PointF(sWidth.toFloat(), 0F)
ZoomStartPosition.RIGHT -> if (forward) PointF(sWidth.toFloat(), 0F) else PointF(0F, 0F)
- ZoomStartPosition.CENTER -> center.also { it?.y = 0F }
+ ZoomStartPosition.CENTER -> center
}
val targetScale = height.toFloat() / sHeight.toFloat()
@@ -249,7 +249,7 @@ open class ReaderPageImageView @JvmOverloads constructor(
when (config?.zoomStartPosition) {
ZoomStartPosition.LEFT -> setScaleAndCenter(scale, PointF(0F, 0F))
ZoomStartPosition.RIGHT -> setScaleAndCenter(scale, PointF(sWidth.toFloat(), 0F))
- ZoomStartPosition.CENTER -> setScaleAndCenter(scale, center.also { it?.y = 0F })
+ ZoomStartPosition.CENTER -> setScaleAndCenter(scale, center)
null -> {}
}
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderTransitionView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderTransitionView.kt
index a29dd16448..425937ef66 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderTransitionView.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderTransitionView.kt
@@ -1,30 +1,17 @@
package eu.kanade.tachiyomi.ui.reader.viewer
import android.content.Context
-import android.text.SpannableStringBuilder
-import android.text.style.ImageSpan
import android.util.AttributeSet
-import android.view.LayoutInflater
-import android.widget.LinearLayout
-import androidx.core.content.ContextCompat
-import androidx.core.text.bold
-import androidx.core.text.buildSpannedString
-import androidx.core.text.inSpans
-import androidx.core.view.isVisible
-import eu.kanade.tachiyomi.R
+import android.widget.FrameLayout
+import androidx.compose.ui.platform.ComposeView
import eu.kanade.tachiyomi.data.download.manga.MangaDownloadManager
-import eu.kanade.tachiyomi.databinding.ReaderTransitionViewBinding
-import eu.kanade.tachiyomi.ui.reader.loader.DownloadPageLoader
+import eu.kanade.tachiyomi.ui.reader.ChapterTransition
import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition
-import eu.kanade.tachiyomi.util.system.dpToPx
+import eu.kanade.tachiyomi.util.view.setComposeContent
import tachiyomi.domain.entries.manga.model.Manga
-import kotlin.math.roundToInt
class ReaderTransitionView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
- LinearLayout(context, attrs) {
-
- private val binding: ReaderTransitionViewBinding =
- ReaderTransitionViewBinding.inflate(LayoutInflater.from(context), this, true)
+ FrameLayout(context, attrs) {
init {
layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
@@ -32,138 +19,18 @@ class ReaderTransitionView @JvmOverloads constructor(context: Context, attrs: At
fun bind(transition: ChapterTransition, downloadManager: MangaDownloadManager, manga: Manga?) {
manga ?: return
- when (transition) {
- is ChapterTransition.Prev -> bindPrevChapterTransition(transition, downloadManager, manga)
- is ChapterTransition.Next -> bindNextChapterTransition(transition, downloadManager, manga)
- }
- missingChapterWarning(transition)
- }
-
- /**
- * Binds a previous chapter transition on this view and subscribes to the page load status.
- */
- private fun bindPrevChapterTransition(
- transition: ChapterTransition,
- downloadManager: MangaDownloadManager,
- manga: Manga,
- ) {
- val prevChapter = transition.to?.chapter
-
- binding.lowerText.isVisible = prevChapter != null
- if (prevChapter != null) {
- binding.upperText.textAlignment = TEXT_ALIGNMENT_TEXT_START
- val isPrevDownloaded = downloadManager.isChapterDownloaded(
- prevChapter.name,
- prevChapter.scanlator,
- manga.title,
- manga.source,
- skipCache = true,
- )
- val isCurrentDownloaded = transition.from.pageLoader is DownloadPageLoader
- binding.upperText.text = buildSpannedString {
- bold { append(context.getString(R.string.transition_previous)) }
- append("\n${prevChapter.name}")
- if (!prevChapter.scanlator.isNullOrBlank()) {
- append(DOT_SEPERATOR)
- append("${prevChapter.scanlator}")
- }
- if (isPrevDownloaded) addDLImageSpan()
- }
- binding.lowerText.text = buildSpannedString {
- bold { append(context.getString(R.string.transition_current)) }
- append("\n${transition.from.chapter.name}")
- if (!transition.from.chapter.scanlator.isNullOrBlank()) {
- append(DOT_SEPERATOR)
- append("${transition.from.chapter.scanlator}")
- }
- if (isCurrentDownloaded) addDLImageSpan()
- }
- } else {
- binding.upperText.textAlignment = TEXT_ALIGNMENT_CENTER
- binding.upperText.text = context.getString(R.string.transition_no_previous)
- }
- }
- /**
- * Binds a next chapter transition on this view and subscribes to the load status.
- */
- private fun bindNextChapterTransition(
- transition: ChapterTransition,
- downloadManager: MangaDownloadManager,
- manga: Manga,
- ) {
- val nextChapter = transition.to?.chapter
+ removeAllViews()
- binding.lowerText.isVisible = nextChapter != null
- if (nextChapter != null) {
- binding.upperText.textAlignment = TEXT_ALIGNMENT_TEXT_START
- val isCurrentDownloaded = transition.from.pageLoader is DownloadPageLoader
- val isNextDownloaded = downloadManager.isChapterDownloaded(
- nextChapter.name,
- nextChapter.scanlator,
- manga.title,
- manga.source,
- skipCache = true,
- )
- binding.upperText.text = buildSpannedString {
- bold { append(context.getString(R.string.transition_finished)) }
- append("\n${transition.from.chapter.name}")
- if (!transition.from.chapter.scanlator.isNullOrBlank()) {
- append(DOT_SEPERATOR)
- append("${transition.from.chapter.scanlator}")
- }
- if (isCurrentDownloaded) addDLImageSpan()
+ val transitionView = ComposeView(context).apply {
+ setComposeContent {
+ ChapterTransition(
+ transition = transition,
+ downloadManager = downloadManager,
+ manga = manga,
+ )
}
- binding.lowerText.text = buildSpannedString {
- bold { append(context.getString(R.string.transition_next)) }
- append("\n${nextChapter.name}")
- if (!nextChapter.scanlator.isNullOrBlank()) {
- append(DOT_SEPERATOR)
- append("${nextChapter.scanlator}")
- }
- if (isNextDownloaded) addDLImageSpan()
- }
- } else {
- binding.upperText.textAlignment = TEXT_ALIGNMENT_CENTER
- binding.upperText.text = context.getString(R.string.transition_no_next)
}
- }
-
- private fun SpannableStringBuilder.addDLImageSpan() {
- val icon = ContextCompat.getDrawable(context, R.drawable.ic_offline_pin_24dp)?.mutate()
- ?.apply {
- val size = binding.lowerText.textSize + 4.dpToPx
- setTint(binding.lowerText.currentTextColor)
- setBounds(0, 0, size.roundToInt(), size.roundToInt())
- } ?: return
- append(" ")
- inSpans(ImageSpan(icon)) { append("image") }
- }
-
- private fun missingChapterWarning(transition: ChapterTransition) {
- if (transition.to == null) {
- binding.warning.isVisible = false
- return
- }
-
- val hasMissingChapters = when (transition) {
- is ChapterTransition.Prev -> hasMissingChapters(transition.from, transition.to)
- is ChapterTransition.Next -> hasMissingChapters(transition.to, transition.from)
- }
-
- if (!hasMissingChapters) {
- binding.warning.isVisible = false
- return
- }
-
- val chapterDifference = when (transition) {
- is ChapterTransition.Prev -> calculateChapterDifference(transition.from, transition.to)
- is ChapterTransition.Next -> calculateChapterDifference(transition.to, transition.from)
- }
-
- binding.warningText.text = resources.getQuantityString(R.plurals.missing_chapters_warning, chapterDifference.toInt(), chapterDifference.toInt())
- binding.warning.isVisible = true
+ addView(transitionView)
}
}
-
-private const val DOT_SEPERATOR = " • "
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerConfig.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerConfig.kt
index 7c9279746a..44dd19db42 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerConfig.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerConfig.kt
@@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.ui.reader.viewer
-import eu.kanade.tachiyomi.data.preference.PreferenceValues.TappingInvertMode
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -17,7 +16,7 @@ abstract class ViewerConfig(readerPreferences: ReaderPreferences, private val sc
var navigationModeChangedListener: (() -> Unit)? = null
- var tappingInverted = TappingInvertMode.NONE
+ var tappingInverted = ReaderPreferences.TappingInvertMode.NONE
var longTapEnabled = true
var usePageTransitions = false
var doubleTapAnimDuration = 500
@@ -38,6 +37,12 @@ abstract class ViewerConfig(readerPreferences: ReaderPreferences, private val sc
var dualPageInvert = false
protected set
+ var dualPageRotateToFit = false
+ protected set
+
+ var dualPageRotateToFitInvert = false
+ protected set
+
abstract var navigator: ViewerNavigation
protected set
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerNavigation.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerNavigation.kt
index 224747466f..d4acd01b1b 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerNavigation.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerNavigation.kt
@@ -4,7 +4,7 @@ import android.graphics.PointF
import android.graphics.RectF
import androidx.annotation.StringRes
import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.data.preference.PreferenceValues
+import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.util.lang.invert
abstract class ViewerNavigation {
@@ -21,8 +21,8 @@ abstract class ViewerNavigation {
val rectF: RectF,
val type: NavigationRegion,
) {
- fun invert(invertMode: PreferenceValues.TappingInvertMode): Region {
- if (invertMode == PreferenceValues.TappingInvertMode.NONE) return this
+ fun invert(invertMode: ReaderPreferences.TappingInvertMode): Region {
+ if (invertMode == ReaderPreferences.TappingInvertMode.NONE) return this
return this.copy(
rectF = this.rectF.invert(invertMode),
)
@@ -33,7 +33,7 @@ abstract class ViewerNavigation {
abstract var regions: List
- var invertMode: PreferenceValues.TappingInvertMode = PreferenceValues.TappingInvertMode.NONE
+ var invertMode: ReaderPreferences.TappingInvertMode = ReaderPreferences.TappingInvertMode.NONE
fun getAction(pos: PointF): NavigationRegion {
val x = pos.x
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt
index c316274250..b319fd9287 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerConfig.kt
@@ -94,6 +94,18 @@ class PagerConfig(
readerPreferences.dualPageInvertPaged()
.register({ dualPageInvert = it }, { imagePropertyChangedListener?.invoke() })
+
+ readerPreferences.dualPageRotateToFit()
+ .register(
+ { dualPageRotateToFit = it },
+ { imagePropertyChangedListener?.invoke() },
+ )
+
+ readerPreferences.dualPageRotateToFitInvert()
+ .register(
+ { dualPageRotateToFitInvert = it },
+ { imagePropertyChangedListener?.invoke() },
+ )
}
private fun zoomTypeFromPreference(value: Int) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt
index 801e03c4bb..76a920c22e 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerPageHolder.kt
@@ -180,6 +180,10 @@ class PagerPageHolder(
}
private fun process(page: ReaderPage, imageStream: BufferedInputStream): InputStream {
+ if (viewer.config.dualPageRotateToFit) {
+ return rotateDualPage(imageStream)
+ }
+
if (!viewer.config.dualPageSplit) {
return imageStream
}
@@ -198,6 +202,16 @@ class PagerPageHolder(
return splitInHalf(imageStream)
}
+ private fun rotateDualPage(imageStream: BufferedInputStream): InputStream {
+ val isDoublePage = ImageUtil.isWideImage(imageStream)
+ return if (isDoublePage) {
+ val rotation = if (viewer.config.dualPageRotateToFitInvert) -90f else 90f
+ ImageUtil.rotateImage(imageStream, rotation)
+ } else {
+ imageStream
+ }
+ }
+
private fun splitInHalf(imageStream: InputStream): InputStream {
var side = when {
viewer is L2RPagerViewer && page is InsertPage -> ImageUtil.Side.RIGHT
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerTransitionHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerTransitionHolder.kt
index d11ef8e4d6..7ba46f5e00 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerTransitionHolder.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerTransitionHolder.kt
@@ -63,7 +63,7 @@ class PagerTransitionHolder(
transitionView.bind(transition, viewer.downloadManager, viewer.activity.viewModel.manga)
- transition.to?.let { observeStatus(it) }
+ transition.to?.let(::observeStatus)
}
/**
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt
index d9e44017e3..07b9a2671f 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt
@@ -74,9 +74,7 @@ abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer {
setChaptersInternal(viewerChapters)
awaitingIdleViewerChapters = null
if (viewerChapters.currChapter.pages?.size == 1) {
- adapter.nextTransition?.to?.let {
- activity.requestPreloadChapter(it)
- }
+ adapter.nextTransition?.to?.let(activity::requestPreloadChapter)
}
}
}
@@ -234,9 +232,7 @@ abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer {
val inPreloadRange = pages.size - page.number < 5
if (inPreloadRange && allowPreload && page.chapter == adapter.currentChapter) {
logcat { "Request preload next chapter because we're at page ${page.number} of ${pages.size}" }
- adapter.nextTransition?.to?.let {
- activity.requestPreloadChapter(it)
- }
+ adapter.nextTransition?.to?.let(activity::requestPreloadChapter)
}
}
@@ -320,7 +316,7 @@ abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer {
*/
protected open fun moveRight() {
if (pager.currentItem != adapter.count - 1) {
- val holder = (currentPage as? ReaderPage)?.let { getPageHolder(it) }
+ val holder = (currentPage as? ReaderPage)?.let(::getPageHolder)
if (holder != null && config.navigateToPan && holder.canPanRight()) {
holder.panRight()
} else {
@@ -334,7 +330,7 @@ abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer {
*/
protected open fun moveLeft() {
if (pager.currentItem != 0) {
- val holder = (currentPage as? ReaderPage)?.let { getPageHolder(it) }
+ val holder = (currentPage as? ReaderPage)?.let(::getPageHolder)
if (holder != null && config.navigateToPan && holder.canPanLeft()) {
holder.panLeft()
} else {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewerAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewerAdapter.kt
index 59c5fb52bd..ec78dc8c6c 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewerAdapter.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewerAdapter.kt
@@ -7,7 +7,7 @@ import eu.kanade.tachiyomi.ui.reader.model.InsertPage
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
-import eu.kanade.tachiyomi.ui.reader.viewer.hasMissingChapters
+import eu.kanade.tachiyomi.ui.reader.viewer.calculateChapterGap
import eu.kanade.tachiyomi.util.system.createReaderThemeContext
import eu.kanade.tachiyomi.widget.ViewPagerAdapter
import tachiyomi.core.util.system.logcat
@@ -48,8 +48,8 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
val newItems = mutableListOf()
// Forces chapter transition if there is missing chapters
- val prevHasMissingChapters = hasMissingChapters(chapters.currChapter, chapters.prevChapter)
- val nextHasMissingChapters = hasMissingChapters(chapters.nextChapter, chapters.currChapter)
+ val prevHasMissingChapters = calculateChapterGap(chapters.currChapter, chapters.prevChapter) > 0
+ val nextHasMissingChapters = calculateChapterGap(chapters.nextChapter, chapters.currChapter) > 0
// Add previous chapter pages and transition.
if (chapters.prevChapter != null) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonAdapter.kt
index 71766a2e6c..87edd1da45 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonAdapter.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonAdapter.kt
@@ -10,7 +10,7 @@ import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.model.StencilPage
import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderPageImageView
-import eu.kanade.tachiyomi.ui.reader.viewer.hasMissingChapters
+import eu.kanade.tachiyomi.ui.reader.viewer.calculateChapterGap
import eu.kanade.tachiyomi.util.system.createReaderThemeContext
import tachiyomi.core.util.system.logcat
@@ -62,8 +62,8 @@ class WebtoonAdapter(val viewer: WebtoonViewer) : RecyclerView.Adapter()
// Forces chapter transition if there is missing chapters
- val prevHasMissingChapters = hasMissingChapters(chapters.currChapter, chapters.prevChapter)
- val nextHasMissingChapters = hasMissingChapters(chapters.nextChapter, chapters.currChapter)
+ val prevHasMissingChapters = calculateChapterGap(chapters.currChapter, chapters.prevChapter) > 0
+ val nextHasMissingChapters = calculateChapterGap(chapters.nextChapter, chapters.currChapter) > 0
// Add previous chapter pages and transition.
if (chapters.prevChapter != null) {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/stats/anime/AnimeStatsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/stats/anime/AnimeStatsScreenModel.kt
index 7196fc4cb3..3ef449d791 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/stats/anime/AnimeStatsScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/stats/anime/AnimeStatsScreenModel.kt
@@ -7,7 +7,6 @@ import eu.kanade.core.util.fastDistinctBy
import eu.kanade.core.util.fastFilter
import eu.kanade.core.util.fastFilterNot
import eu.kanade.core.util.fastMapNotNull
-import eu.kanade.domain.entries.anime.model.isLocal
import eu.kanade.presentation.more.stats.StatsScreenState
import eu.kanade.presentation.more.stats.data.StatsData
import eu.kanade.tachiyomi.animesource.model.SAnime
@@ -25,6 +24,7 @@ import tachiyomi.domain.library.service.LibraryPreferences.Companion.ENTRY_NON_C
import tachiyomi.domain.library.service.LibraryPreferences.Companion.ENTRY_NON_VIEWED
import tachiyomi.domain.track.anime.interactor.GetAnimeTracks
import tachiyomi.domain.track.anime.model.AnimeTrack
+import tachiyomi.source.local.entries.anime.isLocal
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/stats/manga/MangaStatsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/stats/manga/MangaStatsScreenModel.kt
index 9cf3671d4c..8aa3cb552b 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/stats/manga/MangaStatsScreenModel.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/stats/manga/MangaStatsScreenModel.kt
@@ -7,7 +7,6 @@ import eu.kanade.core.util.fastDistinctBy
import eu.kanade.core.util.fastFilter
import eu.kanade.core.util.fastFilterNot
import eu.kanade.core.util.fastMapNotNull
-import eu.kanade.domain.entries.manga.model.isLocal
import eu.kanade.presentation.more.stats.StatsScreenState
import eu.kanade.presentation.more.stats.data.StatsData
import eu.kanade.tachiyomi.data.download.manga.MangaDownloadManager
@@ -25,6 +24,7 @@ import tachiyomi.domain.library.service.LibraryPreferences.Companion.ENTRY_NON_C
import tachiyomi.domain.library.service.LibraryPreferences.Companion.ENTRY_NON_VIEWED
import tachiyomi.domain.track.manga.interactor.GetMangaTracks
import tachiyomi.domain.track.manga.model.MangaTrack
+import tachiyomi.source.local.entries.manga.isLocal
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/AnimeExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/AnimeExtensions.kt
index 10c0814372..8baf8be4c9 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/util/AnimeExtensions.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/AnimeExtensions.kt
@@ -2,12 +2,12 @@ package eu.kanade.tachiyomi.util
import eu.kanade.domain.entries.anime.interactor.UpdateAnime
import eu.kanade.domain.entries.anime.model.hasCustomCover
-import eu.kanade.domain.entries.anime.model.isLocal
import eu.kanade.domain.entries.anime.model.toSAnime
import eu.kanade.tachiyomi.animesource.model.SAnime
import eu.kanade.tachiyomi.data.cache.AnimeCoverCache
import tachiyomi.domain.download.service.DownloadPreferences
import tachiyomi.domain.entries.anime.model.Anime
+import tachiyomi.source.local.entries.anime.isLocal
import tachiyomi.source.local.image.anime.LocalAnimeCoverManager
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt b/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt
index 05611d8ef3..a3f8fe1779 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt
@@ -1,33 +1,25 @@
package eu.kanade.tachiyomi.util
import android.content.Context
-import android.net.Uri
import android.os.Build
import eu.kanade.tachiyomi.BuildConfig
-import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.data.notification.NotificationReceiver
-import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.storage.getUriCompat
import eu.kanade.tachiyomi.util.system.createFileInCacheDir
-import eu.kanade.tachiyomi.util.system.notificationBuilder
-import eu.kanade.tachiyomi.util.system.notificationManager
+import eu.kanade.tachiyomi.util.system.toShareIntent
import eu.kanade.tachiyomi.util.system.toast
import tachiyomi.core.util.lang.withNonCancellableContext
import tachiyomi.core.util.lang.withUIContext
class CrashLogUtil(private val context: Context) {
- private val notificationBuilder = context.notificationBuilder(Notifications.CHANNEL_CRASH_LOGS) {
- setSmallIcon(R.drawable.ic_ani)
- }
-
suspend fun dumpLogs() = withNonCancellableContext {
try {
val file = context.createFileInCacheDir("aniyomi_crash_logs.txt")
Runtime.getRuntime().exec("logcat *:E -d -f ${file.absolutePath}").waitFor()
file.appendText(getDebugInfo())
- showNotification(file.getUriCompat(context))
+ val uri = file.getUriCompat(context)
+ context.startActivity(uri.toShareIntent(context, "text/plain"))
} catch (e: Throwable) {
withUIContext { context.toast("Failed to get logs") }
}
@@ -45,26 +37,4 @@ class CrashLogUtil(private val context: Context) {
Device product name: ${Build.PRODUCT}
""".trimIndent()
}
-
- private fun showNotification(uri: Uri) {
- context.notificationManager.cancel(Notifications.ID_CRASH_LOGS)
-
- with(notificationBuilder) {
- setContentTitle(context.getString(R.string.crash_log_saved))
-
- clearActions()
- addAction(
- R.drawable.ic_folder_24dp,
- context.getString(R.string.action_open_log),
- NotificationReceiver.openErrorLogPendingActivity(context, uri),
- )
- addAction(
- R.drawable.ic_share_24dp,
- context.getString(R.string.action_share),
- NotificationReceiver.shareCrashLogPendingBroadcast(context, uri, Notifications.ID_CRASH_LOGS),
- )
-
- context.notificationManager.notify(Notifications.ID_CRASH_LOGS, build())
- }
- }
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt
index 12bf4cb33f..b9889f5dc1 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt
@@ -2,12 +2,12 @@ package eu.kanade.tachiyomi.util
import eu.kanade.domain.entries.manga.interactor.UpdateManga
import eu.kanade.domain.entries.manga.model.hasCustomCover
-import eu.kanade.domain.entries.manga.model.isLocal
import eu.kanade.domain.entries.manga.model.toSManga
import eu.kanade.tachiyomi.data.cache.MangaCoverCache
import eu.kanade.tachiyomi.source.model.SManga
import tachiyomi.domain.download.service.DownloadPreferences
import tachiyomi.domain.entries.manga.model.Manga
+import tachiyomi.source.local.entries.manga.isLocal
import tachiyomi.source.local.image.manga.LocalMangaCoverManager
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterRemoveDuplicates.kt b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterRemoveDuplicates.kt
new file mode 100644
index 0000000000..ee79988c42
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterRemoveDuplicates.kt
@@ -0,0 +1,15 @@
+package eu.kanade.tachiyomi.util.chapter
+
+import tachiyomi.domain.items.chapter.model.Chapter
+
+/**
+ * Returns a copy of the list with duplicate chapters removed
+ */
+fun List.removeDuplicates(currentChapter: Chapter): List {
+ return groupBy { it.chapterNumber }
+ .map { (_, chapters) ->
+ chapters.find { it.id == currentChapter.id }
+ ?: chapters.find { it.scanlator == currentChapter.scanlator }
+ ?: chapters.first()
+ }
+}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/lang/RectFExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/lang/RectFExtensions.kt
index d1ad0b5b9a..2072bcd421 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/util/lang/RectFExtensions.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/lang/RectFExtensions.kt
@@ -1,9 +1,9 @@
package eu.kanade.tachiyomi.util.lang
import android.graphics.RectF
-import eu.kanade.tachiyomi.data.preference.PreferenceValues
+import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
-fun RectF.invert(invertMode: PreferenceValues.TappingInvertMode): RectF {
+fun RectF.invert(invertMode: ReaderPreferences.TappingInvertMode): RectF {
val horizontal = invertMode.shouldInvertHorizontal
val vertical = invertMode.shouldInvertVertical
return when {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/preference/PreferenceExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/preference/PreferenceExtensions.kt
index e123496306..c6076b790d 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/util/preference/PreferenceExtensions.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/preference/PreferenceExtensions.kt
@@ -3,8 +3,6 @@ package eu.kanade.tachiyomi.util.preference
import android.widget.CompoundButton
import eu.kanade.core.preference.PreferenceMutableState
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.onEach
import tachiyomi.core.preference.Preference
/**
@@ -15,12 +13,6 @@ fun CompoundButton.bindToPreference(pref: Preference) {
setOnCheckedChangeListener { _, isChecked -> pref.set(isChecked) }
}
-fun Preference.asHotFlow(block: (T) -> Unit): Flow {
- block(get())
- return changes()
- .onEach { block(it) }
-}
-
operator fun Preference>.plusAssign(item: T) {
set(get() + item)
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ActivityExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ActivityExtensions.kt
deleted file mode 100644
index bbe0cb1344..0000000000
--- a/app/src/main/java/eu/kanade/tachiyomi/util/system/ActivityExtensions.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-package eu.kanade.tachiyomi.util.system
-
-import android.app.Activity
-import android.os.Build
-
-/**
- * Checks whether if the device has a display cutout (i.e. notch, camera cutout, etc.).
- *
- * Only works in Android 9+.
- */
-fun Activity.hasDisplayCutout(): Boolean {
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P &&
- window.decorView.rootWindowInsets?.displayCutout != null
-}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/AnimationExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/AnimationExtensions.kt
index 3136e38c56..01c6b581bd 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/util/system/AnimationExtensions.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/AnimationExtensions.kt
@@ -1,10 +1,18 @@
package eu.kanade.tachiyomi.util.system
import android.content.Context
+import android.provider.Settings
import android.view.ViewPropertyAnimator
import android.view.animation.Animation
import androidx.constraintlayout.motion.widget.MotionScene.Transition
+/**
+ * Gets the duration multiplier for general animations on the device
+ * @see Settings.Global.ANIMATOR_DURATION_SCALE
+ */
+val Context.animatorDurationScale: Float
+ get() = Settings.Global.getFloat(this.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f)
+
/** Scale the duration of this [Animation] by [Context.animatorDurationScale] */
fun Animation.applySystemAnimatorScale(context: Context) {
this.duration = (this.duration * context.animatorDurationScale).toLong()
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt
index b8f590c57f..7043544ce1 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt
@@ -1,31 +1,22 @@
package eu.kanade.tachiyomi.util.system
import android.app.ActivityManager
-import android.app.NotificationManager
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.content.res.Configuration
-import android.content.res.Resources
import android.graphics.Color
import android.graphics.drawable.Drawable
-import android.net.ConnectivityManager
-import android.net.NetworkCapabilities
import android.net.Uri
-import android.net.wifi.WifiManager
import android.os.Build
import android.os.PowerManager
-import android.provider.Settings
import android.util.TypedValue
-import android.view.Display
-import android.view.View
-import android.view.WindowManager
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.appcompat.view.ContextThemeWrapper
-import androidx.core.content.ContextCompat
+import androidx.core.content.PermissionChecker
import androidx.core.content.getSystemService
import androidx.core.graphics.alpha
import androidx.core.graphics.blue
@@ -34,7 +25,6 @@ import androidx.core.graphics.red
import androidx.core.net.toUri
import com.hippo.unifile.UniFile
import eu.kanade.domain.ui.UiPreferences
-import eu.kanade.domain.ui.model.TabletUiMode
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.delegate.ThemingDelegate
@@ -78,7 +68,7 @@ fun Context.copyToClipboard(label: String, content: String) {
* @param permission the permission to check.
* @return true if it has permissions.
*/
-fun Context.hasPermission(permission: String) = ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
+fun Context.hasPermission(permission: String) = PermissionChecker.checkSelfPermission(this, permission) == PermissionChecker.PERMISSION_GRANTED
/**
* Returns the color for the given attribute.
@@ -112,42 +102,9 @@ fun Context.hasPermission(permission: String) = ContextCompat.checkSelfPermissio
}
}
-/**
- * Converts to px and takes into account LTR/RTL layout.
- */
-val Float.dpToPxEnd: Float
- get() = this * Resources.getSystem().displayMetrics.density *
- if (Resources.getSystem().isLTR) 1 else -1
-
-val Resources.isLTR
- get() = configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR
-
-val Context.notificationManager: NotificationManager
- get() = getSystemService()!!
-
-val Context.connectivityManager: ConnectivityManager
- get() = getSystemService()!!
-
-val Context.wifiManager: WifiManager
- get() = getSystemService()!!
-
val Context.powerManager: PowerManager
get() = getSystemService()!!
-val Context.displayCompat: Display?
- get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
- display
- } else {
- @Suppress("DEPRECATION")
- getSystemService()?.defaultDisplay
- }
-
-/** Gets the duration multiplier for general animations on the device
- * @see Settings.Global.ANIMATOR_DURATION_SCALE
- */
-val Context.animatorDurationScale: Float
- get() = Settings.Global.getFloat(this.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f)
-
/**
* Convenience method to acquire a partial wake lock.
*/
@@ -186,7 +143,7 @@ fun Context.openInBrowser(uri: Uri, forceDefaultBrowser: Boolean = false) {
}
}
-fun Context.defaultBrowserPackageName(): String? {
+private fun Context.defaultBrowserPackageName(): String? {
val browserIntent = Intent(Intent.ACTION_VIEW, "http://".toUri())
val resolveInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
packageManager.resolveActivity(browserIntent, PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong()))
@@ -208,59 +165,6 @@ fun Context.createFileInCacheDir(name: String): File {
return file
}
-private const val TABLET_UI_REQUIRED_SCREEN_WIDTH_DP = 720
-
-// some tablets have screen width like 711dp = 1600px / 2.25
-private const val TABLET_UI_MIN_SCREEN_WIDTH_PORTRAIT_DP = 700
-
-// make sure icons on the nav rail fit
-private const val TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP = 600
-
-fun Context.isTabletUi(): Boolean {
- return resources.configuration.isTabletUi()
-}
-
-fun Configuration.isTabletUi(): Boolean {
- return smallestScreenWidthDp >= TABLET_UI_REQUIRED_SCREEN_WIDTH_DP
-}
-
-fun Configuration.isAutoTabletUiAvailable(): Boolean {
- return smallestScreenWidthDp >= TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP
-}
-
-// TODO: move the logic to `isTabletUi()` when main activity is rewritten in Compose
-fun Context.prepareTabletUiContext(): Context {
- val configuration = resources.configuration
- val expected = when (Injekt.get().tabletUiMode().get()) {
- TabletUiMode.AUTOMATIC ->
- configuration.smallestScreenWidthDp >= when (configuration.orientation) {
- Configuration.ORIENTATION_PORTRAIT -> TABLET_UI_MIN_SCREEN_WIDTH_PORTRAIT_DP
- else -> TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP
- }
- TabletUiMode.ALWAYS -> true
- TabletUiMode.LANDSCAPE -> configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
- TabletUiMode.NEVER -> false
- }
- if (configuration.isTabletUi() != expected) {
- val overrideConf = Configuration()
- overrideConf.setTo(configuration)
- overrideConf.smallestScreenWidthDp = if (expected) {
- overrideConf.smallestScreenWidthDp.coerceAtLeast(TABLET_UI_REQUIRED_SCREEN_WIDTH_DP)
- } else {
- overrideConf.smallestScreenWidthDp.coerceAtMost(TABLET_UI_REQUIRED_SCREEN_WIDTH_DP - 1)
- }
- return createConfigurationContext(overrideConf)
- }
- return this
-}
-
-/**
- * Returns true if current context is in night mode
- */
-fun Context.isNightMode(): Boolean {
- return resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
-}
-
/**
* Creates night mode Context depending on reader theme/background
*
@@ -290,35 +194,6 @@ fun Context.createReaderThemeContext(): Context {
return this
}
-fun Context.isOnline(): Boolean {
- val activeNetwork = connectivityManager.activeNetwork ?: return false
- val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork) ?: return false
- val maxTransport = when {
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 -> NetworkCapabilities.TRANSPORT_LOWPAN
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> NetworkCapabilities.TRANSPORT_WIFI_AWARE
- else -> NetworkCapabilities.TRANSPORT_VPN
- }
- return (NetworkCapabilities.TRANSPORT_CELLULAR..maxTransport).any(networkCapabilities::hasTransport)
-}
-
-/**
- * Returns true if device is connected to Wifi.
- */
-fun Context.isConnectedToWifi(): Boolean {
- if (!wifiManager.isWifiEnabled) return false
-
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- val activeNetwork = connectivityManager.activeNetwork ?: return false
- val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork) ?: return false
-
- networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
- networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- } else {
- @Suppress("DEPRECATION")
- wifiManager.connectionInfo.bssid != null
- }
-}
-
/**
* Gets document size of provided [Uri]
*
@@ -368,11 +243,3 @@ fun Context.getApplicationIcon(pkgName: String): Drawable? {
null
}
}
-
-/**
- * Gets system's config_navBarNeedsScrim boolean flag added in Android 10, defaults to true.
- */
-fun Context.isNavigationBarNeedsScrim(): Boolean {
- return Build.VERSION.SDK_INT < Build.VERSION_CODES.Q ||
- InternalResourceHelper.getBoolean(this, "config_navBarNeedsScrim", true)
-}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/DisplayExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/DisplayExtensions.kt
new file mode 100644
index 0000000000..e93905c749
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/DisplayExtensions.kt
@@ -0,0 +1,106 @@
+package eu.kanade.tachiyomi.util.system
+
+import android.app.Activity
+import android.content.Context
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.os.Build
+import android.view.Display
+import android.view.View
+import android.view.WindowManager
+import androidx.core.content.getSystemService
+import eu.kanade.domain.ui.UiPreferences
+import eu.kanade.domain.ui.model.TabletUiMode
+import uy.kohesive.injekt.Injekt
+import uy.kohesive.injekt.api.get
+
+private const val TABLET_UI_REQUIRED_SCREEN_WIDTH_DP = 720
+
+// some tablets have screen width like 711dp = 1600px / 2.25
+private const val TABLET_UI_MIN_SCREEN_WIDTH_PORTRAIT_DP = 700
+
+// make sure icons on the nav rail fit
+private const val TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP = 600
+
+fun Context.isTabletUi(): Boolean {
+ return resources.configuration.isTabletUi()
+}
+
+fun Configuration.isTabletUi(): Boolean {
+ return smallestScreenWidthDp >= TABLET_UI_REQUIRED_SCREEN_WIDTH_DP
+}
+
+fun Configuration.isAutoTabletUiAvailable(): Boolean {
+ return smallestScreenWidthDp >= TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP
+}
+
+// TODO: move the logic to `isTabletUi()` when main activity is rewritten in Compose
+fun Context.prepareTabletUiContext(): Context {
+ val configuration = resources.configuration
+ val expected = when (Injekt.get().tabletUiMode().get()) {
+ TabletUiMode.AUTOMATIC ->
+ configuration.smallestScreenWidthDp >= when (configuration.orientation) {
+ Configuration.ORIENTATION_PORTRAIT -> TABLET_UI_MIN_SCREEN_WIDTH_PORTRAIT_DP
+ else -> TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP
+ }
+ TabletUiMode.ALWAYS -> true
+ TabletUiMode.LANDSCAPE -> configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
+ TabletUiMode.NEVER -> false
+ }
+ if (configuration.isTabletUi() != expected) {
+ val overrideConf = Configuration()
+ overrideConf.setTo(configuration)
+ overrideConf.smallestScreenWidthDp = if (expected) {
+ overrideConf.smallestScreenWidthDp.coerceAtLeast(TABLET_UI_REQUIRED_SCREEN_WIDTH_DP)
+ } else {
+ overrideConf.smallestScreenWidthDp.coerceAtMost(TABLET_UI_REQUIRED_SCREEN_WIDTH_DP - 1)
+ }
+ return createConfigurationContext(overrideConf)
+ }
+ return this
+}
+
+/**
+ * Returns true if current context is in night mode
+ */
+fun Context.isNightMode(): Boolean {
+ return resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
+}
+
+val Context.displayCompat: Display?
+ get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ display
+ } else {
+ @Suppress("DEPRECATION")
+ getSystemService()?.defaultDisplay
+ }
+
+val Resources.isLTR
+ get() = configuration.layoutDirection == View.LAYOUT_DIRECTION_LTR
+
+/**
+ * Converts to px and takes into account LTR/RTL layout.
+ */
+val Float.dpToPxEnd: Float
+ get() = (
+ this * Resources.getSystem().displayMetrics.density *
+ if (Resources.getSystem().isLTR) 1 else -1
+ )
+
+/**
+ * Checks whether if the device has a display cutout (i.e. notch, camera cutout, etc.).
+ *
+ * Only works in Android 9+.
+ */
+fun Activity.hasDisplayCutout(): Boolean {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.P &&
+ window.decorView.rootWindowInsets?.displayCutout != null
+}
+
+/**
+ * Gets system's config_navBarNeedsScrim boolean flag added in Android 10, defaults to true.
+ */
+fun Context.isNavigationBarNeedsScrim(): Boolean {
+ return Build.VERSION.SDK_INT < Build.VERSION_CODES.Q ||
+ InternalResourceHelper.getBoolean(this, "config_navBarNeedsScrim", true)
+}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/NetworkExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/NetworkExtensions.kt
new file mode 100644
index 0000000000..8d72a9c865
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/NetworkExtensions.kt
@@ -0,0 +1,43 @@
+package eu.kanade.tachiyomi.util.system
+
+import android.content.Context
+import android.net.ConnectivityManager
+import android.net.NetworkCapabilities
+import android.net.wifi.WifiManager
+import android.os.Build
+import androidx.core.content.getSystemService
+
+val Context.connectivityManager: ConnectivityManager
+ get() = getSystemService()!!
+
+val Context.wifiManager: WifiManager
+ get() = getSystemService()!!
+
+fun Context.isOnline(): Boolean {
+ val activeNetwork = connectivityManager.activeNetwork ?: return false
+ val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork) ?: return false
+ val maxTransport = when {
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 -> NetworkCapabilities.TRANSPORT_LOWPAN
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> NetworkCapabilities.TRANSPORT_WIFI_AWARE
+ else -> NetworkCapabilities.TRANSPORT_VPN
+ }
+ return (NetworkCapabilities.TRANSPORT_CELLULAR..maxTransport).any(networkCapabilities::hasTransport)
+}
+
+/**
+ * Returns true if device is connected to Wifi.
+ */
+fun Context.isConnectedToWifi(): Boolean {
+ if (!wifiManager.isWifiEnabled) return false
+
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ val activeNetwork = connectivityManager.activeNetwork ?: return false
+ val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork) ?: return false
+
+ networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
+ networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ } else {
+ @Suppress("DEPRECATION")
+ wifiManager.connectionInfo.bssid != null
+ }
+}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/NotificationExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/NotificationExtensions.kt
index 19ef75a8b2..5a37d66154 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/util/system/NotificationExtensions.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/NotificationExtensions.kt
@@ -1,12 +1,63 @@
package eu.kanade.tachiyomi.util.system
+import android.Manifest
import android.app.Notification
+import android.app.NotificationManager
import android.content.Context
+import android.os.Build
import androidx.core.app.NotificationChannelCompat
import androidx.core.app.NotificationChannelGroupCompat
import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationManagerCompat
+import androidx.core.app.NotificationManagerCompat.NotificationWithIdAndTag
+import androidx.core.content.PermissionChecker
+import androidx.core.content.getSystemService
import eu.kanade.tachiyomi.R
+val Context.notificationManager: NotificationManager
+ get() = getSystemService()!!
+
+fun Context.notify(id: Int, channelId: String, block: (NotificationCompat.Builder.() -> Unit)? = null) {
+ val notification = notificationBuilder(channelId, block).build()
+ this.notify(id, notification)
+}
+
+fun Context.notify(id: Int, notification: Notification) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && PermissionChecker.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PermissionChecker.PERMISSION_GRANTED) {
+ return
+ }
+
+ NotificationManagerCompat.from(this).notify(id, notification)
+}
+
+fun Context.notify(notificationWithIdAndTags: List) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && PermissionChecker.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) != PermissionChecker.PERMISSION_GRANTED) {
+ return
+ }
+
+ NotificationManagerCompat.from(this).notify(notificationWithIdAndTags)
+}
+
+fun Context.cancelNotification(id: Int) {
+ NotificationManagerCompat.from(this).cancel(id)
+}
+
+/**
+ * Helper method to create a notification builder.
+ *
+ * @param id the channel id.
+ * @param block the function that will execute inside the builder.
+ * @return a notification to be displayed or updated.
+ */
+fun Context.notificationBuilder(channelId: String, block: (NotificationCompat.Builder.() -> Unit)? = null): NotificationCompat.Builder {
+ val builder = NotificationCompat.Builder(this, channelId)
+ .setColor(getColor(R.color.accent_blue))
+ if (block != null) {
+ builder.block()
+ }
+ return builder
+}
+
/**
* Helper method to build a notification channel group.
*
@@ -40,31 +91,3 @@ fun buildNotificationChannel(
builder.block()
return builder.build()
}
-
-/**
- * Helper method to create a notification builder.
- *
- * @param id the channel id.
- * @param block the function that will execute inside the builder.
- * @return a notification to be displayed or updated.
- */
-fun Context.notificationBuilder(channelId: String, block: (NotificationCompat.Builder.() -> Unit)? = null): NotificationCompat.Builder {
- val builder = NotificationCompat.Builder(this, channelId)
- .setColor(getColor(R.color.accent_blue))
- if (block != null) {
- builder.block()
- }
- return builder
-}
-
-/**
- * Helper method to create a notification.
- *
- * @param id the channel id.
- * @param block the function that will execute inside the builder.
- * @return a notification to be displayed or updated.
- */
-fun Context.notification(channelId: String, block: (NotificationCompat.Builder.() -> Unit)?): Notification {
- val builder = notificationBuilder(channelId, block)
- return builder.build()
-}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/WorkManagerExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/WorkManagerExtensions.kt
new file mode 100644
index 0000000000..02d721725f
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/WorkManagerExtensions.kt
@@ -0,0 +1,13 @@
+package eu.kanade.tachiyomi.util.system
+
+import android.content.Context
+import androidx.work.WorkInfo
+import androidx.work.WorkManager
+
+val Context.workManager: WorkManager
+ get() = WorkManager.getInstance(this)
+
+fun WorkManager.isRunning(tag: String): Boolean {
+ val list = this.getWorkInfosByTag(tag).get()
+ return list.any { it.state == WorkInfo.State.RUNNING }
+}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt
index 14b0f76d39..60eaacbe93 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt
@@ -19,7 +19,6 @@ import androidx.appcompat.content.res.AppCompatResources
import androidx.appcompat.view.menu.MenuBuilder
import androidx.appcompat.widget.PopupMenu
import androidx.appcompat.widget.TooltipCompat
-import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
@@ -34,10 +33,11 @@ import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.system.getResourceColor
-inline fun ComposeView.setComposeContent(crossinline content: @Composable () -> Unit) {
- consumeWindowInsets = false
- setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
- setContent {
+inline fun ComponentActivity.setComposeContent(
+ parent: CompositionContext? = null,
+ crossinline content: @Composable () -> Unit,
+) {
+ setContent(parent) {
TachiyomiTheme {
CompositionLocalProvider(
LocalTextStyle provides MaterialTheme.typography.bodySmall,
@@ -49,11 +49,11 @@ inline fun ComposeView.setComposeContent(crossinline content: @Composable () ->
}
}
-inline fun ComponentActivity.setComposeContent(
- parent: CompositionContext? = null,
- crossinline content: @Composable () -> Unit,
+fun ComposeView.setComposeContent(
+ content: @Composable () -> Unit,
) {
- setContent(parent) {
+ setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
+ setContent {
TachiyomiTheme {
CompositionLocalProvider(
LocalTextStyle provides MaterialTheme.typography.bodySmall,
diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiTextInputEditText.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiTextInputEditText.kt
index bf77d38fdf..a571b68fa9 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiTextInputEditText.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/widget/TachiyomiTextInputEditText.kt
@@ -7,13 +7,13 @@ import androidx.core.view.inputmethod.EditorInfoCompat
import com.google.android.material.textfield.TextInputEditText
import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.util.preference.asHotFlow
import eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText.Companion.setIncognito
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@@ -49,8 +49,8 @@ class TachiyomiTextInputEditText @JvmOverloads constructor(
* if [BasePreferences.incognitoMode] is true. Some IMEs may not respect this flag.
*/
fun EditText.setIncognito(viewScope: CoroutineScope) {
- Injekt.get().incognitoMode()
- .asHotFlow {
+ Injekt.get().incognitoMode().changes()
+ .onEach {
imeOptions = if (it) {
imeOptions or EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING
} else {
diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/sheet/TabbedBottomSheetDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/sheet/TabbedBottomSheetDialog.kt
deleted file mode 100644
index 447a9c6b63..0000000000
--- a/app/src/main/java/eu/kanade/tachiyomi/widget/sheet/TabbedBottomSheetDialog.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-package eu.kanade.tachiyomi.widget.sheet
-
-import android.app.Activity
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import eu.kanade.tachiyomi.databinding.CommonTabbedSheetBinding
-import eu.kanade.tachiyomi.widget.ViewPagerAdapter
-
-abstract class TabbedBottomSheetDialog(private val activity: Activity) : BaseBottomSheetDialog(activity) {
-
- lateinit var binding: CommonTabbedSheetBinding
-
- override fun createView(inflater: LayoutInflater): View {
- binding = CommonTabbedSheetBinding.inflate(activity.layoutInflater)
-
- val adapter = LibrarySettingsSheetAdapter()
- binding.pager.offscreenPageLimit = 2
- binding.pager.adapter = adapter
- binding.tabs.setupWithViewPager(binding.pager)
-
- return binding.root
- }
-
- abstract fun getTabViews(): List
-
- abstract fun getTabTitles(): List
-
- private inner class LibrarySettingsSheetAdapter : ViewPagerAdapter() {
-
- override fun createView(container: ViewGroup, position: Int): View {
- return getTabViews()[position]
- }
-
- override fun getCount(): Int {
- return getTabViews().size
- }
-
- override fun getPageTitle(position: Int): CharSequence {
- return activity.resources!!.getString(getTabTitles()[position])
- }
- }
-}
diff --git a/app/src/main/res/layout/compose_controller.xml b/app/src/main/res/layout/compose_controller.xml
deleted file mode 100644
index 617287296b..0000000000
--- a/app/src/main/res/layout/compose_controller.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
diff --git a/app/src/main/res/layout/reader_pager_settings.xml b/app/src/main/res/layout/reader_pager_settings.xml
index 3da1b46bee..a180918899 100644
--- a/app/src/main/res/layout/reader_pager_settings.xml
+++ b/app/src/main/res/layout/reader_pager_settings.xml
@@ -91,10 +91,30 @@
android:visibility="gone"
tools:visibility="visible" />
+
+
+
+
+ app:constraint_referenced_ids="pager_nav,tapping_inverted,dual_page_split,dual_page_invert,dual_page_rotate_to_fit,dual_page_rotate_to_fit_invert" />
diff --git a/app/src/main/res/layout/reader_transition_view.xml b/app/src/main/res/layout/reader_transition_view.xml
deleted file mode 100644
index 2ec51224b2..0000000000
--- a/app/src/main/res/layout/reader_transition_view.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
index 57a9a3396f..dcbbdf1218 100644
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -1,6 +1,6 @@
-
-
-
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
index 57a9a3396f..dcbbdf1218 100644
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -1,6 +1,6 @@
-
-
-
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_local_source.webp b/app/src/main/res/mipmap-hdpi/ic_local_source.webp
index 406c5bd47e..87baab9bdf 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_local_source.webp and b/app/src/main/res/mipmap-hdpi/ic_local_source.webp differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_local_source.webp b/app/src/main/res/mipmap-mdpi/ic_local_source.webp
index cfd88e75eb..d2104a55e0 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_local_source.webp and b/app/src/main/res/mipmap-mdpi/ic_local_source.webp differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_local_source.webp b/app/src/main/res/mipmap-xhdpi/ic_local_source.webp
index 8021de84c4..1dc0b96733 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_local_source.webp and b/app/src/main/res/mipmap-xhdpi/ic_local_source.webp differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_local_source.webp b/app/src/main/res/mipmap-xxhdpi/ic_local_source.webp
index 1f10a36401..3e5d449864 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_local_source.webp and b/app/src/main/res/mipmap-xxhdpi/ic_local_source.webp differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_local_source.webp b/app/src/main/res/mipmap-xxxhdpi/ic_local_source.webp
index 80acaf13d0..5cd7482c4f 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_local_source.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_local_source.webp differ
diff --git a/build.gradle.kts b/build.gradle.kts
index e133aa3b86..19aa4629a0 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -20,7 +20,7 @@ plugins {
subprojects {
tasks.withType {
kotlinOptions {
- jvmTarget = JavaVersion.VERSION_1_8.toString()
+ jvmTarget = JavaVersion.VERSION_17.toString()
}
}
@@ -44,8 +44,8 @@ subprojects {
}
compileOptions {
- sourceCompatibility = JavaVersion.VERSION_1_8
- targetCompatibility = JavaVersion.VERSION_1_8
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
isCoreLibraryDesugaringEnabled = true
}
diff --git a/buildSrc/src/main/kotlin/LocalesConfigPlugin.kt b/buildSrc/src/main/kotlin/LocalesConfigPlugin.kt
index b1e53fde90..2c352f1319 100644
--- a/buildSrc/src/main/kotlin/LocalesConfigPlugin.kt
+++ b/buildSrc/src/main/kotlin/LocalesConfigPlugin.kt
@@ -3,25 +3,22 @@ import org.gradle.api.Task
import org.gradle.api.tasks.TaskProvider
import org.gradle.kotlin.dsl.TaskContainerScope
+private val emptyResourcesElement = "\\s*|".toRegex()
+private val valuesPrefix = "values(-(b\\+)?)?".toRegex()
+
fun TaskContainerScope.registerLocalesConfigTask(project: Project): TaskProvider {
return with(project) {
register("generateLocalesConfig") {
- val emptyResourcesElement = "\\s*|".toRegex()
- val valuesPrefix = "values-?".toRegex()
-
val languages = fileTree("$projectDir/src/main/res/")
- .matching {
- include("**/strings.xml")
- }
- .filterNot {
- it.readText().contains(emptyResourcesElement)
- }
+ .matching { include("**/strings.xml") }
+ .filterNot { it.readText().contains(emptyResourcesElement) }
.map { it.parentFile.name }
.sorted()
.joinToString(separator = "\n") {
val language = it
.replace(valuesPrefix, "")
.replace("-r", "-")
+ .replace("+", "-")
.takeIf(String::isNotBlank) ?: "en"
" "
}
diff --git a/core/src/main/java/eu/kanade/tachiyomi/network/interceptor/UncaughtExceptionInterceptor.kt b/core/src/main/java/eu/kanade/tachiyomi/network/interceptor/UncaughtExceptionInterceptor.kt
index 2362b78c60..1de824381b 100644
--- a/core/src/main/java/eu/kanade/tachiyomi/network/interceptor/UncaughtExceptionInterceptor.kt
+++ b/core/src/main/java/eu/kanade/tachiyomi/network/interceptor/UncaughtExceptionInterceptor.kt
@@ -18,7 +18,11 @@ class UncaughtExceptionInterceptor : Interceptor {
return try {
chain.proceed(chain.request())
} catch (e: Exception) {
- throw IOException(e)
+ if (e is IOException) {
+ throw e
+ } else {
+ throw IOException(e)
+ }
}
}
}
diff --git a/core/src/main/java/eu/kanade/tachiyomi/util/system/DeviceUtil.kt b/core/src/main/java/eu/kanade/tachiyomi/util/system/DeviceUtil.kt
index 7f3f6eaaf5..57950eda34 100644
--- a/core/src/main/java/eu/kanade/tachiyomi/util/system/DeviceUtil.kt
+++ b/core/src/main/java/eu/kanade/tachiyomi/util/system/DeviceUtil.kt
@@ -14,15 +14,15 @@ object DeviceUtil {
/**
* Extracts the MIUI major version code from a string like "V12.5.3.0.QFGMIXM".
*
- * @return MIUI major version code (e.g., 13) or -1 if can't be parsed.
+ * @return MIUI major version code (e.g., 13) or null if can't be parsed.
*/
val miuiMajorVersion by lazy {
- if (!isMiui) return@lazy -1
+ if (!isMiui) return@lazy null
Build.VERSION.INCREMENTAL
.substringBefore('.')
.trimStart('V')
- .toIntOrNull() ?: -1
+ .toIntOrNull()
}
@SuppressLint("PrivateApi")
@@ -45,6 +45,20 @@ object DeviceUtil {
Build.MANUFACTURER.equals("samsung", ignoreCase = true)
}
+ val oneUiVersion by lazy {
+ try {
+ val semPlatformIntField = Build.VERSION::class.java.getDeclaredField("SEM_PLATFORM_INT")
+ val version = semPlatformIntField.getInt(null) - 90000
+ if (version < 0) {
+ 1.0
+ } else {
+ ((version / 10000).toString() + "." + version % 10000 / 100).toDouble()
+ }
+ } catch (e: Exception) {
+ null
+ }
+ }
+
val invalidDefaultBrowsers = listOf(
"android",
"com.huawei.android.internal.app",
diff --git a/core/src/main/java/tachiyomi/core/util/system/ImageUtil.kt b/core/src/main/java/tachiyomi/core/util/system/ImageUtil.kt
index 638f33726c..5f29c7d853 100644
--- a/core/src/main/java/tachiyomi/core/util/system/ImageUtil.kt
+++ b/core/src/main/java/tachiyomi/core/util/system/ImageUtil.kt
@@ -7,6 +7,7 @@ import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.BitmapRegionDecoder
import android.graphics.Color
+import android.graphics.Matrix
import android.graphics.Rect
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
@@ -151,6 +152,23 @@ object ImageUtil {
return ByteArrayInputStream(output.toByteArray())
}
+ fun rotateImage(imageStream: InputStream, degrees: Float): InputStream {
+ val imageBytes = imageStream.readBytes()
+
+ val imageBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
+ val rotated = rotateBitMap(imageBitmap, degrees)
+
+ val output = ByteArrayOutputStream()
+ rotated.compress(Bitmap.CompressFormat.JPEG, 100, output)
+
+ return ByteArrayInputStream(output.toByteArray())
+ }
+
+ private fun rotateBitMap(bitmap: Bitmap, degrees: Float): Bitmap {
+ val matrix = Matrix().apply { postRotate(degrees) }
+ return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
+ }
+
/**
* Split the image into left and right parts, then merge them into a new image.
*/
diff --git a/domain/build.gradle.kts b/domain/build.gradle.kts
index 76832447f1..da6715940c 100644
--- a/domain/build.gradle.kts
+++ b/domain/build.gradle.kts
@@ -22,5 +22,5 @@ dependencies {
api(libs.sqldelight.android.paging)
- testImplementation(libs.junit)
+ testImplementation(libs.bundles.test)
}
diff --git a/domain/src/main/java/tachiyomi/domain/backup/service/PreferenceValues.kt b/domain/src/main/java/tachiyomi/domain/backup/service/PreferenceValues.kt
index fee6400eef..148cf5d101 100644
--- a/domain/src/main/java/tachiyomi/domain/backup/service/PreferenceValues.kt
+++ b/domain/src/main/java/tachiyomi/domain/backup/service/PreferenceValues.kt
@@ -4,3 +4,6 @@ const val FLAG_CATEGORIES = "1"
const val FLAG_CHAPTERS = "2"
const val FLAG_HISTORY = "4"
const val FLAG_TRACK = "8"
+const val FLAG_SETTINGS = "10"
+const val FLAG_EXT_SETTINGS = "20"
+const val FLAG_EXTENSIONS = "40"
diff --git a/domain/src/main/java/tachiyomi/domain/download/service/DownloadPreferences.kt b/domain/src/main/java/tachiyomi/domain/download/service/DownloadPreferences.kt
index 922d7a9f06..0fbd603416 100644
--- a/domain/src/main/java/tachiyomi/domain/download/service/DownloadPreferences.kt
+++ b/domain/src/main/java/tachiyomi/domain/download/service/DownloadPreferences.kt
@@ -18,6 +18,8 @@ class DownloadPreferences(
fun saveChaptersAsCBZ() = preferenceStore.getBoolean("save_chapter_as_cbz", true)
+ fun splitTallImages() = preferenceStore.getBoolean("split_tall_images", false)
+
fun autoDownloadWhileReading() = preferenceStore.getInt("auto_download_while_reading", 0)
fun autoDownloadWhileWatching() = preferenceStore.getInt("auto_download_while_watching", 0)
diff --git a/domain/src/main/java/tachiyomi/domain/items/service/MissingItems.kt b/domain/src/main/java/tachiyomi/domain/items/service/MissingItems.kt
new file mode 100644
index 0000000000..e2485f9de9
--- /dev/null
+++ b/domain/src/main/java/tachiyomi/domain/items/service/MissingItems.kt
@@ -0,0 +1,61 @@
+package tachiyomi.domain.items.service
+
+import tachiyomi.domain.items.chapter.model.Chapter
+import tachiyomi.domain.items.episode.model.Episode
+import kotlin.math.floor
+
+fun List.missingItemsCount(): Int {
+ if (this.isEmpty()) {
+ return 0
+ }
+
+ val items = this
+ // Ignore unknown item numbers
+ .filter { it != -1f }
+ // Convert to integers, as we cannot check if 16.5 is missing
+ .map(Float::toInt)
+ // Only keep unique chapters so that -1 or 16 are not counted multiple times
+ .distinct()
+ .sorted()
+
+ if (items.isEmpty()) {
+ return 0
+ }
+
+ var missingItemsCount = 0
+ var previousItem = 0 // The actual chapter number, not the array index
+
+ // We go from 0 to lastChapter - Make sure to use the current index instead of the value
+ for (i in items.indices) {
+ val currentItem = items[i]
+ if (currentItem > previousItem + 1) {
+ // Add the amount of missing chapters
+ missingItemsCount += currentItem - previousItem - 1
+ }
+ previousItem = currentItem
+ }
+
+ return missingItemsCount
+}
+
+fun calculateChapterGap(higherChapter: Chapter?, lowerChapter: Chapter?): Int {
+ if (higherChapter == null || lowerChapter == null) return 0
+ if (!higherChapter.isRecognizedNumber || !lowerChapter.isRecognizedNumber) return 0
+ return calculateChapterGap(higherChapter.chapterNumber, lowerChapter.chapterNumber)
+}
+
+fun calculateChapterGap(higherChapterNumber: Float, lowerChapterNumber: Float): Int {
+ if (higherChapterNumber < 0f || lowerChapterNumber < 0f) return 0
+ return floor(higherChapterNumber).toInt() - floor(lowerChapterNumber).toInt() - 1
+}
+
+fun calculateEpisodeGap(higherEpisode: Episode?, lowerEpisode: Episode?): Int {
+ if (higherEpisode == null || lowerEpisode == null) return 0
+ if (!higherEpisode.isRecognizedNumber || !lowerEpisode.isRecognizedNumber) return 0
+ return calculateChapterGap(higherEpisode.episodeNumber, lowerEpisode.episodeNumber)
+}
+
+fun calculateEpisodeGap(higherEpisodeNumber: Float, lowerEpisodeNumber: Float): Int {
+ if (higherEpisodeNumber < 0f || lowerEpisodeNumber < 0f) return 0
+ return floor(higherEpisodeNumber).toInt() - floor(lowerEpisodeNumber).toInt() - 1
+}
diff --git a/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt b/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt
index be93b2410f..d19deccec3 100644
--- a/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt
+++ b/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt
@@ -25,7 +25,7 @@ class LibraryPreferences(
fun libraryAnimeSortingMode() = preferenceStore.getObject("animelib_sorting_mode", AnimeLibrarySort.default, AnimeLibrarySort.Serializer::serialize, AnimeLibrarySort.Serializer::deserialize)
- fun libraryUpdateInterval() = preferenceStore.getInt("pref_library_update_interval_key", 24)
+ fun libraryUpdateInterval() = preferenceStore.getInt("pref_library_update_interval_key", 0)
fun libraryUpdateLastTimestamp() = preferenceStore.getLong("library_update_last_timestamp", 0L)
diff --git a/domain/src/test/java/tachiyomi/domain/items/chapter/service/ChapterRecognitionTest.kt b/domain/src/test/java/tachiyomi/domain/items/chapter/service/ChapterRecognitionTest.kt
index f7be94b511..431be6974d 100644
--- a/domain/src/test/java/tachiyomi/domain/items/chapter/service/ChapterRecognitionTest.kt
+++ b/domain/src/test/java/tachiyomi/domain/items/chapter/service/ChapterRecognitionTest.kt
@@ -1,6 +1,6 @@
package tachiyomi.domain.items.chapter.service
-import org.junit.jupiter.api.Assertions.assertEquals
+import io.kotest.matchers.shouldBe
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.parallel.Execution
import org.junit.jupiter.api.parallel.ExecutionMode
@@ -261,7 +261,6 @@ class ChapterRecognitionTest {
}
private fun assertChapter(mangaTitle: String, name: String, expected: Float) {
- val chapterNumber = ChapterRecognition.parseChapterNumber(mangaTitle, name)
- assertEquals(chapterNumber, expected)
+ ChapterRecognition.parseChapterNumber(mangaTitle, name) shouldBe expected
}
}
diff --git a/domain/src/test/java/tachiyomi/domain/items/service/MissingItemsTest.kt b/domain/src/test/java/tachiyomi/domain/items/service/MissingItemsTest.kt
new file mode 100644
index 0000000000..2007beffc1
--- /dev/null
+++ b/domain/src/test/java/tachiyomi/domain/items/service/MissingItemsTest.kt
@@ -0,0 +1,84 @@
+package tachiyomi.domain.items.service
+
+import io.kotest.matchers.shouldBe
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.parallel.Execution
+import org.junit.jupiter.api.parallel.ExecutionMode
+import tachiyomi.domain.items.chapter.model.Chapter
+import tachiyomi.domain.items.episode.model.Episode
+
+@Execution(ExecutionMode.CONCURRENT)
+class MissingItemsTest {
+
+ @Test
+ fun `missingItemsCount returns 0 when empty list`() {
+ emptyList().missingItemsCount() shouldBe 0
+ }
+
+ @Test
+ fun `missingItemsCount returns 0 when all unknown item numbers`() {
+ listOf(-1f, -1f, -1f).missingItemsCount() shouldBe 0
+ }
+
+ @Test
+ fun `missingItemsCount handles repeated base item numbers`() {
+ listOf(1f, 1.0f, 1.1f, 1.5f, 1.6f, 1.99f).missingItemsCount() shouldBe 0
+ }
+
+ @Test
+ fun `missingItemsCount returns number of missing items`() {
+ listOf(-1f, 1f, 2f, 2.2f, 4f, 6f, 10f, 11f).missingItemsCount() shouldBe 5
+ }
+
+ @Test
+ fun `calculateChapterGap returns difference`() {
+ calculateChapterGap(chapter(10f), chapter(9f)) shouldBe 0f
+ calculateChapterGap(chapter(10f), chapter(8f)) shouldBe 1f
+ calculateChapterGap(chapter(10f), chapter(8.5f)) shouldBe 1f
+ calculateChapterGap(chapter(10f), chapter(1.1f)) shouldBe 8f
+
+ calculateChapterGap(10f, 9f) shouldBe 0f
+ calculateChapterGap(10f, 8f) shouldBe 1f
+ calculateChapterGap(10f, 8.5f) shouldBe 1f
+ calculateChapterGap(10f, 1.1f) shouldBe 8f
+ }
+
+ @Test
+ fun `calculateChapterGap returns 0 if either are not valid chapter numbers`() {
+ calculateChapterGap(chapter(-1f), chapter(10f)) shouldBe 0
+ calculateChapterGap(chapter(99f), chapter(-1f)) shouldBe 0
+
+ calculateChapterGap(-1f, 10f) shouldBe 0
+ calculateChapterGap(99f, -1f) shouldBe 0
+ }
+
+ private fun chapter(number: Float) = Chapter.create().copy(
+ chapterNumber = number,
+ )
+
+ @Test
+ fun `calculateEpisodeGap returns difference`() {
+ calculateEpisodeGap(episode(10f), episode(9f)) shouldBe 0f
+ calculateEpisodeGap(episode(10f), episode(8f)) shouldBe 1f
+ calculateEpisodeGap(episode(10f), episode(8.5f)) shouldBe 1f
+ calculateEpisodeGap(episode(10f), episode(1.1f)) shouldBe 8f
+
+ calculateEpisodeGap(10f, 9f) shouldBe 0f
+ calculateEpisodeGap(10f, 8f) shouldBe 1f
+ calculateEpisodeGap(10f, 8.5f) shouldBe 1f
+ calculateEpisodeGap(10f, 1.1f) shouldBe 8f
+ }
+
+ @Test
+ fun `calculateEpisodeGap returns 0 if either are not valid episode numbers`() {
+ calculateEpisodeGap(episode(-1f), episode(10f)) shouldBe 0
+ calculateEpisodeGap(episode(99f), episode(-1f)) shouldBe 0
+
+ calculateEpisodeGap(-1f, 10f) shouldBe 0
+ calculateEpisodeGap(99f, -1f) shouldBe 0
+ }
+
+ private fun episode(number: Float) = Episode.create().copy(
+ episodeNumber = number,
+ )
+}
diff --git a/domain/src/test/java/tachiyomi/domain/library/model/LibraryFlagsTest.kt b/domain/src/test/java/tachiyomi/domain/library/model/LibraryFlagsTest.kt
index 7703c5bba3..26b155996a 100644
--- a/domain/src/test/java/tachiyomi/domain/library/model/LibraryFlagsTest.kt
+++ b/domain/src/test/java/tachiyomi/domain/library/model/LibraryFlagsTest.kt
@@ -1,20 +1,23 @@
package tachiyomi.domain.library.model
-import org.junit.jupiter.api.Assertions.assertEquals
-import org.junit.jupiter.api.Assertions.assertNotEquals
+import io.kotest.matchers.shouldBe
+import io.kotest.matchers.shouldNotBe
import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.parallel.Execution
+import org.junit.jupiter.api.parallel.ExecutionMode
import tachiyomi.domain.library.anime.model.AnimeLibrarySort
import tachiyomi.domain.library.manga.model.MangaLibrarySort
+@Execution(ExecutionMode.CONCURRENT)
class LibraryFlagsTest {
@Test
fun `Check the amount of flags`() {
- assertEquals(4, LibraryDisplayMode.values.size)
- assertEquals(8, MangaLibrarySort.types.size)
- assertEquals(2, MangaLibrarySort.directions.size)
- assertEquals(8, AnimeLibrarySort.types.size)
- assertEquals(2, AnimeLibrarySort.directions.size)
+ LibraryDisplayMode.values.size shouldBe 4
+ MangaLibrarySort.types.size shouldBe 8
+ MangaLibrarySort.directions.size shouldBe 2
+ AnimeLibrarySort.types.size shouldBe 8
+ AnimeLibrarySort.directions.size shouldBe 2
}
@Test
@@ -23,7 +26,7 @@ class LibraryFlagsTest {
val new = LibraryDisplayMode.CoverOnlyGrid
val flag = current + new
- assertEquals(0b00000011, flag)
+ flag shouldBe 0b00000011
}
@Test
@@ -35,8 +38,8 @@ class LibraryFlagsTest {
val mangaflag = mangacurrent + newmanga
val animeflag = animecurrent + newanime
- assertEquals(0b01011100, mangaflag)
- assertEquals(0b01011100, animeflag)
+ mangaflag shouldBe 0b01011100
+ animeflag shouldBe 0b01011100
}
@Test
@@ -47,8 +50,8 @@ class LibraryFlagsTest {
val mangaflag = display + mangasort
val animeflag = display + animesort
- assertEquals(0b01011111, mangaflag)
- assertEquals(0b01011111, animeflag)
+ mangaflag shouldBe 0b01011111
+ animeflag shouldBe 0b01011111
}
@Test
@@ -65,12 +68,12 @@ class LibraryFlagsTest {
val animesort = AnimeLibrarySort(AnimeLibrarySort.Type.DateAdded, AnimeLibrarySort.Direction.Ascending)
val animeflag = currentanimeFlag + display + animesort
- assertEquals(0b00001110, currentmangaFlag)
- assertEquals(0b01011111, mangaflag)
- assertNotEquals(currentmangaFlag, mangaflag)
- assertEquals(0b00001110, currentanimeFlag)
- assertEquals(0b01011111, animeflag)
- assertNotEquals(currentanimeFlag, animeflag)
+ currentmangaFlag shouldBe 0b00001110
+ mangaflag shouldBe 0b01011111
+ mangaflag shouldNotBe currentmangaFlag
+ currentanimeFlag shouldBe 0b00001110
+ animeflag shouldBe 0b01011111
+ animeflag shouldNotBe currentanimeFlag
}
@Test
@@ -81,7 +84,7 @@ class LibraryFlagsTest {
val mangaflag = display + mangasort.type + mangasort.direction
val animeflag = display + animesort.type + animesort.direction
- assertEquals(0b01000000, mangaflag)
- assertEquals(0b01000000, animeflag)
+ mangaflag shouldBe 0b01000000
+ animeflag shouldBe 0b01000000
}
}
diff --git a/gradle.properties b/gradle.properties
index f84d9dfb74..097bd50251 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -20,7 +20,9 @@ org.gradle.parallel=true
org.gradle.caching=true
-# AndroidX support
-android.useAndroidX=true
-
kotlin.mpp.androidSourceSetLayoutVersion=2
+
+android.useAndroidX=true
+android.defaults.buildfeatures.buildconfig=true
+android.nonTransitiveRClass=false
+android.nonFinalResIds=false
diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml
index de27923dd6..56b1522536 100644
--- a/gradle/androidx.versions.toml
+++ b/gradle/androidx.versions.toml
@@ -1,27 +1,27 @@
[versions]
-agp_version = "7.4.2"
-lifecycle_version = "2.6.0"
+agp_version = "8.0.0"
+lifecycle_version = "2.6.1"
[libraries]
gradle = { module = "com.android.tools.build:gradle", version.ref = "agp_version" }
-annotation = "androidx.annotation:annotation:1.6.0"
+annotation = "androidx.annotation:annotation:1.7.0-alpha02"
appcompat = "androidx.appcompat:appcompat:1.6.1"
biometricktx = "androidx.biometric:biometric-ktx:1.2.0-alpha05"
constraintlayout = "androidx.constraintlayout:constraintlayout:2.1.4"
coordinatorlayout = "androidx.coordinatorlayout:coordinatorlayout:1.2.0"
-corektx = "androidx.core:core-ktx:1.10.0-rc01"
+corektx = "androidx.core:core-ktx:1.11.0-alpha03"
splashscreen = "androidx.core:core-splashscreen:1.0.0-alpha02"
recyclerview = "androidx.recyclerview:recyclerview:1.3.0"
viewpager = "androidx.viewpager:viewpager:1.1.0-alpha01"
glance = "androidx.glance:glance-appwidget:1.0.0-alpha03"
-profileinstaller = "androidx.profileinstaller:profileinstaller:1.2.2"
+profileinstaller = "androidx.profileinstaller:profileinstaller:1.3.0"
lifecycle-common = { module = "androidx.lifecycle:lifecycle-common", version.ref = "lifecycle_version" }
lifecycle-process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle_version" }
lifecycle-runtimektx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle_version" }
-work-runtime = "androidx.work:work-runtime-ktx:2.8.0"
+work-runtime = "androidx.work:work-runtime-ktx:2.8.1"
guava = "com.google.guava:guava:31.1-android"
paging-runtime = "androidx.paging:paging-runtime:3.1.1"
diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml
index a18f82e436..73cdd7e3bf 100644
--- a/gradle/compose.versions.toml
+++ b/gradle/compose.versions.toml
@@ -1,10 +1,10 @@
[versions]
-compiler = "1.4.3"
-compose-bom = "2023.02.00-rc02"
-accompanist = "0.29.1-alpha"
+compiler = "1.4.6"
+compose-bom = "2023.03.00"
+accompanist = "0.30.1"
[libraries]
-activity = "androidx.activity:activity-compose:1.6.1"
+activity = "androidx.activity:activity-compose:1.7.1"
bom = { group = "dev.chrisbanes.compose", name = "compose-bom", version.ref = "compose-bom" }
foundation = { module = "androidx.compose.foundation:foundation" }
animation = { module = "androidx.compose.animation:animation" }
diff --git a/gradle/kotlinx.versions.toml b/gradle/kotlinx.versions.toml
index d462eb7069..16704bf3c7 100644
--- a/gradle/kotlinx.versions.toml
+++ b/gradle/kotlinx.versions.toml
@@ -1,5 +1,5 @@
[versions]
-kotlin_version = "1.8.10"
+kotlin_version = "1.8.20"
serialization_version = "1.5.0"
xml_serialization_version = "0.85.0"
@@ -10,6 +10,7 @@ gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "
coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version = "1.6.4" }
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" }
serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization_version" }
serialization-json-okio = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json-okio", version.ref = "serialization_version" }
@@ -18,7 +19,7 @@ serialization-xml-core = { module = "io.github.pdvrieze.xmlutil:core-android", v
serialization-xml = { module = "io.github.pdvrieze.xmlutil:serialization-android", version.ref = "xml_serialization_version" }
[bundles]
-coroutines = ["coroutines-core", "coroutines-android"]
+coroutines = ["coroutines-core", "coroutines-android", "coroutines-guava"]
serialization = ["serialization-json", "serialization-json-okio", "serialization-protobuf", "serialization-xml-core", "serialization-xml"]
[plugins]
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 09ab9a065a..12f030c920 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,16 +1,16 @@
[versions]
-aboutlib_version = "10.6.1"
+aboutlib_version = "10.6.2"
okhttp_version = "5.0.0-alpha.11"
-coil_version = "2.2.2"
+coil_version = "2.3.0"
shizuku_version = "12.2.0"
-sqlite = "2.3.0"
+sqlite = "2.3.1"
sqldelight = "1.5.5"
leakcanary = "2.10"
voyager = "1.0.0-rc07"
richtext = "0.16.0"
[libraries]
-desugar = "com.android.tools:desugar_jdk_libs:2.0.2"
+desugar = "com.android.tools:desugar_jdk_libs:2.0.3"
android-shortcut-gradle = "com.github.zellius:android-shortcut-gradle-plugin:0.1.2"
google-services-gradle = "com.google.gms:google-services:4.3.15"
google-crashlytics-gradle = "com.google.firebase:firebase-crashlytics-gradle:2.8.0"
@@ -47,7 +47,7 @@ coil-core = { module = "io.coil-kt:coil", version.ref = "coil_version" }
coil-gif = { module = "io.coil-kt:coil-gif", version.ref = "coil_version" }
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil_version" }
-subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:846abe0"
+subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:c8e2650"
image-decoder = "com.github.tachiyomiorg:image-decoder:7879b45"
natural-comparator = "com.github.gpanther:java-nat-sort:natural-comparator-1.1"
@@ -62,13 +62,13 @@ photoview = "com.github.chrisbanes:PhotoView:2.3.0"
directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0"
insetter = "dev.chrisbanes.insetter:insetter:0.6.1"
compose-cascade = "me.saket.cascade:cascade-compose:2.0.0-rc02"
-compose-materialmotion = "io.github.fornewid:material-motion-compose-core:0.10.4"
+compose-materialmotion = "io.github.fornewid:material-motion-compose-core:0.11.3"
compose-simpleicons = "br.com.devsrsouza.compose.icons.android:simple-icons:1.0.0"
logcat = "com.squareup.logcat:logcat:0.1"
acra-http = "ch.acra:acra-http:5.9.7"
-firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.2.0"
+firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.2.2"
firebase-crashlytics = "com.google.firebase:firebase-crashlytics-ktx:18.3.1"
aboutLibraries-core = { module = "com.mikepenz:aboutlibraries-core", version.ref = "aboutlib_version" }
@@ -87,6 +87,7 @@ sqldelight-android-paging = { module = "com.squareup.sqldelight:android-paging3-
sqldelight-gradle = { module = "com.squareup.sqldelight:gradle-plugin", version.ref = "sqldelight" }
junit = "org.junit.jupiter:junit-jupiter:5.9.2"
+kotest-assertions = "io.kotest:kotest-assertions-core:5.6.1"
voyager-navigator = { module = "ca.gosyer:voyager-navigator", version.ref = "voyager" }
voyager-tab-navigator = { module = "ca.gosyer:voyager-tab-navigator", version.ref = "voyager" }
@@ -109,3 +110,4 @@ coil = ["coil-core", "coil-gif", "coil-compose"]
shizuku = ["shizuku-api", "shizuku-provider"]
voyager = ["voyager-navigator", "voyager-tab-navigator", "voyager-transitions"]
richtext = ["richtext-commonmark", "richtext-m3"]
+test = ["junit", "kotest-assertions"]
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index bdc9a83b1e..37aef8d3f0 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 79a61d421c..c8b0079d93 100755
--- a/gradlew
+++ b/gradlew
@@ -85,9 +85,6 @@ done
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
-
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -197,6 +194,9 @@ if "$cygwin" || "$msys" ; then
done
fi
+ # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
diff --git a/i18n/src/main/res/values-am/strings.xml b/i18n/src/main/res/values-am/strings.xml
index 7d74524cf9..816a12117a 100644
--- a/i18n/src/main/res/values-am/strings.xml
+++ b/i18n/src/main/res/values-am/strings.xml
@@ -105,7 +105,6 @@
እንደገና ሞክር
ቀጣይ ምዕራፍ
ቀዳሚ ምዕራፍ
- ተው
ለአፍታ አቁም
ምዕራፎችን ይመልከቱ
ሽፋን አርትዕ
diff --git a/i18n/src/main/res/values-ar/strings-aniyomi.xml b/i18n/src/main/res/values-ar/strings-aniyomi.xml
index ea5e7658e9..812bf70965 100644
--- a/i18n/src/main/res/values-ar/strings-aniyomi.xml
+++ b/i18n/src/main/res/values-ar/strings-aniyomi.xml
@@ -5,7 +5,7 @@
آخر تحديث للمانجا
عرض المانجا
المانجا المحلية
- مع فصل (فصول) غير مقروءة / حلقة (حلقات) غير مشاهدة
+ فيها فصول لم تُقرأ
إظهار عدد الغير مقروء/ الغير مشاهد على أيقونة التحديثات
فئة المانغا الإفتراضية
المانجا في الفئات المستبعدة لن يتم تحديثها حتى وإن كانت أيضا في الفئات المضمنة.
@@ -34,7 +34,7 @@
تطبيق أيضًا على جميع المانجا في المكتبة
إعادة تعيين جميع الفصول لهذه المانجا
تعذّر تنزيل الفصول بسبب انخفاض مساحة التخزين
- تحذير: التحميلات كبيرة الحجم والعدد يمكن أن تؤدي إلى تباطؤ المصادر و/أو ان تقوم المصادر بحظر Tachiyomi۔ اضغط لمعرفة المزيد۔
+ تحذير: يمكن أن تؤدِّي التنزيلات كبيرة الحجم والعدد إلى إبطاء المصادر، وقد يُحظر Tachiyomi منها بسبب ذلك. اضغط لمعرفة المزيد۔
يتطلب Tachiyomi وجود WebView
تم إيقاف التنزيل مؤقتاً
تحديثات الفصول
@@ -120,7 +120,7 @@
الحلقة التالية غير موجودة!
المستشعر الرأسي
%1$s: %2$s، %3$s
- إظهار زر \"متابعة المشاهدة/القراءة\"
+ زرُّ متابعة القراءة
غير مشاهد
تعديل فئات الأنمي
انقل \"المانغا\" إلى علامة التبويب \"المزيد\"
diff --git a/i18n/src/main/res/values-ar/strings.xml b/i18n/src/main/res/values-ar/strings.xml
index 3e74dbd34c..fda7f3e5f0 100644
--- a/i18n/src/main/res/values-ar/strings.xml
+++ b/i18n/src/main/res/values-ar/strings.xml
@@ -4,7 +4,7 @@
مانجا
الفصول
التعقب
- السجل
+ التاريخ
اﻹعدادات
المكتبة
السجل
@@ -36,7 +36,6 @@
إعادة تسمية الفئة
حدد الفئات
تعديل صورة الغلاف
- إيقاف
إيقاف موقت
الفصل السابق
الفصل التالي
@@ -66,8 +65,8 @@
اﻹعدادات المتقدمة
حول التطبيق
العناصر لكل صف
- رأسي
- افقي
+ طوليٌّ
+ عرضيٌّ
التحديثات التلقائية
إيقاف
كل ٦ ساعات
@@ -78,7 +77,8 @@
الكل
تقييد التحديث التلقائي للجهاز
عند الشحن
- مع حالة \"مكتمل\"
+ حالها «تمَّت»
+ الفئة المبدئية
السؤال دائماً
وضع الشاشة الكاملة
مؤثرات الانتقال بين الصفحات
@@ -94,9 +94,9 @@
أبيض
أسود
وضع القراءة الافتراضي
- من اليسار لليمين
- من اليمين لليسار
- عموديا
+ يسارًا فيمينًا
+ يمينًا فيسارًا
+ عموديٌّ
الويبتون
كيفية ضبط الأبعاد
ملائمة الشاشة
@@ -132,11 +132,11 @@
النسخ الاحتياطيّة التلقائيّة
معدل النسخ الإحتياطي
أقصى عدد للنسخ الاحتياطيّة
- تم إنشاء النسخة الإحتياطية
+ أُنشئت نسخة احتياطية
اكتملت الاستعادة
ما الذي تريد نسخه احتياطيّاً؟
- جار استعادة النسخة الاحتياطية
- جار إنشاء النسخة الاحتياطية
+ تُستعاد النسخة الاحتياطية
+ تُنشأ النسخة الاحتياطية
بحث شامل
R
G
@@ -159,16 +159,16 @@
تم تسجيل الدخول
خطأ غير معروف
جار تحديث الفئة
- لا يوجد نتائج أخرى
- مصدر محلّي
+ لا توجد نتائج أخرى
+ مصدر محلِّي
أخرى
البحث الشامل…
- اﻷخيرة
- تصفّح
- مُستمرّة
- غير معروف
- مُرخّصة
- إزالة من المكتبة
+ الأحدث
+ تصفَّح
+ مستمرَّة
+ مجهول
+ مرخَّصة
+ أزل من المكتبة
حذف الفصول المنزلة؟
الفصل %1$s
جاري التنزيل (%1$d/%2$d)
@@ -181,24 +181,24 @@
غير مقروء
هل أنت متأكد من أنك تريد حذف الفصول المحددة؟
التتبع
- أقرأها
- مكتملة
+ القراءة
+ تمَّت
متروكة
- معلّقة
- أنوي قرأتها
+ معلَّقة
+ أنوي قراءتها
التقييم
العنوان
- الحالة
+ الحال
توجد بالفعل فئة بهذا الاسم!
تم حذف الفئات
سيؤدي هذا إلى إزالة تاريخ قراءة هذا الفصل. هل أنت متأكد؟
تم حفظ الصورة
مُرشح مخصص
- تعيين كغلاف
- تم تحديث صورة الغلاف
+ عيِّنها غلافًا
+ حُدِّث الغلاف
الصفحة: %1$d
لم يتم العثور على الفصل التالي
- تعذّر تحميل الصورة
+ تعذَّر تحميل هذه الصورة
هل تريد تعيين هذه الصورة كغلاف؟
فشل تنزيل الفصول. يمكنك إعادة المحاولة في قسم التنزيلات
تم إيجاد فصول جديدة
@@ -236,7 +236,7 @@
الثقة
غير موثوق فيه
إلغاء التثبيت
- إضافة غير موثوق بها
+ إضافة ذات ريبة
سرعة مؤثر النقر المزدوج
عارض الصفحات
لا مؤثرات
@@ -253,7 +253,7 @@
تم البدء فيها
النوع
اختر بيانات لتضمينها
- ترحيل
+ رحِّل
نسخ
ليست لديك أيّة فئات، اضغط زر اﻹضافة لإنشاء واحدة لتنظيم مكتبتك.
تمَّ الانتهاء من:
@@ -275,7 +275,7 @@
مُرشح الألوان ”وضع الألوان الممزوجة“
مراوغة / تفتيح
حرق / تغميق
- لم يتم العثور على أيّة نتائج
+ لم يُعثر على أيِّ نتائج
اختيار مصدر للترحيل من
عودة
تقدم
@@ -294,10 +294,10 @@
نقل إلى الأسفل
أبدًا
دائماً
- قفل في حالة السكون
- اطلب فكَّ القفل
+ أوصد في حالة السكون
+ اطلب فتح القفل
الأمان و الخصوصية
- إدارة الإشعارات
+ أدر الإشعارات
صيغة التاريخ
اتبع مظهر النظام
مفعّل
@@ -315,23 +315,23 @@
التحديثات معلقة
التحديث العام
العرض
- اخفاء محتوى الإشعارات
- إخفاء محتوى الشاشة عند تغير التطبيقات و منع لقط الشاشة
+ أخفِ محتوى الإشعارات
+ تُخفي الشاشة الآمنة محتوى التطبيق عند التبديل بين التطبيقات، وتمنع التقاط الشاشة كذلك
الشاشة الآمنة
المُنزل فقط
- تحقق من وجود تحديثات
+ تحقَّق من وجود تحديثات
التراخيص مفتوحة المصدر
الموقع الإلكتروني
- تم الغاء الاستعادة
+ أُلغيت الاستعادة
فشل استعادة النسخ الاحتياطي
الاستعادة قيد التقدم بالفعل
فشل النسخ الاحتياطي
- النسخ الاحتياطي قيد التقدم بالفعل
+ يُنسخ احتياطيًّا بالفعل
%02d دقيقة و %02d ثانية
- 25%
- 20%
- 15%
- 10%
+ ٢٥٪
+ ٢٠٪
+ ١٥٪
+ ١٠٪
بلا
تأكيد الخروج
إلغاء التثبيت
@@ -378,16 +378,16 @@
جارٍ التحقق من وجود فصول جديدة
الفصل %1$s - %2$s
- يجري تحديث المكتبة
+ تُحدَّث المكتبة
وضع القراءة
القسم الخاص بهذه السلسلة
- متوقّف
- إضافة تتبع
+ متوقِّفة
+ أضف تتبُّعًا
أقل
المزيد
في المكتبة
إضافة إلى المكتبة
- دليل استخدام ميزة المصدر المحلي
+ دليل استخدام المصادر المحلية
المثبتة
آخر مصدر مُستخدم
التحقق من الموقع الإلكتروني في WebView
@@ -430,25 +430,24 @@
البيانات
المصادر المفقودة:
النسخة الإحتياطية لا تحتوي على أيّة مانجا.
- ملف النسخ الاحتياطي غير صالح
+ ملفُّ النسخ الاحتياطيِّ غير صالح
مزامنة أحاديّة لتحديث تقدم الفصول في خدمات التتبع. قم بإعداد التتبع الخاص بمانجا محدّدة من زر التتبع الخاص بها.
- هذه الإضافة ليست من قائمة إضافات Tachiyomi الرسميّة.
+ هذه الإضافة ليست من قائمة إضافات Tachiyomi الرسمية.
غير رسمي
تحقق من وجود غلاف جديد وتفاصيل جديدة عند تحديث المكتبة
تحديث البيانات الوصفية تلقائياً
- - تم في %1$s مع %2$sأخطاء
- - تم في %1$s ووجد خطأ واحد
- - تم في %1$s ووجد اثنين من الأخطاء
- - تم في %1$s ووجد %2$s من الأخطاء
- - تم في %1$s ووجد %2$s من الأخطاء
- - تم في %1$s ووجد %2$s من الأخطاء
+ - تمَّ في %1$s وبدون أخطاء
+ - تمَّ في %1$s وفيه خطأ
+ - تمَّ في %1$s وفيه خطآن
+ - تمَّ في %1$s وفيه %2$s أخطاء
+ - تمَّ في %1$s وفيه %2$s خطأً
+ - تمَّ في %1$s وفيه %2$s خطأ
حسب تاريخ الرفع
حدّث صور اغلفة المكتبة
شبكة مريحة
قوائم
- شارات
إظهار قوائم الفئة
لم يتم العثور على أيّة صفحات
تفعيل الكل
@@ -499,12 +498,12 @@
إبحث عن \"%1$s\" بشكلٍ شامل
حُدِّث إلى الإصدار v%1$s
- - لم يتم تخطِّي أي فصل
- - تم تخطِّي فصل
- - تم تخطِّي فصلان
- - تم تخطِّي بعض الفصول
- - تم تخطِّي فصول
- - غير ذلك
+ - لا يُتخطَّى أيُّ فصل
+ - يُتخطَّى فصل، وذلك إما لأن المصدر مفقود أو لأنه مصفًّى
+ - يُتخطَّى فصلان، وذلك إما لأن المصدر مفقود أو لأنهما مصفَّان
+ - تُتخطَّى %d فصول، وذلك إما لأن المصدر مفقود أو لأنهم مصفَّون
+ - يُتخطَّى %d فصلًا، وذلك إما لأن المصدر مفقود أو لأنهم مصفَّون
+ - يُتخطَّى %d فصل، وذلك إما لأن المصدر مفقود أو لأنهم مصفَّون
لم يتم العثور على الفصول
%1$s: %2$s, صفحة %3$d
@@ -513,9 +512,9 @@
هل أنت متأكد أنك تريد حفظ هذه الإعدادات كافتراضي؟
إعدادات الفصل
إعدادات البحث
- الفصول المنزّلة
- هل أنت متأكد؟ سيتم حذف السجل.
- تم حذف السجل
+ الفصول المنزَّلة
+ أمتأكِّد؟ سوف يُحذف التاريخ.
+ حُذف التاريخ
الوضع المخفي
مسح السجل
الصفحة التالية
@@ -526,18 +525,16 @@
رجاء سجل دخولك في MAL مجدداً
لم يتم العثور على تطبيق منتقي الملفات
مناطق اللمس
- على شكل حرف L
- حافة
- كيندل العش
+ في شكل حرف L
+ حافَّة
+ كأنه كِندِل
مُتتبَعة
إظهار عدد العناصر
- سجلات الاخطاء
تاريخ انهاء القراءة
تاريخ بدء القراءة
- تم حفظ سجلات الأخطاء
يحفظ سجلات الأخطاء في ملف للمشاركة مع المطورين
- استخراج سجلات الاخطاء
- يميناً و يساراً
+ شارك سجلَّات الانهيار
+ يميناً ويساراً
إذا كان موضع تقسيم الصفحات العريضة لا يتطابق مع اتجاه القراءة
عكس موضع تقسيم الصفحة
تقسيم الصفحات العريضة
@@ -574,7 +571,7 @@
فشل النسخ إلى الحافظة
نسخة الاندرويد هذه اصبحت غير مدعومة
المصدر غير مدعوم
- غير مقروء
+ يُقرأ
التاريخ
ترتيب حسب
لم يتم العثور على الفصل
@@ -582,9 +579,9 @@
تشغيل
قيود: %s
لا يوجد مطابقات
- فشل مشاركة الغطاء
- فشل حفظ الغطاء
- تم حفظ صورة الغطاء
+ فشلت مشاركة الغلاف
+ خطأ في حفظ الغلاف
+ حُفظ الغلاف
غطاء
صيغة الفصل غير صالح
تعليمات التعقب
@@ -598,11 +595,11 @@
منتصف الليل
أخضر
مظهر التطبيق
- أَدان
- أدنى
- أعلى
- أعلَى
- حساسية التمرير لاخفاء القائمة
+ أدنى
+ دنيا
+ عالية
+ أعلى
+ حساسية التمرير لإخفاء القائمة
وضع داكن الأسود النقي
يوتسوبا
ين & يانغ
@@ -611,9 +608,9 @@
جار تحديث المكتبة…( (%2$d) / (%1$d) )
هذا المتتبّع متوافق فقط مع المصدر (Komga)۔
تقوم بعض الشركات المصنعة بوضع قيود إضافية على التطبيقات التي قد تقضي على الخدمات التي تعمل في الخلفية. يحتوي هذا الموقع على مزيد من المعلومات حول كيفية إصلاحه.
- النسخ الاحتياطي/الإستعادة ربما لا يعمل بطريقه صحيحة عندما يكون خيار MIUI Optimization غير مفعل.
+ قد لا يعمل النسخ الاحتياطيُّ أو الاستعادة إن عطِّلت أمثَلَة MIUI.
الخدمات التي تقدم ميزات محسنة لأجل مصادر معينه. المانجات سوف يتم تتبعها عندما يتم إضافتها الي المكتبة.
- خدمات محسنة
+ خدمات محسَّنة
اليوم
مؤخراً
المصادقة لتأكيد التغيير
@@ -652,10 +649,10 @@
تحديث الكل
كل ثلاثة ايام
فقط على شبكة WI-FI
- \"Shizuku\" لا يعمل
- تم النشر
- ملغي
- في فترة راحة
+ «Shizuku» لا يعمل
+ انتهى النشر
+ أُلغيت
+ منقطعة
للحصول على المساعده في إصلاح أخطاء تحديث المكتبة، أنظر هنا%1$s
احفظ كأرشيف CBZ
سياسة الخصوصية
@@ -664,23 +661,23 @@
لا شئ للتنظيف
عدد المانغا غير المتواجدة بقاعدة بيانات المكتبه%1$d
فشل الحصول على قائمة الملحقات
- قم بتثبيت \"Shizuku\" لاستخدامه كمثبت ملحق.
+ ثبِّت «شيزوكو» وشغِّله لتستخدمه مثبِّت إضافات.
الاسئله الشائعه والأدلة
- 5%
+ ٥٪
بدأت
شبكة بالاغلفة
- تم التخطي لعدم وجود فصول مقروءه
- تم التخطي لوجود فصول غير مقروءه
- هذا لم يبدأ بعد
- تم تخطيها لإنتهاء السلسلة
+ تُخُطِّيت بسبب عدم وجود فصول قُرئت
+ تُخُطِّيت لوجود فصول لم تُقرأ
+ لم يبدأ هذا بعد
+ تُخُطِّيت لأنها تمِّت
تحريك الصور أثناء النقر
كبر الصوره الأفقية
- متجاوز
+ متجاوَز
خطأ أثناء حفظ الصورة
معطل
رأسي بالعكس
- فشل تحديث %1$d عناصر
- تم تخطي تحديث %1$d عناصر
+ فشل %1$d تحديث أو تحديثات
+ تُخُطِّي %1$d تحديث أو تحديثات
اضغط لقراءة المزيد
نسخة جديدة متاحة من الإصدارات الرسمية. انقر لمعرفة كيفية الهجرة من إصدارات F-Droid غير الرسمية.
نقل سلسلة الفصول للأعلى
@@ -695,9 +692,8 @@
لا يوجد مصادر مثبته
لا يوجد مصدر
المانجا الغير مقروءة
- يحسن أداء القارئ
- تقسيم للصور الطويلة
- لم يتم العثور على الصفحة %d أثناء التقسيم
+ يحسِّن أداء القارئ
+ لم يُعثر على الصفحة %d أثناء التقسيم
لم يتم العثور على مسار الصفخة %d
اعادة تعيين جميع اعدادات القارئ
إعادة تعيين اعدادات القراءه لكل القصص
@@ -705,15 +701,14 @@
التقييم العمري
الإصدار
لم يمكن إعادة تعيين إعدادات القارئ
- لم يمكن تقسيم الصورة المُنزلَة
هذا محرج
قائمة غير المنتهية
على الشبكات غير المحدودة فقط
إعادة ضبط إعدادات قراءة السلسلة
قائمة القراءة
قائمة الرغبات
- قائمة الكاملة
- قائمة المعلقة
+ قائمة المكتملة
+ قائمة المعلَّقة
غلاف مخصص
غير قادر على فتح آخر فصل تم قراءته
غير مثبت
@@ -725,10 +720,10 @@
مسح الفئة
لا وصف
أُرجواني
- القطعة غير متاحة عند تمكين قفل التطبيق
- شاهد المانقا المجدده حديثاً
+ لا تتاح الأداة حال تمكين قفل التطبيق
+ اطَّلع على ما حُدِّث مؤخَّرًا في مكتبتك
حذف كل شيء
- الصيغه RARv5 غير مدعومه
+ تنسيق RARv5 ليس مدعومًا
موجة مد و جزر
متعدد
إنقسام الصور الطويلة (بيتا)
@@ -742,17 +737,17 @@
شائع
أنت على وشك أن تحذف \"%s\" من مكتبتك
- - الفصل التالي غير المقروء
- - الفصل التالي غير المقروء
- - الفصلان غير المقروءان التاليان
- - بضعة ال %d فصول غير المقروءة التالية
- - عدة ال %d فصول غير المقروءة التالية
- - الفصول %d غير المقروءة التالية
+ - لا يوجد فصل تالٍ لم يُقرأ
+ - الفصل غير المقروء التالي
+ - الفصلان غير المقروءين التاليان
+ - %d فصول تالية لم تُقرأ
+ - %d فصلًا تاليًا لم يُقرؤوا
+ - %d فصل تالٍ لم يُقرؤوا
لم يتم منح أذن التخزين
بحث…
- تم التخطي لأن السلسلة لا تتطلب تحديثات
- %s واجه خطأ غير متوقع. نقترح عليك أخد لقطة شاشة لهذه الرسالة، وتفريغ سجلات التعطل ، ثم مشاركتها في قناة الدعم الخاصة بنا على Discord.
+ تُخُطِّيت لأن السلسلة لا تتطلب تحديثات
+ واجه %s خطأً غير متوقَّع. تنبغي لك مشاركة سجلَّات الانهيار في قناة الدعم في دسكورد.
عفوًا!
أعد تشغيل التطبيق
لغة التطبيق، الإشعارات
@@ -787,4 +782,16 @@
إخفاء الإدخالات الموجودة بالفعل في المكتبة
تحديث الفئة
+ التراكب
+
+ - لا يوجد أيُّ فصل مفقود
+ - فصل واحد مفقود
+ - فصلان مفقودان
+ - %1$s فصول مفقودة
+ - %1$s فصلًا مفقودًا
+ - %1$s فصل مفقود
+
+ لعلَّه فاقد بعض الفصول
+ دوِّر الصفحات العريضة لتلائم الشاشة
+ اعكس اتِّجاه الصفحات العريضة المدوَّرة
\ No newline at end of file
diff --git a/i18n/src/main/res/values-b+es+419/strings.xml b/i18n/src/main/res/values-b+es+419/strings.xml
index 7b0772abaf..22b7eed94b 100644
--- a/i18n/src/main/res/values-b+es+419/strings.xml
+++ b/i18n/src/main/res/values-b+es+419/strings.xml
@@ -267,7 +267,6 @@
Cuadrícula compacta
Iniciar
Reintentar
- Detener
Borrar cookies
Red
Se canceló la restauración
@@ -315,7 +314,6 @@
No se han encontrado resultados
No hay más resultados
Pestañas
- Insignias
Local
Actualizando categoría
Error desconocido
@@ -490,10 +488,8 @@
No se encontró ninguna aplicación de selección de archivos
Por favor iniciar sesión con MAL de nuevo
Mostrar en la lista de fuentes y extensiones
- Registros de accidentes
Fecha de lectura terminada
Fecha de inicio de lectura
- Registros de fallos guardados
Guarda los registros de errores en un archivo para compartirlos con los desarrolladores
Volcar registros de accidentes
Zona de toque
@@ -657,13 +653,11 @@
Ninguna fuente encontrada
No se encontró ninguna fuente instalada
Recuento no leído
- No se pudo dividir la imagen descargada
Bueno, esto es incomodo
Todas las configuraciones del lector se restablecen
No se pudo restablecer la configuración del lector
Restablecer la configuración del lector por serie
Mejora rendimiento del lector al ajustar imágenes altas descargadas.
- Ajustar automáticamente el alto de las imagenes
Página %d no encontrada durante la división
No se encontró la ruta del archivo de la página %d
Reinicie el modo de lectura y la orientación de toda serie
diff --git a/i18n/src/main/res/values-be/strings.xml b/i18n/src/main/res/values-be/strings.xml
index 5176cfe896..d08dd93535 100644
--- a/i18n/src/main/res/values-be/strings.xml
+++ b/i18n/src/main/res/values-be/strings.xml
@@ -33,7 +33,6 @@
Наступная частка
Папярэдняя частка
Паўза
- Спыніць
Прагляд частак
Рэдагаваць вокладку
Дадаць у катэгорыі
diff --git a/i18n/src/main/res/values-bg/strings.xml b/i18n/src/main/res/values-bg/strings.xml
index 547f43a016..e2d733803e 100644
--- a/i18n/src/main/res/values-bg/strings.xml
+++ b/i18n/src/main/res/values-bg/strings.xml
@@ -33,7 +33,6 @@
Преименувай категория
Запиши в категории
Промени корица
- Спри
Паузирай
Предишна глава
Следваща глава
@@ -448,7 +447,6 @@
- %1$s глави
Раздели
- Значки
Данни
Изисква рестартиране, за да влезе в сила
Мрежа
@@ -525,7 +523,6 @@
Разтовари записите от сривовете
Не
Пейзаж
- Записите са запазени
Работа на заден план
Изключи инкогнито
Обновяване на библиотеката… (%1$d/%2$d)
@@ -614,7 +611,6 @@
Не бе намерено приложение за подбор на файлове
Първи стъпки
Стандартните настройки на главите бяха обновени
- Записи от сривове
Предишна страница
Следваща страница
Език
@@ -691,7 +687,6 @@
Списък със спрени
Предстоящо изтегляне
Работи само със заглавия в библиотеката и ако текущата и следващата глава са вече изтеглени
- Изтегленото изображение не можа да бъде разделено
Виж наскоро обновената манга
Не можа да бъде намерен файловият път на страница %d
Показвай дубликати на закачените източници
@@ -742,7 +737,6 @@
Покажи записи от крашове, оптимизации за батерията
Мулти
Увеличи широките изображения при натискане
- Разделяй високите изображения
Подобрява производителността на четеца
Не е дадено разрешение за съхранение
Приключено издаване
diff --git a/i18n/src/main/res/values-bn/strings.xml b/i18n/src/main/res/values-bn/strings.xml
index 16fac515ac..811f07ea9b 100644
--- a/i18n/src/main/res/values-bn/strings.xml
+++ b/i18n/src/main/res/values-bn/strings.xml
@@ -37,7 +37,6 @@
বিভাগের নতুন নামকরণ করুন
বিভাগ নির্বাচন করুন
মোড়ক সম্পাদনা করুন
- থামুন
বিরতি দিন
পূর্ববর্তী অধ্যায়
পরবর্তী আধ্যায়
@@ -448,7 +447,6 @@
সর্বশেষ ব্যবহৃত
ওয়েবসাইটটি ওয়েবভিউতে পরীক্ষা করুন
ট্যাব গুলি
- প্রতীক গুলি
ডাউনলোড করা অধ্যায়
আপনি এখন প্রস্থান করেছেন
প্রস্থান
@@ -480,13 +478,11 @@
আইটেমের সংখ্যা প্রদর্শন করুন
পরের পৃষ্ঠা
আগের পৃষ্ঠা
- ক্র্যাশ লগগুলো
কোনো ফাইল বাছাইকারী অ্যাপ পাওয়া যায় নি
উৎস স্থানান্তর গাইড
দয়া করে MAL এ আবার লগইন করুন
শেষ করার তারিখ
শুরু করার তারিখ
- ক্র্যাশ লগগুলো সংরক্ষিত হয়েছে
বিকাশকারীদের সাথে সেয়ার করার জন্য এরর লগগুলো একটি ফাইলে সংরক্ষণ করে
ডান ও বাম
কিনার
@@ -637,11 +633,9 @@
স্থগিত তালিকা
পড়ার সময় সয়ংক্রিয়ভাবে ডাউনলোড
শিরোনামসমুহ হালনাগাদ এড়িয়ে যান
- ডাউনলোড করা চিত্র বিভক্ত করা যায়নি
এড়িয়ে যাওয়া
যেগুলো শুরু করা হয়নি
ওয়েবভিউ ডাটা মুছা হয়েছে
- লম্বা চিত্র বিভক্ত
পঠন কর্মক্ষমতা উন্নত করে
ওয়েবভিইউ ডাটা মুছুন
বহু
diff --git a/i18n/src/main/res/values-ca/strings-aniyomi.xml b/i18n/src/main/res/values-ca/strings-aniyomi.xml
index 87a9cb5909..ebf74c8b2d 100644
--- a/i18n/src/main/res/values-ca/strings-aniyomi.xml
+++ b/i18n/src/main/res/values-ca/strings-aniyomi.xml
@@ -50,7 +50,7 @@
Moure Història a la pestanya Més
Últim comprovat
Anime local
- Mostra el botó de continuar mirant/llegint
+ Botó per a continuar llegint
Descarregador extern
Episodi anterior
Manga
diff --git a/i18n/src/main/res/values-ca/strings.xml b/i18n/src/main/res/values-ca/strings.xml
index 6af5fcedf6..c940f47c89 100644
--- a/i18n/src/main/res/values-ca/strings.xml
+++ b/i18n/src/main/res/values-ca/strings.xml
@@ -40,7 +40,6 @@
Canvia el nom de la categoria
Defineix les categories
Edita la portada
- Atura
Pausa
Capítol anterior
Capítol següent
@@ -415,7 +414,6 @@
Graella confortable
Migra
Pestanyes
- Icones
Mostra les pestanyes de les categories
No s\'ha trobat cap pàgina
Desactiva-ho tot
@@ -480,10 +478,8 @@
No s\'ha trobat cap aplicació de selecció de fitxers
Mostra a la llista de fonts i extensions
Torneu a iniciar la sessió a MAL
- Registres de fallades
Data de finalització
Data d’inici
- S\'han desat els registres de fallades
Desa els registres d\'errors en un fitxer perquè el pugueu compartir amb els desenvolupadors
Bolca els registres de fallades
Zones de toc
@@ -646,7 +642,6 @@
No s\'ha trobat cap font instal·lada
No s\'ha trobat cap font
Nombre de no llegits
- Divideix les imatges altes
Millora el rendiment del lector
No s\'ha trobat la pàgina %d en dividir
No s\'ha trobat el camí del fitxer de la pàgina %d
@@ -654,7 +649,6 @@
Restableix el mode de lectura i l\'orientació de totes les sèries
S\'han restablert tota la configuració del lector
No s\'ha pogut restablir la configuració del lector
- No s\'ha pogut dividir la imatge baixada
Ostres, que estrany...
Llengua
Versió
@@ -761,4 +755,5 @@
Amaga els elements que ja són a la biblioteca
Copia al porta-retalls
Actualitza la categoria
+ Divideix les imatges altes
\ No newline at end of file
diff --git a/i18n/src/main/res/values-ceb/strings.xml b/i18n/src/main/res/values-ceb/strings.xml
index 25bc34c912..3caa93e7e9 100644
--- a/i18n/src/main/res/values-ceb/strings.xml
+++ b/i18n/src/main/res/values-ceb/strings.xml
@@ -10,7 +10,7 @@
Gisubay
Wala mabasa
Alpabetiko
- Kinatibuk sa manga
+ Hingpit nga mga entry
Kinatibuk sa mga kapitulo
Katapusan nga pagbasa
Wala mabasa nga ihap
@@ -65,7 +65,6 @@
Usba ang ngalan sa kategorya
Usba ang hapin
Tan-awa ang mga kapitulo
- Hunong
Kuhaa
Sa miaging kapitulo
Pagsugod
@@ -353,7 +352,6 @@
Mga serbisyo nga naghatag dugang nga mga bahin alang sa piho nga mga gigikanan. Awtomatikong gisubay ang Manga kon idugang sa imong librarya.
Usa ka paagi nga pag-sync aron ma-update ang pag-uswag sa kapitulo sa mga serbisyo sa pagsubay. I-set up ang tracking para sa indibidwal nga manga entries gikan sa ilang tracking button.
Gipalambo nga mga serbisyo
- Awtomatikong gibahin ang taas nga mga imahe
Nagpauswag sa pasundayag sa magbabasa pinaagi sa pagbahin sa taas nga na-download nga mga imahe.
Mga serbisyo
Pag-tap sa mga zone
@@ -430,7 +428,6 @@
I-reset ang mode sa pagbasa ug oryentasyon sa tanan nga serye
Ang tanan nga mga setting sa magbabasa gi-reset
Dili ma-reset ang mga setting sa magbabasa
- Na-save ang mga crash log
Kalihokan sa background
Nagtabang sa mga update sa librarya sa background ug pag-backup
Verbose logging
diff --git a/i18n/src/main/res/values-cs/strings-aniyomi.xml b/i18n/src/main/res/values-cs/strings-aniyomi.xml
index 6228e54edd..f87f8fe335 100644
--- a/i18n/src/main/res/values-cs/strings-aniyomi.xml
+++ b/i18n/src/main/res/values-cs/strings-aniyomi.xml
@@ -115,7 +115,7 @@
Označit jako nezhlédnuté
Stažené epizody
Lokální anime
- Zobrazit tlařítko pro pokračování sledování / čtení
+ Tlačítko Pokračovat ve čtení
Použít stahovač mimo aplikaci
Přesunout Mangy do záložky Více
Nezhlédnuto
diff --git a/i18n/src/main/res/values-cs/strings.xml b/i18n/src/main/res/values-cs/strings.xml
index cd22785968..28c80d00ac 100644
--- a/i18n/src/main/res/values-cs/strings.xml
+++ b/i18n/src/main/res/values-cs/strings.xml
@@ -111,7 +111,7 @@
Vlastní filtr
Stránka: %1$d
Další kapitola nenalezena
- Obrázek nemohl být načten
+ Obrázek se nepodařilo načíst
Dokončeno:
Aktuální:
Následující:
@@ -146,7 +146,6 @@
Aktualizovat knihovnu
Přejmenovat kategorii
Vybrat kategorie
- Stop
Pauza
Opakovat
Pokračovat
@@ -350,9 +349,8 @@
Optimalizace je již vypnuta
Pomáhá s aktualizacemi knihovny a záloh na pozadí
Vypnout optimalizaci baterie
- Chybový protokol uložen
Uloží chybové protokoly do souboru pro sdílení s vývojáři
- Vypsat protokoly o selhání
+ Sdílet protokoly o selhání
Obnovit přebaly v knihovně
Data
Pro projevení je nutný restart aplikace
@@ -425,7 +423,6 @@
Zakázat
Zobrazovat počet položek
Začít
- Záznamy o pádech
Aktualizace rozšíření
Chyby
Dokončeno
@@ -465,9 +462,9 @@
Kap. %1$s - %2$s
Aktualizuji knihovnu
- - Přeskakuji %d kapitolu, buď chybí ve zdroji nebo byla vyfiltrována
- - Přeskakuji %d kapitoly, buď chybí ve zdroji nebo byly vyfiltrovány
- - Přeskakuji %d kapitol, buď chybí ve zdroji nebo byly vyfiltrovány
+ - Přeskočena %d kapitola, buď chybí ve zdroji nebo byla vyfiltrována
+ - Přeskočeny %d kapitoly, buď chybí ve zdroji nebo byly vyfiltrovány
+ - Přeskočeno %d kapitol, buď chybí ve zdroji nebo byly vyfiltrovány
Zdroj nenalezen
Žádné stránky nenalezeny
@@ -500,7 +497,6 @@
Při otevření čtečky krátce zobrazí aktuální režim
Čekajících aktualizací
Nebyla nalezena žádná aplikace pro výběr souborů
- Odznaky
Filtruje všechnu položky ve vaší knihovně
Položky ve vynechaných kategoriích nebudou staženy, i kdyby byly také v zahrnutých kategoriích.
Automatické stahování
@@ -667,13 +663,11 @@
Jazyk
Věkové hodnocení
Verze
- Rozdělit vysoké obrázky
Zlepšuje výkon čtečky
Nastavení čtečky se nepodařilo resetovat
No, tohle je trapné
Stránka %d nebyla při rozdělení nalezena
Nepodařilo se najít cestu k souboru stránky %d
- Stažený obrázek se nepodařilo rozdělit
Posunutí širokých snímků při klepnutí
Počet nepřečtených
Když baterie není vybitá
@@ -705,7 +699,7 @@
Funguje pouze na položky v knihovně a pokud je již stažena aktuální kapitola a následující kapitola
Jste si jistí\?
Chystáte se odstranit \"%s\" ze své knihovny
- Multi
+ Více
Rozdělit vysoké obrázky (BETA)
Poslední aktualizace knihovny: %s
Populární
@@ -721,7 +715,7 @@
Zdroje, rozšíření, globální vyhledávání
Zámek aplikace, zabezpečená obrazovka
Výpis protokolů selhání, optimalizace baterie
- %s narazil na neočekávanou chybu. Doporučujeme vám pořídit snímek obrazovky, vypsat protokoly o selhání a poté je sdílet v našem kanálu podpory na Discordu.
+ %s narazil na neočekávanou chybu. Doporučujeme vám sdílet protokoly o selhání a poté je sdílet v našem kanálu podpory na Discordu.
Restartujte aplikaci
Ruční a automatické zálohování
Došlo k neočekávané chybě
@@ -751,4 +745,14 @@
Zkopírovat do schránky
Aktualizovat kategorii
+ Rozdělit vysoké obrázky
+ Překrytí
+ Překlopení orientace otočených širokých stránek
+ Otočení širokých stránek tak, aby se vešly
+
+ - Chybí %1$s kapitola
+ - Chybí %1$s kapitoly
+ - Chybí %1$s kapitol
+
+ Mohou chybět kapitoly
\ No newline at end of file
diff --git a/i18n/src/main/res/values-cv/strings-aniyomi.xml b/i18n/src/main/res/values-cv/strings-aniyomi.xml
index 759204e412..771d9a7e59 100644
--- a/i18n/src/main/res/values-cv/strings-aniyomi.xml
+++ b/i18n/src/main/res/values-cv/strings-aniyomi.xml
@@ -4,7 +4,7 @@
Aniyomi уҫ
Юлашки ҫӗнетӗве тӗрӗслени
Ҫырава кӑтарт
- Вырӑнти ҫӑл куҫ
+ Вырӑнти ҫӑл куҫран
Яланхилле пухмӑш
Ку хушмана шанчӑклӑ мар сертификатпа алӑ пуснӑ тата ӑна активламан.
\n
diff --git a/i18n/src/main/res/values-cv/strings.xml b/i18n/src/main/res/values-cv/strings.xml
index 862bbc23c1..ce6200886b 100644
--- a/i18n/src/main/res/values-cv/strings.xml
+++ b/i18n/src/main/res/values-cv/strings.xml
@@ -1,6 +1,6 @@
- Вулавӑшӑн серилӗхӗсем
+ Вулавӑшри серилӗхсем
Усӑ курнӑ: %1$s
Куккисем катертнӗ
Кукки тасат
@@ -40,35 +40,35 @@
- %d пухмӑш
Кашнинчех ыйтмалла
- Тулать
- Ҫӗнетни чарӑвӗсем
- Кашни эрне
+ Петтерей тулнӑ чух
+ Хатӗр валли хӑй-хальлӗн ҫӗнетӳ чарӑвӗсем
+ Кашни ерне
Кашни 2 кун
Кашни кун
Кашни 12 сехет
Кашни 6 сехет
Ҫӗнетни тӑтӑшлӑхӗ
- Пӗтӗм ҫӗнетӳ
+ Пӗтӗмӗшле ҫӗнетӳ
Кӑтарт
- Сыхлав ыкранӗ
+ Ыкран сыхлавӗ
- 1 минут хыҫҫӑн
- %1$s минут хыҫҫӑн
Сыхлав тата вӑрттӑнлӑх
- Систерӳсене ӗнерле
+ Систерӳсене ӗнер
Тухнине ҫирӗплет
- Вӑхӑт формачӗ
+ Ҫул-кун хармачӗ
Ҫутнӑ
Сӳнтернӗ
Тата
- Тиевсем
+ Тийевсем
Вулавӑш
Тӗп
Хушӑма кӗме май ҫук
Ҫӗнет
Малалла
- Каялла
+ Кайалла
Пӑрахӑҫла
Упра
Пайлаш
@@ -77,12 +77,12 @@
Пуҫламӑш патне куҫ
Чи кивви
Чи ҫӗнни
- Веҫех пӑрахӑҫла
+ Пурне те пӑрахӑҫла
Пӑрахӑҫла
Ҫаклат
Сӳнтер
- Тиенисен палли
- Ят-йыш
+ Тийене сыпӑксем шучӗ
+ Йат-йыш
Кӑтарт
Кӑтарту тытӑмӗ
WebView-ра уҫ
@@ -91,18 +91,17 @@
Пуҫла
Катерт
Ҫӗнӗрен
- Вӑхӑтлӑха чар
- Чар
+ Чар
Сыпӑксем пӑх
- Хуплашкине улӑштар
+ Хуплашка улӑштар
Пухмӑша хуш
Пухмӑш ячӗне улӑштар
Пухмӑшсене тӳрлет
Пухмӑша хуш
Хуш
- Унчченхине вуланӑ пек паллӑ ту
- Тухма каялла тепӗр хут пус
- Тӳрлет
+ Умӗнхине вуланӑ пек паллӑ ту
+ Тухма тепӗр хут пус
+ Улӑштар
Веҫех сӳнтер
Веҫех ҫут
Вулавӑша ҫӗнет
@@ -118,7 +117,7 @@
Юлашки сыпӑкпа
Юлашки вуланипе
Сыпӑксен шучӗпе
- Алфавитпа
+ Сас паллисен йӗркипе
Алана катерт
Вуламан
Карт
@@ -154,27 +153,27 @@
Меллӗ сетке
Ҫӑтӑ сетке
Тепӗр майлӑ суйла
- Алӑ вӗҫҫӗн
- Ретри япаласем
+ Сӳнтернӗ
+ Серилӗхсен шучӗ
Урлӑ
Тӑрӑх
- Тепӗр хушӑмсем ҫине куҫнӑ чух ку хушӑмӑн мӗн пуррине пытар тата скриншот тума чар
- Систерӳсен мӗн ҫырнине пытар
+ Ыкран сыхлавӗ тепӗр апсем ҫине куҫнӑ чух ку апӑн мӗн пуррине пытарать тата ыкрана сӑн ҫапма чарать
+ Систерӳсенче мӗн ҫырнине пытарни
Нихӑҫан
- Яланах
+ Йаланах
Ҫӗнӗрен йӗркеле
- Тӑвӑмсен кӗнекине уҫ
+ Тӑвӑм-пулӑм кӗнекине уҫ
Тасат
Уйӑр
- Тӗксӗм тема
- Сӳстемри пекех
+ Тӗксӗм темӑ
+ Системри пекех
Вулӑш
- Сӑнану
- Тиени…
+ Йӗрлев
+ Тийени…
Тавӑр
Салт
Куҫар
- Маллали сыпӑк
+ Хыҫҫӑнхи сыпӑк
Умӗнхи сыпӑк
Сӑнану
Чи лайӑх пӗрлӗхшӗн WebView-а ҫӗнет
@@ -186,8 +185,8 @@
Ҫӗнетӳсем кӗтеҫҫӗ
Вулавӑша ҫӗнетнӗ чухне ҫӗнӗ хуплашка тата вак-тӗвек пуррине тӗрӗслени
Хӑй-халлӗн метта пӗлӗмсене ҫӗнетмелле
- Тухакан манкка ҫеҫ ҫӗнетмелле
- Хушӑм ҫинчен
+ Серилӗх вӗҫленнӗ
+ Ап ҫинчен
Шанчӑклӑ мар
Официаллӑ мар
Шанчӑклӑ
@@ -208,10 +207,10 @@
Вулав тытӑма кӑтарт
Тулли экран
Шанчӑклӑ мар хушма
- Кӗтмелли тытӑмра ҫаклатса лартмалла
+ Ним туман чух ҫаклатни
10%
%1$s-мӗш сыпӑк
- Ҫаклатӑва уҫма ыйт
+ Ҫаклатӑва уҫма пӳрне йӗрӗ ыйтни
25%
20%
15%
@@ -385,7 +384,6 @@
Пӗр тупсӑм та тупӑнман
Урӑх тупсӑмсем ҫук
Кантӑксем
- Паллисем
Тӗрӗс мар янтӑв файлӗ
Вырӑнти
Пухмӑша ҫӗнетни
@@ -440,7 +438,7 @@
Янтӑв ту
v%1$s верссиччен ҫӗнетнӗ
Мӗн ҫӗнни
- Тема
+ Темӑ
Хушнӑ вӑхӑтпа
\"%1$s\" пур ҫӗрте шыра
Вулав тытӑмӗ
@@ -476,10 +474,10 @@
Маллали сыпӑк
Умӗнхи эл
Ҫӑл куҫ куҫарассипе пулӑшу
- NSFW (18+) шалаш
+ NSFW (18+) ҫӑл куҫӗсем
Файлсене суйламалли хушӑм тупӑнман
Тархасшӑн MAL-а ҫӗнӗрен кӗр
- Ҫӑл куҫсен ят-йышӗнче кӑтарт
+ Ҫӑл куҫсен тата хушмасен йат-йышӗнче кӑтартни
Вулама вӗҫленӗ вӑхӑчӗ
Вулама пуҫланӑ вӑхӑчӗ
Хӗрри
@@ -488,10 +486,8 @@
Пӗчӗкленнипе
Пысӑкланнипе
Сыпӑк шучӗпе
- Тиесе илни вӑхӑчӗпе
- Сӑнавланакан
- Тухсан кайни ҫинчен логсем
- Тухса кайни ҫинчен логсем упраннӑ
+ Тийесе илни вӑхӑчӗпе
+ Йӗрленет
Файлсенчи йӑнӑшсен логсене хатерлевҫӗсем патне яма упрать
Тухса кайни инчен логсене тиесе яни
Куҫӑм палли
@@ -499,7 +495,7 @@
Ик эллӗ уйарни вулав майлӑ мар пулсан
Ик эллӗ уйарнине ҫавӑр
Ик эллӗ уйӑрни
- Япаласен шутне кӑтарт
+ Серилӗхсен шутне кӑтарт
Сылтӑм
Сулахай
Малалли
@@ -509,7 +505,7 @@
Кӑлар: %s
Ҫут: %s
Ҫук
- Илни вӑхӑт
+ Йулашки сыпӑк илнипе
Ҫак Android версси урӑх
Пайлашу аса ӑтавланаймарӗ
Тӑрӑх
@@ -520,39 +516,39 @@
Тӑвӑмсем
Пусма вырӑнсене вулӑш уҫӑ чухне кӑтартмалла
Йӑнӑшсене кӑтарт
- Ҫак серилӗх валли веҫ пӑрахӑҫла
- Хӑй-хальлӗн тийесе илни, малтан тийени
+ Ҫак серилӗх валли пурне те пӑрахӑҫла
+ Хӑй-хальлӗн тийесе илни, малтанах тийени
Юлнӑ сыпӑксем
Пурӗ ҫырав
- Веҫех катерт
+ Пурне те катерт
«%s» пухмӑша катертесшӗнех-и\?
- Пухмӑша катерт
+ Пухмӑш катерт
Асӑрхаттару
- Ятсӑр сетке
- Серие пуҫламӑша куҫар
- Улшӑнӑва ҫирӗплетме аутентификацилен
+ Йатсӑр сетке
+ Серилӗхе пуҫламӑша куҫар
+ Улшӑнӑва ҫирӗплетме есӗлӗхе ҫирӗплет
Пуҫланӑ
Кӑтартусем тата ыйту-хурав
Чӗлхе
Шыра…
Хуп
- Тиеве халь пуҫла
- InternalError: Хушма пӗлӗме пӑхма тӑвӑмсен кӗнекине тӗрӗсле
+ Тийеве халь пуҫла
+ InternalError: Хушма пӗлӗме пӑхма тӑвӑм-пулӑм кӗнекине пӑх
Кӑтартӑнни
Ҫутнӑ
Сӳнтернӗ
- Тема, кун тата вӑхӑт хармачӗ
+ Темӑ, кун тата вӑхӑт хармачӗ
Пухмӑшсем, пӗтӗмӗшле ҫӗнетӳ
Вулав тытӑмӗ, кӑтартӑнни, куҫӑм
- Хушӑм чӗлхи, систерӳсем
+ Ап чӗлхи, систерӳсем
Тийев черечӗ
Ҫӗр ҫырли тайккирийӗ
Ҫур ҫӗр ӗнтрӗкӗ
Пӗр йенлӗ ӳсӗм килӗштерӗвӗ, анлӑлатнӑ килӗштерӳ
Wi-Fi урлӑ ҫеҫ
«Малалла вула» пускӑч
- Хушӑма ҫаклатни, ыкран хӳтӗлевӗ
- Петтерей тулли чухне
+ Апа ҫаклатни, ыкран хӳтӗлевӗ
+ Петтерей тулли чух
Чарусем: %s
Вуламан сыпӑк(сем) пур
Пуҫланӑ
@@ -562,7 +558,7 @@
Вӑхӑт паллисем
Чухлавлӑ вӑхӑт палли
Вӑрӑм (кӗске+, n кун кайалла)
- Хушӑм темми
+ Ап темми
Анчахрах
Пайан
Лавантӑ
@@ -575,13 +571,19 @@
- Ӗнер
- %1$d кун кайалла
- Чаравсӑр тетелте ҫеҫ
+ Чараксӑр тетел урлӑ ҫеҫ
Серилӗхе пуҫламан
- Таккӑ
+ Такку
Хуп-хура темӑ
- Хушӑм чӗлхи
+ Ап чӗлхи
Халь мар
Кӗске (пайан, ӗнер)
Кашни 3 кун
Симӗс кӑвак
+ Куҫӑмлӑ
+ Шутлавсем
+ Пайлашу асне ӑт
+ Хӑй тӗллӗн тата хӑй-хальлӗн йантӑлав
+ Пухмӑш ҫӗнет
+ Йӑнӑш кӗнекине тийесе йани, петтерей лайӑхлатни
\ No newline at end of file
diff --git a/i18n/src/main/res/values-da/strings.xml b/i18n/src/main/res/values-da/strings.xml
index 8e4b10fa79..cb9217e445 100644
--- a/i18n/src/main/res/values-da/strings.xml
+++ b/i18n/src/main/res/values-da/strings.xml
@@ -79,7 +79,6 @@
Kompakt grid
Liste
Downloaded kapitler
- Stop
Sporet
Vælg kategorier
Vis kategori tabs
diff --git a/i18n/src/main/res/values-de/strings-aniyomi.xml b/i18n/src/main/res/values-de/strings-aniyomi.xml
index 1e797bed64..05bd8dc63f 100644
--- a/i18n/src/main/res/values-de/strings-aniyomi.xml
+++ b/i18n/src/main/res/values-de/strings-aniyomi.xml
@@ -47,7 +47,7 @@
Heruntergeladene Folgen
Lokale Quelle
Lokale Animequelle
- Fortsetzen-Knopf anzeigen
+ Weiterlesen-Button
Nach Folgennummer
Externen Downloader benutzen
Internen Downloader benutzen
diff --git a/i18n/src/main/res/values-de/strings.xml b/i18n/src/main/res/values-de/strings.xml
index 949c332afb..2cee83b746 100644
--- a/i18n/src/main/res/values-de/strings.xml
+++ b/i18n/src/main/res/values-de/strings.xml
@@ -34,7 +34,6 @@
Kategorie umbenennen
Kategorien festlegen
Cover bearbeiten
- Stopp
Pause
Vorheriges Kapitel
Nächstes Kapitel
@@ -267,7 +266,7 @@
32-Bit-Farben
Gelesene Kapitel überspringen
Bei langem Antippen anzeigen
- Überlagerung
+ Overlay
Multiplizieren
Bildschirm
Brennen / Verdunkeln
@@ -415,7 +414,6 @@
Migrieren
Komfortable Kacheln
Registerkarten
- Abzeichen
Kategorienreiter anzeigen
Keine Seiten gefunden
Alles deaktivieren
@@ -484,12 +482,10 @@
Tippzonen
Kindle-Stil
L-förmig
- Absturzprotokolle
Enddatum
Startdatum
- Absturzprotokolle gespeichert
Speichert Fehlerprotokolle in einer Datei, die dann mit den Entwicklern geteilt wird
- Absturzprotokolle ausgeben
+ Absturzprotokolle teilen
Absteigend
Aufsteigend
Nach Kapitelnummer
@@ -646,7 +642,6 @@
Keine installierte Quelle gefunden
Keine Quelle gefunden
Ungelesenenanzahl
- Hohe Bilder teilen
Verbessert die Leserleistung
Seite %d während dem Aufteilen nicht gefunden
Dateipfad der Seite %d konnte nicht gefunden werden
@@ -654,7 +649,6 @@
Lesereinstellungen für jede Serie zurücksetzen
Alle Lesereinstellungen zurückgesetzt
Lesereinstellungen konnten nicht zurückgesetzt werden
- Heruntergeladenes Bild konnte nicht aufgeteilt werden
Tja, das ist jetzt etwas peinlich
Version
Sprache
@@ -710,7 +704,7 @@
Ein unerwarteter Fehler ist aufgetreten
Absturzprotokolle ausgeben, Akkuverbrauch-Optimierung
Manuelle und automatische Datensicherungen
- %s ist auf einen unerwarteten Fehler gestoßen. Wir empfehlen dir, einen Screenshot von dieser Nachricht zu machen, die Absturzprotokolle auszugeben und sie dann in unserem Support-Kanal auf Discord zu teilen.
+ %s ist auf einen unerwarteten Fehler gestoßen. Wir empfehlen dir, die Absturzprotokolle in unserem Support-Kanal auf Discord zu teilen.
App-Sperre, sicherer Bildschirm
Unbekannter Titel
Ungültiger Speicherort: %s
@@ -736,4 +730,13 @@
In die Zwischenablage kopieren
Kategorie aktualisieren
+ Hohe Bilder teilen
+ Overlay
+ Breite Seiten drehen, damit sie passen
+
+ - %1$s Kapitel fehlt
+ - %1$s Kapitel fehlen
+
+ Es könnten Kapitel fehlen
+ Ausrichtung gedrehter breiter Seiten spiegeln
\ No newline at end of file
diff --git a/i18n/src/main/res/values-el/strings.xml b/i18n/src/main/res/values-el/strings.xml
index a9901b4ba7..94039bcbfd 100644
--- a/i18n/src/main/res/values-el/strings.xml
+++ b/i18n/src/main/res/values-el/strings.xml
@@ -40,7 +40,6 @@
Μετονομασία κατηγορίας
Ορισμός κατηγοριών
Επεξεργασία εξώφυλλου
- Σταμάτημα
Παύση
Προηγούμενο κεφάλαιο
Επόμενο κεφάλαιο
@@ -415,7 +414,6 @@
Μεταφορά
Άνετο πλέγμα
Καρτέλες
- Σήματα
Εμφάνιση καρτελών κατηγοριών
Δεν βρέθηκαν σελίδες
Απενεργοποίηση όλων
@@ -484,12 +482,10 @@
Άκρη
Σαν Kindle
Σχήματος L
- Αρχεία καταγραφής σφαλμάτων
Ημερομηνία λήξης
Ημερομηνία έναρξης
- Τα αρχεία καταγραφής σφαλμάτων αποθηκεύτηκαν
Αποθηκεύει αρχεία καταγραφής σφαλμάτων σε ένα αρχείο για κοινή χρήση με τους προγραμματιστές
- Άδειασμα αρχείων καταγραφής σφαλμάτων
+ Κοινή χρήση αρχείων καταγραφής σφαλμάτων
Φθίνουσα
Αύξουσα
Κατά αριθμό κεφαλαίου
@@ -646,7 +642,6 @@
Δε βρέθηκε εγκατεστημένη πηγή
Αριθμός μη αναγνωσμένων
Δε βρέθηκε πηγή
- Διαχωρισμός ψηλών εικόνων
Βελτιώνει την απόδοση του αναγνώστη
Η σελίδα %d δε βρέθηκε κατά τη διάσπαση
Δεν ήταν δυνατή η εύρεση της διαδρομής αρχείου της σελίδας %d
@@ -654,7 +649,6 @@
Επαναφορά ρυθμίσεων προγράμματος ανάγνωσης ανά σειρά
Δεν ήταν δυνατή η επαναφορά των ρυθμίσεων του προγράμματος ανάγνωσης
Επαναφορά όλων των ρυθμίσεων προγράμματος ανάγνωσης
- Δεν ήταν δυνατή η διαίρεση της εικόνας που έχει ληφθεί
Λοιπόν, αυτό είναι άβολο
Έκδοση
Γλώσσα
@@ -711,7 +705,7 @@
Επανεκκίνηση της εφαρμογής
Αυτόματη λήψη, λήψη εκ των προτέρων
Αρχεία καταγραφής σφαλμάτων, βελτιστοποιήσεις μπαταρίας
- Το %s αντιμετώπισε ένα μη αναμενόμενο σφάλμα. Σας προτείνουμε να κάνετε στιγμιότυπο οθόνης αυτού του μηνύματος, να αποθηκεύσετε τα αρχεία καταγραφής σφαλμάτων και, στη συνέχεια να το μοιραστείτε στο κανάλι υποστήριξης μας στο Discord.
+ Το %s αντιμετώπισε ένα απροσδόκητο σφάλμα. Σας προτείνουμε να μοιραστείτε τα αρχεία καταγραφής σφαλμάτων στο κανάλι υποστήριξης μας στο Discord.
Άγνωστος τίτλος
Μη έγκυρη τοποθεσία: %s
Μη έγκυρη συμβολοσειρά πράκτορα χρήστη
@@ -736,4 +730,13 @@
Απόκρυψη καταχωρήσεων που βρίσκονται ήδη στη βιβλιοθήκη
Αντιγραφή στο πρόχειρο
Ενημέρωση κατηγορίας
+ Διαχωρισμός ψηλών εικόνων
+ Επικάλυψη
+
+ - Λείπει %1$s κεφάλαιο
+ - Λείπουν %1$s κεφάλαια
+
+ Μπορεί να λείπουν κεφάλαια
+ Περιστροφή πλατιών σελίδων για να χωρέσουν
+ Αναστροφή του προσανατολισμού των πλατιών σελίδων που έχουν περιστραφεί
\ No newline at end of file
diff --git a/i18n/src/main/res/values-eo/strings.xml b/i18n/src/main/res/values-eo/strings.xml
index 27dd5436a0..780d855c69 100644
--- a/i18n/src/main/res/values-eo/strings.xml
+++ b/i18n/src/main/res/values-eo/strings.xml
@@ -18,7 +18,6 @@
Uzantnomo
Pasvorto
Retpoŝta adreso
- Halti
Vidi ĉapitrojn
Alinomi kategorion
Redakti kategoriojn
@@ -327,7 +326,6 @@
Neniu rezulto trovita
Ne pli rezultoj
Langetoj
- Ŝildoj
Nekonata eraro
Vi nun estas elsalutita
Elsaluti
diff --git a/i18n/src/main/res/values-es/strings.xml b/i18n/src/main/res/values-es/strings.xml
index aaa0904b2f..f04ab51bed 100644
--- a/i18n/src/main/res/values-es/strings.xml
+++ b/i18n/src/main/res/values-es/strings.xml
@@ -27,7 +27,6 @@
Renombrar categoría
Establecer categorías
Editar la portada
- Detener
Pausar
Capítulo anterior
Capítulo siguiente
@@ -328,7 +327,7 @@
Según ajustes del sistema
Gestionar notificaciones
Seguridad y privacidad
- Desbloqueo obligatorio
+ Requiere desbloqueo
Bloquear por inactividad
Siempre
Nunca
@@ -452,7 +451,6 @@
Cuadrícula amplia
Migrar
Pestañas
- Insignias
Mostrar pestañas de categorías
No parece haber ninguna página
Deshabilitar todo
@@ -497,9 +495,9 @@
18+
Esto no evita que las extensiones extraoficiales o que estén mal clasificadas muestren contenido para mayores de 18 años en la aplicación.
- - Se salta el capítulo %d, o bien falta en la fuente o se ha filtrado
- - Saltándose %d capítulos, o las fuentes que faltan o se han filtrado
- - Saltándose %d capítulos, o las fuentes que faltan o se han filtrado
+ - Se omite %d capítulo, o bien falta en la fuente o ha sido filtrado
+ - Se omiten %d capítulos, o bien faltan en la fuente o han sido filtrados
+ - Se omiten %d capítulos, o bien faltan en la fuente o han sido filtrados
No hay capítulos
Se han actualizado los ajustes predeterminados de capítulo
@@ -521,10 +519,8 @@
No se ha encontrado ninguna aplicación con la que elegir archivos
Vuelve a iniciar sesión en MAL
Estilo Kindle
- Registros de fallos
Fecha de finalización
Fecha de inicio
- Registros de errores guardados
Guarda los registros de errores en un archivo para compartirlos con los desarrolladores
Volcar registros de fallos
Zonas de toque
@@ -687,7 +683,6 @@
Todavía no se ha instalado ninguna fuente
No se ha encontrado ninguna fuente
Capítulos restantes
- Dividir imágenes demasiado altas
Mejora el rendimiento del lector dividiendo páginas descargadas mucho más altas que anchas
No se ha encontrado la página %d al dividir
La ruta al archivo de la página %d no se encuentra
@@ -695,7 +690,6 @@
Se han restablecido los ajustes del visor
Restablecer los ajustes del lector en cada serie
No se pudieron restablecer los ajustes del visor
- No se pudo dividir la imagen descargada
Houston, tenemos un problema
Versión
Idioma
@@ -744,7 +738,7 @@
Sincroniza tu progreso de lectura; unidireccional o mejorada
Descargas automáticas y por adelantado
Categorías y actualizaciones generales
- %s se ha cerrado por un problema inesperado. Te sugerimos que hagas una captura de pantalla de lo que ves, vuelques todos registros de depuración y que nos los envíes a nuestro canal de apoyo en Discord, en inglés.
+ %s se ha cerrado por un problema inesperado. Te sugerimos que compartas todos tus registros de depuración, enviándolos a nuestro canal de apoyo en Discord, en inglés.
Modos de lectura, apariencia y navegación
Idioma de la interfaz y notificaciones
Temas de colores y formatos de fecha
@@ -779,4 +773,14 @@
Saltar elementos que ya estén en la biblioteca
Copiar al portapapeles
Actualizar categoría
+ Dividir las imágenes altas
+ Superposición
+
+ - Falta %1$s capítulo
+ - "Faltan %1$s capítulos"
+ - Faltan %1$s capítulos
+
+ Girar las páginas anchas para adaptarlas a la pantalla
+ Puede que le falte algún capítulo
+ Girar las páginas anchas en la dirección opuesta
\ No newline at end of file
diff --git a/i18n/src/main/res/values-eu/strings.xml b/i18n/src/main/res/values-eu/strings.xml
index 61e4d680b2..441fda15a4 100644
--- a/i18n/src/main/res/values-eu/strings.xml
+++ b/i18n/src/main/res/values-eu/strings.xml
@@ -63,7 +63,6 @@
- %1$s-n egin da %2$s errorerekin
Cookieak garbitu dira
- Gorde dira erroreen erregistroak
Alde batera utzia
Egoera
Fidagarria ez den luzapena
@@ -198,7 +197,6 @@
Desinstalatu
Erakutsi orrialdearen zenbakia
Freskatu jarraipena
- Bereizgarriak
Liburutegian
Gehiago
%1$s Kapitulua
@@ -385,7 +383,6 @@
Kategoriak ezarri
Editatu azala
Ikusi kapituluak
- Gelditu
Pausatu
Aurreko kapitulua
Hurrengo kapitulua
@@ -444,7 +441,6 @@
Ez dago azkenaldiko eguneraketarik
Zure liburutegia hutsik dago
Ez daukazu kategoriarik. Sakatu gehiketa botoia zure liburutegia antolatzeko bat sortzeko.
- Erroreen erregistroak
Aurreko orrialdea
Piztu
Berriena
diff --git a/i18n/src/main/res/values-fa/strings.xml b/i18n/src/main/res/values-fa/strings.xml
index f178700f5c..fe79a3dd5e 100644
--- a/i18n/src/main/res/values-fa/strings.xml
+++ b/i18n/src/main/res/values-fa/strings.xml
@@ -126,7 +126,6 @@
هیچ نتیجه ای یافت نشد
نتیجه بیشتری یافت نشد
تب ها
- تعداد
حداکثر تعداد نسخههای پشتیبان
زمان پشتیبان گیری
پشتیبان گیری خودکار
@@ -332,7 +331,6 @@
قسمت بعد
قسمت قبل
مکث
- متوقف کردن
مشاهده قسمت ها
محلی
درحال به روز رسانی دسته بندی ها
@@ -594,7 +592,6 @@
شامل: %s
بدون: %s
ذخیره به عنوان فایل آرشیو CBZ
- نصف کردن خودکار عکس های بلند
انتشار به پایان رسید
قالب فصل نامعتبر است
لغو شد
@@ -623,4 +620,6 @@
پشتیبانی/بازگردانی ممکن است کار نکند اگر بهینهسازی MIUI غیر فعال باشد
در دسترس است اما افزونه منبع نصب نشده است: %s
شما باید از پشتیبانی ها در جا های دیگر هم کپی داشته باشید.
+ بروزرسانی دسته بندی
+ کپی کردن به کلیپبرد
\ No newline at end of file
diff --git a/i18n/src/main/res/values-fi/strings.xml b/i18n/src/main/res/values-fi/strings.xml
index 92315e99fe..90ccded5d9 100644
--- a/i18n/src/main/res/values-fi/strings.xml
+++ b/i18n/src/main/res/values-fi/strings.xml
@@ -85,7 +85,6 @@
Muokkaa kategorioita
Nimeä kategoria uudelleen
Yritä uudelleen
- Lopeta
Pysäytä
Aiempi luku
Seuraava luku
@@ -415,7 +414,6 @@
Siirrä
Mukava ruudukko
Välilehdet
- Merkit
Näytä kategorioiden välilehdet
Sivuja ei löytynyt
Poista kaikki käytöstä
@@ -485,10 +483,8 @@
Kindle tyylinen
L-muotoinen
Poista kaatumislokit
- Kaatumislokit
Lopetuspäivämäärä
Aloituspäivämäärä
- Kaatumislokit tallennettu
Tallentaa virhelokit tiedostoon jaettavaksi kehittäjien kanssa
Laskeva
Nouseva
@@ -617,7 +613,6 @@
Korkea
Etukäteen lataus
Automaattinen lataus luetessa
- Jaa korkeat kuvat
Parantaa lukijan suorituskykyä
Haluatko poistaa kategorian \"%s\"\?
Toimii vain kirjastossa oleville sarjoille joissa nykyinen ja seuraava luku on jo ladattu
@@ -674,7 +669,6 @@
Ohitettu, koska sarja ei vaadi päivityksiä
Olet poistamassa \"%s\" kirjastostasi
Uusi versio on saatavilla viralliselta alustalta. Napauta saadaksesi tietää, miten siirtyä pois epävirallisesta F-Droid versiosta.
- Ladattua kuvaa ei voitu jakaa
Kirjoittaa yksityiskohtaiset lokit järjestelmälokiin (heikentää sovelluksen suorituskykyä)
Ohitettu
Varmuuskopioita kannattaa säilyttää myös muissa paikoissa.
diff --git a/i18n/src/main/res/values-fil/strings.xml b/i18n/src/main/res/values-fil/strings.xml
index 2535ca4e3f..a2527b8554 100644
--- a/i18n/src/main/res/values-fil/strings.xml
+++ b/i18n/src/main/res/values-fil/strings.xml
@@ -38,7 +38,6 @@
Susunod na kabanata
Nakaraang kabanata
Ihinto
- Itigil
Tingnan ang mga kabanata
Palitan ang cover
Ikategorya
@@ -76,7 +75,7 @@
Nakaraan
Tina-track
Mga Kabanata
- Manga
+ Mga entry ng aklatan
Wala ka pang kategorya. Pindutin ang plus button para makagawa ka ng isa para maayos mo ang Aklatan mo.
Bakante ang Aklatan mo
Walang binasa kamakailan
@@ -101,7 +100,7 @@
Hindi
Palagi
Isara kung nakatambay
- I-lock gamit ang biometrics
+ Nangangailangang i-unlock
Pamahalaan ang mga abiso
Seguridad at privacy
Kumpirmahing aalis
@@ -182,7 +181,7 @@
Pinili kong filter ng kulay
Pinili kong liwanag
Gupitin ang gilid
- Nababawasan ang pagkakaroon ng linya, pero dagdag-pasanin ito sa app
+ Binabawasan ang banding, ngunit maaaring makaapekto ito sa performance
Totoong kulay (32-bit)
Mabilis na ipakita ang kasalukuyang ginagamit kapag nakabukas ang reader
Ipakita ang paraan ng pagbasa
@@ -388,7 +387,6 @@
Walang nakitang resulta
Wala na\'ng resulta
Mga Tab
- Mga Panukoy
Lokal
Ina-update ang kategorya
Di matukoy na error
@@ -458,8 +456,8 @@
Posibleng may NSFW (18+) content ang mga source mula sa extension na ito
18+
- - Nilaktawan ang %d na kabanata, maaaring ito ay wala sa pinagmula o na-filter ang mga ito
- - Nilaktawan ang %d na (mga) kabanata, maaaring wala ang pinagmulan nila o na-filter ang mga ito
+ - Nilaktawan ang %d na kabanata, maaaring ito ay wala sa source o na-filter ang mga ito
+ - Nilaktawan ang %d na (mga) kabanata, maaaring wala sa source o na-filter ang mga
Walang nakitang kabanata
Gusto mo bang i-save at ipagpaubaya ang pagsasaayos na ito\?
@@ -467,7 +465,7 @@
Ini-update na ang Ipagpaubaya
%1$s: %2$s, pahina %3$d
Pagsasaayos ng Kabanata
- Mga naka-download na kabanata
+ Bilang ng na-download
Maghanap
Nakatago
Linisin ang nakaraan
@@ -480,11 +478,9 @@
Ipakita sa mga source at extension
Mga source na NSFW (18+)
Mag-login muli po sa MAL
- Crash log
Natapos basahin
Sinimulang basahin
- Itambak ang mga crash log
- Nai-save na ang crash log
+ Magbahagi ng mga crash log
Sine-save ang mga error log sa isang file para maibahagi sa mga developer
Mga tap zone
Sulok
@@ -569,7 +565,7 @@
Mababa
Mataas
Pinakamataas
- Sensitivity ng pagtago sa menu pagka-scroll
+ Sensitivity para sa pagtatago ng menu sa scroll
Baligtad
Mahaba (Maiksi+, n (na) araw ang nakalipas)
Maiksi (Ngayon, Kahapon)
@@ -620,10 +616,10 @@
5%
Nasimulan
Pabalat lang
- Nilaktawan dahil tapos na ang serye
+ Nilaktawan dahil kumpleto na ang serye
Hindi pa nasisimulan
- Nilaktawan dahil may di pa nababasang kabanata
- Nilaktawan dahil wala pang nababasang kabanata
+ Nilaktawan dahil may di pa nabasang (mga) kabanata
+ Nilaktawan dahil wala pang nabasang (mga) kabanata
Mag-zoom sa pahigang larawan
I-pan ang malapad na larawan kapag nag-tap
Matuto pa
@@ -646,11 +642,9 @@
Walang nakitang naka-install na source
Walang nakitang source
Dami ng di pa nabasa
- Hatiin ang mga matatangkad na larawan
Pinapahusay ang performance ng reader
Hindi nakita ang pahina %d habang naghahati
Di makita ang file path ng pahina %d
- Di mahati ang na-download na larawan
Paano ba \'to
Di ma-reset ang pagsasaayos sa reader
Na-reset na ang lahat ng pagsasaayos sa reader
@@ -685,7 +679,7 @@
Daluyong
I-download agad
Kusang mag-download habang nagbabasa
- Gagana lang sa mga entry sa aklatan at kung naka-download na ang kasalukuyang kabanata pati ang susunod
+ Gumagana lamang sa mga entry sa aklatan at kung ang kasalukuyang kabanata at ang susunod na kabanata ay na-download na
Sigurado ka ba\?
- Susunod ang hindi pa nababasa na chapter
@@ -693,8 +687,8 @@
Marami
Tatanggalin mo na ang \"%s\" mula sa iyong aklatan
- Huling update ng Library: %s
- Hatiin ang mga matatangkad na larawan (BETA)
+ Huling update sa aklatan: %s
+ Hatiin ang mga matatangkad na (mga) larawan (BETA)
Sikat
Hindi binigay ang mga permiso sa storage
Nilaktawan dahil hindi kailangan ang pag-update sa serye
@@ -708,7 +702,7 @@
Itambak ang mga crash log, pag-o-optimisa sa baterya
Mga kategorya, panlahatang update
Mga source, extension, panlahatang paghanap
- Nagkaroon ang %s ng di-inaasahang error. Paki-screenshot ang error, itambak ang mga crash log, at ibahagi sa support channel sa Discord.
+ Nagkaroon ng hindi inaasahang error ang %s. Iminumungkahi naming ibahagi mo ang mga crash log sa aming support channel sa Discord.
Ay!
Wika ng App, mga abiso
Buksan muli ang app
@@ -742,4 +736,13 @@
- Susunod na %d (mga) kabanata
I-update ang kategorya
+
+ - Nawawalang %1$s na kabanata
+ - Nawawalang %1$s mga kabanata
+
+ Baka nawawala ang ilang kabanata
+ I-rotate ang malalawak na (mga) pahina para magkasya
+ I-flip ang oryentasyon ng mga pinaikot na malalawak na (mga) pahina
+ Nakapatong (Overlay)
+ Hatiin ang mga matatangkad na (mga) larawan
\ No newline at end of file
diff --git a/i18n/src/main/res/values-fr/strings-aniyomi.xml b/i18n/src/main/res/values-fr/strings-aniyomi.xml
index 3f4dd10b55..f1fb35b8d3 100644
--- a/i18n/src/main/res/values-fr/strings-aniyomi.xml
+++ b/i18n/src/main/res/values-fr/strings-aniyomi.xml
@@ -41,12 +41,12 @@
Épisode suivant
Mode d\'ajustement de l\'écran
Mode image incrustée
- Afficher l\'entrée
+ Afficher le titre
Afficher l\'animé
Épisodes téléchargés
Source locale
Animé local
- Afficher le bouton Continuer à regarder/lire
+ Bouton \"Reprendre\"
Par numéro d\'épisode
Utiliser un téléchargeur externe
Utiliser un téléchargeur interne
@@ -86,9 +86,9 @@
Préférence pour les lecteurs externes
%1$s - %2$s
%1$d% %
- Supprimer des chapitres
- Après avoir été marqué manuellement comme lu
- Après la lecture, supprimer automatiquement
+ Suppression des chapitres
+ Après avoir été marqué comme lu
+ Suppression automatique après lecture
Permettre la suppression des chapitres marqués d\'un marque-page
Catégories exclues
Catégories d\'animé exclues
@@ -147,7 +147,7 @@
Revisionnage
Ceci supprimera la date de visionnage de cet épisode. Êtes-vous sûr·e \?
Réinitialiser tous les épisodes de cet animé
- Réinitialiser tous les chapitres de cette entrée
+ Réinitialiser tous les chapitres de ce titre
%1$s : %2$s, %3$s
Progression : %1$s/%2$s
Progression : %1$s
diff --git a/i18n/src/main/res/values-fr/strings.xml b/i18n/src/main/res/values-fr/strings.xml
index b43bfd938a..63d55523be 100644
--- a/i18n/src/main/res/values-fr/strings.xml
+++ b/i18n/src/main/res/values-fr/strings.xml
@@ -33,7 +33,6 @@
Renommer la catégorie
Déplacer vers une catégorie
Changer l\'image de couverture
- Arrêter
Pause
Chapitre précédent
Chapitre suivant
@@ -116,7 +115,7 @@
Désactivé
Dernier chapitre lu
Avant-dernier chapitre lu
- Troisième chapitre avant le denier lu
+ Troisième chapitre avant le dernier lu
Quatrième chapitre avant le dernier lu
Télécharger les nouveaux chapitres
@@ -132,7 +131,7 @@
Version
Envoyer des rapports de plantage
- Aide à corriger les erreurs. Aucune donnée sensible ne sera envoyée
+ Aident à corriger les bugs. Aucune donnée sensible ne sera envoyée
Connexion à %1$s
Nom d\'utilisateur
@@ -186,7 +185,7 @@
Des nouveaux chapitres ont été trouvés
La mise à jour de la couverture a échoué
- Veuillez ajouter l\'entrée à votre bibliothèque avant de faire ceci
+ Veuillez ajouter ce titre à votre bibliothèque avant de faire cela
Sélectionner l\'image de couverture
Sélectionner fichier de sauvegarde
@@ -329,7 +328,7 @@
Par défaut du système
Notifications
Sécurité et confidentialité
- Imposer un déverrouillage à l\'ouverture
+ Nécessite un déverrouillage
Verrouiller en cas d\'inactivité
Toujours
Jamais
@@ -454,7 +453,6 @@
Migrer
Grille espacée
Onglets
- Badges
Afficher les onglets des catégories
Aucune page trouvée
Tout désactiver
@@ -522,13 +520,11 @@
Veuillez vous reconnecter à MAL
L’application de sélection de fichier est introuvable
Afficher dans les listes de sources et d\'extensions
- Registres d\'incident
Date de fin
Date de début
Style Kindle
- Registre d\'incident sauvegardé
- Archivage des registres d\'incident
- Enregistre les traces d\'incident dans un fichier pour les partager avec les développeurs
+ Partager les rapports de plantage
+ Enregistre les rapports de plantage dans un fichier pour les partager avec les développeurs
Zones tactiles
Bord
En forme de L
@@ -554,7 +550,7 @@
Afficher les zones tactiles (superposition)
Exclure : %s
Inclure : %s
- Aucun
+ Aucun(e)
Date de récupération du chapitre
Les entrées des catégories exclues ne seront pas mis à jour même si elles appartiennent aussi à des catégories incluses.
Téléchargement automatique
@@ -586,7 +582,7 @@
Couverture enregistrée
Couverture
Manuel de suivi
- Désactivé
+ Désactivé(e)
Activé
Rendre les réglages de tri et d\'affichage propres à chaque catégorie
Vous n\'avez pas encore de catégories.
@@ -607,10 +603,10 @@
Thème de l\'appli
Certains fabricants ont mis en place des restrictions supplémentaires sur les applications qui tuent les services d\'arrière-plan. Ce site Web contient plus d\'informations sur la manière de résoudre ce problème.
Activité en arrière-plan
- La plus basse
+ Minimale
Basse
Élevée
- La plus élevée
+ Maximale
Sensibilité pour masquer le menu lors du défilement
Inversé
Long (Court+, il y a n jours)
@@ -639,8 +635,8 @@
Installateur
Installation de l\'extension…
Total des entrées
- Journalisation détaillée
- Imprimer les journaux détaillés dans le journal du système (réduit les performances de l\'application)
+ Rapports détaillés
+ Inclut des rapports détaillés dans les traces systèmes (réduit les performances de l\'application)
Langue
Attention
Les mises à jour importantes nuisent aux sources et peuvent entraîner un ralentissement des mises à jour ainsi qu\'une augmentation de l\'utilisation de la batterie. Appuyez pour en savoir plus.
@@ -689,7 +685,6 @@
Aucune source installée trouvée
Nombre de non-lus
Aucune source trouvée
- Fractionner les images trop grandes
Améliore les performances du lecteur
Page %d introuvable lors du fractionnement
Impossible de trouver le chemin du fichier de la page %d
@@ -697,7 +692,6 @@
Réinitialise le mode de lecture et l\'orientation de toutes les séries
Paramètres du lecteur réinitialisés
Impossible de réinitialiser les paramètres du lecteur
- Impossible de diviser l\'image téléchargée
Eh bien, c\'est gênant
Version
Langue
@@ -716,7 +710,7 @@
Lavande
Effacer catégorie
Souhaitez-vous supprimer la catégorie « %s » \?
- ErreurInterne : Vérifiez votre journal de plante pour plus d\'information
+ ErreurInterne : Consultez vos rapports de plantage pour plus d\'informations
Réinitialiser la liste d\'agents utilisateurs
Liste d\'agents utilisateurs par défaut
Tout retirer
@@ -726,8 +720,8 @@
La liste d\'agents utilisateurs ne peut être vide
Une mise à jour est déjà en cours
Raz-de-marée
- Télécharger à l\'avance
- Téléchargement automatique pendant la lecture
+ Téléchargement anticipé
+ Téléchargement anticipé pendant la lecture
- Chapitre suivant non lu
- Les %d suivants non lus
@@ -748,14 +742,14 @@
Téléchargement automatique, téléchargement anticipé
Synchronisation unidirectionnelle de la progression, synchronisation améliorée
Sauvegardes manuelles et automatiques
- Dump crash logs, optimisations de la batterie
+ Rapports de plantage, optimisations de la batterie
Redémarrer l\'application
Langage d\'application, notifications
Sources, extensions, recherche globale
Catégories, mise à jour globale
Mode de lecture, affichage, navigation
Verrouillage des applications, écran sécurisé
- %s a rencontré une erreur inattendue. Nous vous suggérons de faire une capture d\'écran de ce message, d\'extraire les dumb crash logs et de les partager dans notre canal d\'assistance sur Discord.
+ %s a rencontré une erreur inattendue. Nous vous suggérons de nous partager les rapports de plantage dans notre salon d\'assistance sur Discord.
Emplacement invalide : %s
Chaîne d\'agent utilisateur invalide
Titre inconnu
@@ -763,9 +757,9 @@
File de téléchargement
Copié dans le presse-papier
Ignorer les chapitres en double
- Vous avez une entrée dans votre bibliothèque avec le même nom.
+ Un titre de votre bibliothèque porte le même nom.
\n
-\nVoulez-vous toujours continuer \?
+\nVoulez-vous vraiment continuer \?
%1$s erreur : %2$s
Disponible mais la source n\'est pas installée : %s
*obligatoire
@@ -778,4 +772,14 @@
Copier dans le presse-papier
Mettre à jour la catégorie
+ Diviser les grandes images
+ Superposition
+
+ - %1$s chapitre manquant
+ - %1$s chapitres manquants
+ - %1$s chapitres manquants
+
+ Il pourrait manquer des chapitres
+ Inverser l\'orientation des pages larges retournées
+ Tourner les pages larges pour qu\'elles rentrent
\ No newline at end of file
diff --git a/i18n/src/main/res/values-gl/strings.xml b/i18n/src/main/res/values-gl/strings.xml
index 9f3b054111..b3dabd43f7 100644
--- a/i18n/src/main/res/values-gl/strings.xml
+++ b/i18n/src/main/res/values-gl/strings.xml
@@ -120,7 +120,7 @@
Amosar
Isto non evita que as extensións non oficiais ou mal clasificadas mostren contido NSFW (+18) dentro da aplicación.
Esconde os contidos das notificacións
- Esconde os contidos da aplicación ao cambiar de aplicación e bloquea as capturas de pantalla
+ O modo discreto esconde os contidos da app ao cambiar de app e bloquea as capturas de pantalla
Modo discreto
- Despois de 1 minuto
@@ -135,7 +135,7 @@
Require reiniciar a aplicación para que surxa efecto
Amosar na lista de fontes e extensións
Fontes NSFW (+18)
- Seguridade
+ Seguridade e privacidade
Xestionar notificacións
Confirmar a saída
Formato da data
@@ -191,7 +191,6 @@
Capítulo seguinte
Capítulo anterior
Pausar
- Parar
Ver capítulos
Editar portada
Establecer categorías
@@ -221,7 +220,7 @@
Capítulos totais
Alfabeticamente
Quitar filtro
- Sen ler
+ Non lidos
Favoritos
Filtro
Menú
@@ -246,7 +245,7 @@
Completado
Título
Licenciado
- Sen ler
+ Non lidos
En curso
Descoñecido
Non se atoparon capítulos
@@ -355,10 +354,9 @@
Actualizar todo
A sincronización destos servizos é unidireccional. Configura o seguemento para os elementos individuais dende o seus botóns de seguemento.
Seguir
- Só incluír as fontes fixadas
+ Só buscar as fontes fixadas na búsqueda global
Hai %1$d elementos na base de datos que non están na biblioteca
Versión
- Outros
Mostrar brevemente ao abrir o lector
Por defecto
Estatísticas
@@ -415,7 +413,6 @@
Crear unha copia de seguridade
Non se puido facer a copia de seguridade
Actualizar as portadas da biblioteca
- Gardáronse os rexistros de erros
O arquivo da copia de seguridade non é válido
A copia de seguriade non contén ningún elemento da biblioteca.
DNS por HTTPS (DoH)
@@ -441,7 +438,6 @@
Fonte local
Quitar todo
Recentemente
- Distintivos
Lavanda
Inverter as zonas de toque
Dereita
@@ -467,7 +463,6 @@
Actualizar os servizos de seguemento ao actualizar a biblioteca
Incluír: %s
Non se puido baixar a listaxe de extensións
- Separar imaxes altas
Borrar a base de datos
Anterior
Só con Wi-Fi
@@ -515,7 +510,7 @@
Pechar
Tema, formatos de data e hora
Aparencia
- Mostrar o botón de seguir lendo
+ Botón de seguir lendo
Mostrar o número de elementos
Cancelar todo para esta serie
Por data de subida
@@ -687,7 +682,6 @@
Páxina: %1$d
Produciuse un erro ao gardar a imaxe
Actualizacións erradas: %1$d
- Rexistros de erros
Baixar
Actualizacións da aplicación
Preme para saber máis
@@ -711,7 +705,6 @@
%1$s: %2$s, páxina %3$d
Por data de subida
%dmin
- Non se puido separar a imaxe baixada
Engadir seguimento
Eliminar o historial
Eliminouse o historial
@@ -777,4 +770,20 @@
Nova versión dispoñible!
Erros
Procurar \"%1$s\" globalmente
+ Dividir as imaxes altas
+ Actualizar categoría
+ Ocultar elementos que xa estén na biblioteca
+ Copiar ao portapapeis
+
+ - O seguinte capítulo
+ - Os seguintes %d capítulos
+
+
+ - Falta %1$s capítulo
+ - Faltan %1$s capítulos
+
+ Pode que falte algún capítulo
+ Xirar as páxinas anchas para adaptalas á pantalla
+ Voltear a orientación das páxinas anchas xiradas
+ Superposición
\ No newline at end of file
diff --git a/i18n/src/main/res/values-he/strings.xml b/i18n/src/main/res/values-he/strings.xml
index 0a2b71f446..1175a32e85 100644
--- a/i18n/src/main/res/values-he/strings.xml
+++ b/i18n/src/main/res/values-he/strings.xml
@@ -205,7 +205,6 @@
פרק הבא
פרק קודם
הפסק
- עצור
הצג פרקים
ערוך את הכריכה
סדר קטגוריות
@@ -481,7 +480,6 @@
כלול רק מקורות נעוצים
מעקב
יש %1$d מנגה שנמצאות במסד הנתונים אבל לא בספרייה
- תגים
לשוניות
חפש את \"%1$s\" גלובלית
מצב לא ידוע
@@ -623,7 +621,6 @@
כמו ספר אלקטרוני
גרסה
כיסוי
- פיצול תמונות ארוכות אוטומטי
הגבלת גיל
שפה
פילטר צבע משתלב
diff --git a/i18n/src/main/res/values-hi/strings-aniyomi.xml b/i18n/src/main/res/values-hi/strings-aniyomi.xml
index 964b252936..97350ccb3c 100644
--- a/i18n/src/main/res/values-hi/strings-aniyomi.xml
+++ b/i18n/src/main/res/values-hi/strings-aniyomi.xml
@@ -24,18 +24,16 @@
ऐप बंद करते समय चैप्टर कैशे साफ़ करें
डेटाबेस साफ़ करें
मैंगा जो आपकी लाइब्रेरी में सहेजे नहीं हैं उनका इतिहास हटाएं
- क्या आपको यकीन है\? अध्यायों और गैर-पुस्तकालय मंगा की प्रगति को खो दिया जाएगा
अपडेट की स्थिति, स्कोर और अंतिम अध्याय ट्रैकिंग सेवाओं से पढ़ें
इतिहास पढ़ने से रोक देता है
- पुस्तकालय से मंगा
लाइब्रेरी में मंगा जोड़ें\?
त्रुटि
रोके गए
मेरी लाइब्रेरी में सभी मंगा पर भी लागू करें
- इस मंगा के लिए सभी अध्यायों को रीसेट करें
+ इस आइटम के लिए सभी अध्यायों को रीसेट करें
कम संग्रहण स्थान के कारण अध्याय डाउनलोड नहीं कर सके
चेतावनी: बड़े बल्क डाउनलोड के कारण स्रोत धीमे हो सकते हैं। और/या ताचियोमी को ब्लॉक कर सकते हैं। अधिक जानने के लिए यह टैप करें ।
- ताचियोमी के लिए वेबव्हिव आवश्यक है
+ ताचियोमी के लिए WebView आवश्यक है
डाउनलोड रोक दिया है
अध्याय अद्यतन
diff --git a/i18n/src/main/res/values-hi/strings.xml b/i18n/src/main/res/values-hi/strings.xml
index 18553a51d9..709f2b466f 100644
--- a/i18n/src/main/res/values-hi/strings.xml
+++ b/i18n/src/main/res/values-hi/strings.xml
@@ -6,9 +6,9 @@
पदचिह्न
इतिहास
सेटिंग्स
- संग्रह
+ पुस्तकालय
इतिहास
- नए अपडेट्स
+ नए अपडेट
बैकअप और पुनर्स्थापना
सेटिंग
फिल्टर
@@ -29,7 +29,7 @@
बुकमार्क अध्याय
अध्याय बुकमार्क हटाये
हटाये
- अद्यतन पुस्तकालय
+ पुस्तकालय अपडेट करें
संपादित करें
जोड़े
श्रेणियाँ जोड़े
@@ -37,7 +37,6 @@
श्रेणियाँ का पुन:नामकरण
श्रेणियां निर्धारित करें
कवर संपादित करें
- रोके
ठहराव
पिछला अध्याय
अगला अध्याय
@@ -148,6 +147,8 @@
साफ़ करने के दौरान त्रुटि हुई
कुकीज़ को साफ़ करें
कुकीज़ को साफ़ किया हुआ
+ उन आइटम का इतिहास हटाएं जो आपकी पुस्तकालय में सहेजी नहीं गई हैं
+ क्या आपको यकीन है\? आपके द्वारा पढ़े गए अध्याय और गैर-पुस्तकालय आइटम की प्रगति खो जाएगी
प्रविष्टियां हटाई गईं
रिफ्रेश ट्रैकिंग
संस्करण
@@ -204,7 +205,7 @@
अध्याय डाउनलोड नहीं कर सका। आप डाउनलोड अनुभाग में फिर से कोशिश कर सकते हैं
नए अध्याय पाए गए
कवर को अपडेट करने में विफल
- ऐसा करने से पहले कृपया अपनी लाइब्रेरी में मंगा को जोड़ें
+ ऐसा करने से पहले कृपया अपनी पुस्तकालय में आइटम जोड़ें
कवर छवि का चयन करें
बैकअप फ़ाइल का चयन करें
डाउनलोड
@@ -264,7 +265,7 @@
पेज लोड हो रहे है …
पृष्ठों को लोड करने में विफल है: %1$s
संवाद के लिए लंबी प्रेस
- वेब दृश्य में खोलें
+ WebView में खोलें
32 बिट रंग
पढ़े हुए अध्यायों को छोड़ें
रंग फिल्टर मिश्रण मोड
@@ -297,7 +298,7 @@
चालू करे
सिस्टम का पालन करें
सूचनाओं का प्रबंधन
- सुरक्षा
+ सुरक्षा और गोपनीयता
अनलॉक की आवश्यकता है
निष्क्रिय होने पर लॉक करें
हमेशा
@@ -332,8 +333,8 @@
ईमेल पता
हमेशा अध्याय संक्रमण दिखाएं
- - %d शीर्षक के लिए
- - %d शीर्षकों के लिए
+ - %d आइटम के लिए
+ - %d आइटम के लिए
मेन्यू
पुनःक्रमित
@@ -346,7 +347,7 @@
- %d एक्सटेंशन अपडेट उपलब्ध
एक्सटेंशन अपडेट
- वेबव्यू में वेबसाइट देखें
+ WebView में वेबसाइट देखें
अद्यतन पुस्तकालय
पठन
फ़िल्टर किए गए अध्यायों को छोड़ें
@@ -377,12 +378,12 @@
बैकअप फेल
पुनर्स्थापना रद्द की गई
पुनर्स्थापना पहले से ही प्रगति पर है
- बैकप पहले से ही प्रगति में है
+ बैकअप पहले से ही प्रगति में है
आखरी इस्त्तमाल किया गया
अद्यतन के लिए जाँच
स्थानीय स्रोत गाइड
%02d मिनट,%02d सेकंड
- अपने पुस्तकालय में सभी मंगा फ़िल्टर करे
+ आपकी पुस्तकालय में सभी आइटम को फ़िल्टर करता है
- %1$s बचा हुआ
- %1$s बचे हुए
@@ -401,20 +402,19 @@
- %1$s में %2$s त्रुटि के साथ किया गया
- %1$s में %2$s त्रुटियों के साथ किया गया
- ट्रैकिंग सेवाओं में अध्याय की प्रगति को अद्यतन करने के लिए एकतरफा सिंक। अपने ट्रैकिंग बटन से व्यक्तिगत मंगा प्रविष्टियों के लिए ट्रैकिंग सेट अप करें।
+ ट्रैकिंग सेवाओं में अध्याय की प्रगति को अद्यतन करने के लिए एकतरफा सिंक। अपने ट्रैकिंग बटन से व्यक्तिगत प्रविष्टियों के लिए ट्रैकिंग सेट अप करें।
रिफ्रेश पुस्तकालय मंगा कवर
यह एक्सटेंशन आधिकारिक ताचियोमी एक्सटेंशन सूची से नहीं है।
अनौपचारिक
अपलोड तिथि द्वारा
डेटा
अनुपलब्ध स्रोत:
- बैकअप में कोई मंगा नहीं होता है।
+ बैकअप में कोई पुस्तकालय आइटम नहीं हैं।
अमान्य बैकअप फ़ाइल
लाइब्रेरी को अपडेट करते समय नए कवर और विवरण की जांच करें
मेटाडेटा को स्वचालित रूप से ताज़ा करें
प्रवास
टैब
- बैज
श्रेणी टैब दिखाएं
आरामदायक ग्रिड
कोई पृष्ठ नहीं मिला
@@ -458,8 +458,8 @@
18+
यह अनौपचारिक या संभावित रूप से फ़्लैग किए गए एक्सटेंशन को ऐप के भीतर NSFW (18+) सामग्री के सामने आने से नहीं रोकता है।
- - %d अध्याय को छोड़कर, या तो स्रोत में यह अनुपलब्ध है या इसे फ़िल्टर कर दिया गया है
- - %d अध्यायों को छोड़कर, या तो स्रोत में वे नहीं हैं या उन्हें फ़िल्टर कर दिया गया है
+ - %d अध्याय को छोड़ा जा रहा है, या तो स्रोत में यह नहीं है या इसे फ़िल्टर कर दिया गया है
+ - %d अध्यायों को छोड़ा जा रहा है, या तो स्रोत उन्हें याद कर रहा है या उन्हें फ़िल्टर कर दिया गया है
अपडेट किए गए डिफ़ॉल्ट अध्याय सेटिंग्स
कोई अध्याय नहीं मिला
@@ -478,12 +478,10 @@
स्रोत माइग्रेशन गाइड
स्रोतों और एक्सटेंशन सूचियों में दिखाएं
NSFW (18+) स्रोत
- क्रैश लॉग
कोई फ़ाइल पिकर ऐप नहीं मिला
कृपया फिर से MAL पर लॉगिन करें
समाप्ति की तिथि
आरंभ करने की तिथि
- क्रैश लॉग सहेजे गए
डेवलपर्स के साथ साझा करने के लिए फ़ाइल में त्रुटि लॉग सहेजता है
डंप क्रैश लॉग
टैप ज़ोन
@@ -500,8 +498,8 @@
बैकअप फ़ाइल से डेटा बहाल किया जाएगा।
\n
\nआपको किसी भी लापता एक्सटेंशन को इंस्टॉल करना होगा और बाद में सेवाओं को ट्रैक करने के लिए लॉग इन करना होगा ताकि उनका उपयोग किया जा सके।
- यदि दोहरे पृष्ठ विभाजन की नियुक्ति पढ़ने की दिशा से मेल नहीं खाती है
- दोहरे पृष्ठ विभाजन प्लेसमेंट को पलटना
+ यदि स्प्लिट वाइड पेजों का प्लेसमेंट पढ़ने की दिशा से मेल नहीं खाता है
+ स्प्लिट पेज प्लेसमेंट को उल्टा करें
चौड़े पृष्ठ को दो भागों में बांट दे
अभी डाउनलोड करना शुरू करें
विवरण देखने के लिए टैप करें
@@ -557,7 +555,7 @@
शिज़ुकु नहीं चल रहा है
उल्टी
काला और सफेद
- पुस्तक के शीर्षक के अनुसार फोल्डर बनाता है
+ आइटम के शीर्षक अनुसार फोल्डर बनाता है
ट्रैकिंग गाइड
उन्नत सेवाएं
MIUI ऑप्टिमाइज़ेशन अक्षम होने पर बैकअप/पुनर्स्थापना ठीक से काम नहीं कर सकता है।
@@ -584,9 +582,9 @@
दिनांक
कवर पृष्ठ
पृष्ठों को अलग-अलग फ़ोल्डरों में सहेजें
- बहिष्कृत श्रेणियों की पुस्तकें डाउनलोड नहीं की जाएंगी, भले ही वे शामिल श्रेणियों में भी हों।
+ बहिष्कृत श्रेणियों की आइटम डाउनलोड नहीं की जाएंगी, भले ही वे शामिल श्रेणियों में भी हों।
सिस्टम लॉग में वर्बोज़ लॉग प्रिंट करें (ऐप प्रदर्शन को कम करता है)
- ऐसी सेवाएँ जो विशिष्ट स्रोतों के लिए उन्नत सुविधाएँ प्रदान करती हैं। आपकी पुस्तकालय में जोड़े जाने पर पुस्तकें स्वचालित रूप से ट्रैक की जाती हैं।
+ ऐसी सेवाएँ जो विशिष्ट स्रोतों के लिए उन्नत सुविधाएँ प्रदान करती हैं। आपकी पुस्तकालय में जोड़े जाने पर आइटम को स्वचालित रूप से ट्रैक किया जाता है।
HTTPS उपर DNS (DoH)
वर्बोज़ लॉगिंग
गुप्त मोड अक्षम करें
@@ -601,13 +599,13 @@
सभी अद्यतन करें
चेतावनी
भाषा
- स्वचालित बैकअप की अत्यधिक अनुशंसा की जाती है। आपको अन्य जगहों पर भी प्रतियां रखनी चाहिए।
+ आपको अन्य स्थानों पर भी बैकअप की प्रतियाँ रखनी चाहिए।
बड़े अपडेट स्रोतों को नुकसान पहुंचाते हैं और इससे धीमे अपडेट हो सकते हैं, और बैटरी का उपयोग भी बढ़ सकता है। अधिक जानने के लिए टैप करें ।
ऐप अपडेट
हर 3 दिन
केवल वाई-फ़ाई पर
- डेटाबेस में %1$d मंगा हैं जो पुस्तकालय में नहीं है
- डेटाबेस साफ़ करें
+ डेटाबेस में %1$d गैर-पुस्तकालय आइटम
+ साफ़ करने के लिए कुछ नहीं है
शीर्षक अद्यतन न करें
CBZ आर्कैव के रूप में सहेजें
गोपनीयता नीति
@@ -630,26 +628,25 @@
रिवर्स पोर्ट्रेट
%1$d अपडेट विफल
%1$d अपडेट छोड़े गए
- पैन पर नेविगेट करें
+ टैप करते समय छवियों को पैन करें
ज़ूम लैंडस्केप इमेज
श्रृंखला को शीर्ष पर ले जाएं
कोई स्रोत नहीं मिला
कोई स्थापित स्रोत नहीं मिला
अपठित गिनती
जब बैटरी कम नहीं
- वेब्यु डेटा साफ हो गया
+ WebView डेटा साफ हो गया
गिटहब में खोलें
चित्र सहेजने में त्रुटि
लंबी डाउनलोड किए गए चित्रों के हिस्से करके पाठमाला के प्रदर्शन में सुधार लाता है
पेज %d हिस्से करते वक्त नहीं मिला
बंद करे
- वेबव्यू डाटा साफ करें
+ WebView डाटा साफ करें
एक जैसे पिन किए गए स्रोत दिखाएँ
उनके संबंधित भाषा समूहों में पिन किए गए स्रोतों को फिर से दिखाएं
चित्र %d का फ़ाइल पथ नहीं खोजा जा सका
बैकअप करने के लिए कोई पुस्तकालय प्रविष्टि नहीं
एक नया संस्करण आधिकारिक रिलीज के द्वारा उपलब्ध है। अनौपचारिक F-Droid रिलीज से माइग्रेट करने का तरीका जानने के लिए यह टैप करें ।
- लंबे चित्रों की ऑटो हिस्से
संस्करण
भाषा
आयु रेटिंग
@@ -660,7 +657,6 @@
श्रेणी हटाएँ
कोई विवरण नहीं
क्या आप %s कैटेगरी को हटाना चाहते हैं \?
- डाउनलोड किया गया चित्र विभाजित नहीं हो सका
प्रति श्रृंखला रीडर सेटिंग्स को रीसेट करें
सभी श्रृंखलाओं के रीडिंग मोड और ओरिएंटेशन को रीसेट करता है
सभी रीडर सेटिंग्स रीसेट करें
@@ -672,7 +668,7 @@
आंतरिक त्रुटि : अधिक जानकारी के लिए क्रैश लॉग की जाँच करें
केवल अनमीटर्ड कनेक्शन पर
RARv5 प्रारूप समर्थित नहीं है
- पुस्तकालय पिछली बार अपडेट किया गया: %1$s
+ पुस्तकालय पिछली बार अपडेट किया गया: %s
अपना हाल ही में अपडेट किया गया मांगा देखें
केवल लाइब्रेरी में प्रविष्टियों पर काम करता है। और यदि वर्तमान अध्याय और अगला अध्याय पहले ही डाउनलोड हो चुका है
कस्टम कवर
@@ -691,7 +687,7 @@
मल्टी
क्या आपको यकीन है \?
ऐप लॉक लागू होने पर विजेट उपलब्ध नहीं होता
- आप इस मांगा को अपनी लाइब्रेरी से निकालने वाले हैं
+ आप अपनी पुस्तकालय से \"%s\" को निकालने जा रहे हैं
एक अपडेट पहले से चल रहा है
अंतिम पढ़ा गया अध्याय खोलने में असमर्थ
यूजर एजेंट स्ट्रिंग(User agent string) डिफॉल्ट रिसेट करें
diff --git a/i18n/src/main/res/values-hr/strings.xml b/i18n/src/main/res/values-hr/strings.xml
index 3c50d21014..0157e68ebb 100644
--- a/i18n/src/main/res/values-hr/strings.xml
+++ b/i18n/src/main/res/values-hr/strings.xml
@@ -117,7 +117,6 @@
Sljedeće poglavlje
Prethodno poglavlje
Zaustavi
- Prekini
Pogledaj poglavlja
Uredi naslovnicu
Postavi kategorije
@@ -424,7 +423,6 @@
Deaktiviraj sve
Aktiviraj sve
Nema stranica
- Značke
Kartice
Nakratko prikaži trenutačni modus kad se čitač otvori
Prikaži modus čitanja
@@ -491,12 +489,10 @@
Nije pronađen nijedan program za biranje datoteka
Ponovo se prijavi na MAL
Prikaži u popisu izvora i proširenja
- Zapisi rušenja programa
Datum kraja
Datum početka
- Zapisi rušenja programa su spremljeni
Sprema zapise grešaka u datoteku za obavještavanje programera
- Spremi zapise rušenja programa
+ Dijeli zapise prekida programa
Područja dodira
Rub
Kao Kindle
@@ -511,9 +507,9 @@
Podaci datoteke sigurnosne kopije će se obnoviti.
\n
\nZa upotrebu podataka, morat ćeš instalirati nedostajuća proširenja i nakon toga se prijaviti na usluge praćenja.
- Ako se dvostranična podjela ne podudara sa smjerom čitanja
- Preokreni smještaj dvostranične podjele
- Dvostranična podjela
+ Ako se položaj rastavljenih širokih stranica ne podudara sa smjerom čitanja
+ Obrni položaj rastavljene stranice
+ Rastavi široke stranice
Kratko prikaži kada je čitač otvoren
Desno
Lijevo
@@ -625,7 +621,7 @@
Najniža
Objavljivanje završeno
Prekinuto
- Prekid
+ Zaustavljeno
Za pomoć o tome kako popraviti greške aktualiziranja biblioteke, pogledaj %1$s
Spremi kao CBZ arhivu
ČPP i vodiči
@@ -648,15 +644,13 @@
Nema unosa u biblioteci za spremanje u sigurnosnu kopiju
Poboljšava performanse čitača
Potpun popis
- Nije bilo moguće podijeliti preuzetu sliku
- Rastavi visoke slike
WebView podaci su izbrisani
Izbriši WebView podatke
Popis čitanja
Popis želja
Na popisu čekanja
Popis nedovršenih
- Stranica %d nije pronađena tijekom razdvajanja
+ Stranica %d nije pronađena tijekom rastavljanja
RARv5 format nije podržan
Radi samo na unosima u biblioteci i ako je trenutačno poglavlje plus sljedeće već preuzeto
@@ -699,4 +693,14 @@
- Sljedećih %d poglavlja
Aktualiziraj katogoriju
+ Rastavi visoke slike
+ Oznake
+
+ - Nedostaje %1$s poglavlje
+ - Nedostaju %1$s poglavlja
+ - Nedostaje %1$s poglavlja
+
+ Možda nedostaje poglavlje
+ Prilagodi prikaz širokih stranica okretanjem
+ Preokreni položaj širokih stranica
\ No newline at end of file
diff --git a/i18n/src/main/res/values-hu/strings.xml b/i18n/src/main/res/values-hu/strings.xml
index 4528a66234..14c32c490c 100644
--- a/i18n/src/main/res/values-hu/strings.xml
+++ b/i18n/src/main/res/values-hu/strings.xml
@@ -86,7 +86,6 @@
Kategória átnevezése
Kategóriák beállítása
Borító szerkesztése
- Megállítás
Szüneteltetés
Előző fejezet
Következő fejezet
@@ -236,7 +235,6 @@
Frissítések keresése
Nyílt forráskódú licenc
Lapok
- Jelvények
Előző
%1$s. fejezet
@@ -266,7 +264,6 @@
Másolás
Áttelepítés
%1$s. fejezet
- Összeomlási naplók
%1$s. fejezetek
A(z) %1$s. fejezet és %2$d egyéb fejezetek
%1$s:%2$s, %3$d. oldal
@@ -591,7 +588,6 @@
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
- Nagy képek automatikus elválasztása
Növeli a teljesítményt a nagy képek elválasztásával.
Nem bejelentkezett trackerek:
Inkognitó mód
@@ -647,7 +643,6 @@
WebView adatok törölve
Helyreállítja az olvasó módot és orientációt minden elemnél
Nem lehetett visszaállítani az olvasói beállításokat
- Hibaüzenetek elmentve
Akkumulátor optimalizálás kikapcsolása
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
@@ -667,7 +662,6 @@
Letöltés befejeződött
%d oldal nem található felosztás közben
Nem található az útvonal a %d-ik oldalhoz
- Nem sikerült a letöltött képet felosztani
Ez a kép legyen a fedlap\?
- %1$s fejezet és még 1
diff --git a/i18n/src/main/res/values-in/strings.xml b/i18n/src/main/res/values-in/strings.xml
index 98b55655a3..9451920ff2 100644
--- a/i18n/src/main/res/values-in/strings.xml
+++ b/i18n/src/main/res/values-in/strings.xml
@@ -35,7 +35,6 @@
Ubah nama kategori
Masukkan ke kategori
Ubah gambar sampul
- Berhenti
Hentikan sementara
Bab sebelumnya
Bab selanjutnya
@@ -298,7 +297,7 @@
Pindahkan ke bawah
Lainnya
Pembaruan
- Kunci dengan biometrik
+ Memerlukan buka kunci
Kunci saat diam
Selalu
Tidak pernah
@@ -405,7 +404,6 @@
Menurut tanggal pengunggahan
Grid nyaman
Tab
- Penanda
Tampilkan tab kategori
Halaman tidak ditemukan
Nonaktifkan semua
@@ -446,7 +444,7 @@
18+
Hal ini tidak mencegah ekstensi yang tidak resmi atau berpotensi salah ditandai untuk menampilkan konten NSFW (18+) di dalam aplikasi.
- - Melewati %d bab, entah sumber kehilangan mereka atau karena sudah difilter
+ - Melewati %d bab, entah sumbernya hilang atau telah difilter
Tidak ada bab yang ditemukan
Pengaturan bab bawaan diperbarui
@@ -474,11 +472,9 @@
Menurut nomor bab
Menurut tanggal unggahan
Dilacak
- Buat log kerusakan
- Log kerusakan
+ Bagikan log kerusakan
Tanggal selesai
Tanggal mulai
- Log kerusakan disimpan
Simpan log kerusakan ke sebuah berkas untuk dibagikan dengan pengembang aplikasi
Zona ketuk
Kanan dan Kiri
@@ -634,14 +630,12 @@
Sumber yang diinstal tidak ditemukan
Tidak ada sumber yang ditemukan
Jumlah belum dibaca
- Membagi gambar panjang
Meningkatkan kinerja pembaca
Halaman %d tidak ditemukan saat dipisah
Tidak dapat menemukan jalur file halaman %d
Atur ulang semua pengaturan pengguna
Muat ulang pengaturan per-pengguna
Atur ulang mode dan orientasi dari semua seri
- Tidak bisa memisah gambar yang telah diunduh
Yah, ini aneh
Tidak bisa mengatur ulang setelan pembaca
Versi
@@ -689,7 +683,7 @@
Sumber, ekstensi, pencarian global
Mode membaca, tampilan, navigasi
Sinkronisasi kemajuan satu arah, sinkronisasi yang ditingkatkan
- %s mengalami kesalahan yang tak terduga. Kami menyarankan Anda mengambil tangkapan layar pesan ini, membuat log kerusakan, dan kemudian membagikannya di saluran dukungan kami di Discord.
+ %s mengalami kesalahan tak terduga. Kami menyarankan Anda membagi log kerusakan di saluran dukungan kami di Discord.
Unduh otomatis, unduh di depan
Kunci aplikasi, layar aman
Bahasa aplikasi, notifikasi
@@ -716,4 +710,12 @@
Salin ke papan klip
Perbarui kategori
+ Potong gambar tinggi
+ Lapisan awal
+ Memutar halaman lebar agar pas
+ Orientasi balik halaman lebar yang diputar
+
+ - Bab %1$s yang hilang
+
+ Mungkin ada bab yang terlewat
\ No newline at end of file
diff --git a/i18n/src/main/res/values-it/strings.xml b/i18n/src/main/res/values-it/strings.xml
index 666996f593..cdc79715c5 100644
--- a/i18n/src/main/res/values-it/strings.xml
+++ b/i18n/src/main/res/values-it/strings.xml
@@ -32,7 +32,6 @@
Rinomina categoria
Assegna categorie
Modifica copertina
- Ferma
Pausa
Capitolo precedente
Capitolo successivo
@@ -446,7 +445,6 @@
Nessuna pagina trovata
Per data di aggiunta
Schede
- Etichette
Dati
Fonti mancanti:
Il backup non contiene alcuna voce di libreria.
@@ -527,12 +525,10 @@
Bordo
Stile Kindle
A forma di L
- Registro dei crash
Data fine
Data inizio
- Registro dei crash salvato
Salva un registro degli errori su un file per condividerlo con gli sviluppatori
- Salva un registro dei crash
+ Condividi il registro dei crash
Decrescente
Crescente
Per numero di capitolo
@@ -691,14 +687,12 @@
Nessuna fonte trovata
Conteggio non letti
Migliora le prestazioni del lettore
- Dividi le immagini troppo alte
Pagina %d non trovata durante la divisione
Impossibile trovare il percorso della pagina %d
Reimposta la modalità di lettura e l\'orientamento per tutte le serie
Reimposta le opzioni del lettore per ogni serie
Ripristinate tutte le opzioni del lettore
Impossibile ripristinare le opzioni del lettore
- Impossibile separare le immagini scaricate
Beh, questo è imbarazzante
Classificazione per età
Versione
@@ -746,7 +740,7 @@
Ricerca…
Lingua dell\'app, notifiche
Tema, formato data e ora
- %s ha riscontrato un errore imprevisto. Ti suggeriamo di fare uno screenshot di questo messaggio, scaricare i registri degli arresti anomali e condividerli nel nostro canale di supporto su Discord.
+ %s ha riscontrato un errore imprevisto. Ti suggeriamo di condividere il registro degli arresti anomali nel nostro canale di supporto su Discord.
Categorie, aggiornamenti globali
Download automatico, download anticipato
Fonti, estensioni, ricerca globale
@@ -782,4 +776,14 @@
Nascondi le voci già in libreria
Copia negli appunti
Aggiorna categoria
+ Dividi immagini alte
+ Sovrimpressione
+ Ruota le pagine larghe per adattareaallo schermo
+ Capovolgi l\'orientamento delle pagine larghe ruotate
+
+ - Manca %1$s capitolo
+ - Mancano %1$s capitoli
+ - Mancano %1$s capitoli
+
+ Potrebbero mancare alcuni capitoli
\ No newline at end of file
diff --git a/i18n/src/main/res/values-ja/strings.xml b/i18n/src/main/res/values-ja/strings.xml
index 73c5057eb1..bc7c1182a8 100644
--- a/i18n/src/main/res/values-ja/strings.xml
+++ b/i18n/src/main/res/values-ja/strings.xml
@@ -147,7 +147,6 @@
追加
カテゴリー名を編集
カテゴリーを設定
- 停止
削除
インストール
取り消し
@@ -310,7 +309,6 @@
ローカルソース
WebViewでサイトを開く
タブ
- 情報バッジ
メールアドレス
- 残り%1$s件
@@ -470,16 +468,14 @@
ファイルを選択できるアプリが見つかりません
もう一度MALにログインしてください
アイテム数を表示する
- クラッシュログ
読み終わった日付
読み始めた日付
- クラッシュログが保存されました
角
章の番号順
アップロードされた日付順
登録済み
開発者に渡すよう、エラー ログを保存します
- クラッシュ ログをダンプ
+ クラッシュ ログを共有
タップ可能なゾーン
右と左
Kindleスタイル
@@ -634,7 +630,6 @@
ソースが見つかりません
インストール済みのソースが見つかりません
未読の章数
- 長い画像を分割
ビューアのパフォーマンスを改善します
分割時にページ%dが見つかりませんでした
シリーズ別のビューア設定をリセット
@@ -642,7 +637,6 @@
ページ%dのファイルパスが見つかりませんでした
ビューア設定を全てリセットしました
ビューア設定をリセットできませんでした
- ダウンロードした画像を分割できませんでした
えっと、こりゃまずいですね
バージョン
言語
@@ -686,7 +680,7 @@
ストレージ権限を持っていません
シリーズは更新を必要としないため、スキップされました
検索…
- %sでは予期せぬエラーが発生しました。お手数ですが、このメッセージのスクリーンショットを作成し、クラッシュ ログをダンプして、Discord のサポート チャネルで共有するようお願い致します。
+ %sでは予期せぬエラーが発生しました。お手数ですが、クラッシュ ログを Discord のサポート チャネルで共有するようお願い致します。
無効な場所: %s
不明なタイトル
ユーザー エージェント文字列が無効です
@@ -722,4 +716,12 @@
- 次の%d章
カテゴリを更新
+ 長い画像を分割
+ オーバーレイ
+ 画面に合わせるように幅広いページを回転
+ 回転した幅広いページの向きを反転
+
+ - %1$s章が欠けています
+
+ 一部の章が足りない可能性あり
\ No newline at end of file
diff --git a/i18n/src/main/res/values-jv/strings.xml b/i18n/src/main/res/values-jv/strings.xml
index 0ad9c74380..0b56c6bc5a 100644
--- a/i18n/src/main/res/values-jv/strings.xml
+++ b/i18n/src/main/res/values-jv/strings.xml
@@ -98,7 +98,6 @@
Setelan
Liyane
Jeneng
- Mandheg
Manga
Donlotan rampung
Donloder
diff --git a/i18n/src/main/res/values-ka-rGE/strings-aniyomi.xml b/i18n/src/main/res/values-ka-rGE/strings-aniyomi.xml
index 807bbb2520..b580d7306a 100644
--- a/i18n/src/main/res/values-ka-rGE/strings-aniyomi.xml
+++ b/i18n/src/main/res/values-ka-rGE/strings-aniyomi.xml
@@ -16,12 +16,12 @@
თავის ქეშის გასუფთავება
მონაცემთა ბაზის გასუფთავება
ბიბლიოთეკაში შეუნახავი მანგების ისტორიის წაშლა
- დარწმუნებული ხარ\? წაკითხული თავები და ბიბლიოთეკაში არ არსებული მანგების პროგრესი დაიკარგება
+ დარწმუნებული ბრძანდებით\? წაკითხული თავები და ბიბლიოთეკაში არ არსებული ჩანაწერების პროგრესი დაიკარგება
ანახლებს სტატუსს, შეფასებას და ბოლო თავს წაკითხულს თვალყურის სადევნებელ სერვისებიდან
დავამატო მანგა ბიბლიოთეკაში\?
შეცდომა
დაპაუზებულია
- ამ მანგას ყველა თავის წაკითხვა
+ ამ ჩანაწერის ყველა თავის თავიდან წაკითხვა
შეუძლებელია თავების გადმოწერა დისკზე ადგილის უკმარისობის გამო
WebView არის აუცილებელი Tachiyomi-ს სამუშაოდ
გადმოწერა დაპაუზებულია
diff --git a/i18n/src/main/res/values-ka-rGE/strings.xml b/i18n/src/main/res/values-ka-rGE/strings.xml
index 44a5fd8078..a4cc9d9034 100644
--- a/i18n/src/main/res/values-ka-rGE/strings.xml
+++ b/i18n/src/main/res/values-ka-rGE/strings.xml
@@ -1,7 +1,7 @@
სახელი
- მანგა
+ ბიბლიოთეკის ჩანაწერები
ისტორია
მეტი
პარამეტრები
@@ -36,7 +36,6 @@
კატეგორიის რედაქტირება
კატეგორიის სახელის ვცლილება
თავების ხილვა
- გაჩერება
პაუზა
წინა თავი
შემდეგი თავი
@@ -44,7 +43,7 @@
წაშლა
გაგრძელება
ბრაუზერში გახსნა
- აპლიკაციაში გახსნა
+ WebView-ში გახსნა
ჩვენების რეჟიმი
ჩვენება
კომპაქტური ბადე
@@ -86,8 +85,8 @@
თარიღის ფორმატი
გამოსვლის დადასტურება
შეტყობინებების მართვა
- უსაფრთხოება
- თითის ანაბეჭდით ბლოკი
+ უსაფრთხოება და კონფიდენციალობა
+ განბლოკვის მოთხოვნა
ბლოკირება უმოქმედობის დროს
ყოველთვის
არასდროს
@@ -198,11 +197,11 @@
მაქსიმალური რეზერვი
რეზერვი შექმნილია
არასწორი სარეზერვო ფაილი
- რეზერვი არ შეიცავს არცერთ მანგას.
+ მარქაფი ბიბლიოთეკის ჩანაწერებს არ შეიცავს.
დაკარგული წყაროები:
აღდგენა შესრულებულია
%02d წუთი, %02d წამი
- რეზერვის შემქნა პროგრესშია
+ მარქაფი უკვე მიმდინარეობს
რას გსურს რომ შეუქმნა რეზერვი\?
რეზერვის შექმნა
რეზერვის შექმნა ვერ მოხერხდა
@@ -227,7 +226,7 @@
ქრეშის რეპორტის გაგზავნა
ეხმარება შეცდომების გამოსწორებაში. სენსიტიური მონაცემები არ იქნება გაგზავნილი
მხოლოდ გადმოწერილები
- ფილტრავს ყველა მანგას შენს ბიბლიოთეკაში
+ თქვენს ბიბლიოთეკაში ყველა ჩანაწერის გაფილტვრა
შესვლა %1$s-ში
მომხმარებლის სახელი
ელ. ფოსტა
@@ -239,11 +238,10 @@
გამოსვლა წარმატებით მოხერხდა
დაფიქსირდა უცნობი შეცდომა
კატეგორია ახლდება
- ნიშნაკები
ჩანართები
შედეგების სიის დასასრული
შედეგი ვერ მოიძებნა
- ვებსაიტის ნახვა აპლიკაციაში
+ ვებსაიტის ნახვა WebView-ში
ლოცალური წყარო
სხვა
ბოლოს გამოყენებული
@@ -294,7 +292,7 @@
სურათი შენახულია
გვერდი: %1$d
შემდეგი თავი ვერ მოიძებნა
- სურათის გადმოტვირთვა ვერ მოხერხდა
+ გამოსახულებსი ჩატვირთვის შეცდომა
ამ სერიებისთვის
კითხვის რეჟიმი
დამთავრებულია:
@@ -319,19 +317,19 @@
თავი %1$s და %2$d მეტი
თავები %1$s
ვერ მოხერხდა ყდის განახლება
- გთხოვთ ჯერ დაამატოთ მანგა თქვენს ბიბლიოთეკაში
+ გთხოვთ ჯერ დაამატოთ ჩანაწერი თქვენს ბიბლიოთეკაში
ამოირჩიე ყდის სურათი
ამოირჩიე რეზერვის ფაილი
გადმოწერა
განახლება ვერ მოიძებნა
განახლების ძებნა…
გადმოწერა…
- გადმოწერა დასრულებულია
+ განახლების დასაყენებლად დაატყაპუნეთ
გადმოწერის შეცდომა
ხელმისაწვდომია ახალი ვერსია!
გადმოწერები არ არის
განახლებები არ არის
- შენი ბიბლიოთეკა ცარიელია, დაამატე სერიები შენს ბიბლიოთეკაში \"დაათვალიერე\"-დან.
+ თქვენი ბიბლიოთეკა ცარიელია
Wi-Fi კავშირი არ არის ხელმისაწვდომი
ინტერნეტთან კავშირი არ არის ხელმისაწვდომი
საერთო
@@ -355,7 +353,7 @@
ნდობა
არასანდო
არასანდო დამატება
- დამატება აღარ არის ხელმისაწვდომი.
+ დამატება ხელმისაწვდომი აღარაა. მან შეიძლება არასწორად იმუშაოს და აპლიკაციას პრობლემები შეუქმნას. გირჩევთ, წაშალოთ ის.
დამატება არ არის Tachiyomi-ს ოფიციალური დამატებების სიიდან.
ორჯერ დაჭერისას ანიმაციის სისწრაფე
აუმჯობესებს ხარისხს, თუმცა ამცირებს წარმადობას
@@ -365,7 +363,7 @@
დიალოგი ხანგრძლივი დაჭერისას
ვებ კომიქსი
დანომრილი
- შეიყვანე მხოლოდ მიმაგრებული წყაროები
+ გლობალურ ძებნაში მხოლოდ მიჭიკარტებულ წყაროებში ძებნა
გამორთე ბატარეის ოპტიმიზაცია
ეხმარება უკანა ფონში მიმდინარე ბიბლიოთეკის განახლებებსდა რეზერვს
ღია კოდის ლიცენზია
@@ -384,7 +382,7 @@
ჩართე ყველა
გამორთე ყველა
აპლიკაციებს შორის გადართვისას შემადგენლობის დამალვა და სკრინშოტების დაბლოკვა
- ცალმხრივი სინქრონიზაცია თვალყურის სადევნებელ სერვისებში თავების პროგრესის განსაახლებლად. მიადევნე თვალყური ინდივიდუალურ მანგებს მათი ჩანართებიდან
+ ცალმხრივი სინქრონიზაცია თვალყურის სადევნებელ სერვისებში თავების პროგრესის განსაახლებლად. მიადევნე თვალყური ინდივიდუალურ ჩანაწერებს მათი ჩანართებიდან.
გადაფარება
ვერ მოხერხდა CloudFlare-ს შემოვლა
ორივე
@@ -405,8 +403,8 @@
- ნაპოვნია %1$d ახალი თავი
- - ნაპოვნია ახალი თავები ნაწარმოებისთვის
- - ნაპოვნია ახალი თავები %d ნაწარმოებისთვის
+ - %d ჩანაწერისთვის
+ - %d ჩანაწერისთვის
წყარო ვერ მოიძებნა
@@ -425,8 +423,8 @@
ასამოქმედებლად საჭიროა აპლიკაციის გადატვირთვა
ქსელი
- - გაკეთებულია %1$s %2$s შეცდომით
- - გაკეთებულია %1$s %2$s შეცდომებით
+ - დასრულების დრო %1$s. %2$s შეცდომა
+ - დასრულების დრო %1$s. %2$s შეცდომა
კითხვის რეჟიმი
@@ -444,7 +442,7 @@
შეიძლება შეიჩაცდეს უცენზურო(18+) კონტენტს
18+
მარჯვენა და მარცხენა
- გვერდების ორმაგი გაყოფა (ალფა)
+ განიერი გვერდების გაყოფა
ეს არ აღმოფხვრავს პროგრამაში NSFW (18+) შიგთავსის არაოფიციალურ, ან შესაძლო არასწორად მონიშნული გაფართოებებოიდან გამოჩენას.
საგნების რაოდენობის ჩვენება
პარამეტრების ძიება
@@ -459,4 +457,5 @@
ნაგულისხმევი
დააჭირე დეტალების სანახავად
გადმოწერის რიგი
+ წაუკითხავი
\ No newline at end of file
diff --git a/i18n/src/main/res/values-kk/strings.xml b/i18n/src/main/res/values-kk/strings.xml
index c153d4992b..a278804e45 100644
--- a/i18n/src/main/res/values-kk/strings.xml
+++ b/i18n/src/main/res/values-kk/strings.xml
@@ -127,7 +127,6 @@
Санаттарды орнату
Мұқабасын өзгерту
Тарауларды қарау
- Доғару
Тоқтату
Алдыңғы тарау
Келесі тарау
@@ -330,7 +329,6 @@
- %1$s дегеннен кейін %2$s қателікпен орындалды
- %1$s дегеннен кейін %2$s қателікпен орындалды
- Ұзын суреттерді авто бөлу
Тараулардың ауысуын әрдайым көрсету
Құлыпталған портрет
Жоғары
@@ -476,7 +474,6 @@
User agent бос бола алмайды
Оқыма баптауларын әрбір туынды үшін қалпына келтіру
Барлық туындылар үшін оқу режимі мен бағдарын қалпына келтіреді
- Қателіктер тіркеуі сақталды
Аялық белсенділік
Батареяның оңтайландыруын өшіру
Құрылғы баптаулары ашылмады
@@ -489,7 +486,6 @@
Сіз шығып кеттіңіз
Санатты жаңарту
Локал
- Белгілер
Нәтиже жоқ
Локал дереккөз
Ақырғы қолданылған
@@ -596,7 +592,6 @@
Жүктеп алу
Ортақ
Прогресс
- Қателіктер тіркеуі
Тарау баптаулары
Қателік
Тоқтатылды
@@ -663,7 +658,6 @@
Жүктеу қателігі
Орнату үшін басыңыз
Қателіктер
- Жүктелген бет бөлінбеді
Аяқталмағандар Тізімі
Бағалау
Атауы
diff --git a/i18n/src/main/res/values-km/strings.xml b/i18n/src/main/res/values-km/strings.xml
index 816e696adb..5ecc799317 100644
--- a/i18n/src/main/res/values-km/strings.xml
+++ b/i18n/src/main/res/values-km/strings.xml
@@ -37,7 +37,6 @@
ទទួលយកថ្នាក់
ផ្លាស់ប្ដូរក្របម៊េងហ្គា
មើលភាគ
- ឈប់
ឈប់បណ្ណោះអាសន្ន
ម្ដងទៀត
លុប
diff --git a/i18n/src/main/res/values-kn/strings.xml b/i18n/src/main/res/values-kn/strings.xml
index c1b55dfadd..8878f5d749 100644
--- a/i18n/src/main/res/values-kn/strings.xml
+++ b/i18n/src/main/res/values-kn/strings.xml
@@ -65,7 +65,6 @@
ಮುಂದಿನ ಅಧ್ಯಾಯ
ಹಿಂದಿನ ಅಧ್ಯಾಯ
ವಿರಾಮ
- ನಿಲ್ಲಿಸಿ
ಅಧ್ಯಾಯಗಳನ್ನು ವೀಕ್ಷಿಸಿ
ಕವರ್ ತಿದ್ದಿ
ವರ್ಗಗಳನ್ನು ಹೊಂದಿಸಿ
@@ -412,7 +411,6 @@
ಮೂಲ ಸ್ಥಳಾಂತರ
ಯಾವುದೇ ಪುಟಗಳು ಸಿಕ್ಕಿಲ್ಲ
ಟ್ಯಾಬ್ಗಳು
- ಬ್ಯಾಡ್ಜ್ಗಳು
ರೀಡರ್ ತೆರೆದಾಗ ಪ್ರಸ್ತುತ ಮೋಡ್ ಅನ್ನು ಸಂಕ್ಷಿಪ್ತವಾಗಿ ತೋರಿಸಿ
ಓದುವ ರೀತಿ ತೋರಿಸಿ
ವರ್ಗ ಟ್ಯಾಬ್ಗಳನ್ನು ತೋರಿಸಿ
@@ -483,10 +481,8 @@
ಅಪ್ಲೋಡ್ ದಿನಾಂಕ ದಂತೆ
ವಸ್ತುಗಳ ಸಂಖ್ಯೆಯನ್ನು ತೋರಿಸಿ
ಸಿಕ್ಕ ಮಾಹಿತಿ
- ಕ್ರ್ಯಾಶ್ ಲಾಗ್ ಗಳು
ಮುಕ್ತಾಯ ದಿನಾಂಕ
ಆರಂಭದ ದಿನ
- ಕ್ರ್ಯಾಶ್ ಲಾಗ್ಗಳನ್ನು ಉಳಿಸಲಾಗಿದೆ
ಡೆವಲಪರ್ಗಳೊಂದಿಗೆ ಹಂಚಿಕೊಳ್ಳಲು ದೋಷದೆ ಲಾಗ್ಗಳನ್ನು ಫೈಲ್ಗೆ ಸೇರಿಸಿ
ಕ್ರ್ಯಾಶ್ ಲಾಗ್ಗಳನ್ನು ಡಂಪ್ ಮಾಡಿ
HTTPS ಮೇಲೆ DNS ಬಳಸಿ
diff --git a/i18n/src/main/res/values-ko/strings.xml b/i18n/src/main/res/values-ko/strings.xml
index 154ae6eff2..48702b1336 100644
--- a/i18n/src/main/res/values-ko/strings.xml
+++ b/i18n/src/main/res/values-ko/strings.xml
@@ -30,7 +30,6 @@
카테고리 이름 바꾸기
카테고리 지정
표지 편집
- 정지
제거
계속
브라우저에서 열기
@@ -398,7 +397,7 @@
데이터 이전
%1$s화 - %2$s
화면 보안
- 잠금 해제 요청
+ 잠금 해제 필요
단어 사용
모양
오늘
@@ -454,7 +453,6 @@
전부 업데이트
백업에 항목이 포함되어 있지 않습니다.
개발자와 공유할 수 있는 오류 로그 파일을 생성합니다
- 오류 로그가 저장되었습니다
개인정보 보호 정책
%1$s: %2$s, %3$d페이지
계속하시겠습니까\? 모든 기록이 삭제됩니다.
@@ -511,10 +509,9 @@
10%
트래킹
서재 표지 새로고침
- 오류 로그 덤프
+ 오류 로그 공유
회차를 찾을 수 없습니다
결과가 없습니다
- 오류 로그
이 확장기능의 소스는 19금 콘텐츠가 포함될 수 있습니다
트래커 사용
시작
@@ -576,7 +573,6 @@
백그라운드 서재 업데이트와 라이브러리 업데이트를 도울 수 있습니다
자세한 로그
자세한 로그를 시스템 로그에 기록 (성능이 하락할 수 있습니다)
- 배지
날짜
정렬
읽기 시작한 날짜
@@ -635,7 +631,6 @@
배터리가 부족하지 않을 때만
WebView 데이터 지우기
WebView 데이터 삭제됨
- 긴 이미지 분할
리더 성능 향상
버전
언어
@@ -665,7 +660,6 @@
마지막 회차를 열 수 없습니다
최근에 업데이트된 항목 보기
보류 목록
- 다운로드한 사진을 나눌 수 없습니다
나누어 짐 중에 %d페이지 안 찾습니다
RARv5 포맷은 지원되지 않습니다
앱 잠금 사용 중에 위젯 이용할 없습니다
@@ -699,7 +693,7 @@
자동 다운로드, 미리 다운로드
예기치 않은 오류가 발생했습니다
애플리케이션 재시작
- %s에 예기치 않은 오류가 발생했습니다. 이 메시지를 스크린샷으로 찍고, 충돌 로그를 덤프 한 다음 공식 디스코드 채널에 공유하는 것을 추천합니다.
+ %s에 예기치 않은 오류가 발생했습니다. 충돌 로그를 공식 디스코드 채널에 공유하는 것을 추천합니다.
알 수 없는 제목
잘못된 위치: %s
잘못된 사용자 에이전트 문자열
@@ -723,4 +717,12 @@
- 다음 %d화
카테고리 업데이트
+ 긴 이미지 분할
+ 오버레이
+ 화면에 맞게 넓은 페이지 회전
+ 회전된 넓은 페이지의 회전 방향
+ 챕터가 누락되었을 수 있습니다
+
+ - 누락된 회차 %1$s개
+
\ No newline at end of file
diff --git a/i18n/src/main/res/values-lt/strings.xml b/i18n/src/main/res/values-lt/strings.xml
index a7eb047ef0..37c2bc7f65 100644
--- a/i18n/src/main/res/values-lt/strings.xml
+++ b/i18n/src/main/res/values-lt/strings.xml
@@ -148,7 +148,6 @@
Sekantis skyrius
Ankstesnis skyrius
Pristabdyti
- Stop
Rodyti skyrius
Keisti viršelį
Perkelti kategorijas
@@ -466,7 +465,6 @@
Bakstelėdami slinkti plačius vaizdus
Priartinti horizontalų vaizdą
Versija
- Automatiškai padalinti aukšti vaizdai
Vienpusė sinchronizacija, skirta skaitymo progresui stebėjimo paslaugose atnaujinti. Nustatykite atskirą įrašų sekimą iš jų sekimo mygtuko.
Galima naudoti dabartinei bibliotekai atkurti
Šoninis spaudinėjimas
@@ -488,7 +486,6 @@
Atidaryti „GitHub“ svetainėje
Programėlių atnaujinimai
Plėtinių atnaujinimai
- Strigčių žurnalai
Ankstesnis puslapis
Atsarginės kopijos kūrimas / atkūrimas gali veikti netinkamai, jei \"MIUI Optimization\" yra išjungtas.
Atšauktas atkūrimas
@@ -526,7 +523,6 @@
- %d plėtinių atnaujinimai pasiekiami
- %d plėtinių atnaujinimai pasiekiami
- Nepavyko padalyti atsisiųsto vaizdo
Atnaujinti numatytus skyrių nustatymus
Parsisiuntėjas
Klaida
@@ -559,7 +555,6 @@
Iš naujo nustati visų serijų skaitymo režimą ir orientaciją
Visų skaitytuvo nustatymų nustatymas iš naujo
Nepavyko atstatyti skaitytuvo nustatymų
- Išsaugoti strigčių žurnalai
Fono veikla
Padeda su fono bibliotekos naujinimais ir atsarginėmis kopijomis
Akumuliatoriaus optimizavimas jau išjungtas
@@ -601,7 +596,6 @@
Viršelis
Prisegtas
El. pašto adresas
- Žymės
Patikrinkite svetainę \"WebView\" aplinkoje
Pasaulinė paieška…
Naujausia
diff --git a/i18n/src/main/res/values-lv/strings-aniyomi.xml b/i18n/src/main/res/values-lv/strings-aniyomi.xml
index ac6802f40a..835754739c 100644
--- a/i18n/src/main/res/values-lv/strings-aniyomi.xml
+++ b/i18n/src/main/res/values-lv/strings-aniyomi.xml
@@ -2,11 +2,10 @@
Kategorijas
Atslēgt Anijomi
- Pēdējais manga atjauninājums
- Rādīt manga
- Lokālā manga
+ Pēdējā atjauninājuma pārbaude
+ Rādīt ierakstu
+ Lokālais avots
Ar nelasītu(ām) nodaļu(ām)
- Rādīt nelasīto skaitu uz Atjauninājumi ikonas
Noklusētā kategorija
Izslēgto kategoriju manga netiks atjaunināta, pat ja tās ir arī iekļautajās kategorijās.
Šis paplašinājums tika parakstīts ar neuzticamu sertifikātu, un tas netika aktivizēts.
@@ -24,13 +23,13 @@
Notīriet nodaļu kešatmiņu, aizverot lietotni
Notīrīt datu bāzi
Dzēst vēsturi preikš manga, kas nav saglabāta bibliotēkā
- Vai esi pārliecināts\? Lasītās nodaļas un progress priekš manga, kuras nav bibliotēkā, būs zudis
+ Vai esi pārliecināts\? Lasītās nodaļas un progress priekš ieraksta, kuras nav bibliotēkā, būs zudis
Atjaunina statusu, vērtējumu un pēdējo izlasīto nodaļu no izsekošanas servisa
Pauzē lasīšanas vēsturi
- Manga no bibliotēkas
+ No bibliotēkas
Pievienot manga bibliotēkai\?
- Attiecas arī uz visām manga manā bibliotēkā
- Atiestatīt visas šīs mangas nodaļas
+ Attiecas arī uz visiem ierakstiem manā bibliotēkā
+ Atiestatīt visas šī ieraksta nodaļas
Nevarēja lejupielādēt nodaļas, jo trūkst krātuves vietas
Brīdinājums: liela apjoma lejupielāde var izraisīt to, ka avoti kļūst lēnāki un/vai bloķē Tachiyomi. Pieskarieties, lai uzzinātu vairāk.
Tachiyomi ir nepieciešams WebView
diff --git a/i18n/src/main/res/values-lv/strings.xml b/i18n/src/main/res/values-lv/strings.xml
index 7c8bffad37..8056b9dabe 100644
--- a/i18n/src/main/res/values-lv/strings.xml
+++ b/i18n/src/main/res/values-lv/strings.xml
@@ -34,7 +34,6 @@
Pārdēvēt kategoriju
Noteikt kategorijas
Rediģēt vāku
- Beigt
Pārtraukt
Iepriekšēja nodaļa
Nākama nodaļa
@@ -63,7 +62,7 @@
Izsekošana
Papildu iestatījumi
Par programmu
- Common
+ Biežs
Tīkla savienojums nav pieejams
Nav pieejams Wi-Fi savienojums
Nevarēja lejupielādēt nodaļu neparedzētas kļūdas dēļ
@@ -85,7 +84,7 @@
%02d min, %02d sec
Atjaunošana pabeigta
Trūkstošie avoti:
- Dublējumā nav nevienas manga.
+ Dublējumā nav neviena bibliotēkas ieraksta.
Paplašinājumu informācija
Paplašinājumi
Abi
@@ -142,7 +141,7 @@
Vienmēr
Bloķēt, ja dīkstāvē
Vajadzīgs atbloķēt
- Drošība
+ Drošība un privātums
Pārvaldiet paziņojumus
Apstipriniet izeju
Datuma formāts
@@ -206,7 +205,7 @@
Instalētājs
Shizuku nedarbojas
Dinamisks
- Kopējā manga
+ Visi ieraksti
Atcelt visu šai sērijai
Pēc augšupielādes datuma
NSFW (18+) avoti
@@ -224,7 +223,7 @@
- Vakar
- Pirms %1$d dienām
- Izlaist sēriju atjaunināšanu
+ Izlaist ierakstu atjaunināšanu
Biežāk uzdotie jautājumi un rokasgrāmatas
Aizvērt
Laikspiedols
@@ -248,7 +247,7 @@
Instalējiet un startējiet Shizuku, lai izmantotu Shizuku kā paplašinājuma instalētāju.
Pilnekrāns
Rādīt skāriena zonu pārklājumu
- Ja dubultlappušu sadalījuma izvietojums neatbilst lasīšanas virzienam
+ Ja sadalīto plato lapu izvietojums neatbilst lasīšanas virzienam
Animēt lappušu pārejas
Rādīt lappušu numuru
Rādīt lasīšanas režīmu
@@ -286,10 +285,10 @@
Uzticams
Neoficiāls
Neuzticams
- Šis paplašinājums vairs nav pieejams.
+ Šis paplašinājums vairs nav pieejams. Tas var nedarboties pareizi un var radīt problēmas ar lietotni. Ieteicams to atinstalēt.
Šis paplašinājums nav no oficiālo Tachiyomi paplašinājumu saraksta.
- Divu lappušu sadalīšana
- Apvērst divu lappušu sadalījuma izvietojumu
+ Sadalīt platas lapas
+ Apvērst dalītās lapas izvietojumu
Rādīt saturu izgriezuma apgabalā
Dubultpieskāriena animācijas ātrums
Apgriezt apmales
@@ -360,7 +359,7 @@
Lasīšanas režīms
No labās puses uz kreiso
Vertikāls
- Manga
+ Bibliotēkas ieraksti
Ātrs
Noklusējuma rotācijas tips
Augsts
@@ -375,7 +374,7 @@
Navigācija
Invertēt skaļuma regulēšanas taustiņus
Rādīt ar ilgu pieskārienu
- Izveido mapes atbilstoši manga nosaukumam
+ Izveido mapes atbilstoši ieraksta nosaukumam
Fona krāsa
Mēroga tips
Ietilpt ekrānā
@@ -383,38 +382,37 @@
Tālummaiņas sākuma pozīcija
Zems
Atspējots
- Izslēgto kategoriju manga netiks lejupielādēta pat tad, ja tās ir arī iekļautajās kategorijās.
- Automātiski sadalīt garus attēlus
+ Izslēgto kategorijas ieraksti netiks lejupielādēti pat tad, ja tie ir iekļautajās kategorijās.
Skaļuma regulēšanas taustiņi
Konfidencialitātes politika
Var izmantot, lai atjaunotu pašreizējo bibliotēku
- Uzlabo lasītāja veiktspēju, sadalot garus lejupielādētus attēlus.
- Pakalpojumi, kas nodrošina uzlabotus līdzekļus konkrētiem avotiem. Pievienojot bibliotēkai, manga tiks automātiski izsekota.
+ Uzlabo lasītāja veiktspēju, sadalot garus lejupielādētus attēlus
+ Pakalpojumi, kas nodrošina uzlabotus līdzekļus konkrētiem avotiem. Pievienojot bibliotēkai, ieraksts tiks automātiski izsekots.
Izveidot dublējumu
Dublējuma atrašanās vieta
Nederīgs dublējuma fails
Automātiskā dublēšana
Dublējumu biežums
- Automātiskā dublēšana ir ļoti ieteicama. Kopijas vajadzētu glabāt arī citās vietās.
+ Kopijas vajadzētu glabāt arī citās vietās.
Dažiem ražotājiem ir papildu lietojumprogrammu ierobežojumi, kas iznīcina fona pakalpojumus. Šajā vietnē ir vairāk informācijas par to, kā to izlabot.
- Vienvirziena sinhronizācija, lai atjauninātu izsekošanas pakalpojumu nodaļas progresu. Iestatiet izsekošanu atsevišķiem manga ierakstiem, izmantojot izsekošanas pogu.
+ Vienvirziena sinhronizācija, lai atjauninātu izsekošanas pakalpojumu nodaļas progresu. Iestatiet izsekošanu atsevišķiem ierakstiem, izmantojot izsekošanas pogu.
Palīdz ar fona bibliotēku atjauninājumiem un dublējumiem
Akumulatora optimizācija jau ir atspējota
Drukāt verbose žurnālus sistēmas žurnālā (samazina programmas veiktspēju)
Izsekošanas rokasgrāmata
Saglabā avārijas žurnālu
Saglabā kļūdu žurnālus failā priekš koplietošanas ar izstrādātājiem
- Iekļaut tikai piespraustos avotus
+ Globālajā meklēšanā izmantot tikai piespraustos avotus
Atsvaidzināt bibliotēkas vākus
Ieraksti izdzēsti
Nav bibliotēkas ierakstu, ko dublēt
Atjaunošana atcelta
Dublēšana/atjaunošana var nedarboties pareizi, ja ir atspējota MIUI Optimization.
- %1$d grāmatas, kas nav bibliotēkas, ir datu bāzē
+ %1$d ierakstu, kas nav bibliotēkas, ir datu bāzē
Tīkls
Dati
Izmantots: %1$s
- Datu bāze tīra
+ Nav ko tīrīt
Atspējot akumulatora optimizāciju
Atjaunot bibliotēku no dublējuma faila
Maksimālais dublējumu skaits
@@ -434,7 +432,6 @@
Pārbaudīt, vai nav atjauninājumu
Notīrīt WebView datus
WebView dati notīrīti
- Avārijas žurnāli saglabāti
Fona darbība
Nevarēja atvērt ierīces iestatījumus
Serviss
@@ -456,8 +453,7 @@
Pieteikumvārds
Inkognito režīms
Rezultāti nav atrasti
- Nozīmītes
- Filtrē visu manga bibliotēkā
+ Filtrēt visus ierakstus bibliotēkā
Vairāk rezultātu nav
Pieteicies
Izrakstīties no %1$s\?
@@ -542,7 +538,6 @@
Atvērt vietnē GitHub
Šī Android versija vairs netiek atbalstīta
Lejupielādē…
- Nevarēja sadalīt lejupielādēto attēlu
Progress
Izlaists
Attēlu nevarēja ielādēt
@@ -550,14 +545,13 @@
Priekš šīs sērijas
Pamests
- - Priekš %d
- - Priekš %d
- - Priekš %d
+ - Priekš %d ierakstiem
+ - Priekš %d ieraksta
+ - Priekš %d ierakstiem
Izlaists, jo neviena nodaļa nav izlasīta
Atlasiet vāka attēlu
Atlasiet dublējuma failu
- Avāriju žurnāli
Iepriekšējā lapa
Kopēt
Jaunā versija ir pieejama no oficiālajām versijām. Pieskarieties, lai uzzinātu, kā migrēt no neoficiālajām F-Droid versijām.
@@ -604,7 +598,7 @@
Atrastas jaunas nodaļas
Nodaļa %1$s un vēl %2$d
Nav atrasta failu atlases programma
- Lūdzu, pievienojiet manga savai bibliotēkai, pirms to darāt
+ Lūdzu, pievienojiet ierakstu savai bibliotēkai, pirms to darāt
- %1$d jaunu nodaļu
- %1$d jauna nodaļa
@@ -620,7 +614,7 @@
Atjaunināti noklusējuma nodaļu iestatījumi
Palielināt ainavas attēlu
Nākošā lapa
- Pārslēgt lapu, kamēr pietuvināts
+ Pieskaroties izplest platus attēlus
Nevar atvērt pēdējo lasīto nodaļu
Pārlasīšana
Vai dzēst lejupielādētās nodaļas\?
@@ -646,7 +640,7 @@
- Nodaļa %1$s un vēl 1
- Nodaļa %1$s un vēl %2$d
- Pieskarieties, lai instalētu
+ Pieskarieties, lai instalētu atjauninājumu
Pielāgots vāks
Nepabeigto saraksts
Sākuma datums
@@ -678,4 +672,111 @@
Darba sākšanas rokasgrāmata
Jums vēl nav nevienas kategorijas.
Lejupieladēšanas rinda
+ Nederīga lietotāja agent virkne
+ Izlaist vienādas nodaļas
+ Tādējādi no %s tiks noņemts iepriekš atlasītais beigu datums
+ Dzēst kategoriju
+ Lasīšanas režīmi, displejs, navigācija
+ Avārijas žurnāli, akumulatora optimizācija
+ Manuālā un automātiskā dublēšana
+ Sadalīt augstus attēlus (BETA)
+ Paisuma vilnis
+ Vairāku
+ Lejupielādēt uz priekšu
+ Darbojas tikai ar ierakstiem bibliotēkā un tad, ja pašreizējā un nākamā nodaļa jau ir lejupielādēta
+ Jūsu bibliotēkā ir ieraksts ar tādu pašu nosaukumu.
+\n
+\nVai joprojām vēlaties turpināt\?
+ Nederīgs lejupielāžu indekss
+ Pārskats
+ Statistika
+ Sākts
+ Lejupielādēts
+ Lokālais
+ Nederīga atrašanās vieta: %s
+ Nezināms nosaukums
+ Izsekotāji
+ Lasīts
+ Izsekotie ieraksti
+ Vidējais vērtējums
+ Izmantots
+ Nav piemērojams
+ %dd
+ %dm
+ %ds
+ Populārs
+ Vai tu esi pārliecināts\?
+ Tikko
+ Atjaunināt kategoriju
+ Atvērt nejaušu ierakstu
+ Noņemt visu
+ Turpināt lasīt poga
+ Automātiska lejupielāde lasīšanas laikā
+ Pieejams, bet avots nav instalēts: %s
+ Krātuves atļaujas nav piešķirtas
+ Kopēts starpliktuvē
+ Bibliotēka pēdējo reizi atjaunināta: %s
+ Izlaists, jo sērijai nav nepieciešami atjauninājumi
+ Tādējādi no %s tiks noņemts iepriekš atlasītais sākuma datums
+ Pārklājums
+ Aplikācijas slēdzis, ekrāna aizsargāšana
+ Aplikācijas valoda, paziņojumi
+ Kopēt starpliktuvē
+ Automātiskā lejupielāde, lejupielādes rinda
+ Vienvirziena progresa sinhronizācija, uzlabota sinhronizācija
+ Avoti, paplašinājumi, globālā meklēšana
+ Tēma, datuma un laika formāti
+ Kategorijas, globāli atjauninājumi
+ Rādīt nelasīto skaitu uz atjauninājumu ikonas
+ Logrīks nav pieejams, ja ir iespējota lietotņu bloķēšana
+ RARv5 formāts netiek atbalstīts
+ %dh
+ InternalError: Par plašāku informāciju skatiet avārijas žurnālu
+ Vai vēlaties dzēst kategoriju \"%s\"\?
+
+ - Trūkst %1$s nodaļu
+ - Trūkst %1$s nodaļa
+ - Trūkst %1$s nodaļas
+
+ Iespējams, trūkst nodaļu
+
+ - Nākošā %d nodaļa
+ - Nākošā %d nodaļa
+ - Nākošās %d nodaļās
+
+ %1$s kļūda: %2$s
+ Atjauninājums jau darbojas
+ %s radās neparedzēta kļūda. Mēs iesakām izveidot šo ziņojumu ekrānuzņēmumu, atrast avārijas žurnālu un pēc tam kopīgot to mūsu atbalsta kanālā Discord lietotnē.
+ Pagrieziet platas lapas, lai tās ietilptu
+ Apvērst orientācija pagrieztām platām lapām
+ Sadalīt augstus attēlus
+ Slēpt ierakstus, kas jau ir bibliotēkā
+ Vai noņemt datumu\?
+ Kategorija ir tukša
+ Skatiet savus nesen atjauninātos bibliotēkas ierakstus
+ Jūs gatavojaties noņemt \"%s\" no savas bibliotēkas
+
+ - Nākamā nelasītā nodaļa
+ - Nākamā nelasītā nodaļa
+ - Nākamās %d nelasītas nodaļas
+
+ Worker info
+ Ne tagad
+ Meklē…
+ F-Droid versijas netiek oficiāli atbalstītas.
+\nPieskarieties, lai uzzinātu vairāk.
+ Pārbauda lejupielādes
+ Lietotāja agent virknes lauks nedrīkst būt tukšs
+ Šajā kategorijā nav atrasts neviens ieraksts
+ Noklusējuma lietotāja agent virkne
+ Atiestatīt noklusējuma lietotāja agent virkni
+ Piespiediet lietotni atkārtoti pārbaudīt lejupielādētās nodaļas
+ Ak nē!
+ Restartējiet lietotni
+ Pabeigtie ieraksti
+ Lasīšanas ilgums
+ Ieraksti
+ Globālajā atjauninājumā
+ Kopā
+ *obligāti
\ No newline at end of file
diff --git a/i18n/src/main/res/values-mr/strings.xml b/i18n/src/main/res/values-mr/strings.xml
index 191322ba8f..823e2ef3c1 100644
--- a/i18n/src/main/res/values-mr/strings.xml
+++ b/i18n/src/main/res/values-mr/strings.xml
@@ -71,7 +71,6 @@
पुढील अध्याय
मागील अध्याय
विराम द्या
- थांबवा
अध्याय बघा
कव्हर बदलवा
श्रेणी सेट करा
diff --git a/i18n/src/main/res/values-ms/strings.xml b/i18n/src/main/res/values-ms/strings.xml
index ddffa767b6..e62a28c716 100644
--- a/i18n/src/main/res/values-ms/strings.xml
+++ b/i18n/src/main/res/values-ms/strings.xml
@@ -37,7 +37,6 @@
Ubah nama kategori
Tetapkan kategori
Ubah muka hadapan
- Henti
Henti sebentar
Bab sebelumnya
Bab seterusnya
@@ -406,7 +405,6 @@
Segar semula metadata secara automatik
Pindah sumber
Grid selesa
- Penanda
Tab
Tunjuk tab kategori
Tiada muka surat ditemui
@@ -477,10 +475,8 @@
Menaik
Mengikut nombor bab
Mengikut tarikh muat naik
- Log kerosakan
- Log kerosakan disimpan
Simpan log ralat untuk dikongsi dengan pembangunan aplikasi
- Kumpulan log kerosakan
+ Kongsi log kerosakan
Tarikh mula
Tarikh selesai
Dijejaki
@@ -634,14 +630,12 @@
Tiada sumber dipasang ditemui
Tiada sumber ditemui
Kiraan belum dibaca
- Memisah imej yang tinggi
Meningkatkan prestasi pembaca
Muka surat %d tidak dijumpai ketika memisah
Lokasi fail muka surat %d tidak ditemui
Menetapkan semula mod bacaan dan orientasi semua siri
Tetapan pembaca diset semula
Set semula tetapan pembaca setiap siri
- Tidak dapat memisahkan imej yang dimuat turun
Hmm, ini agak menjanggalkan
Tidak dapat set semula tetapan pembaca
Versi
@@ -698,7 +692,7 @@
Ralat Tidak Dijangka Berlaku
Mulakan semula aplikasi
Sumber, sambungan, carian keseluruhan
- %s mengalami ralat tidak dijangka. Kami mencadangkan anda untuk tangkapan skrin mesej ini, mengumpulkan log kerosakan, dan kemudian mengongsi ia pada Discord kami di saluran bantuan.
+ %s mengalami ralat tidak dijangka. Kami mencadangkan anda untuk kongsi log kerosakan pada Discord kami di saluran bantuan.
Tajuk tidak diketahui
Lokasi tidak sah: %s
Rentetan ejen pengguna tidak sah
@@ -724,4 +718,12 @@
Sembunyikan entri yang sudah ada di dalam pustaka
Salin ke papan keratan
Kemas kini kategori
+ Memisah imej yang tinggi
+ Tindanan
+
+ - Hilang %1$s bab
+
+ Berkemungkinan ada bab yang hilang
+ Terbalikkan orientasi muka surat lebar yang diputarkan
+ Putar muka surat lebar agar muat
\ No newline at end of file
diff --git a/i18n/src/main/res/values-nb-rNO/strings.xml b/i18n/src/main/res/values-nb-rNO/strings.xml
index eba3cfdb89..011976d8cb 100644
--- a/i18n/src/main/res/values-nb-rNO/strings.xml
+++ b/i18n/src/main/res/values-nb-rNO/strings.xml
@@ -38,7 +38,6 @@
Gi kategori nytt navn
Sett kategorier
Rediger omslag
- Stopp
Pause
Forrige kapittel
Neste kapittel
@@ -114,7 +113,7 @@
Venstre til høyre
Høyre til venstre
Loddrett
- Webtoon
+ Nett-tegneserie
Skalatype
Tilpass skjerm
Strekk
@@ -383,7 +382,6 @@
Ukjent forfatter
Lokal kildeguide
Sjekk nettsted i WebView
- Merker
- %1$s gjenstående
- %1$s gjenstående
@@ -470,7 +468,6 @@
Sidepolstring
Neste side
Forrige side
- Feillogg
Ingen app for filvelging funnet
Veiledning for kilde-flytting
Er du sikker\? All historikk vil gå tapt.
@@ -481,8 +478,7 @@
Startdato
Nedlastede kapitler
Inkognitomodus
- Feillogger lagret
- Dump feillogg
+ Del krasjlogger
Lagrer feillogger i en fil som kan sendes til utviklerne
Navigasjonsoppsett
Kant
@@ -691,10 +687,8 @@
Slett kategori
InternalError: Sjekk krasjlogger for mer informasjon
En uventet feil oppstod
- %s fikk en uventet feil. Vi foreslår at du tar et skjermbilde av denne meldingen, dumper krasjloggene og derette deler dette i vår støttekanal på Discord.
- Kunne ikke dele det nedlastede bildet
+ %s fikk en uventet feil. Vi foreslår at du deler krasjloggene i vår støttekanal på Discord.
Appspråk
- Del høye bilder
Ingen beskrivelse
Søk…
Fjern alt
@@ -736,4 +730,13 @@
Skjul oppføringer som allerede er i biblioteket
Kopier til utklippstavle
Oppdater kategori
+ Del opp høye bilder
+ Elementer
+
+ - Mangler %1$s kapittel
+ - Mangler %1$s kapitler
+
+ Kan mangle kapitler
+ Vend sideretning for roterte brede sider
+ Roter brede sider slik at de passer
\ No newline at end of file
diff --git a/i18n/src/main/res/values-ne/strings-aniyomi.xml b/i18n/src/main/res/values-ne/strings-aniyomi.xml
index 240b4adf5e..98ffae7afa 100644
--- a/i18n/src/main/res/values-ne/strings-aniyomi.xml
+++ b/i18n/src/main/res/values-ne/strings-aniyomi.xml
@@ -1,7 +1,7 @@
वर्गहरू
- अनलक ताचियोमी
+ ताचियोमी अनलक गर्नुहोस्
पछिल्लो अपडेट चेक
इन्ट्री देखाउनुहोस्
लोकल माङ्गा
diff --git a/i18n/src/main/res/values-ne/strings.xml b/i18n/src/main/res/values-ne/strings.xml
index 28ab47bb45..b420036240 100644
--- a/i18n/src/main/res/values-ne/strings.xml
+++ b/i18n/src/main/res/values-ne/strings.xml
@@ -20,7 +20,7 @@
लग खोल्नुहोस्
पूर्ववत गर्नुहोस्
रिसेट गर्नुहोस्
- सेभ गर्नुहोस्
+ बचत गर्नुहोस्
साझा गर्नुहोस्
स्थापना गर्नुहोस्
तल सार्नुहोस्
@@ -53,7 +53,6 @@
अर्को अध्याय
अघिल्लो अध्याय
रोक्नुहोस्
- रोक्नुहोस्
अध्यायहरू हेर्नुहोस्
आवरण सम्पादन गर्नुहोस्
वर्गहरू सेट गर्नुहोस्
@@ -99,7 +98,7 @@
कुनै डाउनलोडहरू छैन
मद्दत
एक्सटेनशनको जानकारी
- एक्सटेनशनहरु
+ एक्सटेनशन
स्थानान्तरण
ब्याकअप र पुनर्स्थापना
स्रोतहरू
@@ -322,15 +321,14 @@
पिन गरिएका स्रोतहरू मात्र समावेश गर्नुहोस्
पुनर्स्थापना सम्पन्न भयो
- - %2$s एररको साथ %1$s मा सम्पन्न भयो
- - %2$s एररहरूसँग %1$s मा सम्पन्न भयो
+ - %2$s त्रुटिको साथ %1$s मा सम्पन्न भयो
+ - %2$s त्रुटिहरूसँग %1$s मा सम्पन्न भयो
कुकीहरू खाली गर्नुहोस्
तपाई के ब्याकअप गर्न चाहनुहुन्छ\?
ब्याकअप असफल भयो
HTTPS (DoH) माथि DNS
प्रभाव पार्न एप पुन: सुरु गर्न आवश्यक छ
- ब्याजहरू
आवरण अपडेट गर्न असफल भयो
यो गर्नु अघि कृपया आफ्नो पुस्तकालयमा यस इन्ट्रीलाई राख्नुहोस्
डाउनलोडर
@@ -352,7 +350,7 @@
नेटवर्क
ब्याकअप पुनर्स्थापना असफल भयो
तपाईंले ब्याकअपको प्रतिलिपिहरू अन्य ठाउँहरूमा पनि राख्नु पर्छ।
- नयाँ अध्यायहरु पायो
+ नयाँ अध्यायहरु फेला पर्यो
- %1$d नयाँ अध्याय
- %1$d नयाँ अध्यायहरू
@@ -361,7 +359,7 @@
असक्षम
परिष्कृत सेवाहरू
पुनर्स्थापना रद्द गरियो
- एरर
+ त्रुटि
तन्काउनुहोस्
स्मार्ट फिट
स्वचालित
@@ -387,7 +385,7 @@
कुनै नेटवर्क जडान उपलब्ध छैन
ट्र्याकिङ सेवाहरूमा अध्याय प्रगति अपडेट गर्न एकतर्फी सिङ्क। तिनीहरूको ट्र्याकिङ बटनबाट व्यक्तिगत इन्ट्रीहरूको लागि ट्र्याकिङ सेट अप गर्नुहोस्।
ट्र्याकरहरूमा लगइन छैनन्:
- हराएको स्रोतहरु:
+ छुटेको स्रोतहरु:
पाँचौं अन्तिम अध्याय
\"बहिष्कृत\" वर्गहरूमा इन्ट्री \"समावेश गरिएका\" वर्गहरूमा भए पनि डाउनलोड गरिने छैन।
@@ -398,12 +396,12 @@
हालको पुस्तकालय पुनर्स्थापना गर्न प्रयोग गर्न सकिन्छ
कुकीहरू खाली गरियो
डाटा
- क्यास खाली गर्दा एरर भयो
+ खाली गर्दा त्रुटि भयो
स्थिति
नयाँ अध्यायहरूको लागि जाँच गर्दै
डाउनलोड गर्दै…
अपडेट स्थापना गर्न ट्याप गर्नुहोस्
- अप्रत्याशित एररका कारण अध्याय डाउनलोड गर्न सकिएन
+ अप्रत्याशित त्रुटिका कारण अध्याय डाउनलोड गर्न सकिएन
अध्याय %1$s
मूल आकार
जुम सुरु स्थिति
@@ -421,7 +419,7 @@
ब्याकअप रिस्टोर गर्नुहोस्
ब्याकअप फाइलबाट पुस्तकालय पुनर्स्थापना गर्नुहोस्
स्वचालित ब्याकअपहरू
- ब्याकअपले कुनै पनि पुस्तकालयका इन्ट्री समावेश गर्दैन।
+ ब्याकअपमा कुनै पनि पुस्तकालयका इन्ट्री समावेश छैन।
ब्याकअप स्थान
ब्याकअप फ्रिक्वेन्सी
अधिकतम ब्याकअपहरु
@@ -432,9 +430,9 @@
क्यास खाली गरियो। %1$d फाइलहरू हटाएका छन्
अध्याय %1$s र %2$d अरु
- डाउनलोड एरर
+ डाउनलोड त्रुटि
एक्सटेनशन अपडेटहरु
- विकासकर्ताहरूसँग साझेदारी गर्नको लागि फाइलमा एरर लगहरू बचत गर्दछ
+ विकासकर्ताहरूसँग साझेदारी गर्नको लागि फाइलमा त्रुटि लगहरू बचत गर्दछ
ब्याकग्राउण्ड गतिविधि
नयाँ के छ
डेटाबेसमा %1$d गैर-पुस्तकालय इन्ट्रीहरु
@@ -455,14 +453,13 @@
%1$s मा लग इन गर्नुहोस्
पुस्तकालयका आवरणहरु ताजा गर्नुहोस्
गुप्त मोड असक्षम गर्नुहोस्
- अज्ञात एरर
+ अज्ञात त्रुटि
डाउनलोड गरिएका अध्यायहरू
अध्याय फेला परेन
तपाईंसँग पिन गरिएको स्रोतहरू छैनन्
अवैध अध्याय ढाँचा
तपाईंको पुस्तकालयमा सबै इन्ट्री फिल्टर गर्नेछ
- क्र्यास लगहरू डम्प गर्नुहोस्
- क्र्यास लगहरू सुरक्षित गरियो
+ क्र्यास लगहरू साझा गर्नुहोस्
ब्याट्री अप्टिमाइजेसन असक्षम पार्नुहोस्
अज्ञात
कुनै परिणाम फेला परेन
@@ -500,7 +497,7 @@
प्रकाशन सकियो
रद्द गरेको
अन्तरालमा छ
- पुस्तकालयमा राख्नुहोस
+ पुस्तकालयमा राख्नुहोस्
पुस्तकालयमा राखियो
पुस्तकालयमा छ
समाप्त
@@ -529,7 +526,7 @@
अपलोड मिति द्वारा
- %d अध्याय छोड्दै, या त स्रोत सँग छैन वा यसलाई फिल्टर गरिएको छ
- - %d अध्यायहरु छोड्दै, या त स्रोत सँग छैन वा तिनिहरू फिल्टर गरिएका छन्
+ - %d अध्यायहरु छोड्दै, या त स्रोत सँग छैन वा यसलाई फिल्टर गरिएको छ
स्रोत स्थानान्तरण गाइड
स्थानान्तरण गर्नको लागि स्रोत चयन गर्नुहोस्
@@ -540,7 +537,7 @@
के तपाईँले चयन गर्नुभएको अध्यायहरू हटाउन चाहनुहुन्छ\?
अध्याय सेटिङ
पूर्वनिर्धारित रूपमा सेट गर्नुहोस्
- यो छवि आवरण कलाको रूपमा राख्न चाहनुहुन्छ\?
+ यो छवि आवरण को रूपमा राख्न चाहनुहुन्छ\?
नपढिएको
आवरण बचत भयो
यो श्रृंखलाको लागि
@@ -628,7 +625,6 @@
यो एन्ड्रोइड संस्करण अब समर्थित छैन
सुरु गर्ने गाइड
ट्याप गर्दा चौडा छविहरू प्यान गर्नुहोस्
- क्र्यास लगहरू
जूम ल्यान्डस्केप छवि
%1$d अपडेट(हरू) असफल भयो
%1$d अपडेट(हरू) छोडियो
@@ -653,9 +649,9 @@
अग्लो छविहरू विभाजित गर्नुहोस् (BETA)
थुप्रै
डुप्लिकेट अध्यायहरू छोड्नुहोस्
- \"जारी राख्नुहोस्\" बटन देखाउनुहोस्
+ जारी राख्नुहोस् बटन
तथ्याङ्क
- सुरु गरेको
+ सुरु भएको
लोकल
डाउनलोड गरेको
मापन नगरिएको नेटवर्कमा मात्र
@@ -666,7 +662,6 @@
संस्करण
भाषा
उमेर मूल्याङ्कन
- अग्लो छविहरू विभाजित गर्नुहोस्
अहिले हैन
अनियमित इन्ट्री खोल्नुहोस्
तपाईको पुस्तकालयमा एउटै नामको इन्ट्री छ।
@@ -674,7 +669,7 @@
\nके तपाईं अझै जारी राख्न चाहनुहुन्छ\?
पढ्ने सूची
पुस्तकालय पछिल्लो पटक अपडेट गरिएको: %s
- %s एक अप्रत्याशित त्रुटिमा पर्यो। हामी तपाईंलाई यो सन्देशको स्क्रिनसट, क्र्यास लगहरू डम्प गर्ने, र त्यसपछि Discord मा रहेको हाम्रो support च्यानलमा साझेदारी गर्न सुझाव दिन्छौं।
+ %s एक अप्रत्याशित त्रुटिमा पर्यो। समर्थन को लागी हामी तपाईंलाई हाम्रो Discord को #support च्यानलमा क्र्यास लगहरू साझेदारी गर्न सुझाव दिन्छौं।
GitHub मा खोल्नुहोस्
डाउनलोड गरिएका अध्यायहरू पुन: जाँच गर्न एपलाई फोर्स गर्नुहोस्
एकतर्फी प्रगति सिंक, परिष्कृत सिंक
@@ -691,7 +686,6 @@
औसत स्कोर
तपाईंको भर्खरै अपडेट गरिएको पुस्तकालय इन्ट्रीहरू हेर्नुहोस्
उपलब्ध छ तर स्रोत स्थापना गरिएको छैन: %s
- अन्य
क्लिपबोर्डमा प्रतिलिपि गरियो
यस वर्गमा कुनै इन्ट्रीहरु फेला परेनन्
अज्ञात शीर्षक
@@ -713,7 +707,6 @@
अधूरो सूची
कुनै स्थापित स्रोत फेला परेन
पृष्ठ %d को फाइल मार्ग फेला पार्न सकेन
- डाउनलोड गरिएको छवि विभाजित गर्न सकिएन
कस्टम आवरण
इच्छा सूची
लोकप्रिय
@@ -723,7 +716,7 @@
यसले %s बाट तपाइँको पहिले चयन गरिएको सुरु मिति हटाउनेछ
यसले %s बाट तपाइँको पहिले चयन गरिएको समाप्त मिति हटाउनेछ
एप को भाषा, सूचनाहरू
- एउटा अपडेट पहिले नै चलिरहेको छ
+ एउटा अपडेट पहिले नै जारी छ
ल, यो के भयो
स्थापना गरिएको छैन
पढ्ने मोड र सबै शृङ्खलाहरूको अभिमुखीकरण रिसेट गर्दछ
@@ -765,4 +758,13 @@
%dदि
%dघ
वर्ग अपडेट गर्नुहोस्
+ अग्लो छविहरू विभाजित गर्नुहोस्
+ आवरण ओभरले
+ फिट गर्न चौडा पृष्ठहरू घुमाउनुहोस्
+ घुमाइएका चौडा पृष्ठहरूको अभिमुखीकरण फ्लिप गर्नुहोस्
+ अध्यायहरू छुटेको हुन सक्छ
+
+ - %1$s अध्याय छुटेको छ
+ - %1$s अध्यायहरू छुटेका छन्
+
\ No newline at end of file
diff --git a/i18n/src/main/res/values-nl/strings.xml b/i18n/src/main/res/values-nl/strings.xml
index f9ad1d96ad..c902bb6e1e 100644
--- a/i18n/src/main/res/values-nl/strings.xml
+++ b/i18n/src/main/res/values-nl/strings.xml
@@ -32,7 +32,6 @@
Categorie hernoemen
Zet categorieën
Omslag wijzigen
- Stop
Pauzeren
Vorige hoofdstuk
Volgende hoofdstuk
@@ -410,7 +409,6 @@
Geen pagina\'s gevonden
Op uploaddatum
Tabbladen
- Badges
Data
Missende bronnen:
Back-up bevat geen items.
@@ -484,10 +482,8 @@
Rand
Kindle-achtig
L-vormig
- Crashlogboeken
Einddatum
Begindatum
- Crashlogboeken opgeslagen
Slaat foutmeldingslogboeken op in een bestand om te delen met de ontwikkelaars
Crashlogboeken opslaan
Aflopend
@@ -632,14 +628,12 @@
Breng serie naar boven
Geen gelezen hoofdstukken
Overgeslagen want de serie is af
- Split tall afbeeldingen
Verbetert de prestaties van de lezer
Volledige lijst
Onvoltooide lijst
Download vooruit
WebView-gegevens gewist
WebView-gegevens wissen
- Kan gedownloade afbeelding niet splitsen
RARv5-indeling wordt niet ondersteund
Pagina %d niet gevonden tijdens het splitsen
InternalError: Bekijk de crash logs voor meer informatie
@@ -714,7 +708,6 @@
%1$s-fout: %2$s
User agent terugzetten naar standaardwaarde
Datum verwijderen\?
- Anders
Verlanglijst
Worker-informatie
Aangepaste omslag
diff --git a/i18n/src/main/res/values-nn/strings.xml b/i18n/src/main/res/values-nn/strings.xml
index 16af6657b1..89561f2265 100644
--- a/i18n/src/main/res/values-nn/strings.xml
+++ b/i18n/src/main/res/values-nn/strings.xml
@@ -37,7 +37,6 @@
Rediger kategoriar
Rediger omslag
Vis kapittel
- Stopp
Pause
Neste kapittel
Prøv igjen
diff --git a/i18n/src/main/res/values-pl/strings.xml b/i18n/src/main/res/values-pl/strings.xml
index 3a47513448..177c882c59 100644
--- a/i18n/src/main/res/values-pl/strings.xml
+++ b/i18n/src/main/res/values-pl/strings.xml
@@ -33,7 +33,6 @@
Zmień kategorie
Edytuj okładkę
Ilość rozdziałów
- Zatrzymaj
Wstrzymaj
Poprzedni rozdział
Następny rozdział
@@ -432,7 +431,6 @@
Migracja
Nie znaleziono żadnych stron
Karty
- Plakietki
Pokaż karty kategorii
Wyłącz wszystkie
Włącz wszystkie
@@ -502,10 +500,8 @@
Nie znaleziono aplikacji do przeglądania plików
Wymagane ponowne zalogowanie do MAL
Pokazuj na liście źródeł i rozszerzeń
- Logi crashy
Data zakończenia
Data rozpoczęcia
- Logi crasha zapisane
Zapisuje logi błędów do pliku do udostępnienia programistom
Zapisz logi crasha
Strefy kliknięcia
@@ -656,7 +652,6 @@
Zamknij
Przenieś serię na górę
Ilość nieprzeczytanych
- Rozdzielaj wysokie obrazy
Kliknij, by dowiedzieć się więcej
Otwórz na GitHubie
Reset wszystkich ustawień czytnika
@@ -671,7 +666,6 @@
Błąd podczas zapisywania obrazka
Wyczyść dane WebView
Dane WebView wyczyszczone
- Nie można rozdzielić pobranego obrazka
Poprawia wydajność czytnika
Brak opisu
Lista życzeń
diff --git a/i18n/src/main/res/values-pt-rBR/strings.xml b/i18n/src/main/res/values-pt-rBR/strings.xml
index 97657d8745..84a8217b27 100644
--- a/i18n/src/main/res/values-pt-rBR/strings.xml
+++ b/i18n/src/main/res/values-pt-rBR/strings.xml
@@ -36,7 +36,6 @@
Renomear categoria
Definir categorias
Editar a capa
- Parar
Pausar
Capítulo anterior
Próximo capítulo
@@ -422,7 +421,6 @@
Atualizar os metadados automaticamente
Migrar
Grade confortável
- Selos
Mostrar as abas de categoria
Abas
Nenhuma página encontrada
@@ -495,12 +493,10 @@
Kindle
Em forma de L
Zonas de toque
- Registros de travamentos
Data de término
Data de início
- Registros de travamento salvos
Salva os registros de erro em um arquivo para o compartilhamento com os desenvolvedores
- Exportar os registros de travamentos
+ Compartilhar os registros de travamentos
Decrescente
Crescente
Pelo número do capítulo
@@ -658,7 +654,6 @@
Nenhuma fonte instalada foi encontrada
Nenhuma fonte encontrada
Contagem de não lidos
- Dividir imagens compridas
Melhora o desempenho do leitor
Página %d não encontrada durante a divisão
Não foi possível encontrar o caminho do arquivo da página %d
@@ -666,7 +661,6 @@
Todas as configurações do leitor foram redefinidas
Não foi possível redefinir as configurações do leitor
Redefinir as configurações do leitor em cada série
- Não foi possível dividir a imagem transferida
Bem, isso é esquisito
Versão
Idioma
@@ -724,7 +718,7 @@
Exportar registros de travamento, otimizações de bateria
Ocorreu um erro inesperado
Reiniciar o aplicativo
- %s teve um erro inesperado. Nós sugerimos que você capture a tela com essa mensagem, exporte os registros de travamento e então compartilhá-los no nosso canal de suporte no Discord.
+ %s teve um erro inesperado. Nós sugerimos que você compartilhe os registros de travamento em nosso canal de suporte no Discord.
Título desconhecido
Local inválido: %s
Valor de user agent inválido
@@ -750,4 +744,14 @@
Ocultar títulos que já estão na biblioteca
Copiar para a área de transferência
Atualizar categoria
+ Dividir imagens compridas
+ Sobreposição
+ Rotacionar páginas largas para caber
+ Inverter a orientação das páginas largas rotacionadas
+ Pode ter capítulos faltando
+
+ - Faltando %1$s capítulo
+ - Faltando %1$s capítulos
+ - Faltando %1$s capítulos
+
\ No newline at end of file
diff --git a/i18n/src/main/res/values-pt/strings.xml b/i18n/src/main/res/values-pt/strings.xml
index dc085fa375..d03c1fac0a 100644
--- a/i18n/src/main/res/values-pt/strings.xml
+++ b/i18n/src/main/res/values-pt/strings.xml
@@ -27,7 +27,6 @@
Renomear categoria
Definir categorias
Alterar capa
- Parar
Pausar
Capítulo anterior
Capítulo seguinte
@@ -444,7 +443,6 @@
Nenhuma página encontrada
Por data de envio
Abas
- Distintivos
Dados
Fontes em falta:
O backup não possui nenhum item da biblioteca.
@@ -528,10 +526,8 @@
Por data de envio
Monitorizado
Borda
- Registos de falhas
Data de conclusão
Data de início
- Registos de falhas guardados
Guardar registos de falhas
Guarda registos de erros num ficheiro para partilhar com os desenvolvedores
Mostrar número de itens
@@ -684,9 +680,7 @@
Mostrar fontes fixadas duplicadas
Repita as fontes fixadas nos seus respetivos grupos de idiomas
Contagem de não lidos
- Dividir imagens altas
Melhora o desempenho do leitor
- Não foi possível dividir a imagem transferida
Bem, isto é estranho
Não foi possível repor as definições do leitor
Todas as definições do leitor reiniciadas
@@ -770,7 +764,6 @@
Local inválido: %s
Erro em %1$s: %2$s
Itens
- Outros
Duração de leitura
Bloqueio da app, ecrã seguro
Exportar registos de travamento, otimizações de bateria
@@ -800,4 +793,14 @@
- Próximos %d capítulos
Atualizar categoria
+ Dividir Imagens altas
+ Sobreposição
+ Girar páginas largas para caber
+ Virar a orientação de páginas largas giradas
+
+ - Falta %1$s capítulo
+ - Faltam %1$s capítulos
+ - Faltam %1$s capítulos
+
+ Talvez esteja a faltar capítulos
\ No newline at end of file
diff --git a/i18n/src/main/res/values-ro/strings.xml b/i18n/src/main/res/values-ro/strings.xml
index 6134f0575b..281f12dfee 100644
--- a/i18n/src/main/res/values-ro/strings.xml
+++ b/i18n/src/main/res/values-ro/strings.xml
@@ -40,7 +40,6 @@
Redenumește categoria
Setați categoriile
Modifică coperta
- Stop
Pauză
Capitolul anterior
Capitolul următor
@@ -424,7 +423,6 @@
Neoficial
Nu s-au găsit pagini
Tab-uri
- Insigne
Afișează filele categoriei
Dezactivați toate
Activează toate
@@ -505,10 +503,8 @@
Afișare număr de elemente
Nici unul
DNS prin HTTPS (DoH)
- Jurnale de erori fatale
Data de terminare
Data începerii
- Jurnalele de erori fatale salvate
Aruncați jurnalele de erori fatale
Salvează jurnalele de erori într-un fișier pentru partajarea cu dezvoltatorii
Zone de atingere
@@ -586,7 +582,6 @@
Mod întunecat negru pur
Servicii care oferă caracteristici îmbunătățite pentru anumite surse. Manga sunt urmărite în mod automat atunci când sunt adăugate la biblioteca dumneavoastră.
Ar trebui să păstrați copii ale backupurilor și în alte locuri.
- Împărțiți imagini înalte
Îmbunătățește performanța cititorului
Închide
Când bateria nu este scăzută
@@ -610,7 +605,6 @@
Scăzut
Ștergeți datele WebView
Lista în așteptare
- Nu s-a putut diviza imaginea descărcată
Cel mai scăzut
Lista neterminată
Scurt (Astăzi, Ieri)
diff --git a/i18n/src/main/res/values-ru/strings.xml b/i18n/src/main/res/values-ru/strings.xml
index 64e3439e34..596898cfcf 100644
--- a/i18n/src/main/res/values-ru/strings.xml
+++ b/i18n/src/main/res/values-ru/strings.xml
@@ -41,7 +41,6 @@
Сортировка
Алфавит
Последнее прочитанное
- Остановить
Обновить библиотеку
Все
Чёрный
@@ -431,7 +430,6 @@
Удобная сетка
Перенести
Вкладки
- Значки
Вкладки категорий
Страницы не найдены
Включить всё
@@ -506,12 +504,10 @@
По краям
Kindle-подобная
L-образная
- Журнал с ошибками
Дата окончания
Дата начала
- Журнал с ошибками сохранён
Сохраняет журнал с ошибками в файл для отправки разработчикам
- Выгрузить журнал с ошибками
+ Поделиться журналом с ошибками
По убыванию
По возрастанию
Номер главы
@@ -670,7 +666,6 @@
Не найдено установленных источников
Не найдено источников
Оставшиеся главы
- Разделять длинные изображения
Страница %d не найдена, при разделении
Улучшает производительность читалки
Путь к файлам страницы %d не найден
@@ -678,7 +673,6 @@
Сбрасывает режим чтения и ориентацию для всех серий
Все настройки читалки сброшены
Не удалось сбросить настройки читалки
- Не удалось разделить загруженное изображение
Упс, ошибочка вышла
Возрастное ограничение
Версия
@@ -737,7 +731,7 @@
Блокировка приложения, защита экрана
Выгрузка журнала с ошибками, оптимизация батареи
Возникла непредвиденная ошибка
- %s столкнулось с непредвиденной ошибкой. Мы рекомендуем сделать скриншот этого сообщения, выгрузить журнал с ошибками и после поделиться этим журналом/скиншотом в нашем Discord сервере в канале поддержки.
+ %s столкнулось с непредвиденной ошибкой. Мы рекомендуем поделиться журналом с ошибками в нашем Discord сервере в ветке support.
Неизвестное название
Недопустимое расположение: %s
Недопустимый параметр user agent
@@ -764,4 +758,15 @@
Скрывать серии, уже находящиеся в библиотеке
Копировать в буфер обмена
Обновить категорию
+ Разделять длинные изображения
+ Наложение
+ Развернуть широкие страницы под размер экрана
+ Могут отсутствовать главы
+
+ - Отсутствует %1$s глава
+ - Отсутствуют %1$s главы
+ - Отсутствуют %1$s глав
+ - Отсутствуют %1$s глав
+
+ Отразить ориентацию развёрнутых широких страниц
\ No newline at end of file
diff --git a/i18n/src/main/res/values-sa/strings.xml b/i18n/src/main/res/values-sa/strings.xml
index 6c4409abc5..defaa45bbd 100644
--- a/i18n/src/main/res/values-sa/strings.xml
+++ b/i18n/src/main/res/values-sa/strings.xml
@@ -42,7 +42,6 @@
वर्गस्य नाम परिणमतु
वर्गानि स्थापयतु
आवरणं सम्पादयतु
- निरामयतु
विरामयतु
पूर्वः अध्यायः
पुनः यतताम्
@@ -397,7 +396,6 @@
यद्यपि अन्तर्भूतवर्गेषु स्थिताः सन्ति तथापि वर्जितवर्गेषु स्थिताः माङ्गाः अवारोपिताः न भविष्यन्ति।
अनुप्रज्ञानसेवासु अध्यायप्रगतिं नवीकर्तुं एकमार्गसमाकलनम्। तदीयात् अनुप्रज्ञानगण्डात् व्यक्तिगतेभ्यः माङ्गानिवेशेभ्यः अनुप्रज्ञानं स्थापयतु॥
दत्तांशः
- पदकानि
प्लवनखण्डाः
अन्यम्
तिथिः
@@ -434,7 +432,6 @@
अग्रिमपुटः
दत्तनिधिः मार्जितः
पृष्ठभूमिकार्यक्रमम्
- ध्वंसेतिवृत्तानि रक्षितानि
विद्युत्कोषवृद्धिकरणम् अशक्तं करोतु
नूतनं किम्
टाबलेट् यू॰ऐ
@@ -504,7 +501,6 @@
पूर्णम्
अनुप्रयोगनवीकरणानि
विस्तारनवीकरणानि
- ध्वंसेतिवृत्तानि
- १ शेषम्
- २ शेषे
diff --git a/i18n/src/main/res/values-sah/strings.xml b/i18n/src/main/res/values-sah/strings.xml
index 3bcef45066..271a6cec17 100644
--- a/i18n/src/main/res/values-sah/strings.xml
+++ b/i18n/src/main/res/values-sah/strings.xml
@@ -136,7 +136,6 @@
Аныгыскы түһүмэх
Ааспыттанны түһүмэх
Тохтобул
- Тохтоо
Түһүмэхтэри көр
Таһын уларытыы
Бөлө(ххө/хтөргө) киллэр
@@ -296,7 +295,6 @@
Түмүк суох
Эбии түмүк суох
Кыбытыктар
- Бэлиэлэр
Бөлөх саҥардыыта
Биллибэт сыыһыы
Эн таҕыстын
diff --git a/i18n/src/main/res/values-sc/strings.xml b/i18n/src/main/res/values-sc/strings.xml
index bd27dad53c..15017139d0 100644
--- a/i18n/src/main/res/values-sc/strings.xml
+++ b/i18n/src/main/res/values-sc/strings.xml
@@ -40,7 +40,6 @@
Cambia su nùmene de sa categoria
Imposta sas categorias
Muda sa cobertedda
- Firma
Pone in pàusa
Capìtulu antepostu
Capìtulu imbeniente
@@ -416,7 +415,6 @@
Grìllia discansosa
Peruna pàgina agatada
Ischedas
- Distintivos
Ammustra sas ischedas de sas categorias
Disabìlita totu
Abìlita totu
@@ -484,12 +482,10 @@
Zonas de tocu
A tipu Kindle
A forma de L
- Registros de sos arrestos anòmalos
Data de fine
Data de incumintzu
- Registros de sos arrestos anòmalos sarvados
Sarva sos registros de sos errores in unu documentu pro lu cumpartzire cun sos isvilupadores
- Iscàrriga sos registros de sos arrestos anòmalos
+ Cumpartzi sos registros de sos arrestos anòmalos
In achirrada
Creschente
Pro nùmeru de capìtulu
@@ -646,7 +642,6 @@
Contu de non lèghidos
Peruna fonte agatada
Peruna fonte installada agatada
- Partzi sas immàgines artas
Megiorat su rendimentu de su leghidore
Pàgina %d no agatada partzende
Oja, custu est impitzosu
@@ -655,7 +650,6 @@
Resetat sa modalidade de leghidura e s\'orientamentu de totu sas sèries
Totu sas impostatziones de su leghidore resetadas
No at fatu a resetare sas impostatziones de su leghidore
- No at fatu a partzire s\'immàgine iscarrigada
Classificatzione pro edade
Limba
Versione
@@ -699,7 +693,7 @@
Permissu de archiviatzione non cuntzessu
Brincadu ca sa sèrie non tenet bisòngiu de agiornamentos
Chirca…
- %s at tentu un\'errore non prevìdidu. Ti cussigiamus de sarvare un\'ischermada de custu messàgiu, iscarrigare sos registros de sas serraduras anòmalas e de cumpartzire totu custu in su canale de suportu nostru de Discord.
+ %s at tentu un\'errore non prevìdidu. Ti cussigiamus de cumpartzire sos registros de sas serraduras anòmalas in su canale de suportu nostru de Discord.
Torra a allùghere s\'aplicatzione
Limba de s\'aplicatzione, notìficas
Tema, data e formadu de s\'ora
@@ -734,4 +728,13 @@
Cua sos elementos giai in sa biblioteca
Còpia in punta de billete
Agiorna sa categoria
+ Partzi sas immàgines artas
+ Istratu superiore
+ Diant pòdere mancare capìtulos
+ Gira sas pàginas largas pro las adatare
+ Fùrria s\'orientamentu de sas pàginas largas giradas
+
+ - Mancat %1$s capìtulu
+ - Mancant %1$s capìtulos
+
\ No newline at end of file
diff --git a/i18n/src/main/res/values-sdh/strings.xml b/i18n/src/main/res/values-sdh/strings.xml
index 265a919f01..d5d525f227 100644
--- a/i18n/src/main/res/values-sdh/strings.xml
+++ b/i18n/src/main/res/values-sdh/strings.xml
@@ -61,7 +61,6 @@
نیشان نەکردنی چاپتەر
دەستکاری کردن
دەربارە
- وەستان
ڕاگرتنی کاتی
لابردن
دەست پێکردن
diff --git a/i18n/src/main/res/values-sk/strings.xml b/i18n/src/main/res/values-sk/strings.xml
index def218b7dc..299c1a4e2b 100644
--- a/i18n/src/main/res/values-sk/strings.xml
+++ b/i18n/src/main/res/values-sk/strings.xml
@@ -41,7 +41,6 @@
Premenovať kategóriu
Určiť kategórie
Upraviť obal
- Zastaviť
Pauza
Predošlá kapitola
Nasledujúca kapitola
@@ -417,7 +416,6 @@
Ďalší
Obnovenie zálohy zlyhalo
Obnovenie bolo zrušené
- Odznaky
Karty
Reťazec User-Agent nemôže byť prázdny
Obnovenie nastavení čítačky pre jednotlivé série
@@ -527,7 +525,6 @@
Automatické sťahovanie
Opakovať pripnuté zdroje v ich príslušných jazykových skupinách
Nenašiel sa žiadny zdroj
- Stiahnutý obrázok sa nepodarilo rozdeliť
Priebeh
Vymazať históriu
Chyba pri ukladaní obalu
@@ -554,7 +551,6 @@
Kap. %1$s - %2$s
Strana %d sa pri rozdeľovaní nenašla
- Logy zlyhaní
Publikovanie ukončené
Nemáte žiadne pripnuté zdroje
Táto verzia systému Android už nie je podporovaná
@@ -616,7 +612,6 @@
Otvoriť na GitHub-e
Nová verzia je k dispozícii z oficiálnych vydaní. Klepnutím sa dozviete, ako migrovať z neoficiálnych vydaní F-Droid.
Príručka Začíname
- Uložené logy o zlyhaní
Zakázať režim inkognito
Menej
5%
@@ -674,7 +669,6 @@
Levanduľa
20%
Mangy vo vylúčených kategóriách nebudú stiahnuté, aj keď sú tiež v zahrnutých kategóriách.
- Rozdelenie vysokých obrázkov
Čistenie databázy
Vymazanie údajov WebView
Údaje WebView boli vymazané
diff --git a/i18n/src/main/res/values-sq/strings.xml b/i18n/src/main/res/values-sq/strings.xml
index 6fcc91446c..d77e1c630a 100644
--- a/i18n/src/main/res/values-sq/strings.xml
+++ b/i18n/src/main/res/values-sq/strings.xml
@@ -108,7 +108,6 @@
Përditësimet e kapitullit
U anashkalua
Shkarkimet e indeksimit
- Imazhi i shkarkuar nuk mund të ndahej
Faqja %d nuk u gjet gjatë ndarjes
Shkarkues
Përditësoni aplikacionin WebView për përputhshmëri më të mirë
@@ -249,7 +248,6 @@
Fshi kategorinë
Redakto kopertinën
Shiko kapitujt
- Ndalo
Ndalo
Kapitulli i mëparshëm
Kapitulli tjetër
@@ -383,7 +381,6 @@
Shkarkoni përpara
Shkarkim automatik gjatë leximit
Ruaje si arkiv CBZ
- Ndani imazhet e gjata
Udhëzues gjurmimi
Shërbime të përmirësuara
@@ -593,7 +590,6 @@
Aktiviteti në sfond
Ndihmon me përditësimet dhe kopjet rezervë të bibliotekës në sfond
Ruan regjistrat e gabimeve në një skedar për t\'i ndarë me zhvilluesit
- Regjistrat e aksidenteve u ruajtën
Optimizimi i baterisë është tashmë i çaktivizuar
Cilësimet e pajisjes nuk mund të hapeshin
Kontrolloni për përditësime
@@ -612,9 +608,7 @@
Kategoria po përditësohet
Nga biblioteka
Kapitujt e shkarkuar
- Distinktivët
Skedat
- Tjetër
Nuk ka më rezultate
Kontrolloni faqen e internetit në WebView
Burimi lokal
@@ -746,7 +740,6 @@
Kjo do të heqë datën e leximit të këtij kapitulli. A je i sigurt\?
Kjo do të heqë datën e fillimit të zgjedhur më parë nga %s
Ky version Android nuk mbështetet më
- Regjistrat e përplasjeve
Faqja e internetit
Version
Cfare ka te re
diff --git a/i18n/src/main/res/values-sr/strings.xml b/i18n/src/main/res/values-sr/strings.xml
index bd1dfaca0f..b7e44b36b7 100644
--- a/i18n/src/main/res/values-sr/strings.xml
+++ b/i18n/src/main/res/values-sr/strings.xml
@@ -40,7 +40,6 @@
Преименуј категорију
Изабери категорије
Промени омот
- Заустави
Паузирај
Претходно поглавље
Наредно поглавље
@@ -528,7 +527,6 @@
Нема доступне интернет везе
Преузимање је завршено
Ажурирања додатака
- Логови крешовања
Претходна страница
Следећа страница
Потамни (Burn)
@@ -565,7 +563,6 @@
- %1$s преосталих
Грешке
- Логови крешовања су сачувани
Подешавања поглавља
Скоро
Данас
@@ -610,7 +607,6 @@
Једносмерна синхронизација за ажурирање броја прочитаних поглавља у трекерима. За појединачно праћење наслова додирни дугме за њихово праћење.
Подаци
Инкогнито мод
- Беџеви
Омот је ажуриран
%1$s: %2$s, страница %3$d
За овај наслов
@@ -666,7 +662,6 @@
Извори, екстензије, глобална претрага
Ручне и аутоматске резервне копије
Преузми аутоматски током читања
- Раздели високе слике
Копирано
Сва подешавања читача су ресетована
Присили апликацију да поново провери преузета поглавља
@@ -675,7 +670,6 @@
Widget није доступан када је омогућено закључавање апликације
Листа жеља
Доступно, али извор није преузет: %s
- Остало
F-Droid верзије нису званично подржане.
\nДодирните да сазнате више.
Уклони датум\?
@@ -740,7 +734,6 @@
Уклони све
Упс!
Поново покрени апликацију
- Није могуће разделити преузету слику
Није могуће отворити последње прочитано поглавље
Претражи…
Преузми унапред
diff --git a/i18n/src/main/res/values-sv/strings-aniyomi.xml b/i18n/src/main/res/values-sv/strings-aniyomi.xml
index a1fda1b4fa..715f95df31 100644
--- a/i18n/src/main/res/values-sv/strings-aniyomi.xml
+++ b/i18n/src/main/res/values-sv/strings-aniyomi.xml
@@ -2,8 +2,8 @@
Kategorier
Lås upp Aniyomi
- Sista manga-uppdateringen
- Visa manga
+ Senaste uppdateringskontroll
+ Visa inlägg
Lokal manga
Med olästa kapitel
Visa antalet olästa på ikonen Uppdateringar
@@ -24,15 +24,15 @@
Rensa kapitelcache när appen stängs
Rensa databas
Ta bort historik för manga som inte finns i ditt bibliotek
- Är du säker\? Lästa kapitel och framsteg av icke-bibliotek manga kommer att gå förlorade
+ Är du säker\? Lästa kapitel och framsteg för poster som inte är biblioteksinlägg kommer att gå förlorade
Uppdaterar status, betyg och senaste kapitel läst från spårningstjänsterna
Pausar läshistoriken
- Manga från biblioteket
+ Från biblioteket
Lägg till manga i biblioteket\?
Fel
Pausad
- Gäller även all manga i biblioteket
- Återställ alla kapitel för denna manga
+ Gäller även alla inlägg i biblioteket
+ Återställ alla kapitel för denna post
Det gick inte att ladda ner kapitelt på grund av lågt lagringsutrymme
Varning: stora massnerladdningar kan leda till att källor blir långsamma och/eller blockerar Tachiyomi. Tryck för att få veta mer.
WebView krävs för Tachiyomi
diff --git a/i18n/src/main/res/values-sv/strings.xml b/i18n/src/main/res/values-sv/strings.xml
index 1c1124fa6a..def5884120 100644
--- a/i18n/src/main/res/values-sv/strings.xml
+++ b/i18n/src/main/res/values-sv/strings.xml
@@ -1,7 +1,7 @@
Namn
- Manga
+ Biblioteksposter
Kapitel
Spårning
Historik
@@ -40,7 +40,6 @@
Byt namn på kategori
Välj kategorier
Redigera omslaget
- Stop
Paus
Föregående kapitel
Nästa kapitel
@@ -180,7 +179,7 @@
Uppdaterar kategori
Lokal
Inga mer resultat
- Lokal manga
+ Lokal källa
Andra
Global sökning…
Senaste
@@ -242,14 +241,14 @@
Kunde inte ladda ned kapitlen. Du kan försöka igen i nedladdningssektionen
Nya kapitel hittades
Misslyckades att uppdatera omslag
- Vänligen lägg till mangan i ditt bibliotek innan du gör detta
+ Vänligen lägg till post i ditt bibliotek innan du gör detta
Välj omslagsbild
Välj säkerhetskopia
Ladda ner
Inga nya uppdateringar tillgängliga
Söker efter uppdateringar…
Laddar ner…
- Tryck på för att installera
+ Tryck för att installera uppdateringen
Nedladdningsfel
Ny version tillgänglig!
Inga nedladdningar
@@ -281,7 +280,7 @@
Uppdatera
Bibliotek
Obsolet
- Detta tillägg är inte längre tillgänglig.
+ Detta tillägg är inte längre tillgängligt. Det kanske inte fungerar korrekt och kan orsaka problem med appen. Vi rekommenderar att du avinstallerar det.
Datumformat
Global uppdatering
Logga ut från %1$s\?
@@ -297,8 +296,8 @@
På
Följ systemet
Hantera aviseringar
- Säkerhet
- Kräv upplåsning
+ Säkerhet och integritet
+ Kräver upplåsning
Lås vid inaktivitet
Alltid
Aldrig
@@ -332,8 +331,8 @@
E-postadress
Visa alltid kapitelövergång
- - För %d titel
- - För %d titlar
+ - För %d post
+ - För %d poster
Meny
Ändra ordning
@@ -382,12 +381,12 @@
Säkerhetskopiering pågår redan
%02d minuter, %02d sekunder
Endast nedladdat
- Filtrerar all manga i ditt bibliotek
+ Filtrerar alla poster i ditt bibliotek
- %1$s återstående
- %1$s återstående
- Inkludera endast fästa källor
+ Sök endast i pinnade källor i global sökning
Läsläge
För denna serie
Grå
@@ -402,11 +401,11 @@
Det gick inte att öppna enhetsinställningarna
Uppdatera bibliotekets omslag
- Envägssynkronisering för att uppdatera kapitelförloppet i spårningstjänster. Ställ in spårning för enskilda manga från spårningsknappen under manga info.
+ Envägssynkronisering för att uppdatera kapitlets framsteg i spårningstjänsterna. Ställ in spårning för enskilda inlägg från deras spårningsknapp.
Efter uppladdningsdatum
Data
Saknade källor:
- Säkerhetskopian innehåller ingen manga.
+ Säkerhetskopian innehåller inga biblioteksposter.
Ogiltig säkerhetskopia
Detta tillägg kommer inte från den officiella Tachiyomi-tilläggslistan.
Inofficiell
@@ -416,7 +415,6 @@
Inaktivera alla
Hittade inga sidor
Flikar
- Emblem
Visa nuvarande läge när läsaren öppnas
Visa läsläge
Visa kategoriflikar
@@ -480,10 +478,8 @@
Visa i källor och tilläggslistor
Ingen filväljare-app hittades
Vänligen logga in på MAL igen
- Kraschloggar
- Kraschloggar sparade
Sparar felloggar till en fil för delning med utvecklarna
- Dumpa kraschloggar
+ Dela kraschloggar
Fallande
Efter kapitelnummer
Efter uppladdningsdatum
@@ -514,7 +510,7 @@
Inkludera: %s
Ingen
Datum för hämtning av kapitel
- Manga i uteslutna kategorier laddas inte ner även om de också ingår i inkluderade kategorier.
+ Inlägg i uteslutna kategorier laddas inte ner även om de också ingår i inkluderade kategorier.
Ladda ned automatiskt
Tryck för att se detaljer
Denna Android-version stöds inte längre
@@ -522,7 +518,7 @@
Liggande
Porträtt
Rotationstyp
- Skapar mappar enligt mangatitel
+ Skapa mappar enligt posternas titel
Spara sidor i separata mappar
Åtgärder
Gråskala
@@ -552,7 +548,7 @@
Uppdaterar biblioteket ... (%1$d / %2$d)
Denna tracker är endast kompatibel med Komga-källan.
Säkerhetskopiering/återställning kanske inte fungerar korrekt om MIUI-optimering är inaktiverat.
- Tjänster som erbjuder förbättrade funktioner för specifika källor. Manga spåras automatiskt när de läggs till i ditt bibliotek.
+ Tjänster som erbjuder förbättrade funktioner för specifika källor. Inlägg spåras automatiskt när de läggs till i ditt bibliotek.
Förbättrade tjänster
Rent svart mörkt läge
Yotsuba
@@ -594,7 +590,7 @@
Äldre
Installatör
Installerar tillägg…
- Total manga
+ Totalt antal inlägg
Installera och starta Shizuku för att använda Shizuku som tilläggsinstallationsprogram.
Skriv ut utförliga loggar till systemloggen (minskar appprestanda)
Du bör också förvara säkerhetskopiorna på andra ställen.
@@ -606,10 +602,10 @@
Var 3:e dag
Uppdatera alla
Appuppdateringar
- %1$d manga i databasen som inte är biblioteksmanga
+ %1$d poster i databasen som inte är biblioteksposter
Inget att rensa
För hjälp med att åtgärda fel i biblioteksuppdateringar, se %1$s
- Hoppa över att uppdatera titlar
+ Hoppa över uppdatering av poster
Avbruten
På uppehåll
Publiceringen avslutad
@@ -645,7 +641,6 @@
Upprepa källor som är upptagna i sina respektive språkgrupper
Ingen installerad källa hittades
Antal olästa
- Dela upp höga bilder
Förbättrar läsarens prestanda
Sida %d hittades inte under delning
Ingen källa hittad
@@ -662,7 +657,6 @@
Önskelista
Klart lista
Åldersklassificering
- Kunde inte dela den nedladdade bilden
Version
Språk
Det går inte att öppna det senast lästa kapitlet
@@ -708,12 +702,12 @@
Ogiltig plats: %s
Lagrings rättigheter inte tillagda
Hoppat över för att serien inte behöver uppdateras
- %s stötte på ett oväntat fel. Vi förslår att du tar en skärmdump av det här meddelandet, dumpar fel loggarna, och delar det i vår support kanal på Discord.
+ %s stötte på ett oväntat fel. Vi föreslår att du delar med dig av kraschloggen i vår supportkanal på Discord.
Appspråk, notifikationer
Tema, datum och tids format
Källor, tillägg, global sökning
Starta om applikationen
- Ett oväntat fel inträffade
+ Hoppsan!
Dumpa krasch logg, batterioptimering
Applikations lås, säkerhetsskärm
Nedladdningskö
@@ -723,9 +717,26 @@
Nedladdade
Tillgänglig men källan är ej installerad: %s
Hoppa över duplicerade kapitel
- Visa knappen för att återuppta läsning
+ Fortsätt läsa knapp
Inte nu
Öppna en slumpmässig manga
Gör nedladdningsindexet ogiltigt
Tvinga appen att läsa in nedladdade kapitel på nytt
+ F-Droid-versioner stöds inte officiellt.
+\nTryck för att få veta mer.
+ Dela höga bilder
+ Uppdatera kategori
+ Kopiera till urklipp
+ Dölja poster som redan finns i biblioteket
+ Kontrollerar nedladdningar
+ Överlägg
+
+ - Saknar %1$s kapitel
+ - Saknar %1$s kapitel
+
+ Rotera breda sidor så att de passar
+ Vänd orientering av roterade breda sidor
+ Det kan saknas kapitel
+ Information om arbetare
+ *krävs
\ No newline at end of file
diff --git a/i18n/src/main/res/values-te/strings.xml b/i18n/src/main/res/values-te/strings.xml
index 0ff46bd1f7..4a7ab17a45 100644
--- a/i18n/src/main/res/values-te/strings.xml
+++ b/i18n/src/main/res/values-te/strings.xml
@@ -58,7 +58,6 @@
తదుపరి అధ్యాయము
మునుపటి అధ్యాయము
తాత్కాలికముగా నిలిపి వేయుము
- ఆపుము
అధ్యాయాములను చూడుము
ముఖచిత్రమును సవరించుము
విభాగములను క్రమీకరించుము
diff --git a/i18n/src/main/res/values-th/strings.xml b/i18n/src/main/res/values-th/strings.xml
index fd005e495f..fa6ebdfea7 100644
--- a/i18n/src/main/res/values-th/strings.xml
+++ b/i18n/src/main/res/values-th/strings.xml
@@ -74,7 +74,6 @@
บุ๊คมาร์คตอน
ยกเลิกบุ๊คมาร์คตอน
ตั้งหมวดหมู่
- หยุด
หยุดชั่วคราว
นำออก
ดำเนินการต่อ
@@ -236,7 +235,7 @@
อัปเดตปกแล้ว
หน้า: %1$d
ไม่พบตอนถัดไป
- ไม่สามารถโหลดรูปภาพได้
+ ไม่สามารถโหลดภาพนี้ได้
ใช้ภาพนี้เป็นรูปปก\?
อ่านจบแล้ว:
ปัจจุบัน:
@@ -439,7 +438,6 @@
ใช้ล่าสุด
ตรวจสอบเว็บไซต์ใน WebView
แท็บ
- เครื่องหมาย
ตอนที่ดาวน์โหลดแล้ว
ออกจากระบบแล้ว
ออกจากระบบ
@@ -461,9 +459,8 @@
การเพิ่มประสิทธิภาพแบตเตอรี่ถูกปิดใช้งานอยู่แล้ว
ช่วยในการอัปเดตและสำรองข้อมูลคลังในพื้นหลัง
ปิดการใช้งานการเพิ่มประสิทธิภาพแบตเตอรี่
- บันทึกบันทึกข้อขัดข้องแล้ว
บันทึกบันทึกข้อผิดพลาดลงในแฟ้มเพื่อแบ่งปันกับผู้พัฒนา
- ดัมพ์บันทึกข้อขัดข้อง
+ แบ่งปันบันทึกข้อขัดข้อง
โหลดปกในคลังใหม่
ข้อมูล
ต้องรีสตาร์ทแอปจึงจะมีผล
@@ -505,7 +502,6 @@
- ของเรื่อง %d
หน้าก่อน
- บันทึกข้อขัดข้อง
การอัปเดตส่วนขยาย
ข้อผิดพลาด
เสร็จสมบูรณ์
@@ -634,7 +630,6 @@
ไม่พบแหล่งที่มาที่ติดตั้งแล้ว
ไม่พบแหล่งที่มาใด ๆ
จำนวนตอนที่ยังไม่ได้อ่าน
- แยกรูปภาพสูงยาว
ไม่พบหน้า %d ขณะกำลังแยกหน้า
เพิ่มประสิทธิภาพตัวอ่าน
ไม่พบตำแหน่งไฟล์ของหน้า %d
@@ -642,7 +637,6 @@
รีเซ็ตโหมดการอ่านและการจัดวางของทุกเรื่อง
รีเซ็ตค่ากำหนดตัวอ่านทั้งหมดแล้ว
ไม่สามารถรีเซ็ตค่ากำหนดตัวอ่าน
- ไม่สามารถแยกภาพที่ดาวน์โหลดไว้ได้
เอ่อ นี้มันงี่เง่าชะมัด
เวอร์ชัน
ภาษา
@@ -697,7 +691,7 @@
การล็อกแอป, หน้าจอความปลอดภัย
ดัมพ์บันทึกข้อขัดข้อง, การเพิ่มประสิทธิภาพแบตเตอรี่
เกิดความผิดพลาดอย่างไม่ได้คาดคิด
- %s เกิดข้อผิดพลาดอย่างไม่คาดคิด ขอแนะนำให้คุณบันทึกภาพหน้าจอข้อความนี้ จากนั้นดัมพ์บันทึกข้อขัดข้อง แล้วส่งลงไปในช่องสนับสนุนของเราใน Discord
+ %s เกิดข้อผิดพลาดอย่างไม่คาดคิด ขอแนะนำให้คุณแบ่งปันบันทึกข้อขัดข้อง แล้วส่งลงไปในช่องสนับสนุนของเราใน Discord
ตำแหน่งไม่ถูกต้อง: %s
ไม่ทราบชื่อเรื่อง
เริ่มแอปพลิเคชันใหม่
@@ -717,4 +711,12 @@
ซ่อนรายการที่มีอยู่ในคลังอยู่แล้ว
คัดลอกไปยังคลิปบอร์ด
อัปเดตหมวดหมู่
+ อาจเป็นตอนที่ไม่พบ
+ แยกภาพสูงยาว
+ โอเวอร์เลย์
+
+ - ไม่พบ %1$s ตอน
+
+ หมุนหน้ากว้างให้พอดี
+ พลิกการวางแนวของหน้ากว้างที่หมุน
\ No newline at end of file
diff --git a/i18n/src/main/res/values-tr/strings.xml b/i18n/src/main/res/values-tr/strings.xml
index 7aef115d65..6347bd6973 100644
--- a/i18n/src/main/res/values-tr/strings.xml
+++ b/i18n/src/main/res/values-tr/strings.xml
@@ -2,7 +2,7 @@
Ad
Kategoriler
- Manga
+ Kütüphane girdileri
Bölümler
İzleme
Geçmiş
@@ -38,10 +38,9 @@
Ekle
Kategori ekle
Kategorileri düzenle
- Kategoriyi yeniden adlandır
+ Kategorileri yeniden adlandır
Kategorileri ayarla
Kapağı düzenle
- Durdur
Duraklat
Önceki bölüm
Sonraki bölüm
@@ -416,7 +415,6 @@
Taşın
Rahat ızgara
Sekmeler
- Rozetler
Kategori sekmelerini göster
Sayfa bulunamadı
Tümünü devre dışı bırak
@@ -485,12 +483,10 @@
Kenar
Kindle gibi
L şeklinde
- Çökme günlükleri
Bitirme tarihi
Başlama tarihi
- Çökme günlükleri kaydedildi
Geliştiricilerle paylaşmak için hata günlüklerini bir dosyaya kaydeder
- Çökme günlüklerini kaydet
+ Çökme günlüklerini paylaş
Azalan
Artan
Bölüm numarasına göre
@@ -647,7 +643,6 @@
Kurulu kaynak bulunamadı
Kaynak bulunamadı
Okunmayan sayısı
- Uzun görselleri böl
Okuyucu performansını iyileştirir
Bölünürken sayfa %d bulunamadı
Sayfa %d dosya yolu bulunamadı
@@ -655,7 +650,6 @@
Tüm serilerin okuma kipini ve yönünü sıfırlar
Tüm okuyucu ayarları sıfırlandı
Okuyucu ayarları sıfırlanamadı
- İndirilen görsel bölünemedi
Peki, bu garip
Sürüm
Dil
@@ -673,7 +667,7 @@
Açıklama yok
Lavanta
Kategoriyi sil
- \"%s\" kategorisini silmek istiyor musunuz\?
+ \"%s\" kategorilerini silmek istiyor musunuz\?
Dahili hata: Daha fazla 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
@@ -704,7 +698,7 @@
Uygulama kilidi, güvenli ekran
Çökme günlükleri dökümü, pil iyileştirmeleri
Uygulama dili, bildirimler
- Kategoriler, genel güncelleme
+ Kategorileri, genel güncelle
Kaynaklar, uzantılar, genel arama
Otomatik indir, önceden indir
Okuma kipi, görüntüleme, gezinme
@@ -712,7 +706,7 @@
Tek yönlü ilerleme eşitlemesi, gelişmiş eşitleme
Uygulamayı yeniden başlat
Beklenmeyen Bir Hata Oluştu
- %s beklenmeyen bir hatayla karşılaştı. Bu mesajın ekran görüntüsünü ve çökme günlüklerinin dökümünü almanızı, ardından Discord\'daki destek kanalımızda paylaşmanızı öneririz.
+ %s beklenmeyen bir hatayla karşılaştı. Çökme günlüklerini Discord\'daki destek kanalımızda paylaşmanızı öneririz.
Bilinmeyen başlık
Geçersiz konum: %s
Geçersiz kullanıcı aracısı dizgesi
@@ -737,4 +731,13 @@
Panoya kopyala
Kategoriyi güncelle
+ Uzun görselleri otomatik böl
+ Kaplama
+ Eksik bölümler olabilir
+
+ - %1$s bölüm eksik
+ - %1$s bölüm eksik
+
+ Geniş sayfaları sığdırmak için döndür
+ Döndürülen geniş sayfaların yönünü çevir
\ No newline at end of file
diff --git a/i18n/src/main/res/values-uk/strings.xml b/i18n/src/main/res/values-uk/strings.xml
index b6f9952462..9ad9fd817b 100644
--- a/i18n/src/main/res/values-uk/strings.xml
+++ b/i18n/src/main/res/values-uk/strings.xml
@@ -40,7 +40,6 @@
Перейменувати категорію
Помістити в категорію
Змінити обкладинку
- Стоп
Пауза
Попередній розділ
Наступний розділ
@@ -228,7 +227,7 @@
Обкладинку оновлено
Сторінка %1$d
Наступний розділ не знайдено
- Зображення не може бути завантажене
+ Зображення не вдалося завантажити
Ви бажаєте встановити цю картинку як обкладинку\?
Завершено:
Поточна:
@@ -399,7 +398,6 @@
Посібник місцевого джерела
Останній використаний
Вкладки
- Значки
- %1$s залишилось
- %1$s залишилось
@@ -502,12 +500,10 @@
Будь ласка, увійдіть в MAL знову
Показувати в списку джерел та розширень
NSFW (18+) джерела
- Логи падінь
Дата закінчення
Дата початку
- Логи падінь збережено
Зберігає логи помилок до файлу для надсилання розробникам
- Дамп логів про падіння
+ Поділіться журналами збоїв
Зони тицяння
Межа
Kindle-подібна
@@ -655,8 +651,6 @@
Пропущено, оскільки серія не почата
Немає елементів у бібліотеці для резервування
Сторінку %d не знайдено, при розділенні
- Неможливо розділити завантажене зображення
- Розділяти довгі зображення
Покращує продуктивність читалки
Дані WebView очищені
Очистити дані WebView
@@ -723,7 +717,7 @@
Рядок User agent не може бути пустим
Популярне
Автоматичне завантаження під час читання
- %s зіткнулось з непередбачуваною помилкою. Ми пропонуємо вам, зробити скріншот цього повідомлення, завантажити журнал помилок та поділитись ними з нами на нашому каналі підтримки в Discord.
+ %s зіткнулися з неочікуваною помилкою. Ми пропонуємо вам поділитися журналами збоїв у нашому каналі підтримки на Discord.
Мова застосунку, сповіщення
Тема, формат дати та часу
Категорії, глобальне оновлення
@@ -759,4 +753,15 @@
- Наступних %d розділів
Оновити категорію
+ Розділяти довгі зображення
+ Оверлей
+ Можливо, відсутні розділи
+ Повернути широкі сторінки за розміром
+ Перевернути орієнтацію обернутих широких сторінок
+
+ - Відсутній %1$s розділ
+ - Відсутні %1$s розділи
+ - Відсутні %1$s розділів
+ - Відсутні %1$s розділів
+
\ No newline at end of file
diff --git a/i18n/src/main/res/values-uz/strings.xml b/i18n/src/main/res/values-uz/strings.xml
index 8e62d40a39..d5aa8ed6a5 100644
--- a/i18n/src/main/res/values-uz/strings.xml
+++ b/i18n/src/main/res/values-uz/strings.xml
@@ -1,6 +1,5 @@
- Tõxatish
Bõlimlarni kõrish
Muqovani õzgartirish
Kategoriyalar õrnatish
diff --git a/i18n/src/main/res/values-vi/strings-aniyomi.xml b/i18n/src/main/res/values-vi/strings-aniyomi.xml
index 499e4970e5..eb565bae42 100644
--- a/i18n/src/main/res/values-vi/strings-aniyomi.xml
+++ b/i18n/src/main/res/values-vi/strings-aniyomi.xml
@@ -27,7 +27,7 @@
Bạn có chắc không\? Các chương đã đọc và tiến độ đọc các truyện không nằm trong thư viện sẽ bị mất
Cập nhật trạng thái, điểm số và chương cuối đã đọc từ dịch vụ theo dõi
Dừng lưu giữ lịch sử đọc
- Truyện từ thư viện
+ Từ thư viện
Thêm truyện vào thư viện\?
Lỗi
Đã tạm dừng
diff --git a/i18n/src/main/res/values-vi/strings.xml b/i18n/src/main/res/values-vi/strings.xml
index 5780147b31..d8f0187c4a 100644
--- a/i18n/src/main/res/values-vi/strings.xml
+++ b/i18n/src/main/res/values-vi/strings.xml
@@ -33,7 +33,6 @@
Đổi tên danh mục
Đặt danh mục
Sửa ảnh bìa
- Dừng lại
Tạm dừng
Chương trước
Chương kế
@@ -186,7 +185,7 @@
Đã cập nhật ảnh bìa
Trang: %1$d
Không tìm thấy chương kế tiếp
- Loạt ảnh hiện không thể được tải lên
+ Hình ảnh không thể hiển thị được
Bạn có chắc muốn đặt ảnh này làm ảnh bìa?
@@ -274,7 +273,7 @@
Đã thêm vào thư viện
Đã xóa khỏi thư viện
Muốn xóa các chương đã tải\?
- Đã sao chép từ bộ nhớ đệm:
+ Đã sao chép vào bảng tạm:
\n%1$s
Nguồn truyện không thể cài đặt: %1$s
Đọc lại
@@ -315,7 +314,7 @@
- %1$d chương mới có
- - Cho %d tiêu đề
+ - Cho %d chương
Kiểm tra các chương cập nhật mới
Đang cập nhật thư viện
@@ -371,7 +370,7 @@
Mục chính
Nguồn
Thêm
- Chỉ bao gồm các nguồn được ghim
+ Chỉ bao gồm những nguồn được ghim khi tìm kiếm toàn bộ
Đồng bộ một chiều để cập nhật tiến trình chương trong các dịch vụ theo dõi. Thiết lập theo dõi cho các truyện riêng lẻ từ nút theo dõi.
25%
20%
@@ -427,14 +426,11 @@
Ngày bắt đầu
Chế độ ẩn danh
Làm mới ảnh bìa trong thư viện
- Thông tin lỗi đã được lưu
- Tệp tin báo lỗi tạm
+ Chia sẻ tệp báo lỗi tạm
Lưu thông tin lỗi và gửi về nhà phát triển
Không tìm thấy ứng dụng chọn tệp
Trang trước
- Logs lỗi
Trang tiếp theo
- Huy hiệu
Đã cập nhật tuỳ chỉnh mặc định cho chương
Lỗi
Hoàn thành
@@ -464,7 +460,7 @@
Bạn có chắc chắn muốn lưu lại tuỳ chỉnh này hay không\?
Tuỳ chỉnh chương
Bởi ngày đăng
- Lưu vào bộ nhớ tạm thất bại
+ Sao chép vào bảng tạm thất bại
- %1$s chương
@@ -666,7 +662,6 @@
Xóa dữ liệu WebView
Chỉ trên mạng lưới không hạn định
Số lượng chưa đọc
- Tự động chia cắt những ảnh quá dài
Cải thiện khả năng đọc bằng việc cắt nhỏ các ảnh quá dài
Tất cả cài đặt đọc đã được cài lại
Không thể cài lại cài đặt đọc
@@ -687,7 +682,6 @@
Danh sách hoàn thành
Danh sách đang chờ
Danh sách muốn đọc
- Không thể chia cắt ảnh đã tải xuống
Trang %d không thể được tìm thấy khi chia cắt
Phân loại độ tuổi
Không thể tìm thấy đường dẫn của trang %d
@@ -728,6 +722,37 @@
Đồng bộ tiến trình đọc, đồng bộ nâng cao
Nguồn, tiện ích, tìm kiếm toàn bộ
Hàng chờ tải xuống
+ Bản dựng F-Droid không được hỗ trợ chính thức.
+\nNhấn để tìm hiểu thêm.
Bỏ qua chương đúp
Có sẵn nhưng nguồn không được cài đặt: %s
+ Ngày %d
+ Tách ra những hình ảnh dài
+ Giấu những truyện đã có trong thư viện
+ %d phút
+ Đang kiểm tra những truyện đã tải xuống
+ %s đã có lỗi. Bạn nên chia sẻ tệp báo lỗi tạm ở trong kênh hỗ trợ của chúng tôi trên Discord.
+ Cập nhật danh mục
+ Truyện này đã có sẵn trong thư viện bạn.
+\n
+\nBạn có muốn thêm lại không\?
+ Đã sao chép vào bảng tạm
+
+ - %d Chương tiếp theo
+
+ Sao chép vào bảng tạm
+ %d giây
+ %d giờ
+ Quay những trang rộng để vừa màn hình
+ Lật hướng của các trang rộng đã xoay
+ Phủ lên
+ Có thể đang thiếu chương
+
+ - Đang thiếu %1$s
+
+ Loại bỏ ngày\?
+ Thông tin worker
+ %1$s Lỗi: %2$s
+ N/A
+ *bắt buộc
\ No newline at end of file
diff --git a/i18n/src/main/res/values-zh-rCN/strings.xml b/i18n/src/main/res/values-zh-rCN/strings.xml
index 183a1fd332..f79acb6876 100644
--- a/i18n/src/main/res/values-zh-rCN/strings.xml
+++ b/i18n/src/main/res/values-zh-rCN/strings.xml
@@ -37,7 +37,6 @@
重命名分类
设置分类
编辑封面
- 停止
暂停
上一章
下一章
@@ -407,7 +406,6 @@
迁移
松散网格
标签
- 标记
显示分类标签
没有找到页面
全部禁用
@@ -473,12 +471,10 @@
边缘样式
Kindle 样式
L 样式
- 崩溃日志
阅读完毕日期
开始阅读日期
- 崩溃日志已保存
将错误日志保存到文件中,以便分享给开发者
- 导出崩溃日志
+ 分享崩溃日志
降序
升序
按章节编号
@@ -634,15 +630,13 @@
未找到已安装的图源
未找到图源
未读数
- 分割长图
可以改善阅读器的性能
拆分时未找到页 %d
找不到页面 %d 的文件路径
- 重置每一连载阅读器设置
- 重置所有连载的阅读模式和方向
+ 重置各作品阅读器独立设置
+ 重置所有作品的阅读模式和屏幕方向
已重置所有阅读器设置
- 无法重置阅读器设置
- 无法拆分下载的图像
+ 重置阅读器设置失败
额,尴尬了
年龄分级
版本
@@ -687,7 +681,7 @@
已跳过,因为作品无需更新
搜索…
发生了意外错误
- %s 发生了意外错误。建议你将此信息截图,导出崩溃日志,并在 Discord #support 频道或者 GitHub 上反馈。
+ %s 发生了意外错误。建议你将崩溃日志分享到 Discord #support 频道或者在 GitHub 上反馈。
重启应用
主题 • 日期格式
应用语言 • 通知
@@ -720,4 +714,12 @@
隐藏已添加到书架的作品
复制到剪贴板
更新分类
+ 分割长图
+ 标记和按钮
+ 旋转横向图片
+
+ - 缺少 %1$s 章
+
+ 可能缺少章节
+ 旋转横向图片时反转方向
\ No newline at end of file
diff --git a/i18n/src/main/res/values-zh-rTW/strings.xml b/i18n/src/main/res/values-zh-rTW/strings.xml
index b0bcf245e1..19e1dd3870 100644
--- a/i18n/src/main/res/values-zh-rTW/strings.xml
+++ b/i18n/src/main/res/values-zh-rTW/strings.xml
@@ -39,7 +39,6 @@
重新命名類別
設定類別
編輯封面
- 停止
暫停
上一章
下一章
@@ -221,7 +220,7 @@
標題
狀態
狀態
- 無法載入該圖片
+ 圖片無法載入
請先將作品收藏至書櫃
選擇備份檔案
尚無任何類別,輕觸新增按鈕即可建立類別以組織你的書櫃。
@@ -302,7 +301,7 @@
永不
立即
閒置時鎖定
- 需要解鎖
+ 上鎖應用程式
在切換應用程式時隱藏預覽,並禁止擷取螢幕畫面
防窺畫面
隱私
@@ -383,7 +382,6 @@
還原失敗
備份失敗
登出 %1$s?
- 標記
- 剩餘 %1$s 本
@@ -474,10 +472,8 @@
Kindle 式
L 式
已紀錄歷程
- 當機記錄
- 已產生當機記錄
產生錯誤記錄檔以便分享予開發人員
- 傾印當機記錄
+ 分享當機記錄
輕觸區域
左右式
依上傳日期
@@ -488,7 +484,7 @@
已下載的章節
分割寬頁
若分割後的排版方向與翻閱順序不符
- 反轉分割頁面排版
+ 調換分割寬頁排版
DNS over HTTPS (DoH)
自動下載
直向
@@ -511,7 +507,7 @@
\n隨後請安裝所有遺失的擴充套件並重新登入各歷程平台。
類別同時屬於「排除」及「包含」的作品,將不會自動下載。
儲存頁面至個別資料夾
- 根據漫畫標題建立資料夾
+ 根據作品標題建立資料夾
開始閱讀時,短暫浮現輕觸區域
輕觸區域提示
取消此叢書
@@ -607,7 +603,7 @@
常見問題與指南
5%
縮放橫向圖片
- 輕觸時移動圖片
+ 輕觸大圖時先平移後翻頁
純封面格狀
已開始閱讀
無已讀的章節
@@ -635,14 +631,12 @@
找不到已安裝的來源
未讀章數
分割時找不到第 %d 頁
- 分割過高的圖片
用以改善閱讀器效能
找不到第 %d 頁的檔案路徑
重設個別閱讀器設定
將所有叢書的閱讀模式和螢幕方向恢復為預設值
已重設所有閱讀器設定
無法重設閱讀器設定
- 無法分割下載的圖片
呃…尷尬了
版本
語言
@@ -696,7 +690,7 @@
傾印當機記錄、電池效能最佳化
重新啟動應用程式
發生意外錯誤
- 「%s」發生了未預期的錯誤。我們建議你擷取此畫面、傾印當機記錄並分享至我們位於 Discord 上的 support 頻道。
+ 「%s」發生了未預期的錯誤。我們建議你將當機記錄分享至我們位於 Discord 上的 support 頻道。
類別、全域更新
來源、擴充套件、全域搜尋
無效的位置:%s
@@ -722,4 +716,12 @@
- 後續 %d 章
更新類別
+ 分割過高的圖片
+ 封面附加元素
+ 部分章節可能缺漏
+
+ - 缺漏 %1$s 章
+
+ 旋轉寬頁以符合螢幕
+ 調換旋轉寬頁方向
\ No newline at end of file
diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml
index c071e2cd80..5287a912dd 100644
--- a/i18n/src/main/res/values/strings.xml
+++ b/i18n/src/main/res/values/strings.xml
@@ -85,7 +85,6 @@
Delete category
Edit cover
View chapters
- Stop
Pause
Previous chapter
Next chapter
@@ -312,6 +311,8 @@
Split wide pages
Invert split page placement
If the placement of the split wide pages don\'t match reading direction
+ Rotate wide pages to fit
+ Flip orientation of rotated wide pages
Split tall images (BETA)
Show content in cutout area
Animate page transitions
@@ -364,11 +365,11 @@
Next
Left
Right
- Left to right
- Right to left
- Vertical
- Webtoon
- Continuous vertical
+ Paged (left to right)
+ Paged (right to left)
+ Paged (vertical)
+ Long strip
+ Long strip with gaps
Paged
Tap zones
Scale type
@@ -438,6 +439,7 @@
Only works on entries in library and if the current chapter plus the next one are already downloaded
Save as CBZ archive
+ Split tall images
Improves reader performance
Maximum downloads
@@ -516,9 +518,8 @@
Resets reading mode and orientation of all series
All reader settings reset
Couldn\'t reset reader settings
- Dump crash logs
+ Share crash logs
Saves error logs to a file for sharing with the developers
- Crash logs saved
Background activity
Disable battery optimization
Helps with background library updates and backups
@@ -528,7 +529,7 @@
Tablet UI
Verbose logging
Print verbose logs to system log (reduces app performance)
- Worker info
+ Debug info
Website
@@ -573,8 +574,8 @@
Updating category
Local
Downloaded chapters
-
- Complications
+
+ Overlay
Tabs
@@ -599,6 +600,12 @@
Order by
Date
+
+
+ - Missing %1$s item
+ - Missing %1$s items
+
+
Ongoing
Unknown
@@ -770,7 +777,7 @@
Whoops!
- %s ran into an unexpected error. We suggest you screenshot this message, dump the crash logs, and then share it in our support channel on Discord.
+ %s ran into an unexpected error. We suggest you share the crash logs in our support channel on Discord.
Restart the application
@@ -887,7 +894,6 @@
Skipped
App updates
Extension updates
- Crash logs
Previous page
diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt
index d52362b19d..ee47ea68cb 100644
--- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt
+++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/AdaptiveSheet.kt
@@ -3,15 +3,12 @@ package tachiyomi.presentation.core.components
import androidx.activity.compose.BackHandler
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
-import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
-import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.only
@@ -21,7 +18,6 @@ import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.windowInsetsPadding
-import androidx.compose.foundation.shape.ZeroCornerSize
import androidx.compose.material.SwipeableState
import androidx.compose.material.rememberSwipeableState
import androidx.compose.material.swipeable
@@ -46,18 +42,14 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.Velocity
import androidx.compose.ui.unit.dp
-import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
import kotlin.math.roundToInt
-import kotlin.time.Duration.Companion.milliseconds
-private const val SheetAnimationDuration = 500
+private const val SheetAnimationDuration = 350
private val SheetAnimationSpec = tween(durationMillis = SheetAnimationDuration)
-private const val ScrimAnimationDuration = 350
-private val ScrimAnimationSpec = tween(durationMillis = ScrimAnimationDuration)
@Composable
fun AdaptiveSheet(
@@ -72,12 +64,11 @@ fun AdaptiveSheet(
var targetAlpha by remember { mutableStateOf(0f) }
val alpha by animateFloatAsState(
targetValue = targetAlpha,
- animationSpec = ScrimAnimationSpec,
+ animationSpec = SheetAnimationSpec,
)
val internalOnDismissRequest: () -> Unit = {
scope.launch {
targetAlpha = 0f
- delay(ScrimAnimationSpec.durationMillis.milliseconds)
onDismissRequest()
}
}
@@ -93,11 +84,6 @@ fun AdaptiveSheet(
.alpha(alpha),
contentAlignment = Alignment.Center,
) {
- Box(
- modifier = Modifier
- .matchParentSize()
- .background(MaterialTheme.colorScheme.scrim.copy(alpha = 0.32f)),
- )
Surface(
modifier = Modifier
.requiredWidthIn(max = 460.dp)
@@ -138,16 +124,6 @@ fun AdaptiveSheet(
) {
val fullHeight = constraints.maxHeight.toFloat()
val anchors = mapOf(0f to 0, fullHeight to 1)
- val scrimAlpha by animateFloatAsState(
- targetValue = if (swipeState.targetValue == 1) 0f else 1f,
- animationSpec = ScrimAnimationSpec,
- )
- Box(
- modifier = Modifier
- .matchParentSize()
- .alpha(scrimAlpha)
- .background(MaterialTheme.colorScheme.scrim.copy(alpha = 0.32f)),
- )
Surface(
modifier = Modifier
.widthIn(max = 460.dp)
@@ -180,12 +156,8 @@ fun AdaptiveSheet(
.windowInsetsPadding(
WindowInsets.systemBars
.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
- )
- .consumeWindowInsets(
- WindowInsets.systemBars
- .only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
),
- shape = MaterialTheme.shapes.extraLarge.copy(bottomStart = ZeroCornerSize, bottomEnd = ZeroCornerSize),
+ shape = MaterialTheme.shapes.extraLarge,
tonalElevation = tonalElevation,
content = {
BackHandler(enabled = swipeState.targetValue == 0, onBack = internalOnDismissRequest)
@@ -199,7 +171,6 @@ fun AdaptiveSheet(
.drop(1)
.filter { it == 1 }
.collectLatest {
- delay(ScrimAnimationSpec.durationMillis.milliseconds)
onDismissRequest()
}
}
diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItemsPaddings.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItemsPaddings.kt
index 5bdf37ffe2..e4c05afead 100644
--- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItemsPaddings.kt
+++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItemsPaddings.kt
@@ -49,6 +49,18 @@ fun HeadingItem(
)
}
+@Composable
+fun BasicItem(
+ label: String,
+ onClick: () -> Unit,
+) {
+ SortItem(
+ label = label,
+ sortDescending = null,
+ onClick = onClick,
+ )
+}
+
@Composable
fun SortItem(
label: String,
diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/VerticalFastScroller.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/VerticalFastScroller.kt
index 92fcd0ca34..b7d769f357 100644
--- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/VerticalFastScroller.kt
+++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/VerticalFastScroller.kt
@@ -48,6 +48,7 @@ import androidx.compose.ui.util.fastMaxBy
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.sample
import tachiyomi.presentation.core.components.Scroller.STICKY_HEADER_KEY_PREFIX
import kotlin.math.abs
import kotlin.math.max
@@ -124,14 +125,16 @@ fun VerticalFastScroller(
val alpha = remember { Animatable(0f) }
val isThumbVisible = alpha.value > 0f
LaunchedEffect(scrolled, alpha) {
- scrolled.collectLatest {
- if (thumbAllowed()) {
- alpha.snapTo(1f)
- alpha.animateTo(0f, animationSpec = FadeOutAnimationSpec)
- } else {
- alpha.animateTo(0f, animationSpec = ImmediateFadeOutAnimationSpec)
+ scrolled
+ .sample(100)
+ .collectLatest {
+ if (thumbAllowed()) {
+ alpha.snapTo(1f)
+ alpha.animateTo(0f, animationSpec = FadeOutAnimationSpec)
+ } else {
+ alpha.animateTo(0f, animationSpec = ImmediateFadeOutAnimationSpec)
+ }
}
- }
}
Box(
@@ -304,14 +307,16 @@ fun VerticalGridFastScroller(
val alpha = remember { Animatable(0f) }
val isThumbVisible = alpha.value > 0f
LaunchedEffect(scrolled, alpha) {
- scrolled.collectLatest {
- if (thumbAllowed()) {
- alpha.snapTo(1f)
- alpha.animateTo(0f, animationSpec = FadeOutAnimationSpec)
- } else {
- alpha.animateTo(0f, animationSpec = ImmediateFadeOutAnimationSpec)
+ scrolled
+ .sample(100)
+ .collectLatest {
+ if (thumbAllowed()) {
+ alpha.snapTo(1f)
+ alpha.animateTo(0f, animationSpec = FadeOutAnimationSpec)
+ } else {
+ alpha.animateTo(0f, animationSpec = ImmediateFadeOutAnimationSpec)
+ }
}
- }
}
Box(
diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/WheelPicker.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/WheelPicker.kt
index 093a4a8dd8..e244d08cc3 100644
--- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/WheelPicker.kt
+++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/WheelPicker.kt
@@ -4,15 +4,17 @@ import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.foundation.lazy.LazyListItemInfo
import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -20,75 +22,55 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
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.graphics.SolidColor
+import androidx.compose.ui.hapticfeedback.HapticFeedbackType
+import androidx.compose.ui.platform.LocalHapticFeedback
+import androidx.compose.ui.text.TextRange
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
import tachiyomi.presentation.core.components.material.padding
-import java.text.DateFormatSymbols
-import java.time.LocalDate
+import tachiyomi.presentation.core.util.clearFocusOnSoftKeyboardHide
+import tachiyomi.presentation.core.util.clickableNoIndication
+import tachiyomi.presentation.core.util.showSoftKeyboard
import kotlin.math.absoluteValue
@Composable
-fun WheelPicker(
+fun WheelNumberPicker(
modifier: Modifier = Modifier,
startIndex: Int = 0,
- count: Int,
+ items: List,
size: DpSize = DpSize(128.dp, 128.dp),
onSelectionChanged: (index: Int) -> Unit = {},
backgroundContent: (@Composable (size: DpSize) -> Unit)? = {
WheelPickerDefaults.Background(size = it)
},
- itemContent: @Composable LazyItemScope.(index: Int) -> Unit,
) {
- val lazyListState = rememberLazyListState(startIndex)
-
- LaunchedEffect(lazyListState, onSelectionChanged) {
- snapshotFlow { lazyListState.firstVisibleItemScrollOffset }
- .map { calculateSnappedItemIndex(lazyListState) }
- .distinctUntilChanged()
- .collectLatest {
- onSelectionChanged(it)
- }
- }
-
- Box(
+ WheelPicker(
modifier = modifier,
- contentAlignment = Alignment.Center,
+ startIndex = startIndex,
+ items = items,
+ size = size,
+ onSelectionChanged = onSelectionChanged,
+ manualInputType = KeyboardType.Number,
+ backgroundContent = backgroundContent,
) {
- backgroundContent?.invoke(size)
-
- LazyColumn(
- modifier = Modifier
- .height(size.height)
- .width(size.width),
- state = lazyListState,
- contentPadding = PaddingValues(vertical = size.height / RowCount * ((RowCount - 1) / 2)),
- flingBehavior = rememberSnapFlingBehavior(lazyListState = lazyListState),
- ) {
- items(count) { index ->
- Box(
- modifier = Modifier
- .height(size.height / RowCount)
- .width(size.width)
- .alpha(
- calculateAnimatedAlpha(
- lazyListState = lazyListState,
- index = index,
- ),
- ),
- contentAlignment = Alignment.Center,
- ) {
- itemContent(index)
- }
- }
- }
+ WheelPickerDefaults.Item(text = "$it")
}
}
@@ -96,7 +78,7 @@ fun WheelPicker(
fun WheelTextPicker(
modifier: Modifier = Modifier,
startIndex: Int = 0,
- texts: List,
+ items: List,
size: DpSize = DpSize(128.dp, 128.dp),
onSelectionChanged: (index: Int) -> Unit = {},
backgroundContent: (@Composable (size: DpSize) -> Unit)? = {
@@ -106,122 +88,126 @@ fun WheelTextPicker(
WheelPicker(
modifier = modifier,
startIndex = startIndex,
- count = remember(texts) { texts.size },
+ items = items,
size = size,
onSelectionChanged = onSelectionChanged,
backgroundContent = backgroundContent,
) {
- WheelPickerDefaults.Item(text = texts[it])
+ WheelPickerDefaults.Item(text = it)
}
}
@Composable
-fun WheelDatePicker(
+private fun WheelPicker(
modifier: Modifier = Modifier,
- startDate: LocalDate = LocalDate.now(),
- minDate: LocalDate? = null,
- maxDate: LocalDate? = null,
- size: DpSize = DpSize(256.dp, 128.dp),
+ startIndex: Int = 0,
+ items: List,
+ size: DpSize = DpSize(128.dp, 128.dp),
+ onSelectionChanged: (index: Int) -> Unit = {},
+ manualInputType: KeyboardType? = null,
backgroundContent: (@Composable (size: DpSize) -> Unit)? = {
WheelPickerDefaults.Background(size = it)
},
- onSelectionChanged: (date: LocalDate) -> Unit = {},
+ itemContent: @Composable LazyItemScope.(item: T) -> Unit,
) {
- var internalSelection by remember { mutableStateOf(startDate) }
- val internalOnSelectionChange: (LocalDate) -> Unit = {
- internalSelection = it
- onSelectionChanged(internalSelection)
+ val haptic = LocalHapticFeedback.current
+ val lazyListState = rememberLazyListState(startIndex)
+
+ var internalIndex by remember { mutableStateOf(startIndex) }
+ val internalOnSelectionChanged: (Int) -> Unit = {
+ internalIndex = it
+ onSelectionChanged(it)
+ }
+
+ LaunchedEffect(lazyListState, onSelectionChanged) {
+ snapshotFlow { lazyListState.firstVisibleItemScrollOffset }
+ .map { calculateSnappedItemIndex(lazyListState) }
+ .distinctUntilChanged()
+ .drop(1)
+ .collectLatest {
+ haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove)
+ internalOnSelectionChanged(it)
+ }
}
- Box(modifier = modifier, contentAlignment = Alignment.Center) {
+ Box(
+ modifier = modifier
+ .height(size.height)
+ .width(size.width),
+ contentAlignment = Alignment.Center,
+ ) {
backgroundContent?.invoke(size)
- Row {
- val singularPickerSize = DpSize(
- width = size.width / 3,
- height = size.height,
- )
- // Day
- val dayOfMonths = remember(internalSelection, minDate, maxDate) {
- if (minDate == null && maxDate == null) {
- 1..internalSelection.lengthOfMonth()
- } else {
- val minDay = if (minDate?.month == internalSelection.month &&
- minDate?.year == internalSelection.year
- ) {
- minDate.dayOfMonth
- } else {
- 1
- }
- val maxDay = if (maxDate?.month == internalSelection.month &&
- maxDate?.year == internalSelection.year
- ) {
- maxDate.dayOfMonth
- } else {
- 31
- }
- minDay..maxDay.coerceAtMost(internalSelection.lengthOfMonth())
- }.toList()
+ var showManualInput by remember { mutableStateOf(false) }
+ if (showManualInput) {
+ var value by remember {
+ val currentString = items[internalIndex].toString()
+ mutableStateOf(TextFieldValue(text = currentString, selection = TextRange(currentString.length)))
}
- WheelTextPicker(
- size = singularPickerSize,
- texts = dayOfMonths.map { it.toString() },
- backgroundContent = null,
- startIndex = dayOfMonths.indexOfFirst { it == startDate.dayOfMonth }.coerceAtLeast(0),
- onSelectionChanged = { index ->
- val newDayOfMonth = dayOfMonths[index]
- internalOnSelectionChange(internalSelection.withDayOfMonth(newDayOfMonth))
- },
- )
- // Month
- val months = remember(internalSelection, minDate, maxDate) {
- val monthRange = if (minDate == null && maxDate == null) {
- 1..12
- } else {
- val minMonth = if (minDate?.year == internalSelection.year) {
- minDate.monthValue
- } else {
- 1
- }
- val maxMonth = if (maxDate?.year == internalSelection.year) {
- maxDate.monthValue
- } else {
- 12
+ val scope = rememberCoroutineScope()
+ BasicTextField(
+ modifier = Modifier
+ .align(Alignment.Center)
+ .showSoftKeyboard(true)
+ .clearFocusOnSoftKeyboardHide {
+ scope.launch {
+ items
+ .indexOfFirst { it.toString() == value.text }
+ .takeIf { it >= 0 }
+ ?.apply {
+ internalOnSelectionChanged(this)
+ lazyListState.scrollToItem(this)
+ }
+
+ showManualInput = false
+ }
+ },
+ value = value,
+ onValueChange = { value = it },
+ singleLine = true,
+ keyboardOptions = KeyboardOptions(
+ keyboardType = manualInputType!!,
+ imeAction = ImeAction.Done,
+ ),
+ textStyle = MaterialTheme.typography.titleMedium +
+ TextStyle(
+ color = MaterialTheme.colorScheme.onSurface,
+ textAlign = TextAlign.Center,
+ ),
+ cursorBrush = SolidColor(MaterialTheme.colorScheme.primary),
+ )
+ } else {
+ LazyColumn(
+ modifier = Modifier
+ .let {
+ if (manualInputType != null) {
+ it.clickableNoIndication { showManualInput = true }
+ } else {
+ it
+ }
+ },
+ state = lazyListState,
+ contentPadding = PaddingValues(vertical = size.height / RowCount * ((RowCount - 1) / 2)),
+ flingBehavior = rememberSnapFlingBehavior(lazyListState = lazyListState),
+ ) {
+ itemsIndexed(items) { index, item ->
+ Box(
+ modifier = Modifier
+ .height(size.height / RowCount)
+ .width(size.width)
+ .alpha(
+ calculateAnimatedAlpha(
+ lazyListState = lazyListState,
+ index = index,
+ ),
+ ),
+ contentAlignment = Alignment.Center,
+ ) {
+ itemContent(item)
}
- minMonth..maxMonth
}
- val dateFormatSymbols = DateFormatSymbols()
- monthRange.map { it to dateFormatSymbols.months[it - 1] }
- }
- WheelTextPicker(
- size = singularPickerSize,
- texts = months.map { it.second },
- backgroundContent = null,
- startIndex = months.indexOfFirst { it.first == startDate.monthValue }.coerceAtLeast(0),
- onSelectionChanged = { index ->
- val newMonth = months[index].first
- internalOnSelectionChange(internalSelection.withMonth(newMonth))
- },
- )
-
- // Year
- val years = remember(minDate, maxDate) {
- val minYear = minDate?.year?.coerceAtLeast(1900) ?: 1900
- val maxYear = maxDate?.year?.coerceAtMost(2100) ?: 2100
- val yearRange = minYear..maxYear
- yearRange.toList()
}
- WheelTextPicker(
- size = singularPickerSize,
- texts = years.map { it.toString() },
- backgroundContent = null,
- startIndex = years.indexOfFirst { it == startDate.year }.coerceAtLeast(0),
- onSelectionChanged = { index ->
- val newYear = years[index]
- internalOnSelectionChange(internalSelection.withYear(newYear))
- },
- )
}
}
}
diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/screens/EmptyScreen.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/screens/EmptyScreen.kt
index 80265cd815..6327632399 100644
--- a/presentation-core/src/main/java/tachiyomi/presentation/core/screens/EmptyScreen.kt
+++ b/presentation-core/src/main/java/tachiyomi/presentation/core/screens/EmptyScreen.kt
@@ -9,6 +9,8 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.paddingFromBaseline
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -21,6 +23,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastForEach
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.util.secondaryItemAlpha
import kotlin.random.Random
@@ -54,6 +57,7 @@ fun EmptyScreen(
Column(
modifier = modifier
.fillMaxSize()
+ .verticalScroll(rememberScrollState())
.padding(horizontal = 24.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
@@ -66,7 +70,9 @@ fun EmptyScreen(
Text(
text = message,
- modifier = Modifier.paddingFromBaseline(top = 24.dp).secondaryItemAlpha(),
+ modifier = Modifier
+ .paddingFromBaseline(top = 24.dp)
+ .secondaryItemAlpha(),
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center,
)
@@ -74,14 +80,10 @@ fun EmptyScreen(
if (!actions.isNullOrEmpty()) {
Row(
modifier = Modifier
- .padding(
- top = 24.dp,
- start = 24.dp,
- end = 24.dp,
- ),
+ .padding(top = 24.dp),
horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
) {
- actions.forEach {
+ actions.fastForEach {
ActionButton(
modifier = Modifier.weight(1f),
title = stringResource(it.stringResId),
diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/util/Modifier.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/util/Modifier.kt
index ca538a8660..fb8e139966 100644
--- a/presentation-core/src/main/java/tachiyomi/presentation/core/util/Modifier.kt
+++ b/presentation-core/src/main/java/tachiyomi/presentation/core/util/Modifier.kt
@@ -4,14 +4,25 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.isImeVisible
import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onPreviewKeyEvent
+import androidx.compose.ui.platform.LocalFocusManager
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
fun Modifier.selectedBackground(isSelected: Boolean): Modifier = composed {
@@ -52,3 +63,56 @@ fun Modifier.runOnEnterKeyPressed(action: () -> Unit): Modifier = this.onPreview
else -> false
}
}
+
+/**
+ * For TextField on AppBar, this modifier will request focus
+ * to the element the first time it's composed.
+ */
+fun Modifier.showSoftKeyboard(show: Boolean): Modifier = if (show) {
+ composed {
+ val focusRequester = remember { FocusRequester() }
+ var openKeyboard by rememberSaveable { mutableStateOf(show) }
+ LaunchedEffect(focusRequester) {
+ if (openKeyboard) {
+ focusRequester.requestFocus()
+ openKeyboard = false
+ }
+ }
+
+ Modifier.focusRequester(focusRequester)
+ }
+} else {
+ this
+}
+
+/**
+ * For TextField, this modifier will clear focus when soft
+ * keyboard is hidden.
+ */
+fun Modifier.clearFocusOnSoftKeyboardHide(
+ onFocusCleared: (() -> Unit)? = null,
+): Modifier = composed {
+ var isFocused by remember { mutableStateOf(false) }
+ var keyboardShowedSinceFocused by remember { mutableStateOf(false) }
+ if (isFocused) {
+ val imeVisible = WindowInsets.isImeVisible
+ val focusManager = LocalFocusManager.current
+ LaunchedEffect(imeVisible) {
+ if (imeVisible) {
+ keyboardShowedSinceFocused = true
+ } else if (keyboardShowedSinceFocused) {
+ focusManager.clearFocus()
+ onFocusCleared?.invoke()
+ }
+ }
+ }
+
+ Modifier.onFocusChanged {
+ if (isFocused != it.isFocused) {
+ if (isFocused) {
+ keyboardShowedSinceFocused = false
+ }
+ isFocused = it.isFocused
+ }
+ }
+}
diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/util/Scrollbar.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/util/Scrollbar.kt
index 55552d78c1..b842952e8f 100644
--- a/presentation-core/src/main/java/tachiyomi/presentation/core/util/Scrollbar.kt
+++ b/presentation-core/src/main/java/tachiyomi/presentation/core/util/Scrollbar.kt
@@ -67,6 +67,7 @@ import androidx.compose.ui.util.fastSumBy
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.sample
import tachiyomi.presentation.core.components.Scroller.STICKY_HEADER_KEY_PREFIX
/**
@@ -206,10 +207,12 @@ private fun Modifier.drawScrollbar(
val alpha = remember { Animatable(0f) }
LaunchedEffect(scrolled, alpha) {
- scrolled.collectLatest {
- alpha.snapTo(1f)
- alpha.animateTo(0f, animationSpec = FadeOutAnimationSpec)
- }
+ scrolled
+ .sample(100)
+ .collectLatest {
+ alpha.snapTo(1f)
+ alpha.animateTo(0f, animationSpec = FadeOutAnimationSpec)
+ }
}
val isLtr = LocalLayoutDirection.current == LayoutDirection.Ltr
diff --git a/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/animesource/AnimeSource.kt b/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/animesource/AnimeSource.kt
index 3182a91959..ce81fefe81 100644
--- a/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/animesource/AnimeSource.kt
+++ b/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/animesource/AnimeSource.kt
@@ -7,12 +7,12 @@ import eu.kanade.tachiyomi.util.awaitSingle
import rx.Observable
/**
- * A basic interface for creating a source. It could be an online source, a local source, etc...
+ * A basic interface for creating a source. It could be an online source, a local source, etc.
*/
interface AnimeSource {
/**
- * Id for the source. Must be unique.
+ * ID for the source. Must be unique.
*/
val id: Long
@@ -46,9 +46,9 @@ interface AnimeSource {
)
fun fetchEpisodeList(anime: SAnime): Observable> = throw IllegalStateException("Not used")
- // TODO: remove direct usages on this method
/**
- * Returns an observable with the list of videos a episode has.
+ * Returns an observable with the list of videos a episode has. Videos should be returned
+ * in the expected order; the index is ignored.
*
* @param episode the episode.
*/
@@ -75,7 +75,8 @@ interface AnimeSource {
}
/**
- * [1.x API] Get the list of videos a episode has.
+ * [1.x API] Get the list of videos a episode has. Videos should be returned
+ * in the expected order; the index is ignored.
*/
@Suppress("DEPRECATION")
suspend fun getVideoList(episode: SEpisode): List