From 39407407f282dbb7fa972b12053c26b3e3bd66d8 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 21 Jan 2024 18:56:17 +0600 Subject: [PATCH 001/212] Remove usage of `.not()` where possible --- .../domain/chapter/interactor/SyncChaptersWithSource.kt | 2 +- .../domain/extension/interactor/GetExtensionSources.kt | 2 +- .../domain/extension/interactor/GetExtensionsByType.kt | 6 +++--- .../source/interactor/GetSourcesWithFavoriteCount.kt | 8 ++++---- app/src/main/java/eu/kanade/domain/track/model/Track.kt | 2 +- .../eu/kanade/tachiyomi/data/download/DownloadManager.kt | 4 ++-- .../eu/kanade/tachiyomi/data/download/DownloadProvider.kt | 2 +- .../eu/kanade/tachiyomi/data/track/anilist/Anilist.kt | 2 +- .../tachiyomi/data/track/myanimelist/MyAnimeList.kt | 2 +- .../eu/kanade/tachiyomi/data/track/shikimori/Shikimori.kt | 2 +- .../tachiyomi/ui/reader/viewer/pager/PagerViewer.kt | 2 +- .../tachiyomi/network/interceptor/RateLimitInterceptor.kt | 2 +- .../domain/release/interactor/GetApplicationRelease.kt | 2 +- .../main/java/tachiyomi/domain/source/model/StubSource.kt | 2 +- 14 files changed, 20 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt b/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt index abd13a849..300794b4c 100644 --- a/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt +++ b/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt @@ -163,7 +163,7 @@ class SyncChaptersWithSource( var updatedToAdd = newChapters.map { toAddItem -> var chapter = toAddItem.copy(dateFetch = nowMillis + itemCount--) - if (chapter.isRecognizedNumber.not() || chapter.chapterNumber !in deletedChapterNumbers) return@map chapter + if (!chapter.isRecognizedNumber || chapter.chapterNumber !in deletedChapterNumbers) return@map chapter chapter = chapter.copy( read = chapter.chapterNumber in deletedReadChapterNumbers, diff --git a/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionSources.kt b/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionSources.kt index 280dfae04..7c7a6a919 100644 --- a/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionSources.kt +++ b/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionSources.kt @@ -23,7 +23,7 @@ class GetExtensionSources( ExtensionSourceItem( source = source, enabled = source.isEnabled(), - labelAsName = isMultiSource && isMultiLangSingleSource.not(), + labelAsName = isMultiSource && !isMultiLangSingleSource, ) } } diff --git a/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionsByType.kt b/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionsByType.kt index 4156a27c5..dadbd35f6 100644 --- a/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionsByType.kt +++ b/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionsByType.kt @@ -22,9 +22,9 @@ class GetExtensionsByType( extensionManager.availableExtensionsFlow, ) { _activeLanguages, _installed, _untrusted, _available -> val (updates, installed) = _installed - .filter { (showNsfwSources || it.isNsfw.not()) } + .filter { (showNsfwSources || !it.isNsfw) } .sortedWith( - compareBy { it.isObsolete.not() } + compareBy { !it.isObsolete } .thenBy(String.CASE_INSENSITIVE_ORDER) { it.name }, ) .partition { it.hasUpdate } @@ -36,7 +36,7 @@ class GetExtensionsByType( .filter { extension -> _installed.none { it.pkgName == extension.pkgName } && _untrusted.none { it.pkgName == extension.pkgName } && - (showNsfwSources || extension.isNsfw.not()) + (showNsfwSources || !extension.isNsfw) } .flatMap { ext -> if (ext.sources.isEmpty()) { diff --git a/app/src/main/java/eu/kanade/domain/source/interactor/GetSourcesWithFavoriteCount.kt b/app/src/main/java/eu/kanade/domain/source/interactor/GetSourcesWithFavoriteCount.kt index c37be75aa..0cf342caf 100644 --- a/app/src/main/java/eu/kanade/domain/source/interactor/GetSourcesWithFavoriteCount.kt +++ b/app/src/main/java/eu/kanade/domain/source/interactor/GetSourcesWithFavoriteCount.kt @@ -34,15 +34,15 @@ class GetSourcesWithFavoriteCount( when (sorting) { SetMigrateSorting.Mode.ALPHABETICAL -> { when { - a.first.isStub && b.first.isStub.not() -> -1 - b.first.isStub && a.first.isStub.not() -> 1 + a.first.isStub && !b.first.isStub -> -1 + b.first.isStub && !a.first.isStub -> 1 else -> a.first.name.lowercase().compareToWithCollator(b.first.name.lowercase()) } } SetMigrateSorting.Mode.TOTAL -> { when { - a.first.isStub && b.first.isStub.not() -> -1 - b.first.isStub && a.first.isStub.not() -> 1 + a.first.isStub && !b.first.isStub -> -1 + b.first.isStub && !a.first.isStub -> 1 else -> a.second.compareTo(b.second) } } diff --git a/app/src/main/java/eu/kanade/domain/track/model/Track.kt b/app/src/main/java/eu/kanade/domain/track/model/Track.kt index 332ddd8d5..3ce611bab 100644 --- a/app/src/main/java/eu/kanade/domain/track/model/Track.kt +++ b/app/src/main/java/eu/kanade/domain/track/model/Track.kt @@ -29,7 +29,7 @@ fun Track.toDbTrack(): DbTrack = DbTrack.create(trackerId).also { } fun DbTrack.toDomainTrack(idRequired: Boolean = true): Track? { - val trackId = id ?: if (idRequired.not()) -1 else return null + val trackId = id ?: if (!idRequired) -1 else return null return Track( id = trackId, mangaId = manga_id, diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt index 3e447f4ab..61d0be98e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt @@ -315,13 +315,13 @@ class DownloadManager( val capitalizationChanged = oldFolder.name.equals(newName, ignoreCase = true) if (capitalizationChanged) { val tempName = newName + Downloader.TMP_DIR_SUFFIX - if (oldFolder.renameTo(tempName).not()) { + if (!oldFolder.renameTo(tempName)) { logcat(LogPriority.ERROR) { "Failed to rename source download folder: ${oldFolder.name}" } return } } - if (oldFolder.renameTo(newName).not()) { + if (!oldFolder.renameTo(newName)) { logcat(LogPriority.ERROR) { "Failed to rename source download folder: ${oldFolder.name}" } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt index f6d8e064b..469cc0765 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt @@ -130,7 +130,7 @@ class DownloadProvider( val newChapterName = sanitizeChapterName(chapterName) return DiskUtil.buildValidFilename( when { - chapterScanlator.isNullOrBlank().not() -> "${chapterScanlator}_$newChapterName" + !chapterScanlator.isNullOrBlank() -> "${chapterScanlator}_$newChapterName" else -> newChapterName }, ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt index 93fd75a45..dc799dfe1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt @@ -185,7 +185,7 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker { if (track.status != COMPLETED) { val isRereading = track.status == REREADING - track.status = if (isRereading.not() && hasReadChapters) READING else track.status + track.status = if (!isRereading && hasReadChapters) READING else track.status } update(track) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt index 5d66b73d4..5f75b12e8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt @@ -104,7 +104,7 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker { if (track.status != COMPLETED) { val isRereading = track.status == REREADING - track.status = if (isRereading.not() && hasReadChapters) READING else track.status + track.status = if (!isRereading && hasReadChapters) READING else track.status } update(track) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/Shikimori.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/Shikimori.kt index 8f70e03dd..3036cc8a8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/Shikimori.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/Shikimori.kt @@ -72,7 +72,7 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker { if (track.status != COMPLETED) { val isRereading = track.status == REREADING - track.status = if (isRereading.not() && hasReadChapters) READING else track.status + track.status = if (!isRereading && hasReadChapters) READING else track.status } update(track) 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 93ea9495b..5508d1acd 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 @@ -90,7 +90,7 @@ abstract class PagerViewer(val activity: ReaderActivity) : Viewer { pager.addOnPageChangeListener( object : ViewPager.SimpleOnPageChangeListener() { override fun onPageSelected(position: Int) { - if (activity.isScrollingThroughPages.not()) { + if (!activity.isScrollingThroughPages) { activity.hideMenu() } onPageChange(position) diff --git a/core/src/main/java/eu/kanade/tachiyomi/network/interceptor/RateLimitInterceptor.kt b/core/src/main/java/eu/kanade/tachiyomi/network/interceptor/RateLimitInterceptor.kt index c4727f8a9..eab5ec302 100644 --- a/core/src/main/java/eu/kanade/tachiyomi/network/interceptor/RateLimitInterceptor.kt +++ b/core/src/main/java/eu/kanade/tachiyomi/network/interceptor/RateLimitInterceptor.kt @@ -89,7 +89,7 @@ internal class RateLimitInterceptor( while (requestQueue.size >= permits) { // queue is full, remove expired entries val periodStart = SystemClock.elapsedRealtime() - rateLimitMillis var hasRemovedExpired = false - while (requestQueue.isEmpty().not() && requestQueue.first <= periodStart) { + while (!requestQueue.isEmpty() && requestQueue.first <= periodStart) { requestQueue.removeFirst() hasRemovedExpired = true } diff --git a/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt b/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt index bf966fa12..380da7b1e 100644 --- a/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt +++ b/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt @@ -20,7 +20,7 @@ class GetApplicationRelease( val now = Instant.now() // Limit checks to once every 3 days at most - if (arguments.forceCheck.not() && now.isBefore( + if (!arguments.forceCheck && now.isBefore( Instant.ofEpochMilli(lastChecked.get()).plus(3, ChronoUnit.DAYS), ) ) { diff --git a/domain/src/main/java/tachiyomi/domain/source/model/StubSource.kt b/domain/src/main/java/tachiyomi/domain/source/model/StubSource.kt index 544326a4c..0fee2af18 100644 --- a/domain/src/main/java/tachiyomi/domain/source/model/StubSource.kt +++ b/domain/src/main/java/tachiyomi/domain/source/model/StubSource.kt @@ -22,7 +22,7 @@ class StubSource( throw SourceNotInstalledException() override fun toString(): String = - if (isInvalid.not()) "$name (${lang.uppercase()})" else id.toString() + if (!isInvalid) "$name (${lang.uppercase()})" else id.toString() companion object { fun from(source: Source): StubSource { From cb068984303f811692531bf6f14902ae118d8ac7 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 22 Jan 2024 02:27:45 +0600 Subject: [PATCH 002/212] Fix issues when updating extensions --- .../eu/kanade/presentation/browse/ExtensionsScreen.kt | 8 +++++++- .../eu/kanade/tachiyomi/extension/ExtensionManager.kt | 4 ++++ .../tachiyomi/extension/util/ExtensionInstallReceiver.kt | 3 +-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt index 8e6c7913b..d08a2d93b 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt @@ -203,7 +203,13 @@ private fun ExtensionContent( items( items = items, contentType = { "item" }, - key = { "extension-${it.hashCode()}" }, + key = { item -> + when (item.extension) { + is Extension.Untrusted -> "extension-untrusted-${item.hashCode()}" + is Extension.Installed -> "extension-installed-${item.hashCode()}" + is Extension.Available -> "extension-available-${item.hashCode()}" + } + }, ) { item -> ExtensionItem( modifier = Modifier.animateItemPlacement(), diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt index b252c0fbe..2c119a18b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt @@ -338,6 +338,10 @@ class ExtensionManager( } override fun onExtensionUntrusted(extension: Extension.Untrusted) { + val installedExtension = _installedExtensionsFlow.value + .find { it.pkgName == extension.pkgName } + ?: return + _installedExtensionsFlow.value -= installedExtension _untrustedExtensionsFlow.value += extension } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt index 6a3e21860..3d4b360c9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt @@ -70,8 +70,7 @@ internal class ExtensionInstallReceiver(private val listener: Listener) : launchNow { when (val result = getExtensionFromIntent(context, intent)) { is LoadResult.Success -> listener.onExtensionUpdated(result.extension) - // Not needed as a package can't be upgraded if the signature is different - // is LoadResult.Untrusted -> {} + is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension) else -> {} } } From 9fd1419142647455e86851413353c97bb9491990 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 22 Jan 2024 15:45:37 +0600 Subject: [PATCH 003/212] Translations (#189) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Small fix on french translation. (#104) Update fr/strings.xml Remove mentions of "official" extensions repos. (On 18+ extensions warning) Fixed a setting label who had the first letter in lowercase. * Updated Turkish suffixes (#125) Update strings.xml * Fix zh-rTW Translation (#118) fix zh-tw translate * Update Filipino Plurals (#112) Updated some Filipino Plurals to make sense grammatically * Update Filipino Strings (#111) Changed/updated a few grammatical strings for the Filipino Translation --------- Co-authored-by: Med <45147847+kitsumed@users.noreply.github.com> Co-authored-by: NukeSource <123626751+NukeSource@users.noreply.github.com> Co-authored-by: ɴᴇᴋᴏ <111511925+NeKoOuO@users.noreply.github.com> Co-authored-by: InfinityDouki56 <31158494+infyProductions@users.noreply.github.com> --- .../commonMain/resources/MR/fil/plurals.xml | 14 +++++++------- .../commonMain/resources/MR/fil/strings.xml | 18 +++++++++--------- .../src/commonMain/resources/MR/fr/strings.xml | 6 +++--- .../src/commonMain/resources/MR/tr/strings.xml | 10 +++++----- .../commonMain/resources/MR/zh-rTW/strings.xml | 12 ++++++------ 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/i18n/src/commonMain/resources/MR/fil/plurals.xml b/i18n/src/commonMain/resources/MR/fil/plurals.xml index bbebdac6d..04755d125 100644 --- a/i18n/src/commonMain/resources/MR/fil/plurals.xml +++ b/i18n/src/commonMain/resources/MR/fil/plurals.xml @@ -38,7 +38,7 @@ %d tracker - %d mga tracker + %d na tracker Nilaktawan ang %d na kabanata, maaaring ito ay wala sa source o na-filter ang mga ito @@ -46,11 +46,11 @@ Kahapon - %1$d araw na ang makalipas + %1$d araw na ang nakakalipas - Susunod na hindi pa nababasa na kabanata - Susunod na %d di pa nababasa na kabanata + Susunod na hindi pa nababasang kabanata + Susunod na %d hindi pa nababasang kabanata Sunod na kabanata @@ -58,14 +58,14 @@ Nawawalang %1$s na kabanata - Nawawalang %1$s mga kabanata + Nawawalang %1$s na mga kabanata 1 araw - %d (mga) araw + %d (na) araw %d na repo %d na mga repo - \ No newline at end of file + diff --git a/i18n/src/commonMain/resources/MR/fil/strings.xml b/i18n/src/commonMain/resources/MR/fil/strings.xml index 8bad24a31..b26aa0a81 100644 --- a/i18n/src/commonMain/resources/MR/fil/strings.xml +++ b/i18n/src/commonMain/resources/MR/fil/strings.xml @@ -22,7 +22,7 @@ Isara Ipakita ang mga tab ng kategorya Bilang ng kabanatang na-download - Maalwan na grid + Kumportableng grid Listahan Siksik na grid Pagpapakita @@ -114,7 +114,7 @@ Pangalawa sa huling nabasa Huling nabasang kabanata Sarado - Pagkamarkahang nabasa na + Markahang nabasa na Pagkatapos basahin, awtomatikong burahin Kapal ng gilid Pagbabasa @@ -243,7 +243,7 @@ Mga update sa extension Wala Mga update sa kabanata - Komon + Pangkaraniwan Nakahinto ang mga pag-download Walang koneksyon sa Internet Walang koneksyon sa Wifi @@ -291,7 +291,7 @@ Di mai-load ang larawan Di makita ang susunod na kabanata Pahina: %1$d - Napalitan na ang cover + Napalitan ang cover Gawin itong cover Pinili kong filter Na-save na ang larawan @@ -556,7 +556,7 @@ Para sa tulong sa pag-aayos ng mga error sa pag-update ng aklatan, tingnan ang %1$s Laktawan ang mga entry na hindi pa nababasang kabanata I-save bilang CBZ archive - Tapos na\'ng mailathala + Tapos na ang paglalathala Naka-hiatus Kinansela Ipakita ang entry @@ -639,9 +639,9 @@ Mga kategorya, panlahatang update, pag-swipe ng kabanata Mga source, extension, panlahatang paghanap Nagkaroon ng hindi inaasahang error ang %s. Iminumungkahi naming ibahagi mo ang mga crash log sa aming support channel sa Discord. - Ay! + Ay Naku! Buksan muli ang app - Invalid na lugar: %s + Imbalidong lugar: %s Di alam na pamagat Di-wastong string ng user agent Ngayon lang @@ -693,7 +693,7 @@ I-rotate ang malalawak na pahina upang magkasya I-flip ang oryentasyon ng mga pinaikot na malalawak na pahina Nakapatong (Overlay) - Hatiin ang mga matatangkad na larawan + Hatiin ang mga matataas na larawan Impormasyon sa pag-debug Mag-swipe ng pakaliwang pagkilos Mag swipe ng kabanata @@ -798,4 +798,4 @@ Malapit na Bawiin ang mga pinagkakatiwalaang hindi kilalang extension Open source na repo - \ No newline at end of file + diff --git a/i18n/src/commonMain/resources/MR/fr/strings.xml b/i18n/src/commonMain/resources/MR/fr/strings.xml index 56e0641dc..31a9a27f0 100644 --- a/i18n/src/commonMain/resources/MR/fr/strings.xml +++ b/i18n/src/commonMain/resources/MR/fr/strings.xml @@ -412,7 +412,7 @@ Suppression des chapitres Les sources de cette extension peuvent contenir du contenu NSFW (18+) 18+ - Ceci n\'empêche pas les extensions non officielles ou potentiellement mal signalées de diffuser du contenu +18 dans l\'application. + Ceci n\'empêche pas les extensions de diffuser du contenu +18 dans l\'application. Aucun chapitre trouvé Appliquer ce paramétrage par défaut \? Paramètres du chapitre @@ -534,7 +534,7 @@ Installez et démarrez Shizuku pour utiliser Shizuku comme installateur d\'extensions. Shizuku n\'est pas en cours d\'exécution Legacy - installeur + Installeur Installation de l\'extension… Entrées totales Rapports détaillés @@ -798,4 +798,4 @@ Impossible de créer un fichier de sauvegarde Dernière sauvegarde automatique : %s Paramètres sources - \ No newline at end of file + diff --git a/i18n/src/commonMain/resources/MR/tr/strings.xml b/i18n/src/commonMain/resources/MR/tr/strings.xml index 6c29808df..c76040e45 100644 --- a/i18n/src/commonMain/resources/MR/tr/strings.xml +++ b/i18n/src/commonMain/resources/MR/tr/strings.xml @@ -544,7 +544,7 @@ Ayrıntılı günlük kaydı Ayrıntılı günlükleri sistem günlüğüne yaz (uygulama performansını düşürür) Yalnızca kablosuz ağda - Uyarı: Büyük toplu indirmeler kaynakların yavaşlamasına ve/veya Mihon\'yi engellemesine neden olabilir. Daha çok öğrenmek için dokunun. + Uyarı: Büyük toplu indirmeler kaynakların yavaşlamasına ve/veya Mihon\'u engellemesine neden olabilir. Daha çok öğrenmek için dokunun. 3 günde bir Tümünü güncelle Uygulama güncellemeleri @@ -755,11 +755,11 @@ Yukarı git Klasör seç Başlangıç rehberi - %s\'de yeni misiniz? Başlangıç rehberine göz atmanızı tavsiye ederiz. + %s\'da yeni misiniz? Başlangıç rehberine göz atmanızı tavsiye ederiz. Başlayın Bir klasör seçilmelidir Hoş geldiniz! - %s\'yi yeniden mi kuruyorsunuz? + %s\'u yeniden mi kuruyorsunuz? Atla Sonraki Önce bazı şeyleri ayarlayalım. Bunları daha sonra ayarlardan da değiştirebilirsiniz. @@ -785,7 +785,7 @@ Depolama kılavuzu Depo ekle Depo URL\'si - Mihon\'ye ek depolar ekleyin. Bu, \"index.min.json\" ile biten bir URL olmalıdır. + Mihon\'a ek depolar ekleyin. Bu, \"index.min.json\" ile biten bir URL olmalıdır. Bu depo zaten var! Depoyu sil Geçersiz depo URL\'si @@ -798,4 +798,4 @@ Güvenilen bilinmeyen uzantıları iptal et Yakında Açık kaynaklı depo - \ No newline at end of file + diff --git a/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml b/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml index 61e262fff..f098088d4 100644 --- a/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml +++ b/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml @@ -594,11 +594,11 @@ 版本 語言 分級 - 未成清單 - 擱置清單 - 閱畢清單 - 願望清單 - 閱讀清單 + 已拋棄 + 擱置中 + 已完結 + 準備讀 + 閱讀中 僅透過非計量付費網路 無法開啟上次閱讀章節 自訂封面 @@ -784,7 +784,7 @@ 自訂更新頻率: 儲存庫網址 已有該儲存庫! - 智慧型更新 + 智慧更新 無效的儲存庫網址 將額外的擴充套件儲存庫新增至 Mihon。此處應填入一個結尾為「index.min.json」的網址。 確定要刪除「%s」儲存庫嗎? From e3f33e24f5e928ac8a85d1f500fd42d4715fc6b5 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 22 Jan 2024 16:17:08 +0600 Subject: [PATCH 004/212] Use own client for trackers + custom user agents Closes #114 Closes #143 Co-authored-by: Med <45147847+kitsumed@users.noreply.github.com> --- .../data/track/anilist/AnilistInterceptor.kt | 2 ++ .../data/track/bangumi/BangumiApi.kt | 4 +-- .../data/track/bangumi/BangumiInterceptor.kt | 32 +++++++++---------- .../data/track/kavita/KavitaInterceptor.kt | 2 +- .../data/track/kitsu/KitsuInterceptor.kt | 2 ++ .../tachiyomi/data/track/komga/KomgaApi.kt | 15 +++++++-- .../myanimelist/MyAnimeListInterceptor.kt | 3 ++ .../data/track/shikimori/ShikimoriApi.kt | 4 +-- .../track/shikimori/ShikimoriInterceptor.kt | 3 +- 9 files changed, 42 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistInterceptor.kt index 8c21070c4..388b3e1b5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistInterceptor.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.data.track.anilist +import eu.kanade.tachiyomi.BuildConfig import okhttp3.Interceptor import okhttp3.Response import java.io.IOException @@ -40,6 +41,7 @@ class AnilistInterceptor(val anilist: Anilist, private var token: String?) : Int // Add the authorization header to the original request. val authRequest = originalRequest.newBuilder() .addHeader("Authorization", "Bearer ${oauth!!.access_token}") + .header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})") .build() return chain.proceed(authRequest) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt index 9d2073707..0b905b514 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt @@ -182,8 +182,8 @@ class BangumiApi( ) companion object { - private const val clientId = "bgm10555cda0762e80ca" - private const val clientSecret = "8fff394a8627b4c388cbf349ec865775" + private const val clientId = "bgm291665acbd06a4c28" + private const val clientSecret = "43e5ce36b207de16e5d3cfd3e79118db" private const val apiUrl = "https://api.bgm.tv" private const val oauthUrl = "https://bgm.tv/oauth/access_token" diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt index d2183fdbf..c8a0d15fc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt @@ -1,8 +1,10 @@ package eu.kanade.tachiyomi.data.track.bangumi +import eu.kanade.tachiyomi.BuildConfig import kotlinx.serialization.json.Json import okhttp3.FormBody import okhttp3.Interceptor +import okhttp3.Request import okhttp3.Response import uy.kohesive.injekt.injectLazy @@ -29,22 +31,20 @@ class BangumiInterceptor(private val bangumi: Bangumi) : Interceptor { } } - val authRequest = if (originalRequest.method == "GET") { - originalRequest.newBuilder() - .header("User-Agent", "Tachiyomi") - .url( - originalRequest.url.newBuilder() - .addQueryParameter("access_token", currAuth.access_token).build(), - ) - .build() - } else { - originalRequest.newBuilder() - .post(addToken(currAuth.access_token, originalRequest.body as FormBody)) - .header("User-Agent", "Tachiyomi") - .build() - } - - return chain.proceed(authRequest) + return originalRequest.newBuilder() + .header("User-Agent", "antsylich/Mihon/v${BuildConfig.VERSION_NAME} (Android) (http://github.com/mihonapp/mihon)") + .apply { + if (originalRequest.method == "GET") { + val newUrl = originalRequest.url.newBuilder() + .addQueryParameter("access_token", currAuth.access_token) + .build() + url(newUrl) + } else { + post(addToken(currAuth.access_token, originalRequest.body as FormBody)) + } + } + .build() + .let(chain::proceed) } fun newAuth(oauth: OAuth?) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaInterceptor.kt index 8b26c3e02..89708ccc5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaInterceptor.kt @@ -18,7 +18,7 @@ class KavitaInterceptor(private val kavita: Kavita) : Interceptor { // Add the authorization header to the original request. val authRequest = originalRequest.newBuilder() .addHeader("Authorization", "Bearer $jwtToken") - .header("User-Agent", "Tachiyomi Kavita v${BuildConfig.VERSION_NAME}") + .header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})") .build() return chain.proceed(authRequest) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuInterceptor.kt index fe7e42292..da9aff7fc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuInterceptor.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.data.track.kitsu +import eu.kanade.tachiyomi.BuildConfig import kotlinx.serialization.json.Json import okhttp3.Interceptor import okhttp3.Response @@ -34,6 +35,7 @@ class KitsuInterceptor(private val kitsu: Kitsu) : Interceptor { // Add the authorization header to the original request. val authRequest = originalRequest.newBuilder() .addHeader("Authorization", "Bearer ${oauth!!.access_token}") + .header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})") .header("Accept", "application/vnd.api+json") .header("Content-Type", "application/vnd.api+json") .build() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt index 5992727ae..47c3d7b41 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.data.track.komga +import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.network.GET @@ -8,6 +9,7 @@ import eu.kanade.tachiyomi.network.parseAs import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import logcat.LogPriority +import okhttp3.Headers import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.Request @@ -23,6 +25,12 @@ class KomgaApi( private val client: OkHttpClient, ) { + private val headers: Headers by lazy { + Headers.Builder() + .add("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})") + .build() + } + private val json: Json by injectLazy() suspend fun getTrackSearch(url: String): TrackSearch = @@ -30,12 +38,12 @@ class KomgaApi( try { val track = with(json) { if (url.contains(READLIST_API)) { - client.newCall(GET(url)) + client.newCall(GET(url, headers)) .awaitSuccess() .parseAs() .toTrack() } else { - client.newCall(GET(url)) + client.newCall(GET(url, headers)) .awaitSuccess() .parseAs() .toTrack() @@ -43,7 +51,7 @@ class KomgaApi( } val progress = client - .newCall(GET("${url.replace("/api/v1/series/", "/api/v2/series/")}/read-progress/tachiyomi")) + .newCall(GET("${url.replace("/api/v1/series/", "/api/v2/series/")}/read-progress/tachiyomi", headers)) .awaitSuccess().let { with(json) { if (url.contains("/api/v1/series/")) { @@ -80,6 +88,7 @@ class KomgaApi( client.newCall( Request.Builder() .url("${track.tracking_url.replace("/api/v1/series/", "/api/v2/series/")}/read-progress/tachiyomi") + .headers(headers) .put(payload.toRequestBody("application/json".toMediaType())) .build(), ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt index a8ec37708..4fa1ec6ac 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.data.track.myanimelist +import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.network.parseAs import kotlinx.serialization.json.Json import okhttp3.Interceptor @@ -34,6 +35,7 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList, private var t // Add the authorization header to the original request val authRequest = originalRequest.newBuilder() .addHeader("Authorization", "Bearer ${oauth!!.access_token}") + .header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})") .build() val response = chain.proceed(authRequest) @@ -50,6 +52,7 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList, private var t val newRequest = originalRequest.newBuilder() .addHeader("Authorization", "Bearer ${newToken.access_token}") + .header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})") .build() return chain.proceed(newRequest) 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 d84b01330..5db8a5799 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 @@ -192,8 +192,8 @@ class ShikimoriApi( ) companion object { - private const val clientId = "1aaf4cf232372708e98b5abc813d795b539c5a916dbbfe9ac61bf02a360832cc" - private const val clientSecret = "229942c742dd4cde803125d17d64501d91c0b12e14cb1e5120184d77d67024c0" + private const val clientId = "PB9dq8DzI405s7wdtwTdirYqHiyVMh--djnP7lBUqSA" + private const val clientSecret = "NajpZcOBKB9sJtgNcejf8OB9jBN1OYYoo-k4h2WWZus" private const val baseUrl = "https://shikimori.one" private const val apiUrl = "$baseUrl/api" diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriInterceptor.kt index 84c64462f..aa2d4247a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriInterceptor.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.data.track.shikimori +import eu.kanade.tachiyomi.BuildConfig import kotlinx.serialization.json.Json import okhttp3.Interceptor import okhttp3.Response @@ -33,7 +34,7 @@ class ShikimoriInterceptor(private val shikimori: Shikimori) : Interceptor { // Add the authorization header to the original request. val authRequest = originalRequest.newBuilder() .addHeader("Authorization", "Bearer ${oauth!!.access_token}") - .header("User-Agent", "Tachiyomi") + .header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})") .build() return chain.proceed(authRequest) From 89ea0a271b20fc1314c0e82d333e1fafeee13748 Mon Sep 17 00:00:00 2001 From: Soitora Date: Mon, 22 Jan 2024 15:12:39 +0100 Subject: [PATCH 005/212] Add translation widget (#195) --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index cb0f1f6f8..644391e4e 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,9 @@ + + Translation status +

Download

From 58085336a5399a066b04baf679445bb0e047cc5d Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 22 Jan 2024 20:20:11 +0600 Subject: [PATCH 006/212] Lint --- .../tachiyomi/data/track/bangumi/BangumiInterceptor.kt | 6 ++++-- .../java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt index c8a0d15fc..a1822cca0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt @@ -4,7 +4,6 @@ import eu.kanade.tachiyomi.BuildConfig import kotlinx.serialization.json.Json import okhttp3.FormBody import okhttp3.Interceptor -import okhttp3.Request import okhttp3.Response import uy.kohesive.injekt.injectLazy @@ -32,7 +31,10 @@ class BangumiInterceptor(private val bangumi: Bangumi) : Interceptor { } return originalRequest.newBuilder() - .header("User-Agent", "antsylich/Mihon/v${BuildConfig.VERSION_NAME} (Android) (http://github.com/mihonapp/mihon)") + .header( + "User-Agent", + "antsylich/Mihon/v${BuildConfig.VERSION_NAME} (Android) (http://github.com/mihonapp/mihon)", + ) .apply { if (originalRequest.method == "GET") { val newUrl = originalRequest.url.newBuilder() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt index 47c3d7b41..628c4eb53 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt @@ -51,7 +51,9 @@ class KomgaApi( } val progress = client - .newCall(GET("${url.replace("/api/v1/series/", "/api/v2/series/")}/read-progress/tachiyomi", headers)) + .newCall( + GET("${url.replace("/api/v1/series/", "/api/v2/series/")}/read-progress/tachiyomi", headers), + ) .awaitSuccess().let { with(json) { if (url.contains("/api/v1/series/")) { From de75561402b5de0f8b5389fefbf34582e5180699 Mon Sep 17 00:00:00 2001 From: Soitora Date: Tue, 23 Jan 2024 09:14:05 +0100 Subject: [PATCH 007/212] Change README to Markdown (#208) * Change README to Markdown Also fix license and add disclaimer * Change some links to markdown as well --- README.md | 196 ++++++++++++++++++++++++++---------------------------- 1 file changed, 96 insertions(+), 100 deletions(-) diff --git a/README.md b/README.md index 644391e4e..e0a09a692 100644 --- a/README.md +++ b/README.md @@ -1,117 +1,113 @@ -

-
- - - -

+
-

Mihon App

-

Full-featured reader

-

Discover and read manga, webtoons, comics, and more – easier than ever on your Android device.

+ + Mihon logo + -

- - - - - - -
- - - - - Translation status - -

+# Mihon [App](#) -

Download

+### Full-featured reader +Discover and read manga, webtoons, comics, and more – easier than ever on your Android device. -

- - - - - - -
- Requires Android 8.0 or higher. -

+[![Discord server](https://img.shields.io/discord/1195734228319617024.svg?label=&labelColor=6A7EC2&color=7389D8&logo=discord&logoColor=FFFFFF)](https://discord.gg/mihon) +[![GitHub downloads](https://img.shields.io/github/downloads/mihonapp/mihon/total?label=downloads&labelColor=27303D&color=0D1117&logo=github&logoColor=FFFFFF&style=flat)](https://github.com/mihonapp/mihon/releases) -

Features

+[![CI](https://img.shields.io/github/actions/workflow/status/mihonapp/mihon/build_push.yml?labelColor=27303D)](https://github.com/mihonapp/mihon/actions/workflows/build_push.yml) +[![License: Apache-2.0](https://img.shields.io/github/license/mihonapp/mihon?labelColor=27303D&color=0877d2)](/LICENSE) +[![Translation status](https://img.shields.io/weblate/progress/mihon?labelColor=27303D&color=946300)](https://hosted.weblate.org/engage/mihon/) -

Features include

-
    -
  • Local reading of content
  • -
  • A configurable reader with multiple viewers, reading directions and other settings.
  • -
  • Tracker support: MyAnimeList, AniList, Kitsu, MangaUpdates, Shikimori, and Bangumi support
  • -
  • Categories to organize your library
  • -
  • Light and dark themes
  • -
  • Schedule updating your library for new chapters
  • -
  • Create backups locally to read offline or to your desired cloud service
  • -
  • and much more...
  • -
+## Download -

Contributing

+[![Mihon Stable](https://img.shields.io/github/release/mihonapp/mihon.svg?maxAge=3600&label=Stable&labelColor=06599d&color=043b69)](https://github.com/mihonapp/mihon/releases) +[![Mihon Beta](https://img.shields.io/github/v/release/mihonapp/mihon-preview.svg?maxAge=3600&label=Beta&labelColor=2c2c47&color=1c1c39)](https://github.com/mihonapp/mihon-preview/releases) -

- Code of conduct - · - Contributing guide - · -

+*Requires Android 8.0 or higher.* -

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

-

If you got any questions, join our Discord server.

+## Features -
Issues +
-1. Before reporting a new issue, take a look at the FAQ, the changelog and the already opened issues. -2. If you are unsure, ask here: Discord +* Local reading of content. +* A configurable reader with multiple viewers, reading directions and other settings. +* Tracker support: [MyAnimeList](https://myanimelist.net/), [AniList](https://anilist.co/), [Kitsu](https://kitsu.io/), [MangaUpdates](https://mangaupdates.com), [Shikimori](https://shikimori.one), and [Bangumi](https://bgm.tv/) support. +* Categories to organize your library. +* Light and dark themes. +* Schedule updating your library for new chapters. +* Create backups locally to read offline or to your desired cloud service. +* Plus much more... -
- -
Bugs - - Include version (More → About → Version) - If not latest, try updating, it may have already been solved - Beta version is equal to the number of commits as seen on the main page - Include steps to reproduce (if not obvious from description) - Include screenshot (if needed) - If it could be device-dependent, try reproducing on another device (if possible) - -- Don't group unrelated requests into one issue - -
- -
Feature requests - - Write a detailed issue, explaining what it should do or how. Avoid writing just "like X app does" - Include screenshot (if needed) - -Source requests are not accepted. - -
- -

Repositories

- -
-

- - Website - -

-

License

+## Contributing -
Copyright © 2024 The Mihon Open Source Project

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
+[Code of conduct](./CODE_OF_CONDUCT.md) · [Contributing guide](./CONTRIBUTING.md) -

Credits

+Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. -

Thank you to all the people who have already contributed!

-

- - - -

+If you got any questions, [join our Discord server](https://discord.gg/mihon). + +
Issues
+ +Before reporting a new issue, take a look at the [FAQ](https://mihon.app/docs/faq/general), the [changelog](https://mihon.app/changelogs/) and the already opened [issues](https://github.com/mihonapp/mihon/issues). + +
+ +
Bugs
+ +* Include version (**More → About → Version**). +* If not latest, try updating, it may have already been solved. +* Beta version is equal to the number of commits as seen on the main page. +* Include steps to reproduce (if not obvious from description). +* Include screenshot (if needed). +* If it could be device-dependent, try reproducing on another device (if possible). +* Don't group unrelated requests into one issue +- **DO:** [#24](https://git.mihon.dev/tachiyomi/tachiyomi/issues/24), [#71](https://git.mihon.dev/tachiyomi/tachiyomi/issues/71) +- **DON'T:** [#75](https://git.mihon.dev/tachiyomi/tachiyomi/issues/75) + +
+ +
Feature requests
+ +* Write a detailed issue, explaining what it should do or how. + * Avoid writing just "like X app does"; +* Include screenshot (if needed) +* Source requests are not accepted. + +
+ +### Repositories + +[![mihonapp/website - GitHub](https://github-readme-stats.vercel.app/api/pin/?username=mihonapp&repo=website&bg_color=161B22&text_color=c9d1d9&title_color=0877d2&icon_color=0877d2&border_radius=8&hide_border=true)](https://github.com/mihonapp/website/) + +### Credits + +Thank you to all the people who have contributed! + + + Mihon app contributors + + +### Disclaimer + +The developer(s) of this application does not have any affiliation with the content providers available, and this application hosts zero content. + +### License + +``` +Copyright © 2015 Javier Tomás +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Modifications Copyright © 2024 The Mihon Open Source Project +``` + +
\ No newline at end of file From e17d87f35749715946a96dc0e654792227c6722d Mon Sep 17 00:00:00 2001 From: Theodoro Loureiro mota Date: Tue, 23 Jan 2024 09:35:58 -0300 Subject: [PATCH 008/212] Adding Type-safe project accessors (#194) * replace the windowInsetsPadding for navigationBarsPadding + statusBarsPadding * Enabling TYPESAFE_PROJECT_ACCESSORS * Adding typesafe project accessors in the app module * Adding typesafe project accessors in the core module * Adding typesafe project accessors in the core-metadata module * Adding typesafe project accessors in the data module * Adding typesafe project accessors in the domain module * Adding typesafe project accessors in the presentation-core module * Adding typesafe project accessors in the presentation-widget module * Adding typesafe project accessors in the source-local module * Adding typesafe project accessors in the source-api module * Rolling back * Changing TYPESAFE_PROJECT_ACCESSORS line * Removing extra spaces --- app/build.gradle.kts | 18 +++++++++--------- core-metadata/build.gradle.kts | 2 +- core/build.gradle.kts | 2 +- data/build.gradle.kts | 6 +++--- domain/build.gradle.kts | 4 ++-- presentation-core/build.gradle.kts | 4 ++-- presentation-widget/build.gradle.kts | 8 ++++---- settings.gradle.kts | 2 ++ source-api/build.gradle.kts | 2 +- source-local/build.gradle.kts | 10 +++++----- 10 files changed, 30 insertions(+), 28 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ca5acb919..291377779 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -139,15 +139,15 @@ android { } dependencies { - implementation(project(":i18n")) - implementation(project(":core")) - implementation(project(":core-metadata")) - implementation(project(":source-api")) - implementation(project(":source-local")) - implementation(project(":data")) - implementation(project(":domain")) - implementation(project(":presentation-core")) - implementation(project(":presentation-widget")) + implementation(projects.i18n) + implementation(projects.core) + implementation(projects.coreMetadata) + implementation(projects.sourceApi) + implementation(projects.sourceLocal) + implementation(projects.data) + implementation(projects.domain) + implementation(projects.presentationCore) + implementation(projects.presentationWidget) // Compose implementation(platform(compose.bom)) diff --git a/core-metadata/build.gradle.kts b/core-metadata/build.gradle.kts index 43b8846ab..5c8605879 100644 --- a/core-metadata/build.gradle.kts +++ b/core-metadata/build.gradle.kts @@ -14,7 +14,7 @@ android { } dependencies { - implementation(project(":source-api")) + implementation(projects.sourceApi) implementation(kotlinx.bundles.serialization) } diff --git a/core/build.gradle.kts b/core/build.gradle.kts index e90a1fd06..841217152 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -17,7 +17,7 @@ android { } dependencies { - implementation(project(":i18n")) + implementation(projects.i18n) api(libs.logcat) diff --git a/data/build.gradle.kts b/data/build.gradle.kts index 4b2532e8c..2d86bf577 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -24,9 +24,9 @@ android { } dependencies { - implementation(project(":source-api")) - implementation(project(":domain")) - implementation(project(":core")) + implementation(projects.sourceApi) + implementation(projects.domain) + implementation(projects.core) api(libs.bundles.sqldelight) } diff --git a/domain/build.gradle.kts b/domain/build.gradle.kts index 425551ca0..949de1f8e 100644 --- a/domain/build.gradle.kts +++ b/domain/build.gradle.kts @@ -14,8 +14,8 @@ android { } dependencies { - implementation(project(":source-api")) - implementation(project(":core")) + implementation(projects.sourceApi) + implementation(projects.core) implementation(platform(kotlinx.coroutines.bom)) implementation(kotlinx.bundles.coroutines) diff --git a/presentation-core/build.gradle.kts b/presentation-core/build.gradle.kts index 31d10bdbd..f36ca3857 100644 --- a/presentation-core/build.gradle.kts +++ b/presentation-core/build.gradle.kts @@ -21,8 +21,8 @@ android { } dependencies { - api(project(":core")) - api(project(":i18n")) + api(projects.core) + api(projects.i18n) // Compose implementation(platform(compose.bom)) diff --git a/presentation-widget/build.gradle.kts b/presentation-widget/build.gradle.kts index b5124f50b..d3e56e373 100644 --- a/presentation-widget/build.gradle.kts +++ b/presentation-widget/build.gradle.kts @@ -21,10 +21,10 @@ android { } dependencies { - implementation(project(":core")) - implementation(project(":domain")) - implementation(project(":presentation-core")) - api(project(":i18n")) + implementation(projects.core) + implementation(projects.domain) + implementation(projects.presentationCore) + api(projects.i18n) implementation(compose.glance) lintChecks(compose.lintchecks) diff --git a/settings.gradle.kts b/settings.gradle.kts index 5aafcad34..42b473db8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -35,6 +35,8 @@ dependencyResolutionManagement { } } +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") + rootProject.name = "Mihon" include(":app") include(":i18n") diff --git a/source-api/build.gradle.kts b/source-api/build.gradle.kts index f8c78fb19..125252def 100644 --- a/source-api/build.gradle.kts +++ b/source-api/build.gradle.kts @@ -17,7 +17,7 @@ kotlin { } val androidMain by getting { dependencies { - implementation(project(":core")) + implementation(projects.core) api(libs.preferencektx) // Workaround for https://youtrack.jetbrains.com/issue/KT-57605 diff --git a/source-local/build.gradle.kts b/source-local/build.gradle.kts index 98eb4d55a..75739a8d7 100644 --- a/source-local/build.gradle.kts +++ b/source-local/build.gradle.kts @@ -8,8 +8,8 @@ kotlin { sourceSets { val commonMain by getting { dependencies { - implementation(project(":source-api")) - api(project(":i18n")) + implementation(projects.sourceApi) + api(projects.i18n) implementation(libs.unifile) implementation(libs.junrar) @@ -17,11 +17,11 @@ kotlin { } val androidMain by getting { dependencies { - implementation(project(":core")) - implementation(project(":core-metadata")) + implementation(projects.core) + implementation(projects.coreMetadata) // Move ChapterRecognition to separate module? - implementation(project(":domain")) + implementation(projects.domain) implementation(kotlinx.bundles.serialization) } From 1dd130df9ee389fbacb200ee7a13b6d625b372fc Mon Sep 17 00:00:00 2001 From: Theodoro Loureiro mota Date: Tue, 23 Jan 2024 09:47:05 -0300 Subject: [PATCH 009/212] Fix #126: Inconsistent button height with some languages in "Data and storage" (#202) * replace the windowInsetsPadding for navigationBarsPadding + statusBarsPadding * Fixing bug in the MultiChoiceSegmentedButtonRow * Rollback file --- .../presentation/more/settings/screen/SettingsDataScreen.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt index f0a2864ee..f82a58aca 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt @@ -7,8 +7,11 @@ import android.net.Uri import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.HelpOutline @@ -189,9 +192,11 @@ object SettingsDataScreen : SearchableSettings { MultiChoiceSegmentedButtonRow( modifier = Modifier .fillMaxWidth() + .height(intrinsicSize = IntrinsicSize.Min) .padding(horizontal = PrefsHorizontalPadding), ) { SegmentedButton( + modifier = Modifier.fillMaxHeight(), checked = false, onCheckedChange = { navigator.push(CreateBackupScreen()) }, shape = SegmentedButtonDefaults.itemShape(0, 2), @@ -199,6 +204,7 @@ object SettingsDataScreen : SearchableSettings { Text(stringResource(MR.strings.pref_create_backup)) } SegmentedButton( + modifier = Modifier.fillMaxHeight(), checked = false, onCheckedChange = { if (!BackupRestoreJob.isRunning(context)) { From 121b2ec82998e90ea175c115d811ae5957c3b3a1 Mon Sep 17 00:00:00 2001 From: Theodoro Loureiro mota Date: Wed, 24 Jan 2024 10:05:40 -0300 Subject: [PATCH 010/212] [skip ci] Removing extensions from config issues (#224) --- .github/ISSUE_TEMPLATE/config.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 76f836b82..9f0508ef1 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,11 +1,5 @@ blank_issues_enabled: false contact_links: - - name: ⚠️ Extension/source issue - url: https://github.com/tachiyomiorg/extensions/issues/new/choose - about: Issues and requests for official extensions and sources should be opened in the extensions repository instead - - name: 📦 Mihon extensions - url: https://mihon.app/extensions/ - about: List of all available extensions with download links - name: 🖥️ Mihon website url: https://mihon.app/ about: Guides, troubleshooting, and answers to common questions From 348b23a9fd833d9040cfbdd60b11f9d5ce6e5fb7 Mon Sep 17 00:00:00 2001 From: Secozzi <49240133+Secozzi@users.noreply.github.com> Date: Wed, 24 Jan 2024 13:16:28 +0000 Subject: [PATCH 011/212] Fix refreshing from enhanced tracker (#219) fix refreshing from enhanced tracker --- .../java/eu/kanade/domain/track/interactor/RefreshTracks.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/RefreshTracks.kt b/app/src/main/java/eu/kanade/domain/track/interactor/RefreshTracks.kt index 457dbf87e..3d1e337ff 100644 --- a/app/src/main/java/eu/kanade/domain/track/interactor/RefreshTracks.kt +++ b/app/src/main/java/eu/kanade/domain/track/interactor/RefreshTracks.kt @@ -30,9 +30,9 @@ class RefreshTracks( .map { (track, service) -> async { return@async try { - val updatedTrack = service!!.refresh(track.toDbTrack()) - insertTrack.await(updatedTrack.toDomainTrack()!!) - syncChapterProgressWithTrack.await(mangaId, track, service) + val updatedTrack = service!!.refresh(track.toDbTrack()).toDomainTrack()!! + insertTrack.await(updatedTrack) + syncChapterProgressWithTrack.await(mangaId, updatedTrack, service) null } catch (e: Throwable) { service to e From d736bec00361c4192bddb8ec27908a5a833a00bf Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Wed, 24 Jan 2024 14:23:39 +0100 Subject: [PATCH 012/212] Translations update from Hosted Weblate (#225) * Translated using Weblate (Swedish) Currently translated at 100.0% (17 of 17 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/sv/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (17 of 17 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/zh_Hant/ --------- Co-authored-by: bittin1ddc447d824349b2 Co-authored-by: Lzmxya --- i18n/src/commonMain/resources/MR/sv/plurals.xml | 4 ++++ i18n/src/commonMain/resources/MR/zh-rTW/plurals.xml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/i18n/src/commonMain/resources/MR/sv/plurals.xml b/i18n/src/commonMain/resources/MR/sv/plurals.xml index 1e7f8b937..976b04004 100644 --- a/i18n/src/commonMain/resources/MR/sv/plurals.xml +++ b/i18n/src/commonMain/resources/MR/sv/plurals.xml @@ -64,4 +64,8 @@ Nästa kapitel Nästa %d kapitel + + %d förråd + %d flera förråd + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/zh-rTW/plurals.xml b/i18n/src/commonMain/resources/MR/zh-rTW/plurals.xml index 2169508f2..f3d9a6f0b 100644 --- a/i18n/src/commonMain/resources/MR/zh-rTW/plurals.xml +++ b/i18n/src/commonMain/resources/MR/zh-rTW/plurals.xml @@ -7,7 +7,7 @@ %d 個擴充套件可更新 - 剩餘 %1$s 本 + 剩餘 %1$s 項 共 %1$s 章 From b8a9998bbd2b4f937095728c19e834122e580230 Mon Sep 17 00:00:00 2001 From: CodeSpoof <42744253+CodeSpoof@users.noreply.github.com> Date: Thu, 25 Jan 2024 18:27:23 +0100 Subject: [PATCH 013/212] [skip ci] Remove official extensions check from issue templates (#233) * Update report_issue.yml There are no official extensions anymore and the URL was for the tachiyomi repo anyway * Update request_feature.yml No more official extensions --- .github/ISSUE_TEMPLATE/report_issue.yml | 2 -- .github/ISSUE_TEMPLATE/request_feature.yml | 2 -- 2 files changed, 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/report_issue.yml b/.github/ISSUE_TEMPLATE/report_issue.yml index d3a83249c..94f0c6f6b 100644 --- a/.github/ISSUE_TEMPLATE/report_issue.yml +++ b/.github/ISSUE_TEMPLATE/report_issue.yml @@ -94,8 +94,6 @@ body: required: true - label: I have written a short but informative title. required: true - - label: If this is an issue with an official extension, I should be opening an issue in the [extensions repository](https://github.com/tachiyomiorg/extensions/issues/new/choose). - required: true - label: I have gone through the [FAQ](https://mihon.app/docs/faq/general) and [troubleshooting guide](https://mihon.app/docs/guides/troubleshooting/). required: true - label: I have updated the app to version **[0.16.1](https://github.com/mihonapp/mihon/releases/latest)**. diff --git a/.github/ISSUE_TEMPLATE/request_feature.yml b/.github/ISSUE_TEMPLATE/request_feature.yml index d8b93757e..f98db1543 100644 --- a/.github/ISSUE_TEMPLATE/request_feature.yml +++ b/.github/ISSUE_TEMPLATE/request_feature.yml @@ -31,8 +31,6 @@ body: required: true - label: I have written a short but informative title. required: true - - label: If this is an issue with an official extension, I should be opening an issue in the [extensions repository](https://github.com/tachiyomiorg/extensions/issues/new/choose). - required: true - label: I have updated the app to version **[0.16.1](https://github.com/mihonapp/mihon/releases/latest)**. required: true - label: I will fill out all of the requested information in this form. From 65bfa083f2a5d83cba43c52fa9d081c790f1c5b6 Mon Sep 17 00:00:00 2001 From: MajorTanya <39014446+MajorTanya@users.noreply.github.com> Date: Thu, 25 Jan 2024 20:00:23 +0100 Subject: [PATCH 014/212] Replace "tachiyomi" with "mihon" in crash log name (#234) Closes #223. --- app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4b2ef2ab1..33e21be6b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt @@ -21,7 +21,7 @@ class CrashLogUtil( suspend fun dumpLogs() = withNonCancellableContext { try { - val file = context.createFileInCacheDir("tachiyomi_crash_logs.txt") + val file = context.createFileInCacheDir("mihon_crash_logs.txt") file.appendText(getDebugInfo() + "\n\n") getExtensionsInfo()?.let { file.appendText("$it\n\n") } From 05efc4ebeb197cabd2bc494d36fbb27fea923292 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 27 Jan 2024 23:17:09 +0600 Subject: [PATCH 015/212] Update types of legacy tracker model to match to domain one (#245) * `score` to Double * `tracker_id` to Long * `last_chapter_read` to Double * `total_chapters` to Long * `status` to Long --- .../eu/kanade/domain/track/model/Track.kt | 20 ++++----- .../presentation/track/TrackInfoDialogHome.kt | 2 +- .../track/TrackInfoDialogSelector.kt | 18 ++++---- .../presentation/track/TrackerSearch.kt | 2 +- .../track/TrackerSearchPreviewProvider.kt | 10 ++--- .../tachiyomi/data/database/models/Track.kt | 12 ++--- .../data/database/models/TrackImpl.kt | 10 ++--- .../tachiyomi/data/track/BaseTracker.kt | 16 +++---- .../eu/kanade/tachiyomi/data/track/Tracker.kt | 14 +++--- .../tachiyomi/data/track/anilist/Anilist.kt | 44 +++++++++---------- .../data/track/anilist/AnilistApi.kt | 3 +- .../data/track/anilist/AnilistModels.kt | 8 ++-- .../tachiyomi/data/track/bangumi/Bangumi.kt | 24 +++++----- .../data/track/bangumi/BangumiApi.kt | 7 +-- .../data/track/bangumi/BangumiModels.kt | 4 +- .../tachiyomi/data/track/kavita/Kavita.kt | 18 ++++---- .../tachiyomi/data/track/kavita/KavitaApi.kt | 16 +++---- .../tachiyomi/data/track/kitsu/Kitsu.kt | 30 ++++++------- .../tachiyomi/data/track/kitsu/KitsuModels.kt | 13 +++--- .../tachiyomi/data/track/komga/Komga.kt | 18 ++++---- .../tachiyomi/data/track/komga/KomgaApi.kt | 2 +- .../tachiyomi/data/track/komga/KomgaModels.kt | 6 +-- .../data/track/mangaupdates/MangaUpdates.kt | 26 +++++------ .../track/mangaupdates/MangaUpdatesApi.kt | 5 ++- .../data/track/mangaupdates/dto/ListItem.kt | 4 +- .../data/track/mangaupdates/dto/Rating.kt | 4 +- .../tachiyomi/data/track/model/TrackSearch.kt | 14 +++--- .../data/track/myanimelist/MyAnimeList.kt | 28 ++++++------ .../data/track/myanimelist/MyAnimeListApi.kt | 12 ++--- .../data/track/shikimori/Shikimori.kt | 26 +++++------ .../data/track/shikimori/ShikimoriApi.kt | 11 ++--- .../tachiyomi/data/track/suwayomi/Suwayomi.kt | 18 ++++---- .../data/track/suwayomi/SuwayomiApi.kt | 4 +- .../data/track/suwayomi/SuwayomiModels.kt | 2 +- .../ui/manga/track/TrackInfoDialog.kt | 8 ++-- .../main/java/eu/kanade/test/DummyTracker.kt | 34 +++++++------- 36 files changed, 249 insertions(+), 244 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/track/model/Track.kt b/app/src/main/java/eu/kanade/domain/track/model/Track.kt index 3ce611bab..e44133433 100644 --- a/app/src/main/java/eu/kanade/domain/track/model/Track.kt +++ b/app/src/main/java/eu/kanade/domain/track/model/Track.kt @@ -19,10 +19,10 @@ fun Track.toDbTrack(): DbTrack = DbTrack.create(trackerId).also { it.remote_id = remoteId it.library_id = libraryId it.title = title - it.last_chapter_read = lastChapterRead.toFloat() - it.total_chapters = totalChapters.toInt() - it.status = status.toInt() - it.score = score.toFloat() + it.last_chapter_read = lastChapterRead + it.total_chapters = totalChapters + it.status = status + it.score = score it.tracking_url = remoteUrl it.started_reading_date = startDate it.finished_reading_date = finishDate @@ -33,16 +33,14 @@ fun DbTrack.toDomainTrack(idRequired: Boolean = true): Track? { return Track( id = trackId, mangaId = manga_id, - trackerId = tracker_id.toLong(), + trackerId = tracker_id, remoteId = remote_id, libraryId = library_id, title = title, - lastChapterRead = last_chapter_read.toDouble(), - totalChapters = total_chapters.toLong(), - status = status.toLong(), - // Jank workaround due to precision issues while converting - // See https://github.com/tachiyomiorg/tachiyomi/issues/10343 - score = score.toString().toDouble(), + lastChapterRead = last_chapter_read, + totalChapters = total_chapters, + status = status, + score = score, remoteUrl = tracking_url, startDate = started_reading_date, finishDate = finished_reading_date, diff --git a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHome.kt b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHome.kt index 2dd518f3a..11f2b9a06 100644 --- a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHome.kt +++ b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHome.kt @@ -88,7 +88,7 @@ fun TrackInfoDialogHome( TrackInfoItem( title = item.track.title, tracker = item.tracker, - status = item.tracker.getStatus(item.track.status.toInt()), + status = item.tracker.getStatus(item.track.status), onStatusClick = { onStatusClick(item) }, chapters = "${item.track.lastChapterRead.toInt()}".let { val totalChapters = item.track.totalChapters 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 a2c8cc918..053ba7bbc 100644 --- a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogSelector.kt +++ b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogSelector.kt @@ -48,9 +48,9 @@ import tachiyomi.presentation.core.util.isScrolledToStart @Composable fun TrackStatusSelector( - selection: Int, - onSelectionChange: (Int) -> Unit, - selections: Map, + selection: Long, + onSelectionChange: (Long) -> Unit, + selections: Map, onConfirm: () -> Unit, onDismissRequest: () -> Unit, ) { @@ -236,12 +236,12 @@ private fun TrackStatusSelectorPreviews() { onSelectionChange = {}, selections = persistentMapOf( // Anilist values - 1 to MR.strings.reading, - 2 to MR.strings.plan_to_read, - 3 to MR.strings.completed, - 4 to MR.strings.on_hold, - 5 to MR.strings.dropped, - 6 to MR.strings.repeating, + 1L to MR.strings.reading, + 2L to MR.strings.plan_to_read, + 3L to MR.strings.completed, + 4L to MR.strings.on_hold, + 5L to MR.strings.dropped, + 6L to MR.strings.repeating, ), onConfirm = {}, onDismissRequest = {}, diff --git a/app/src/main/java/eu/kanade/presentation/track/TrackerSearch.kt b/app/src/main/java/eu/kanade/presentation/track/TrackerSearch.kt index 1a2ef7800..ef158218f 100644 --- a/app/src/main/java/eu/kanade/presentation/track/TrackerSearch.kt +++ b/app/src/main/java/eu/kanade/presentation/track/TrackerSearch.kt @@ -301,7 +301,7 @@ private fun SearchResultItem( text = status, ) } - if (trackSearch.score != -1f) { + if (trackSearch.score != -1.0) { SearchResultItemDetails( title = stringResource(MR.strings.score), text = trackSearch.score.toString(), diff --git a/app/src/main/java/eu/kanade/presentation/track/TrackerSearchPreviewProvider.kt b/app/src/main/java/eu/kanade/presentation/track/TrackerSearchPreviewProvider.kt index 7bc78781b..808f41f7d 100644 --- a/app/src/main/java/eu/kanade/presentation/track/TrackerSearchPreviewProvider.kt +++ b/app/src/main/java/eu/kanade/presentation/track/TrackerSearchPreviewProvider.kt @@ -62,14 +62,14 @@ internal class TrackerSearchPreviewProvider : PreviewParameterProvider<@Composab private fun randTrackSearch() = TrackSearch().let { it.id = Random.nextLong() it.manga_id = Random.nextLong() - it.tracker_id = Random.nextInt() + it.tracker_id = Random.nextLong() it.remote_id = Random.nextLong() it.library_id = Random.nextLong() it.title = lorem((1..10).random()).joinToString() - it.last_chapter_read = (0..100).random().toFloat() - it.total_chapters = (100..1000).random() - it.score = (0..10).random().toFloat() - it.status = Random.nextInt() + it.last_chapter_read = (0..100).random().toDouble() + it.total_chapters = (100L..1000L).random() + it.score = (0..10).random().toDouble() + it.status = Random.nextLong() it.started_reading_date = 0L it.finished_reading_date = 0L it.tracking_url = "https://example.com/tracker-example" diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Track.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Track.kt index 9d7f98983..aac7ec4c3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Track.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Track.kt @@ -8,7 +8,7 @@ interface Track : Serializable { var manga_id: Long - var tracker_id: Int + var tracker_id: Long var remote_id: Long @@ -16,13 +16,13 @@ interface Track : Serializable { var title: String - var last_chapter_read: Float + var last_chapter_read: Double - var total_chapters: Int + var total_chapters: Long - var score: Float + var score: Double - var status: Int + var status: Long var started_reading_date: Long @@ -40,7 +40,7 @@ interface Track : Serializable { companion object { fun create(serviceId: Long): Track = TrackImpl().apply { - tracker_id = serviceId.toInt() + tracker_id = serviceId } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/TrackImpl.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/TrackImpl.kt index 2a0abce31..85868219f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/TrackImpl.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/TrackImpl.kt @@ -6,7 +6,7 @@ class TrackImpl : Track { override var manga_id: Long = 0 - override var tracker_id: Int = 0 + override var tracker_id: Long = 0 override var remote_id: Long = 0 @@ -14,13 +14,13 @@ class TrackImpl : Track { override lateinit var title: String - override var last_chapter_read: Float = 0F + override var last_chapter_read: Double = 0.0 - override var total_chapters: Int = 0 + override var total_chapters: Long = 0 - override var score: Float = 0f + override var score: Double = 0.0 - override var status: Int = 0 + override var status: Long = 0 override var started_reading_date: Long = 0 diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt index 5a57e2563..3da3c392a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt @@ -40,8 +40,8 @@ abstract class BaseTracker( return track.score } - override fun indexToScore(index: Int): Float { - return index.toFloat() + override fun indexToScore(index: Int): Double { + return index.toDouble() } @CallSuper @@ -70,24 +70,24 @@ abstract class BaseTracker( } } - override suspend fun setRemoteStatus(track: Track, status: Int) { + override suspend fun setRemoteStatus(track: Track, status: Long) { track.status = status - if (track.status == getCompletionStatus() && track.total_chapters != 0) { - track.last_chapter_read = track.total_chapters.toFloat() + if (track.status == getCompletionStatus() && track.total_chapters != 0L) { + track.last_chapter_read = track.total_chapters.toDouble() } updateRemote(track) } override suspend fun setRemoteLastChapterRead(track: Track, chapterNumber: Int) { if ( - track.last_chapter_read == 0f && + track.last_chapter_read == 0.0 && track.last_chapter_read < chapterNumber && track.status != getRereadingStatus() ) { track.status = getReadingStatus() } - track.last_chapter_read = chapterNumber.toFloat() - if (track.total_chapters != 0 && track.last_chapter_read.toInt() == track.total_chapters) { + track.last_chapter_read = chapterNumber.toDouble() + if (track.total_chapters != 0L && track.last_chapter_read.toLong() == track.total_chapters) { track.status = getCompletionStatus() track.finished_reading_date = System.currentTimeMillis() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/Tracker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/Tracker.kt index 59cf64648..06644e932 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/Tracker.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/Tracker.kt @@ -27,22 +27,22 @@ interface Tracker { @DrawableRes fun getLogo(): Int - fun getStatusList(): List + fun getStatusList(): List - fun getStatus(status: Int): StringResource? + fun getStatus(status: Long): StringResource? - fun getReadingStatus(): Int + fun getReadingStatus(): Long - fun getRereadingStatus(): Int + fun getRereadingStatus(): Long - fun getCompletionStatus(): Int + fun getCompletionStatus(): Long fun getScoreList(): ImmutableList // TODO: Store all scores as 10 point in the future maybe? fun get10PointScore(track: DomainTrack): Double - fun indexToScore(index: Int): Float + fun indexToScore(index: Int): Double fun displayScore(track: DomainTrack): String @@ -70,7 +70,7 @@ interface Tracker { // TODO: move this to an interactor, and update all trackers based on common data suspend fun register(item: Track, mangaId: Long) - suspend fun setRemoteStatus(track: Track, status: Int) + suspend fun setRemoteStatus(track: Track, status: Long) suspend fun setRemoteLastChapterRead(track: Track, chapterNumber: Int) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt index dc799dfe1..abf0d702a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/Anilist.kt @@ -20,12 +20,12 @@ import tachiyomi.domain.track.model.Track as DomainTrack class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker { companion object { - const val READING = 1 - const val COMPLETED = 2 - const val ON_HOLD = 3 - const val DROPPED = 4 - const val PLAN_TO_READ = 5 - const val REREADING = 6 + const val READING = 1L + const val COMPLETED = 2L + const val ON_HOLD = 3L + const val DROPPED = 4L + const val PLAN_TO_READ = 5L + const val REREADING = 6L const val POINT_100 = "POINT_100" const val POINT_10 = "POINT_10" @@ -58,11 +58,11 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker { override fun getLogoColor() = Color.rgb(18, 25, 35) - override fun getStatusList(): List { + override fun getStatusList(): List { return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLAN_TO_READ, REREADING) } - override fun getStatus(status: Int): StringResource? = when (status) { + override fun getStatus(status: Long): StringResource? = when (status) { READING -> MR.strings.reading PLAN_TO_READ -> MR.strings.plan_to_read COMPLETED -> MR.strings.completed @@ -72,11 +72,11 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker { else -> null } - override fun getReadingStatus(): Int = READING + override fun getReadingStatus(): Long = READING - override fun getRereadingStatus(): Int = REREADING + override fun getRereadingStatus(): Long = REREADING - override fun getCompletionStatus(): Int = COMPLETED + override fun getCompletionStatus(): Long = COMPLETED override fun getScoreList(): ImmutableList { return when (scorePreference.get()) { @@ -99,24 +99,24 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker { return track.score / 10.0 } - override fun indexToScore(index: Int): Float { + override fun indexToScore(index: Int): Double { return when (scorePreference.get()) { // 10 point - POINT_10 -> index * 10f + POINT_10 -> index * 10.0 // 100 point - POINT_100 -> index.toFloat() + POINT_100 -> index.toDouble() // 5 stars POINT_5 -> when (index) { - 0 -> 0f - else -> index * 20f - 10f + 0 -> 0.0 + else -> index * 20.0 - 10.0 } // Smiley POINT_3 -> when (index) { - 0 -> 0f - else -> index * 25f + 10f + 0 -> 0.0 + else -> index * 25.0 + 10.0 } // 10 point decimal - POINT_10_DECIMAL -> index.toFloat() + POINT_10_DECIMAL -> index.toDouble() else -> throw Exception("Unknown score type") } } @@ -153,12 +153,12 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker { if (track.status != COMPLETED) { if (didReadChapter) { - if (track.last_chapter_read.toInt() == track.total_chapters && track.total_chapters > 0) { + if (track.last_chapter_read.toLong() == track.total_chapters && track.total_chapters > 0) { track.status = COMPLETED track.finished_reading_date = System.currentTimeMillis() } else if (track.status != REREADING) { track.status = READING - if (track.last_chapter_read == 1F) { + if (track.last_chapter_read == 1.0) { track.started_reading_date = System.currentTimeMillis() } } @@ -192,7 +192,7 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker { } else { // Set default fields if it's not found in the list track.status = if (hasReadChapters) READING else PLAN_TO_READ - track.score = 0F + track.score = 0.0 add(track) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt index 3037220fe..2917952a0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt @@ -20,6 +20,7 @@ import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.long +import kotlinx.serialization.json.longOrNull import kotlinx.serialization.json.put import kotlinx.serialization.json.putJsonObject import okhttp3.OkHttpClient @@ -312,7 +313,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) { struct["format"]!!.jsonPrimitive.content.replace("_", "-"), struct["status"]!!.jsonPrimitive.contentOrNull ?: "", parseDate(struct, "startDate"), - struct["chapters"]!!.jsonPrimitive.intOrNull ?: 0, + struct["chapters"]!!.jsonPrimitive.longOrNull ?: 0, struct["averageScore"]?.jsonPrimitive?.intOrNull ?: -1, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistModels.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistModels.kt index ed5550464..d7c037afe 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistModels.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistModels.kt @@ -19,7 +19,7 @@ data class ALManga( val format: String, val publishing_status: String, val start_date_fuzzy: Long, - val total_chapters: Int, + val total_chapters: Long, val average_score: Int, ) { @@ -29,7 +29,7 @@ data class ALManga( total_chapters = this@ALManga.total_chapters cover_url = image_url_lge summary = description?.htmlDecode() ?: "" - score = average_score.toFloat() + score = average_score.toDouble() tracking_url = AnilistApi.mangaUrl(remote_id) publishing_status = this@ALManga.publishing_status publishing_type = format @@ -58,10 +58,10 @@ data class ALUserManga( remote_id = manga.remote_id title = manga.title_user_pref status = toTrackStatus() - score = score_raw.toFloat() + score = score_raw.toDouble() started_reading_date = start_date_fuzzy finished_reading_date = completed_date_fuzzy - last_chapter_read = chapters_read.toFloat() + last_chapter_read = chapters_read.toDouble() library_id = this@ALUserManga.library_id total_chapters = manga.total_chapters } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Bangumi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Bangumi.kt index a85d5e583..b8e7d2acc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Bangumi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/Bangumi.kt @@ -35,7 +35,7 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") { override suspend fun update(track: Track, didReadChapter: Boolean): Track { if (track.status != COMPLETED) { if (didReadChapter) { - if (track.last_chapter_read.toInt() == track.total_chapters && track.total_chapters > 0) { + if (track.last_chapter_read.toLong() == track.total_chapters && track.total_chapters > 0) { track.status = COMPLETED } else { track.status = READING @@ -64,7 +64,7 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") { } else { // Set default fields if it's not found in the list track.status = if (hasReadChapters) READING else PLAN_TO_READ - track.score = 0F + track.score = 0.0 add(track) update(track) } @@ -87,11 +87,11 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") { override fun getLogoColor() = Color.rgb(240, 145, 153) - override fun getStatusList(): List { + override fun getStatusList(): List { return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLAN_TO_READ) } - override fun getStatus(status: Int): StringResource? = when (status) { + override fun getStatus(status: Long): StringResource? = when (status) { READING -> MR.strings.reading PLAN_TO_READ -> MR.strings.plan_to_read COMPLETED -> MR.strings.completed @@ -100,11 +100,11 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") { else -> null } - override fun getReadingStatus(): Int = READING + override fun getReadingStatus(): Long = READING - override fun getRereadingStatus(): Int = -1 + override fun getRereadingStatus(): Long = -1 - override fun getCompletionStatus(): Int = COMPLETED + override fun getCompletionStatus(): Long = COMPLETED override suspend fun login(username: String, password: String) = login(password) @@ -137,11 +137,11 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") { } companion object { - const val READING = 3 - const val COMPLETED = 2 - const val ON_HOLD = 4 - const val DROPPED = 5 - const val PLAN_TO_READ = 1 + const val READING = 3L + const val COMPLETED = 2L + const val ON_HOLD = 4L + const val DROPPED = 5L + const val PLAN_TO_READ = 1L private val SCORE_LIST = IntRange(0, 10) .map(Int::toString) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt index 0b905b514..e1f290d6f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt @@ -11,6 +11,7 @@ import eu.kanade.tachiyomi.network.parseAs import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.contentOrNull +import kotlinx.serialization.json.doubleOrNull import kotlinx.serialization.json.floatOrNull import kotlinx.serialization.json.int import kotlinx.serialization.json.jsonArray @@ -105,11 +106,11 @@ class BangumiApi( "" } val totalChapters = if (obj["eps_count"] != null) { - obj["eps_count"]!!.jsonPrimitive.int + obj["eps_count"]!!.jsonPrimitive.long } else { 0 } - val rating = obj["rating"]?.jsonObject?.get("score")?.jsonPrimitive?.floatOrNull ?: -1f + val rating = obj["rating"]?.jsonObject?.get("score")?.jsonPrimitive?.doubleOrNull ?: -1.0 return TrackSearch.create(trackId).apply { remote_id = obj["id"]!!.jsonPrimitive.long title = obj["name_cn"]!!.jsonPrimitive.content @@ -152,7 +153,7 @@ class BangumiApi( } else { json.decodeFromString(responseBody).let { track.status = it.status?.id!! - track.last_chapter_read = it.ep_status!!.toFloat() + track.last_chapter_read = it.ep_status!!.toDouble() track.score = it.rating!! track } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiModels.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiModels.kt index 445fb5194..c4b1aeed7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiModels.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiModels.kt @@ -16,7 +16,7 @@ data class Collection( val comment: String? = "", val ep_status: Int? = 0, val lasttouch: Int? = 0, - val rating: Float? = 0f, + val rating: Double? = 0.0, val status: Status? = Status(), val tag: List? = emptyList(), val user: User? = User(), @@ -25,7 +25,7 @@ data class Collection( @Serializable data class Status( - val id: Int? = 0, + val id: Long? = 0, val name: String? = "", val type: String? = "", ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/Kavita.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/Kavita.kt index bcfbb1ec5..a9aed629b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/Kavita.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/Kavita.kt @@ -22,9 +22,9 @@ import tachiyomi.domain.track.model.Track as DomainTrack class Kavita(id: Long) : BaseTracker(id, "Kavita"), EnhancedTracker { companion object { - const val UNREAD = 1 - const val READING = 2 - const val COMPLETED = 3 + const val UNREAD = 1L + const val READING = 2L + const val COMPLETED = 3L } var authentications: OAuth? = null @@ -38,20 +38,20 @@ class Kavita(id: Long) : BaseTracker(id, "Kavita"), EnhancedTracker { override fun getLogoColor() = Color.rgb(74, 198, 148) - override fun getStatusList() = listOf(UNREAD, READING, COMPLETED) + override fun getStatusList(): List = listOf(UNREAD, READING, COMPLETED) - override fun getStatus(status: Int): StringResource? = when (status) { + override fun getStatus(status: Long): StringResource? = when (status) { UNREAD -> MR.strings.unread READING -> MR.strings.reading COMPLETED -> MR.strings.completed else -> null } - override fun getReadingStatus(): Int = READING + override fun getReadingStatus(): Long = READING - override fun getRereadingStatus(): Int = -1 + override fun getRereadingStatus(): Long = -1 - override fun getCompletionStatus(): Int = COMPLETED + override fun getCompletionStatus(): Long = COMPLETED override fun getScoreList(): ImmutableList = persistentListOf() @@ -60,7 +60,7 @@ class Kavita(id: Long) : BaseTracker(id, "Kavita"), EnhancedTracker { override suspend fun update(track: Track, didReadChapter: Boolean): Track { if (track.status != COMPLETED) { if (didReadChapter) { - if (track.last_chapter_read.toInt() == track.total_chapters && track.total_chapters > 0) { + if (track.last_chapter_read.toLong() == track.total_chapters && track.total_chapters > 0) { track.status = COMPLETED } else { track.status = READING diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaApi.kt index 5eb753e03..5a23b14d9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaApi.kt @@ -93,7 +93,7 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor * Ignores volumes. * Volumes consisting of 1 file treated as chapter */ - private fun getTotalChapters(url: String): Int { + private fun getTotalChapters(url: String): Long { val requestUrl = getApiVolumesUrl(url) try { val listVolumeDto = with(json) { @@ -101,13 +101,13 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor .execute() .parseAs>() } - var volumeNumber = 0 - var maxChapterNumber = 0 + var volumeNumber = 0L + var maxChapterNumber = 0L for (volume in listVolumeDto) { if (volume.chapters.maxOf { it.number!!.toFloat() } == 0f) { volumeNumber++ } else if (maxChapterNumber < volume.chapters.maxOf { it.number!!.toFloat() }) { - maxChapterNumber = volume.chapters.maxOf { it.number!!.toFloat().toInt() } + maxChapterNumber = volume.chapters.maxOf { it.number!!.toFloat().toLong() } } } @@ -118,17 +118,17 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor } } - private fun getLatestChapterRead(url: String): Float { + private fun getLatestChapterRead(url: String): Double { val seriesId = getIdFromUrl(url) val requestUrl = "${getApiFromUrl(url)}/Tachiyomi/latest-chapter?seriesId=$seriesId" try { with(json) { authClient.newCall(GET(requestUrl)).execute().use { if (it.code == 200) { - return it.parseAs().number!!.replace(",", ".").toFloat() + return it.parseAs().number!!.replace(",", ".").toDouble() } if (it.code == 204) { - return 0F + return 0.0 } } } @@ -139,7 +139,7 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor ) { "Exception getting latest chapter read. Could not get itemRequest: $requestUrl" } throw e } - return 0F + return 0.0 } suspend fun getTrackSearch(url: String): TrackSearch = withIOContext { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/Kitsu.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/Kitsu.kt index 03bfcd137..4b0db8bce 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/Kitsu.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/Kitsu.kt @@ -19,11 +19,11 @@ import tachiyomi.domain.track.model.Track as DomainTrack class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker { companion object { - const val READING = 1 - const val COMPLETED = 2 - const val ON_HOLD = 3 - const val DROPPED = 4 - const val PLAN_TO_READ = 5 + const val READING = 1L + const val COMPLETED = 2L + const val ON_HOLD = 3L + const val DROPPED = 4L + const val PLAN_TO_READ = 5L } override val supportsReadingDates: Boolean = true @@ -38,11 +38,11 @@ class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker { override fun getLogoColor() = Color.rgb(51, 37, 50) - override fun getStatusList(): List { + override fun getStatusList(): List { return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLAN_TO_READ) } - override fun getStatus(status: Int): StringResource? = when (status) { + override fun getStatus(status: Long): StringResource? = when (status) { READING -> MR.strings.reading PLAN_TO_READ -> MR.strings.plan_to_read COMPLETED -> MR.strings.completed @@ -51,19 +51,19 @@ class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker { else -> null } - override fun getReadingStatus(): Int = READING + override fun getReadingStatus(): Long = READING - override fun getRereadingStatus(): Int = -1 + override fun getRereadingStatus(): Long = -1 - override fun getCompletionStatus(): Int = COMPLETED + override fun getCompletionStatus(): Long = COMPLETED override fun getScoreList(): ImmutableList { val df = DecimalFormat("0.#") return (listOf("0") + IntRange(2, 20).map { df.format(it / 2f) }).toImmutableList() } - override fun indexToScore(index: Int): Float { - return if (index > 0) (index + 1) / 2f else 0f + override fun indexToScore(index: Int): Double { + return if (index > 0) (index + 1) / 2.0 else 0.0 } override fun displayScore(track: DomainTrack): String { @@ -78,12 +78,12 @@ class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker { override suspend fun update(track: Track, didReadChapter: Boolean): Track { if (track.status != COMPLETED) { if (didReadChapter) { - if (track.last_chapter_read.toInt() == track.total_chapters && track.total_chapters > 0) { + if (track.last_chapter_read.toLong() == track.total_chapters && track.total_chapters > 0) { track.status = COMPLETED track.finished_reading_date = System.currentTimeMillis() } else { track.status = READING - if (track.last_chapter_read == 1F) { + if (track.last_chapter_read == 1.0) { track.started_reading_date = System.currentTimeMillis() } } @@ -110,7 +110,7 @@ class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker { update(track) } else { track.status = if (hasReadChapters) READING else PLAN_TO_READ - track.score = 0F + track.score = 0.0 add(track) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt index ba6684339..25aceef32 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt @@ -12,6 +12,7 @@ import kotlinx.serialization.json.intOrNull import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.long +import kotlinx.serialization.json.longOrNull import java.text.SimpleDateFormat import java.util.Date import java.util.Locale @@ -19,7 +20,7 @@ import java.util.Locale class KitsuSearchManga(obj: JsonObject) { val id = obj["id"]!!.jsonPrimitive.long private val canonicalTitle = obj["canonicalTitle"]!!.jsonPrimitive.content - private val chapterCount = obj["chapterCount"]?.jsonPrimitive?.intOrNull + private val chapterCount = obj["chapterCount"]?.jsonPrimitive?.longOrNull val subType = obj["subtype"]?.jsonPrimitive?.contentOrNull val original = try { obj["posterImage"]?.jsonObject?.get("original")?.jsonPrimitive?.content @@ -28,7 +29,7 @@ class KitsuSearchManga(obj: JsonObject) { null } private val synopsis = obj["synopsis"]?.jsonPrimitive?.contentOrNull - private val rating = obj["averageRating"]?.jsonPrimitive?.contentOrNull?.toFloatOrNull() + private val rating = obj["averageRating"]?.jsonPrimitive?.contentOrNull?.toDoubleOrNull() private var startDate = obj["startDate"]?.jsonPrimitive?.contentOrNull?.let { val outputDf = SimpleDateFormat("yyyy-MM-dd", Locale.US) outputDf.format(Date(it.toLong() * 1000)) @@ -43,7 +44,7 @@ class KitsuSearchManga(obj: JsonObject) { cover_url = original ?: "" summary = synopsis ?: "" tracking_url = KitsuApi.mangaUrl(remote_id) - score = rating ?: -1f + score = rating ?: -1.0 publishing_status = if (endDate == null) { "Publishing" } else { @@ -57,7 +58,7 @@ class KitsuSearchManga(obj: JsonObject) { class KitsuLibManga(obj: JsonObject, manga: JsonObject) { val id = manga["id"]!!.jsonPrimitive.int private val canonicalTitle = manga["attributes"]!!.jsonObject["canonicalTitle"]!!.jsonPrimitive.content - private val chapterCount = manga["attributes"]!!.jsonObject["chapterCount"]?.jsonPrimitive?.intOrNull + private val chapterCount = manga["attributes"]!!.jsonObject["chapterCount"]?.jsonPrimitive?.longOrNull val type = manga["attributes"]!!.jsonObject["mangaType"]?.jsonPrimitive?.contentOrNull.orEmpty() val original = manga["attributes"]!!.jsonObject["posterImage"]!!.jsonObject["original"]!!.jsonPrimitive.content private val synopsis = manga["attributes"]!!.jsonObject["synopsis"]!!.jsonPrimitive.content @@ -82,8 +83,8 @@ class KitsuLibManga(obj: JsonObject, manga: JsonObject) { started_reading_date = KitsuDateHelper.parse(startedAt) finished_reading_date = KitsuDateHelper.parse(finishedAt) status = toTrackStatus() - score = ratingTwenty?.let { it.toInt() / 2f } ?: 0f - last_chapter_read = progress.toFloat() + score = ratingTwenty?.let { it.toInt() / 2.0 } ?: 0.0 + last_chapter_read = progress.toDouble() } private fun toTrackStatus() = when (status) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/Komga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/Komga.kt index 6e69bf764..eee8941a3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/Komga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/Komga.kt @@ -19,9 +19,9 @@ import tachiyomi.domain.track.model.Track as DomainTrack class Komga(id: Long) : BaseTracker(id, "Komga"), EnhancedTracker { companion object { - const val UNREAD = 1 - const val READING = 2 - const val COMPLETED = 3 + const val UNREAD = 1L + const val READING = 2L + const val COMPLETED = 3L } override val client: OkHttpClient = @@ -35,20 +35,20 @@ class Komga(id: Long) : BaseTracker(id, "Komga"), EnhancedTracker { override fun getLogoColor() = Color.rgb(51, 37, 50) - override fun getStatusList() = listOf(UNREAD, READING, COMPLETED) + override fun getStatusList(): List = listOf(UNREAD, READING, COMPLETED) - override fun getStatus(status: Int): StringResource? = when (status) { + override fun getStatus(status: Long): StringResource? = when (status) { UNREAD -> MR.strings.unread READING -> MR.strings.reading COMPLETED -> MR.strings.completed else -> null } - override fun getReadingStatus(): Int = READING + override fun getReadingStatus(): Long = READING - override fun getRereadingStatus(): Int = -1 + override fun getRereadingStatus(): Long = -1 - override fun getCompletionStatus(): Int = COMPLETED + override fun getCompletionStatus(): Long = COMPLETED override fun getScoreList(): ImmutableList = persistentListOf() @@ -57,7 +57,7 @@ class Komga(id: Long) : BaseTracker(id, "Komga"), EnhancedTracker { override suspend fun update(track: Track, didReadChapter: Boolean): Track { if (track.status != COMPLETED) { if (didReadChapter) { - if (track.last_chapter_read.toInt() == track.total_chapters && track.total_chapters > 0) { + if (track.last_chapter_read.toLong() == track.total_chapters && track.total_chapters > 0) { track.status = COMPLETED } else { track.status = READING diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt index 628c4eb53..2852d8e8e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt @@ -67,7 +67,7 @@ class KomgaApi( track.apply { cover_url = "$url/thumbnail" tracking_url = url - total_chapters = progress.maxNumberSort.toInt() + total_chapters = progress.maxNumberSort.toLong() status = when (progress.booksCount) { progress.booksUnreadCount -> Komga.UNREAD progress.booksReadCount -> Komga.COMPLETED diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaModels.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaModels.kt index 3b8a4ce4a..6d1601ac0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaModels.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaModels.kt @@ -65,7 +65,7 @@ data class ReadProgressUpdateDto( @Serializable data class ReadProgressUpdateV2Dto( - val lastBookNumberSortRead: Float, + val lastBookNumberSortRead: Double, ) @Serializable @@ -91,7 +91,7 @@ data class ReadProgressDto( booksReadCount, booksUnreadCount, booksInProgressCount, - lastReadContinuousIndex.toFloat(), + lastReadContinuousIndex.toDouble(), booksCount.toFloat(), ) } @@ -102,6 +102,6 @@ data class ReadProgressV2Dto( val booksReadCount: Int, val booksUnreadCount: Int, val booksInProgressCount: Int, - val lastReadContinuousNumberSort: Float, + val lastReadContinuousNumberSort: Double, val maxNumberSort: Float, ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdates.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdates.kt index 24ad1a56b..6219e728b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdates.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdates.kt @@ -19,11 +19,11 @@ import tachiyomi.domain.track.model.Track as DomainTrack class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker { companion object { - const val READING_LIST = 0 - const val WISH_LIST = 1 - const val COMPLETE_LIST = 2 - const val UNFINISHED_LIST = 3 - const val ON_HOLD_LIST = 4 + const val READING_LIST = 0L + const val WISH_LIST = 1L + const val COMPLETE_LIST = 2L + const val UNFINISHED_LIST = 3L + const val ON_HOLD_LIST = 4L private val SCORE_LIST = (0..10) .flatMap { decimal -> @@ -46,11 +46,11 @@ class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker override fun getLogoColor(): Int = Color.rgb(146, 160, 173) - override fun getStatusList(): List { + override fun getStatusList(): List { return listOf(READING_LIST, COMPLETE_LIST, ON_HOLD_LIST, UNFINISHED_LIST, WISH_LIST) } - override fun getStatus(status: Int): StringResource? = when (status) { + override fun getStatus(status: Long): StringResource? = when (status) { READING_LIST -> MR.strings.reading_list WISH_LIST -> MR.strings.wish_list COMPLETE_LIST -> MR.strings.complete_list @@ -59,15 +59,15 @@ class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker else -> null } - override fun getReadingStatus(): Int = READING_LIST + override fun getReadingStatus(): Long = READING_LIST - override fun getRereadingStatus(): Int = -1 + override fun getRereadingStatus(): Long = -1 - override fun getCompletionStatus(): Int = COMPLETE_LIST + override fun getCompletionStatus(): Long = COMPLETE_LIST override fun getScoreList(): ImmutableList = SCORE_LIST - override fun indexToScore(index: Int): Float = if (index == 0) 0f else SCORE_LIST[index].toFloat() + override fun indexToScore(index: Int): Double = if (index == 0) 0.0 else SCORE_LIST[index].toDouble() override fun displayScore(track: DomainTrack): String = track.score.toString() @@ -88,7 +88,7 @@ class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker val (series, rating) = api.getSeriesListItem(track) track.copyFrom(series, rating) } catch (e: Exception) { - track.score = 0f + track.score = 0.0 api.addSeriesToList(track, hasReadChapters) track } @@ -108,7 +108,7 @@ class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker private fun Track.copyFrom(item: ListItem, rating: Rating?): Track = apply { item.copyTo(this) - score = rating?.rating ?: 0f + score = rating?.rating ?: 0.0 } override suspend fun login(username: String, password: String) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdatesApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdatesApi.kt index 794ad11ef..fa7e4556d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdatesApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdatesApi.kt @@ -79,7 +79,7 @@ class MangaUpdatesApi( .let { if (it.code == 200) { track.status = status - track.last_chapter_read = 1f + track.last_chapter_read = 1.0 } } } @@ -133,7 +133,8 @@ class MangaUpdatesApi( } private suspend fun updateSeriesRating(track: Track) { - if (track.score != 0f) { + if (track.score < 0.0) return + if (track.score != 0.0) { val body = buildJsonObject { put("rating", track.score) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/dto/ListItem.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/dto/ListItem.kt index 4ed8bd705..15a551078 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/dto/ListItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/dto/ListItem.kt @@ -9,7 +9,7 @@ import kotlinx.serialization.Serializable data class ListItem( val series: Series? = null, @SerialName("list_id") - val listId: Int? = null, + val listId: Long? = null, val status: Status? = null, val priority: Int? = null, ) @@ -17,6 +17,6 @@ data class ListItem( fun ListItem.copyTo(track: Track): Track { return track.apply { this.status = listId ?: READING_LIST - this.last_chapter_read = this@copyTo.status?.chapter?.toFloat() ?: 0f + this.last_chapter_read = this@copyTo.status?.chapter?.toDouble() ?: 0.0 } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/dto/Rating.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/dto/Rating.kt index b550a37f4..89a55b413 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/dto/Rating.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/dto/Rating.kt @@ -5,11 +5,11 @@ import kotlinx.serialization.Serializable @Serializable data class Rating( - val rating: Float? = null, + val rating: Double? = null, ) fun Rating.copyTo(track: Track): Track { return track.apply { - this.score = rating ?: 0f + this.score = rating ?: 0.0 } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/model/TrackSearch.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/model/TrackSearch.kt index b8a71687f..c1af76ebf 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/model/TrackSearch.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/model/TrackSearch.kt @@ -8,7 +8,7 @@ class TrackSearch : Track { override var manga_id: Long = 0 - override var tracker_id: Int = 0 + override var tracker_id: Long = 0 override var remote_id: Long = 0 @@ -16,13 +16,13 @@ class TrackSearch : Track { override lateinit var title: String - override var last_chapter_read: Float = 0F + override var last_chapter_read: Double = 0.0 - override var total_chapters: Int = 0 + override var total_chapters: Long = 0 - override var score: Float = -1f + override var score: Double = -1.0 - override var status: Int = 0 + override var status: Long = 0 override var started_reading_date: Long = 0 @@ -55,14 +55,14 @@ class TrackSearch : Track { override fun hashCode(): Int { var result = manga_id.hashCode() - result = 31 * result + tracker_id + result = 31 * result + tracker_id.hashCode() result = 31 * result + remote_id.hashCode() return result } companion object { fun create(serviceId: Long): TrackSearch = TrackSearch().apply { - tracker_id = serviceId.toInt() + tracker_id = serviceId } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt index 5f75b12e8..ee377b9e5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt @@ -18,12 +18,12 @@ import tachiyomi.domain.track.model.Track as DomainTrack class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker { companion object { - const val READING = 1 - const val COMPLETED = 2 - const val ON_HOLD = 3 - const val DROPPED = 4 - const val PLAN_TO_READ = 6 - const val REREADING = 7 + const val READING = 1L + const val COMPLETED = 2L + const val ON_HOLD = 3L + const val DROPPED = 4L + const val PLAN_TO_READ = 6L + const val REREADING = 7L private const val SEARCH_ID_PREFIX = "id:" private const val SEARCH_LIST_PREFIX = "my:" @@ -44,11 +44,11 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker { override fun getLogoColor() = Color.rgb(46, 81, 162) - override fun getStatusList(): List { + override fun getStatusList(): List { return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLAN_TO_READ, REREADING) } - override fun getStatus(status: Int): StringResource? = when (status) { + override fun getStatus(status: Long): StringResource? = when (status) { READING -> MR.strings.reading PLAN_TO_READ -> MR.strings.plan_to_read COMPLETED -> MR.strings.completed @@ -58,11 +58,11 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker { else -> null } - override fun getReadingStatus(): Int = READING + override fun getReadingStatus(): Long = READING - override fun getRereadingStatus(): Int = REREADING + override fun getRereadingStatus(): Long = REREADING - override fun getCompletionStatus(): Int = COMPLETED + override fun getCompletionStatus(): Long = COMPLETED override fun getScoreList(): ImmutableList = SCORE_LIST @@ -77,12 +77,12 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker { override suspend fun update(track: Track, didReadChapter: Boolean): Track { if (track.status != COMPLETED) { if (didReadChapter) { - if (track.last_chapter_read.toInt() == track.total_chapters && track.total_chapters > 0) { + if (track.last_chapter_read.toLong() == track.total_chapters && track.total_chapters > 0) { track.status = COMPLETED track.finished_reading_date = System.currentTimeMillis() } else if (track.status != REREADING) { track.status = READING - if (track.last_chapter_read == 1F) { + if (track.last_chapter_read == 1.0) { track.started_reading_date = System.currentTimeMillis() } } @@ -111,7 +111,7 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker { } else { // Set default fields if it's not found in the list track.status = if (hasReadChapters) READING else PLAN_TO_READ - track.score = 0F + track.score = 0.0 add(track) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt index 518b872b4..dabb25aae 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt @@ -16,6 +16,8 @@ import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.boolean import kotlinx.serialization.json.contentOrNull +import kotlinx.serialization.json.double +import kotlinx.serialization.json.doubleOrNull import kotlinx.serialization.json.float import kotlinx.serialization.json.floatOrNull import kotlinx.serialization.json.int @@ -119,8 +121,8 @@ class MyAnimeListApi( remote_id = obj["id"]!!.jsonPrimitive.long title = obj["title"]!!.jsonPrimitive.content summary = obj["synopsis"]?.jsonPrimitive?.content ?: "" - total_chapters = obj["num_chapters"]!!.jsonPrimitive.int - score = obj["mean"]?.jsonPrimitive?.floatOrNull ?: -1f + total_chapters = obj["num_chapters"]!!.jsonPrimitive.long + score = obj["mean"]?.jsonPrimitive?.doubleOrNull ?: -1.0 cover_url = obj["main_picture"]?.jsonObject?.get("large")?.jsonPrimitive?.content ?: "" @@ -187,7 +189,7 @@ class MyAnimeListApi( .awaitSuccess() .parseAs() .let { obj -> - track.total_chapters = obj["num_chapters"]!!.jsonPrimitive.int + track.total_chapters = obj["num_chapters"]!!.jsonPrimitive.long obj.jsonObject["my_list_status"]?.jsonObject?.let { parseMangaItem(it, track) } @@ -249,8 +251,8 @@ class MyAnimeListApi( return track.apply { val isRereading = obj["is_rereading"]!!.jsonPrimitive.boolean status = if (isRereading) MyAnimeList.REREADING else getStatus(obj["status"]?.jsonPrimitive?.content) - last_chapter_read = obj["num_chapters_read"]!!.jsonPrimitive.float - score = obj["score"]!!.jsonPrimitive.int.toFloat() + last_chapter_read = obj["num_chapters_read"]!!.jsonPrimitive.double + score = obj["score"]!!.jsonPrimitive.int.toDouble() obj["start_date"]?.let { started_reading_date = parseDate(it.jsonPrimitive.content) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/Shikimori.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/Shikimori.kt index 3036cc8a8..118d005c1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/Shikimori.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/Shikimori.kt @@ -18,12 +18,12 @@ import tachiyomi.domain.track.model.Track as DomainTrack class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker { companion object { - const val READING = 1 - const val COMPLETED = 2 - const val ON_HOLD = 3 - const val DROPPED = 4 - const val PLAN_TO_READ = 5 - const val REREADING = 6 + const val READING = 1L + const val COMPLETED = 2L + const val ON_HOLD = 3L + const val DROPPED = 4L + const val PLAN_TO_READ = 5L + const val REREADING = 6L private val SCORE_LIST = IntRange(0, 10) .map(Int::toString) @@ -49,7 +49,7 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker { override suspend fun update(track: Track, didReadChapter: Boolean): Track { if (track.status != COMPLETED) { if (didReadChapter) { - if (track.last_chapter_read.toInt() == track.total_chapters && track.total_chapters > 0) { + if (track.last_chapter_read.toLong() == track.total_chapters && track.total_chapters > 0) { track.status = COMPLETED } else if (track.status != REREADING) { track.status = READING @@ -79,7 +79,7 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker { } else { // Set default fields if it's not found in the list track.status = if (hasReadChapters) READING else PLAN_TO_READ - track.score = 0F + track.score = 0.0 add(track) } } @@ -101,11 +101,11 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker { override fun getLogoColor() = Color.rgb(40, 40, 40) - override fun getStatusList(): List { + override fun getStatusList(): List { return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLAN_TO_READ, REREADING) } - override fun getStatus(status: Int): StringResource? = when (status) { + override fun getStatus(status: Long): StringResource? = when (status) { READING -> MR.strings.reading PLAN_TO_READ -> MR.strings.plan_to_read COMPLETED -> MR.strings.completed @@ -115,11 +115,11 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker { else -> null } - override fun getReadingStatus(): Int = READING + override fun getReadingStatus(): Long = READING - override fun getRereadingStatus(): Int = REREADING + override fun getRereadingStatus(): Long = REREADING - override fun getCompletionStatus(): Int = COMPLETED + override fun getCompletionStatus(): Long = COMPLETED override suspend fun login(username: String, password: String) = login(password) 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 5db8a5799..a4bde2a3a 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 @@ -15,6 +15,7 @@ import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.contentOrNull +import kotlinx.serialization.json.double import kotlinx.serialization.json.float import kotlinx.serialization.json.int import kotlinx.serialization.json.jsonObject @@ -102,10 +103,10 @@ class ShikimoriApi( return TrackSearch.create(trackId).apply { remote_id = obj["id"]!!.jsonPrimitive.long title = obj["name"]!!.jsonPrimitive.content - total_chapters = obj["chapters"]!!.jsonPrimitive.int + total_chapters = obj["chapters"]!!.jsonPrimitive.long cover_url = baseUrl + obj["image"]!!.jsonObject["preview"]!!.jsonPrimitive.content summary = "" - score = obj["score"]!!.jsonPrimitive.float + score = obj["score"]!!.jsonPrimitive.double tracking_url = baseUrl + obj["url"]!!.jsonPrimitive.content publishing_status = obj["status"]!!.jsonPrimitive.content publishing_type = obj["kind"]!!.jsonPrimitive.content @@ -117,10 +118,10 @@ class ShikimoriApi( return Track.create(trackId).apply { title = mangas["name"]!!.jsonPrimitive.content remote_id = obj["id"]!!.jsonPrimitive.long - total_chapters = mangas["chapters"]!!.jsonPrimitive.int + total_chapters = mangas["chapters"]!!.jsonPrimitive.long library_id = obj["id"]!!.jsonPrimitive.long - last_chapter_read = obj["chapters"]!!.jsonPrimitive.float - score = (obj["score"]!!.jsonPrimitive.int).toFloat() + last_chapter_read = obj["chapters"]!!.jsonPrimitive.double + score = obj["score"]!!.jsonPrimitive.int.toDouble() status = toTrackStatus(obj["status"]!!.jsonPrimitive.content) tracking_url = baseUrl + mangas["url"]!!.jsonPrimitive.content } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/Suwayomi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/Suwayomi.kt index d8d1ba975..6b2ca63f9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/Suwayomi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/Suwayomi.kt @@ -23,25 +23,25 @@ class Suwayomi(id: Long) : BaseTracker(id, "Suwayomi"), EnhancedTracker { override fun getLogoColor() = Color.rgb(255, 35, 35) // TODO companion object { - const val UNREAD = 1 - const val READING = 2 - const val COMPLETED = 3 + const val UNREAD = 1L + const val READING = 2L + const val COMPLETED = 3L } - override fun getStatusList() = listOf(UNREAD, READING, COMPLETED) + override fun getStatusList(): List = listOf(UNREAD, READING, COMPLETED) - override fun getStatus(status: Int): StringResource? = when (status) { + override fun getStatus(status: Long): StringResource? = when (status) { UNREAD -> MR.strings.unread READING -> MR.strings.reading COMPLETED -> MR.strings.completed else -> null } - override fun getReadingStatus(): Int = READING + override fun getReadingStatus(): Long = READING - override fun getRereadingStatus(): Int = -1 + override fun getRereadingStatus(): Long = -1 - override fun getCompletionStatus(): Int = COMPLETED + override fun getCompletionStatus(): Long = COMPLETED override fun getScoreList(): ImmutableList = persistentListOf() @@ -50,7 +50,7 @@ class Suwayomi(id: Long) : BaseTracker(id, "Suwayomi"), EnhancedTracker { override suspend fun update(track: Track, didReadChapter: Boolean): Track { if (track.status != COMPLETED) { if (didReadChapter) { - if (track.last_chapter_read.toInt() == track.total_chapters && track.total_chapters > 0) { + if (track.last_chapter_read.toLong() == track.total_chapters && track.total_chapters > 0) { track.status = COMPLETED } else { track.status = READING diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiApi.kt index 5c5367bec..ef1d2e1b1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiApi.kt @@ -66,9 +66,9 @@ class SuwayomiApi(private val trackId: Long) { cover_url = "$url/thumbnail" summary = manga.description.orEmpty() tracking_url = url - total_chapters = manga.chapterCount.toInt() + total_chapters = manga.chapterCount publishing_status = manga.status - last_chapter_read = manga.lastChapterRead?.chapterNumber ?: 0F + last_chapter_read = manga.lastChapterRead?.chapterNumber ?: 0.0 status = when (manga.unreadCount) { manga.chapterCount -> Suwayomi.UNREAD 0L -> Suwayomi.COMPLETED diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiModels.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiModels.kt index 27aef1af7..c3fb5023a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiModels.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiModels.kt @@ -64,7 +64,7 @@ data class ChapterDataClass( val url: String, val name: String, val uploadDate: Long, - val chapterNumber: Float, + val chapterNumber: Double, val scanlator: String?, val mangaId: Int, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt index 9f08bb5dc..980153732 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt @@ -285,13 +285,13 @@ private data class TrackStatusSelectorScreen( private class Model( private val track: Track, private val tracker: Tracker, - ) : StateScreenModel(State(track.status.toInt())) { + ) : StateScreenModel(State(track.status)) { - fun getSelections(): Map { + fun getSelections(): Map { return tracker.getStatusList().associateWith { tracker.getStatus(it) } } - fun setSelection(selection: Int) { + fun setSelection(selection: Long) { mutableState.update { it.copy(selection = selection) } } @@ -303,7 +303,7 @@ private data class TrackStatusSelectorScreen( @Immutable data class State( - val selection: Int, + val selection: Long, ) } } diff --git a/app/src/main/java/eu/kanade/test/DummyTracker.kt b/app/src/main/java/eu/kanade/test/DummyTracker.kt index e8183310d..56092b440 100644 --- a/app/src/main/java/eu/kanade/test/DummyTracker.kt +++ b/app/src/main/java/eu/kanade/test/DummyTracker.kt @@ -18,10 +18,10 @@ data class DummyTracker( override val isLoggedIn: Boolean = false, val valLogoColor: Int = Color.rgb(18, 25, 35), val valLogo: Int = R.drawable.ic_tracker_anilist, - val valStatuses: List = (1..6).toList(), - val valReadingStatus: Int = 1, - val valRereadingStatus: Int = 1, - val valCompletionStatus: Int = 2, + val valStatuses: List = (1L..6L).toList(), + val valReadingStatus: Long = 1L, + val valRereadingStatus: Long = 1L, + val valCompletionStatus: Long = 2L, val valScoreList: ImmutableList = (0..10).map(Int::toString).toImmutableList(), val val10PointScore: Double = 5.4, val valSearchResults: List = listOf(), @@ -34,29 +34,29 @@ data class DummyTracker( override fun getLogo(): Int = valLogo - override fun getStatusList(): List = valStatuses + override fun getStatusList(): List = valStatuses - override fun getStatus(status: Int): StringResource? = when (status) { - 1 -> MR.strings.reading - 2 -> MR.strings.plan_to_read - 3 -> MR.strings.completed - 4 -> MR.strings.on_hold - 5 -> MR.strings.dropped - 6 -> MR.strings.repeating + override fun getStatus(status: Long): StringResource? = when (status) { + 1L -> MR.strings.reading + 2L -> MR.strings.plan_to_read + 3L -> MR.strings.completed + 4L -> MR.strings.on_hold + 5L -> MR.strings.dropped + 6L -> MR.strings.repeating else -> null } - override fun getReadingStatus(): Int = valReadingStatus + override fun getReadingStatus(): Long = valReadingStatus - override fun getRereadingStatus(): Int = valRereadingStatus + override fun getRereadingStatus(): Long = valRereadingStatus - override fun getCompletionStatus(): Int = valCompletionStatus + override fun getCompletionStatus(): Long = valCompletionStatus override fun getScoreList(): ImmutableList = valScoreList override fun get10PointScore(track: Track): Double = val10PointScore - override fun indexToScore(index: Int): Float = getScoreList()[index].toFloat() + override fun indexToScore(index: Int): Double = getScoreList()[index].toDouble() override fun displayScore(track: Track): String = track.score.toString() @@ -94,7 +94,7 @@ data class DummyTracker( override suspend fun setRemoteStatus( track: eu.kanade.tachiyomi.data.database.models.Track, - status: Int, + status: Long, ) = Unit override suspend fun setRemoteLastChapterRead( From 32188f9f65009a18250674ef1bd6e57d351c1fba Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 28 Jan 2024 00:12:18 +0600 Subject: [PATCH 016/212] Refactor MAL code to not spam refresh token when it fails --- .../domain/track/service/TrackPreferences.kt | 6 +++ .../data/track/myanimelist/MyAnimeList.kt | 10 ++++- .../data/track/myanimelist/MyAnimeListApi.kt | 38 +++++++++---------- .../myanimelist/MyAnimeListInterceptor.kt | 33 ++++++++-------- 4 files changed, 49 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/track/service/TrackPreferences.kt b/app/src/main/java/eu/kanade/domain/track/service/TrackPreferences.kt index 8ed234b90..3e99d5ba0 100644 --- a/app/src/main/java/eu/kanade/domain/track/service/TrackPreferences.kt +++ b/app/src/main/java/eu/kanade/domain/track/service/TrackPreferences.kt @@ -19,9 +19,15 @@ class TrackPreferences( "", ) + fun trackAuthExpired(tracker: Tracker) = preferenceStore.getBoolean( + Preference.privateKey("pref_tracker_auth_expired_${tracker.id}"), + false, + ) + fun setCredentials(tracker: Tracker, username: String, password: String) { trackUsername(tracker).set(username) trackPassword(tracker).set(password) + trackAuthExpired(tracker).set(false) } fun trackToken(tracker: Tracker) = preferenceStore.getString(Preference.privateKey("track_token_${tracker.id}"), "") diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt index ee377b9e5..33daee1a9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt @@ -35,7 +35,7 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker { private val json: Json by injectLazy() - private val interceptor by lazy { MyAnimeListInterceptor(this, getPassword()) } + private val interceptor by lazy { MyAnimeListInterceptor(this) } private val api by lazy { MyAnimeListApi(id, client, interceptor) } override val supportsReadingDates: Boolean = true @@ -155,6 +155,14 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker { interceptor.setAuth(null) } + fun getIfAuthExpired(): Boolean { + return trackPreferences.trackAuthExpired(this).get() + } + + fun setAuthExpired() { + trackPreferences.trackAuthExpired(this).set(true) + } + fun saveOAuth(oAuth: OAuth?) { trackPreferences.trackToken(this).set(json.encodeToString(oAuth)) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt index dabb25aae..563100304 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt @@ -25,6 +25,7 @@ import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.long +import logcat.logcat import okhttp3.FormBody import okhttp3.Headers import okhttp3.OkHttpClient @@ -49,13 +50,13 @@ class MyAnimeListApi( suspend fun getAccessToken(authCode: String): OAuth { return withIOContext { val formBody: RequestBody = FormBody.Builder() - .add("client_id", clientId) + .add("client_id", CLIENT_ID) .add("code", authCode) .add("code_verifier", codeVerifier) .add("grant_type", "authorization_code") .build() with(json) { - client.newCall(POST("$baseOAuthUrl/token", body = formBody)) + client.newCall(POST("$BASE_OAUTH_URL/token", body = formBody)) .awaitSuccess() .parseAs() } @@ -65,7 +66,7 @@ class MyAnimeListApi( suspend fun getCurrentUser(): String { return withIOContext { val request = Request.Builder() - .url("$baseApiUrl/users/@me") + .url("$BASE_API_URL/users/@me") .get() .build() with(json) { @@ -79,7 +80,7 @@ class MyAnimeListApi( suspend fun search(query: String): List { return withIOContext { - val url = "$baseApiUrl/manga".toUri().buildUpon() + val url = "$BASE_API_URL/manga".toUri().buildUpon() // MAL API throws a 400 when the query is over 64 characters... .appendQueryParameter("q", query.take(64)) .appendQueryParameter("nsfw", "true") @@ -104,7 +105,7 @@ class MyAnimeListApi( suspend fun getMangaDetails(id: Int): TrackSearch { return withIOContext { - val url = "$baseApiUrl/manga".toUri().buildUpon() + val url = "$BASE_API_URL/manga".toUri().buildUpon() .appendPath(id.toString()) .appendQueryParameter( "fields", @@ -180,7 +181,7 @@ class MyAnimeListApi( suspend fun findListItem(track: Track): Track? { return withIOContext { - val uri = "$baseApiUrl/manga".toUri().buildUpon() + val uri = "$BASE_API_URL/manga".toUri().buildUpon() .appendPath(track.remote_id.toString()) .appendQueryParameter("fields", "num_chapters,my_list_status{start_date,finish_date}") .build() @@ -218,7 +219,7 @@ class MyAnimeListApi( // Check next page if there's more if (!obj["paging"]!!.jsonObject["next"]?.jsonPrimitive?.contentOrNull.isNullOrBlank()) { - matches + findListItems(query, offset + listPaginationAmount) + matches + findListItems(query, offset + LIST_PAGINATION_AMOUNT) } else { matches } @@ -227,9 +228,9 @@ class MyAnimeListApi( private suspend fun getListPage(offset: Int): JsonObject { return withIOContext { - val urlBuilder = "$baseApiUrl/users/@me/mangalist".toUri().buildUpon() + val urlBuilder = "$BASE_API_URL/users/@me/mangalist".toUri().buildUpon() .appendQueryParameter("fields", "list_status{start_date,finish_date}") - .appendQueryParameter("limit", listPaginationAmount.toString()) + .appendQueryParameter("limit", LIST_PAGINATION_AMOUNT.toString()) if (offset > 0) { urlBuilder.appendQueryParameter("offset", offset.toString()) } @@ -279,30 +280,29 @@ class MyAnimeListApi( } companion object { - // Registered under arkon's MAL account - private const val clientId = "f46004a9c16483b6d87b5bf10de56d97" + private const val CLIENT_ID = "c46c9e24640a64dad5be5ca7a1a53a0f" - private const val baseOAuthUrl = "https://myanimelist.net/v1/oauth2" - private const val baseApiUrl = "https://api.myanimelist.net/v2" + private const val BASE_OAUTH_URL = "https://myanimelist.net/v1/oauth2" + private const val BASE_API_URL = "https://api.myanimelist.net/v2" - private const val listPaginationAmount = 250 + private const val LIST_PAGINATION_AMOUNT = 250 private var codeVerifier: String = "" - fun authUrl(): Uri = "$baseOAuthUrl/authorize".toUri().buildUpon() - .appendQueryParameter("client_id", clientId) + fun authUrl(): Uri = "$BASE_OAUTH_URL/authorize".toUri().buildUpon() + .appendQueryParameter("client_id", CLIENT_ID) .appendQueryParameter("code_challenge", getPkceChallengeCode()) .appendQueryParameter("response_type", "code") .build() - fun mangaUrl(id: Long): Uri = "$baseApiUrl/manga".toUri().buildUpon() + fun mangaUrl(id: Long): Uri = "$BASE_API_URL/manga".toUri().buildUpon() .appendPath(id.toString()) .appendPath("my_list_status") .build() fun refreshTokenRequest(oauth: OAuth): Request { val formBody: RequestBody = FormBody.Builder() - .add("client_id", clientId) + .add("client_id", CLIENT_ID) .add("refresh_token", oauth.refresh_token) .add("grant_type", "refresh_token") .build() @@ -314,7 +314,7 @@ class MyAnimeListApi( .add("Authorization", "Bearer ${oauth.access_token}") .build() - return POST("$baseOAuthUrl/token", body = formBody, headers = headers) + return POST("$BASE_OAUTH_URL/token", body = formBody, headers = headers) } private fun getPkceChallengeCode(): String { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt index 4fa1ec6ac..b109bd763 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt @@ -8,28 +8,26 @@ import okhttp3.Response import uy.kohesive.injekt.injectLazy import java.io.IOException -class MyAnimeListInterceptor(private val myanimelist: MyAnimeList, private var token: String?) : Interceptor { +class MyAnimeListInterceptor(private val myanimelist: MyAnimeList) : Interceptor { private val json: Json by injectLazy() - private var oauth: OAuth? = null + private var oauth: OAuth? = myanimelist.loadOAuth() + private val tokenExpired get() = myanimelist.getIfAuthExpired() override fun intercept(chain: Interceptor.Chain): Response { + if (tokenExpired) { + throw MALTokenExpired() + } val originalRequest = chain.request() - if (token.isNullOrEmpty()) { - throw IOException("Not authenticated with MyAnimeList") - } - if (oauth == null) { - oauth = myanimelist.loadOAuth() - } // Refresh access token if expired if (oauth != null && oauth!!.isExpired()) { setAuth(refreshToken(chain)) } if (oauth == null) { - throw IOException("No authentication token") + throw IOException("MAL: User is not authenticated") } // Add the authorization header to the original request @@ -66,15 +64,16 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList, private var t * and the oauth object. */ fun setAuth(oauth: OAuth?) { - token = oauth?.access_token this.oauth = oauth myanimelist.saveOAuth(oauth) } private fun refreshToken(chain: Interceptor.Chain): OAuth { - val newOauth = runCatching { + return runCatching { val oauthResponse = chain.proceed(MyAnimeListApi.refreshTokenRequest(oauth!!)) - + if (oauthResponse.code == 401) { + myanimelist.setAuthExpired() + } if (oauthResponse.isSuccessful) { with(json) { oauthResponse.parseAs() } } else { @@ -82,11 +81,9 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList, private var t null } } - - if (newOauth.getOrNull() == null) { - throw IOException("Failed to refresh the access token") - } - - return newOauth.getOrNull()!! + .getOrNull() + ?: throw MALTokenExpired() } } + +class MALTokenExpired: IOException("MAL: Login has expired") From 88ed634978948a48bc9b6eab9eaf0d41d45db62a Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 28 Jan 2024 00:15:17 +0600 Subject: [PATCH 017/212] Lint --- .../java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt | 1 - .../java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt | 1 - .../kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt | 3 --- .../tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt | 2 +- .../eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt | 1 - 5 files changed, 1 insertion(+), 7 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt index e1f290d6f..8609d405a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt @@ -12,7 +12,6 @@ import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.contentOrNull import kotlinx.serialization.json.doubleOrNull -import kotlinx.serialization.json.floatOrNull import kotlinx.serialization.json.int import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonObject diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt index 25aceef32..752ef4e12 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt @@ -8,7 +8,6 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.contentOrNull import kotlinx.serialization.json.int -import kotlinx.serialization.json.intOrNull import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.long diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt index 563100304..83a2032d2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt @@ -18,14 +18,11 @@ import kotlinx.serialization.json.boolean import kotlinx.serialization.json.contentOrNull import kotlinx.serialization.json.double import kotlinx.serialization.json.doubleOrNull -import kotlinx.serialization.json.float -import kotlinx.serialization.json.floatOrNull import kotlinx.serialization.json.int import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.long -import logcat.logcat import okhttp3.FormBody import okhttp3.Headers import okhttp3.OkHttpClient diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt index b109bd763..caabcf416 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt @@ -86,4 +86,4 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList) : Interceptor } } -class MALTokenExpired: IOException("MAL: Login has expired") +class MALTokenExpired : IOException("MAL: Login has expired") 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 a4bde2a3a..46740c595 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 @@ -16,7 +16,6 @@ import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.contentOrNull import kotlinx.serialization.json.double -import kotlinx.serialization.json.float import kotlinx.serialization.json.int import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive From 8489b0dd8be7371b53367755a2785de2a881107d Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Sat, 27 Jan 2024 19:25:43 +0100 Subject: [PATCH 018/212] [skip ci] Translations update from Hosted Weblate (#190) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Translated using Weblate (Nepali) Currently translated at 94.1% (747 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/ * Translated using Weblate (Italian) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/it/ * Translated using Weblate (Japanese) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/ * Translated using Weblate (Nepali) Currently translated at 98.3% (780 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/ * Translated using Weblate (Nepali) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/ * Translated using Weblate (Polish) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pl/ * Translated using Weblate (Turkish) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/ * Translated using Weblate (Swedish) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sv/ * Translated using Weblate (German) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/ * Translated using Weblate (Swedish) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sv/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/ * Translated using Weblate (Finnish) Currently translated at 80.9% (642 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fi/ * Translated using Weblate (Indonesian) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/id/ * Translated using Weblate (Nepali) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/ * Translated using Weblate (Arabic) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ar/ * Translated using Weblate (Persian) Currently translated at 82.7% (656 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fa/ * Translated using Weblate (Finnish) Currently translated at 80.9% (642 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fi/ * Translated using Weblate (Arabic) Currently translated at 100.0% (17 of 17 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ar/ * Translated using Weblate (Chuvash) Currently translated at 88.2% (15 of 17 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/cv/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (17 of 17 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/uk/ --------- Co-authored-by: FateXBlood Co-authored-by: Federico Pierantoni Co-authored-by: Zero O Co-authored-by: stevenlele Co-authored-by: Paweł Waresiak Co-authored-by: kret Co-authored-by: bittin1ddc447d824349b2 Co-authored-by: Lyfja <45209212+lyfja@users.noreply.github.com> Co-authored-by: dan-malprod Co-authored-by: Lzmxya Co-authored-by: A Co-authored-by: Christian Elbrianno Co-authored-by: abdelbasset jabrane Co-authored-by: Arash Co-authored-by: C201 Co-authored-by: Сергій --- .../commonMain/resources/MR/ar/plurals.xml | 8 +++ .../commonMain/resources/MR/ar/strings.xml | 43 +++++++++--- .../commonMain/resources/MR/cv/plurals.xml | 22 ++++++- .../commonMain/resources/MR/de/strings.xml | 22 +++---- .../commonMain/resources/MR/fa/strings.xml | 36 ++++++++++ .../commonMain/resources/MR/fi/strings.xml | 39 +++++++++-- .../commonMain/resources/MR/in/strings.xml | 2 +- .../commonMain/resources/MR/it/strings.xml | 27 ++++++-- .../commonMain/resources/MR/ja/strings.xml | 4 +- .../commonMain/resources/MR/ne/strings.xml | 66 ++++++++++++++++--- .../commonMain/resources/MR/pl/strings.xml | 57 ++++++++++++++++ .../commonMain/resources/MR/sv/strings.xml | 20 +++++- .../commonMain/resources/MR/tr/strings.xml | 4 +- .../commonMain/resources/MR/uk/plurals.xml | 6 ++ .../resources/MR/zh-rCN/strings.xml | 8 +-- .../resources/MR/zh-rTW/strings.xml | 6 +- 16 files changed, 315 insertions(+), 55 deletions(-) diff --git a/i18n/src/commonMain/resources/MR/ar/plurals.xml b/i18n/src/commonMain/resources/MR/ar/plurals.xml index 64f4492f9..ff772f02d 100644 --- a/i18n/src/commonMain/resources/MR/ar/plurals.xml +++ b/i18n/src/commonMain/resources/MR/ar/plurals.xml @@ -128,4 +128,12 @@ %d يومًا %d يوم + + %d + مستودع + مستودعان + %d مستودعات + %d مستودعات + + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ar/strings.xml b/i18n/src/commonMain/resources/MR/ar/strings.xml index c9990d91b..a4fb5a345 100644 --- a/i18n/src/commonMain/resources/MR/ar/strings.xml +++ b/i18n/src/commonMain/resources/MR/ar/strings.xml @@ -232,7 +232,7 @@ الإضافات معلومات الإضافة الفصول المحملة - تحديث + حدث تثبيت المُعلقة جارى التنزيل @@ -242,11 +242,9 @@ غير موثوق فيه إلغاء التثبيت إضافة ذات ريبة - هذه اﻹضافة موقَّعة بشهادة ذات ريبة ولم تفعَّل. + يمكن للملحقات الخبيثة قراءة أي بيانات اعتماد مخزنة لتسجيل الدخول أو تنفيذ تعليمات برمجية عشوائية. \n -\nيمكن لأي إضافة خبيثة قراءة بيانات اعتماد تسجيل الدخول المخزَّنة أو تنفيذ تعليمات برمجية عشوائية. -\n -\nأنت تقبل هذه المخاطر إن وثقت بالشهادة. +\nمن خلال الوثوق بهذا الامتداد، فإنك تقبل هذه المخاطر. سرعة مؤثر النقر المزدوج عارض الصفحات لا مؤثرات @@ -372,7 +370,7 @@ البيانات المصادر المفقودة: النسخة الإحتياطية لا تحتوي على أيّة إدخالات المكتبة. - ملفُّ النسخ الاحتياطيِّ غير صالح + ملف النسخ الاحتياطي غير صالح: مزامنة أحادية تُحدِّث قراءة الفصول في المتتبعات الخارجية، ولك تعيين التتبِّع لكلِّ مدخلة على حدى، وذلك من زرِّ التتبع فيهم. تحقق من وجود غلاف جديد وتفاصيل جديدة عند تحديث المكتبة تحديث البيانات الوصفية تلقائياً @@ -456,9 +454,7 @@ حسب تاريخ الرفع حسب رقم الفصل إستخدام DNS عبر HTTPS (DoH) - تم استيراد البيانات من ملف نسخ الاحتياطي. -\n -\nيتوجب تثبيت الإضافات المفقودة و تسجيل الدخول إلى منصات التعقب تالياً لاستعمالهم. + قد تحتاج إلى تثبيت أي إضافات مفقودة وتسجيل الدخول إلى خدمات التتبع بعد ذلك لاستخدامها. يمين يسار التالي @@ -773,4 +769,33 @@ \nوالأحسن أن يكون المجلَّد مخصوصًا لذلك. \n \nالمجلَّد المحدَّد: %2$s + إذن تثبيت التطبيقات + لتثبيت ملحقات المصدر. + إذن الإشعار + احصل على تنبيهات لتحديثات المكتبة والمزيد. + استخدام البطارية في الخلفية + منح + الأذونات مطلوبة لتثبيت الإضافات. انقر هنا لمنحها. + إبطال الإضافات غير المعروفة الموثوق بها + مستودع الإضافات + رابط المستودع + إضافة مستودعات إضافية إلى ميهون. يجب أن يكون هذا الرابط ينتهي بـ \"index.min.json\". + هذا المستودع موجود بالفعل! + حذف المستودع + رابط المستودع غير صالح + هل ترغب في حذف \"1%s\" من المستودع؟ + مستودع مفتوح المصدر + الخطأ الكامل: + تضمين إعدادات حساسة (على سبيل المثال، رموز تسجيل دخول المتتبع) + قريباً + تكرار تحديث مخصص: + هل تقوم بالتحديث من إصدار أقدم ولست متأكداً مما تختاره؟ ارجع إلى دليل التخزين لمزيد من المعلومات. + دليل التخزين + تجنّب الانقطاعات في تحديثات المكتبة الطويلة الأمد والتنزيلات واستعادة النسخ الاحتياطية. + تحديث ذكي + Nord + لم يتم تعيين أي مستودع . + إضافة مستودع + من المتوقع أن يتم إصدار فصول جديدة في حوالي 1%1$s، والتحقق من كل 2%2$s . + متاح: %1$s / الكل: %2$s \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/cv/plurals.xml b/i18n/src/commonMain/resources/MR/cv/plurals.xml index 612cb2ab5..9cc1c822f 100644 --- a/i18n/src/commonMain/resources/MR/cv/plurals.xml +++ b/i18n/src/commonMain/resources/MR/cv/plurals.xml @@ -29,12 +29,12 @@ Ҫӗнӗ сыпӑксем %d хайлав валли тупӑннӑ - 1 сыпӑк + %1$s сыпӑк %1$s сыпӑк - 1 юлчӗ - %1$s юлчӗ + %1$s йулчӗ + %1$s йулчӗ 1 сӑнану @@ -48,4 +48,20 @@ Ӗнер %1$d кун кайалла + + Тепӗр вуламан сыпӑк + Тепӗр %d вуламан сыпӑк + + + 1 кун + %d кун + + + %1$s сыпӑк ҫук + %1$s сыпӑк ҫук + + + Тепӗр сыпӑк + Тепӗр %d сыпӑк + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/de/strings.xml b/i18n/src/commonMain/resources/MR/de/strings.xml index 234c8536a..a402087fb 100644 --- a/i18n/src/commonMain/resources/MR/de/strings.xml +++ b/i18n/src/commonMain/resources/MR/de/strings.xml @@ -128,16 +128,16 @@ Ab fünftletzt gelesenem Kapitel Neue Kapitel herunterladen Tracker - Sicherung erstellen + Datensicherung erstellen Kann benutzt werden, um die aktuelle Bibliothek wiederherzustellen - Sicherung wiederherstellen + Datensicherung wiederherstellen Bibliothek mit Hilfe einer Datensicherung wiederherstellen Automatische Sicherungshäufigkeit - Sicherung erstellt + Datensicherung erstellt Wiederherstellen abgeschlossen Was möchtest du sichern\? - Sicherung wird wiederhergestellt - Sicherung wird erstellt + Datensicherung wird wiederhergestellt + Datensicherung wird erstellt Kapitel-Zwischenspeicher leeren Belegt: %1$s Zwischenspeicher geleert, %1$d Dateien gelöscht @@ -351,11 +351,11 @@ Webseite Nur Heruntergeladenes Kap. %1$s - %2$s - Sicherungswiederherstellung fehlgeschlagen - Sicherung fehlgeschlagen + Datensicherungswiederherstellung fehlgeschlagen + Datensicherung fehlgeschlagen Wiederherstellung abgebrochen Wiederherstellung wird bereits durchgeführt - Sicherung wird bereits durchgeführt + Datensicherung wird bereits durchgeführt Nach Aktualisierungen suchen Zuletzt genutzt Anleitung für lokale Quellen @@ -371,7 +371,7 @@ Nach Uploaddatum Daten Fehlende Quellen: - Sicherung beinhaltet keinerlei Bibliothekseinträge. + Datensicherung beinhaltet keinerlei Bibliothekseinträge. Ungültige Sicherungsdatei: Auf neue Cover und Details überprüfen, wenn die Bibliothek aktualisiert wird Metadaten automatisch aktualisieren @@ -542,7 +542,7 @@ Sprache Warnung Große Aktualisierungen schaden Quellen und könnten zu langsameren Aktualisierungen sowie höherem Akkuverbrauch führen. Tippe, um mehr zu erfahren. - Du solltest Kopien der Sicherungen auch an anderen Orten aufbewahren. Sicherungen beinhalten möglicherweise sensible Daten, einschließlich gespeicherter Passwörter. Sei vorsichtig beim Teilen. + Du solltest Kopien der Datensicherungen auch an anderen Orten aufbewahren. Datensicherungen beinhalten möglicherweise sensible Daten, einschließlich gespeicherter Passwörter. Sei vorsichtig beim Teilen. Nur über WLAN Alle 3 Tage Achtung: Große Downloads könnten dazu führen, dass Quellen langsamer werden und/oder Mihon blockieren. Tippe, um mehr zu erfahren. @@ -793,7 +793,7 @@ Repository-URL Füge zusätzliche Repositorys zu Mihon hinzu. Deren URL sollte mit „index.min.json“ enden. Ungültige Repository-URL - Neue Kapitel vsl. in ca. %1$s, überprüfe ca. alle %2$s + Ca. %1$s bis zur Veröffentlichung neuer Kapitel, wird ca. alle %2$s überprüft. Nord Open-Source-Repository Bald diff --git a/i18n/src/commonMain/resources/MR/fa/strings.xml b/i18n/src/commonMain/resources/MR/fa/strings.xml index 45e7cb1a0..3466ae242 100644 --- a/i18n/src/commonMain/resources/MR/fa/strings.xml +++ b/i18n/src/commonMain/resources/MR/fa/strings.xml @@ -622,4 +622,40 @@ آخرین به روز رسانی کتابخانه: %s خارج از دوره انتشار موزد انتظار برای بزرگ نمایی دوبار ضربه بزنید + تناوب به روز رسانی شخسی سازی شده + خوش آمدید! + بیاید برخی چیز ها را تنظیم کنیم. شما همیشه میتوانید این تنظیمات را در بخش تنظیمات تغییر دهید. + بعدی + برای نصب افزانه منبع. + دسترسی اعلان ها + برای به روز رسانی های کتابخانه و بیشتر مطلع شوید. + استفاده از باطری + از وقفه در به روز رسانی های کتاب خانه، بارگیری و پشتیبان گیری های طولانی اجتناب کنید. + اعطا کردن + به %s نا آشنا هستید؟ ما پیشنهاد میکنیم تا یک سر به راهنمای شروع بزنید. + نصب مجدد %s؟ + زمان بندی های نسبی + به روز رسانی هوشمند + کشیدن قسمت + لغو اعتماد افزونه های ناشناخته + مخازن افزونه ها + اضافه کردن مخزن + آدرس مخزن + این مخزن در حال حاضر وجود دارد! + حذف مخزن + آدرس مخزن بی اعتبار است + آشا شما میخواهید تا \"%s\" مخزن را حذف کنید؟ + جهت چرخش صفحات گسترده را برعکس کن + مکان ذخیره سازی تنظیم نشده است + عکس های بلند را تقسیم کن + ورود به ناضر + اجرای داخل کتاب خانه را مخفی کن + محل زخیره سازی + شروع کنید + پرش + مجوز برای نصب افزونه ها لازم است. به اینجا ضربه بزنید تا اعطا کنید. + به میهون مخازن اضافی اضافه کنید. این باید یک آدرس باشد که با \"index.min.json\" تمام شود. + شما هیچ مخزنی برای تنظیم ندارید. + مخزن منبع باز + مقدار رد باقی مانده در نمایش گر های E-ink کاهش میابد \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/fi/strings.xml b/i18n/src/commonMain/resources/MR/fi/strings.xml index b44e5d964..1eec93175 100644 --- a/i18n/src/commonMain/resources/MR/fi/strings.xml +++ b/i18n/src/commonMain/resources/MR/fi/strings.xml @@ -72,7 +72,7 @@ Laajennukset Laajennuksen tiedot Suodatus - Kirjanmerkki + Kirjanmerkityt Lukemattomat Poista suodattimet Aakkosjärjestyksessä @@ -103,7 +103,7 @@ Asenna Jaa Tallenna - Resetoi + Nollaa Kumoa Avaa loki Palauta @@ -447,7 +447,7 @@ Tallentaa virhelokit tiedostoon jaettavaksi kehittäjien kanssa Laskeva Nouseva - Luvunumeron mukaan + Lukunumeron mukaan Lisäyspäivämäärän mukaan Seuratut Näytä kohteiden määrä @@ -615,4 +615,35 @@ Varmuuskopioita kannattaa säilyttää myös muissa paikoissa. Toivelista Viimeksi luettua lukua ei voitu avata - + Valitut + Skanlaattori + Data ja tallennustila + Tilastotiedot + Ladattu + Seuraava odotettu päivitys + Seurannan pisteytys + Päivitä kategoria + Järjestä kategoriat + Jatka lukemista painike + Käytä + OK + Palauta oletus + %s kohtasi odottamattoman virheen. Ehdotamme että jaat kaatumisen lokitiedot tukikanavallemme Discordissa. + Poista ladatut + Varmuuskopiotiedoston luonti epäonnistui + Kirjastossasi on jo samanniminen merkintä. +\n +\nHaluatko silti jatkaa? + Hupsista! + Kopioitu leikepöydälle + Uudelleenkäynnistä applikaatio + Mukautettu kansikuva + Ei valitut + Lisää asetuksia + Paikallinen + Aloitettu + Avaa satunnainen merkintä + Haluatko järjestää kategoriat aakkosjärjestykseen? + Kopioi leikepöydälle + Siirrä sarja pohjimmaiseksi + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/in/strings.xml b/i18n/src/commonMain/resources/MR/in/strings.xml index df35b8264..9859b62c0 100644 --- a/i18n/src/commonMain/resources/MR/in/strings.xml +++ b/i18n/src/commonMain/resources/MR/in/strings.xml @@ -728,7 +728,7 @@ Pindahkan seri ke bawah Penanda waktu \"%1$s\" seharusnya \"%2$s\" - Mengurutkan kategori + Urutkan kategori Memperbarui pustaka... (%s) Apakah Anda ingin mengurutkan kategori menurut abjad\? Tidak ada file yang dipilih diff --git a/i18n/src/commonMain/resources/MR/it/strings.xml b/i18n/src/commonMain/resources/MR/it/strings.xml index 7a09812cb..b68658c88 100644 --- a/i18n/src/commonMain/resources/MR/it/strings.xml +++ b/i18n/src/commonMain/resources/MR/it/strings.xml @@ -199,7 +199,7 @@ Ogni settimana Categoria predefinita Tracking - Tracking + Tracciamento Chiedi sempre Ritaglia bordi Inverti i tasti del volume @@ -236,11 +236,9 @@ Non attendibile Disinstalla Estensione non attendibile - Questa estensione è stata firmata con un certificato non attendibile e non è stata attivata. + Le estensioni dannose possono leggere le credenziali di accesso memorizzate o eseguire codice arbitrario. \n -\nUn\'estensione maliziosa potrebbe leggere credenziali di accesso salvate o eseguire codice dannoso. -\n -\nFidandoti di questo certificato accetti questi rischi. +\nFidandoti di questa estensione, accetti questi rischi. Velocità animazioni doppio tocco Per pagina Senza animazione @@ -783,4 +781,23 @@ Includi impostazioni sensibili (es. token di login dei tracker) Consenti Escludi scanlator + Guida all\'archiviazione + Repository delle estensioni + Aggiungi repository + URL repository + Questa repository esiste già! + Elimina repository + URL repository non valida + Repository open source + Presto + Frequenza di aggiornamento personalizzata: + State aggiornando da una versione precedente e non siete sicuri di cosa selezionare? Per ulteriori informazioni, consultare la guida all\'archiviazione + Nord + Aggiornamento intelligente + Revoca le estensioni sconosciute affidabili + Non hai repository impostate. + Aggiungi altri repository a Mihon. Questo dovrebbe essere un URL che termina con \"index.min.json\". + Vuoi eleminare la repository \"%s\"? + Errore completo: + Si prevede che i nuovi capitoli saranno rilasciati tra circa %1$s, con controlli ogni %2$s. \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ja/strings.xml b/i18n/src/commonMain/resources/MR/ja/strings.xml index df4e258b2..9c7429d80 100644 --- a/i18n/src/commonMain/resources/MR/ja/strings.xml +++ b/i18n/src/commonMain/resources/MR/ja/strings.xml @@ -500,7 +500,7 @@ 横向き 縦向き 真っ黒モード - Yotsuba + 四つ葉 陰陽 Tako ストロベリーダイキリ @@ -620,7 +620,7 @@ 津波 事前ダウンロード 読書中に自動でダウンロード - 現在のと次の章は既にダウンロード済みの場合のみ有効です + 現在のと次の章は既にダウンロード済みの場合のみ有効です。 本当に実行しますか? 多言語 前回のライブラリ更新: %s diff --git a/i18n/src/commonMain/resources/MR/ne/strings.xml b/i18n/src/commonMain/resources/MR/ne/strings.xml index 048894780..955f018de 100644 --- a/i18n/src/commonMain/resources/MR/ne/strings.xml +++ b/i18n/src/commonMain/resources/MR/ne/strings.xml @@ -210,7 +210,7 @@ दुबै कार्यहरू लामो ट्यापमा कार्यहरू देखाउनुहोस् - पृष्ठभूमि रङ + ब्याकग्राउण्ड रङ खैरो कालो स्वत @@ -595,7 +595,7 @@ थुप्रै अपडेटहरू आइकनमा नपढिएको गणना देखाउनुहोस् डुप्लिकेट अध्यायहरू छोड्नुहोस् - जारी राख्नुहोस् बटन + जारी राख्ने बटन तथ्याङ्क सुरु गरिएको लोकल @@ -705,7 +705,7 @@ जूम गर्न डबल ट्याप गर्नुहोस् %d प्रति पङ्क्ति अन्तराल सेट गर्नुहोस् - कस्टम गरिएको ल्याउने अन्तराल + कस्टम गरिएको अपडेट फ्रिक्वेन्सी प्रत्येक अपडेट गर्न सेट गर्नुहोस् छोडियो किनभने आज कुनै रिलीज अपेक्षित थिएन अन्तरालहरू @@ -715,8 +715,8 @@ %s ट्र्याकिङ हटाउने हो\? %s बाट पनि हटाउनुहोस् यसले लोकल रूपमा ट्र्याकिङ हटाउनेछ। - ठीक छ - डाउनलोड गरिएको मेट्नुहोस् + ठिक छ + डाउनलोड गरिएको हटाउनुहोस् परिणामहरू भएको पुस्तकालय सिङ्क सम्पन्न भयो पुस्तकालय सिङ्क गर्दै @@ -745,11 +745,61 @@ फाइल पिकर एपमा फाइल फर्काउन असफल भयो चयन गरिएको चयन नगरिएको - ट्र्याकर मुल्यन्कन - स्क्यानलेतर + ट्र्याकर मूल्याङ्कन + स्क्यानलेटर अरु मेनु - स्वागत! + स्वागत छ! अर्को सुरु छोड + एउटा फोल्डर चयन गर्नुपर्छ + भण्डारण गाइड + सूचना अनुमति + प्रदान गर्नुहोस् + %s मा नयाँ हुनुहुन्छ? हामी स्टार्टिङ गाइड जाँच गर्न सिफारिस गर्छौं। + नोर्ड + स्मार्ट अपडेट + यो रिपो पहिले नै अवस्थित छ! + रिपो हटाउनुहोस् + रिपो URL अवैध छ + माथि नेभिगेट गर्नुहोस् + पूर्वनिर्धारितमा फर्कनुहोस् + अनबोर्डिङ गाइड + पहिले केहि चीजहरू सेट गरौं। तपाईं पछि पनि सेटिङहरूमा गएर यसलाई परिवर्तन गर्न सक्नुहुन्छ। + एउटा फोल्डर चयन गर्नुहोस् जहाँ %1$s ले अध्याय डाउनलोड, ब्याकअप, र थप भण्डारण गर्नेछ। +\n +\nएक समर्पित फोल्डर सिफारिस गरिएको छ। +\n +\nचयन गरिएको फोल्डर: %2$s + फोल्डर चयन गर्नुहोस् + एप स्थापना गर्ने अनुमति + स्रोत एक्सटेन्शन स्थापना गर्न को लागि। + पुस्तकालय अपडेट र थपका लागि सूचना प्राप्त गर्नुहोस्। + लामो समयदेखि चलिरहेको पुस्तकालय अपडेटहरू, डाउनलोडहरू, र ब्याकअप रिस्टोरमा अवरोधहरू बेवास्ता गर्नुहोस्। + पुरानो संस्करणबाट अपडेट गर्दै र के चयन गर्ने निश्चित छैन? थप जानकारीको लागि भण्डारण गाइड हेर्नुहोस्। + ब्याकग्राउण्ड ब्याट्री प्रयोग + %s पुन: स्थापना गर्दै हुनुहुन्छ? + एक्सटेन्शनहरू स्थापना गर्न अनुमति आवश्यक छ। प्रदान गर्न यहाँ ट्याप गर्नुहोस्। + विश्वसनीय अज्ञात एक्सटेन्शनहरू रद्द गर्नुहोस् + तपाईंले कुनै पनि रिपो सेट गर्नु भएको छैन। + रिपो URL + एक्सटेन्शन रिपो + रिपो थप्नुहोस् + Mihon मा अतिरिक्त रिपो थप्नुहोस्। URL को अन्त्यमा \"index.min.json\" हुनुपर्छ। + अप्लाई + के तपाइँ रिपो \"%s\" हटाउन चाहनुहुन्छ? + खुला स्रोत रिपो + भण्डारण स्थान + उपलब्ध: %1$s / कुल: %2$s + नयाँ अध्यायहरू लगभग %1$s मा रिलीज हुने भविष्यवाणी गरिएको छ, हरेक %2$s को वरिपरि जाँच गर्दै। + छिटै + कस्टम अपडेट फ्रिक्वेन्सी: + स्क्यालेटरहरू समावेश नगर्नुहोस् + कुनै स्क्यालेटर फेला परेन + भण्डारण प्रयोग + कुनै भण्डारण स्थान सेट गरिएको छैन + स्वचालित ब्याकअप, अध्याय डाउनलोड, र स्थानीय स्रोतको लागि प्रयोग गरिन्छ। + सिर्जना गर्नुहोस् + पूर्ण त्रुटि: + संवेदनशील सेटिङहरू समावेश गर्नुहोस् (जस्तै, ट्र्याकर लगइन टोकनहरू) \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/pl/strings.xml b/i18n/src/commonMain/resources/MR/pl/strings.xml index 6ba1d7d94..f079535ed 100644 --- a/i18n/src/commonMain/resources/MR/pl/strings.xml +++ b/i18n/src/commonMain/resources/MR/pl/strings.xml @@ -745,4 +745,61 @@ Ustawienia aplikacji Usuń repozytorium Dodaj repozytorium + Zaznaczone + Przewiń w górę + Zaczynajmy + Przewodnik po pamięci + Zainstaluj uprawnienia aplikacji + Do instalacji rozszerzeń. + Zezwól + Pierwszy raz w %s? Zalecamy zapoznać się z poradnikiem. + Ponowna instalacja %s? + Repozytoria rozszerzeń + Link repozytorium + Zużycie pamięci + Niestandardowa częstotliwość aktualizacji: + Nie znaleziono skanlatorów + Nie wybrano pliku + Nie zaznaczone + Serwisy śledzące + Dane i pamięć + Szybka konfiguracja + Unikaj przerywania długich operacji takich jak pobieranie rozdziałów, aktualizacje bibliotek lub przywracanie kopii zapasowej. + Otrzymuj powiadomienia ze swojej biblioteki i więcej. + Nord + Użyj dat względnych + \"%1$s\" zamiast \"%2$s\" + Inteligentna aktualizacja + Na początek ustawmy kilka rzeczy. Zawsze możesz je później zmienić w ustawieniach. + Aktualizujesz ze starszej wersji i nie jesteś pewien co wybrać? Zobacz na przewodnik po pamięci. + Przewiduj następną aktualizację + Uprawnienia są wymagane aby zainstalować rozszerzenia. Kliknij tutaj aby je przyznać. + Wybierz folder gdzie %1$s będzie zapisywał pobrane rozdziały, kopie zapasowe i inne. +\n +\nZalecany jest dedykowany folder. +\n +\nWybrany folder:%2$s + Unieważnij zaufanie nieznanym rozszerzeniom + Nie masz ustawionych repozytoriów. + Dodaj repozytoria do Mihon. Powinien to być link z końcówką \"index.min.json\". + To repozytorium jest już dodane! + Na pewno chcesz usunąć repozytorium \"%s\"? + Repozytorium open source + Redukuje ghosting na wyświetlaczach e-ink + Brak ustawionej ścieżki pamięci + Miejsce przechowywania danych + Używane do automatycznych kopii zapasowych, pobierania rozdziałów i lokalnych źródeł. + Pełny błąd: + Login serwisu śledzącego + Utwórz + Włącz przerwy między stronami + Dołącz wrażliwe ustawienia (np. tokeny serwisu śledzącego) + Ostatnia automatyczna kopia zapasowa: %s + Ma wyniki + Oszacuj co + Wolne: %1$s / Ogółem: %2$s + Wkrótce + Licencjonowany - Brak rozdziałów + Pominięto, ponieważ nie spodziewano się dzisiaj żadnej publikacji + Wyklucz skanlatorów \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/sv/strings.xml b/i18n/src/commonMain/resources/MR/sv/strings.xml index b26fdcd89..c5baf79a6 100644 --- a/i18n/src/commonMain/resources/MR/sv/strings.xml +++ b/i18n/src/commonMain/resources/MR/sv/strings.xml @@ -445,7 +445,7 @@ Start datum Tryckzoner Kant - Kindle-ish + Kindle-lik L-formad Spåras Höger och Vänster @@ -616,7 +616,7 @@ Se dina nyligen uppdaterade biblioteket inlägg Widget är inte tillgänglig när applåset är aktiverat En uppdatering pågår redan - Multi + Flerspråkig Strängen för användaragent kan inte vara tom Är du säker\? Biblioteket uppdaterades senast: %s @@ -740,7 +740,7 @@ Återgå till standard Senaste automatiska säkerhetskopieringen: %s Inga scanlatorer hittades - Scanlator + Översättare Blinka vid sidbyte Lagringsanvändning Spårares betyg @@ -784,4 +784,18 @@ Osäker på vad du ska välja då du uppdaterar från en äldre version? Se lagerhanteringsguiden för mer information. Smart uppdatering Lagerhanteringsguide + Nord + Vill du ta bort förråd \"%s\"? + Återkalla tillförlitliga okända tillägg + Tilläggsförråd + Du har inga förråd inställda. + Lägg till förråd + Förråd URL + Lägg till ytterliga förråd till Mihon. Detta ska vara en URL som slutar med \"index.min.json\". + Detta förråd existerar redan! + Ta bort förråd + Ogiltig förråds URL + Öppenkällkods förråd + Snart + Anpassad uppdateringsfrekvens: \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/tr/strings.xml b/i18n/src/commonMain/resources/MR/tr/strings.xml index c76040e45..d08383a88 100644 --- a/i18n/src/commonMain/resources/MR/tr/strings.xml +++ b/i18n/src/commonMain/resources/MR/tr/strings.xml @@ -1,7 +1,7 @@ Ad - Ulamlar + Kategoriler Kitaplık girdileri Bölümler İzleme @@ -798,4 +798,4 @@ Güvenilen bilinmeyen uzantıları iptal et Yakında Açık kaynaklı depo - + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/uk/plurals.xml b/i18n/src/commonMain/resources/MR/uk/plurals.xml index ef8ddc586..ad4538677 100644 --- a/i18n/src/commonMain/resources/MR/uk/plurals.xml +++ b/i18n/src/commonMain/resources/MR/uk/plurals.xml @@ -95,4 +95,10 @@ %d днів %d днів + + %d репозиторій + %d репозиторії + %d репозиторіїв + %d репозиторіїв + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml b/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml index 05fd2086b..c450be769 100644 --- a/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml +++ b/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml @@ -288,7 +288,7 @@ 刷新 书架 废弃 - 此插件已被废弃,可能无法正常运行或导致应用发生故障,建议卸载 + 此插件已被废弃,可能无法正常运行或导致应用发生故障,建议卸载。 日期格式 全局更新 要退出 %1$s 吗? @@ -624,12 +624,12 @@ 你确定吗? 多语言 书架更新时间:%s - 即将从书架中删除“%s”。 + 即将从书架中删除“%s” 热门 未授予存储权限 已跳过,因为作品无需更新 搜索… - 哦豁 + 哦豁! %s 发生了意外错误。建议你反馈时提供崩溃日志。 重启应用 主题 • 日期格式 @@ -783,7 +783,7 @@ 尚未设置仓库 仓库网址 在 Mihon 中添加仓库,输入的网址结尾应为“index.min.json”。 - 如果你是从旧版更新而来,请选择之前使用的“Mihon”目录。 + 如果你是从旧版更新而来,可以查看存储指南了解详情。 存储指南 存储位置 选择文件夹 diff --git a/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml b/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml index f098088d4..84b680379 100644 --- a/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml +++ b/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml @@ -180,7 +180,7 @@ 符合頁寬 符合頁高 原始大小 - 智慧型填充 + 智慧填充 縮放原點 自動 左邊 @@ -304,7 +304,7 @@ 立即 閒置時鎖定 上鎖應用程式 - 在切換應用程式時隱藏預覽,並禁止擷取螢幕畫面。 + 在切換應用程式時隱藏預覽,並禁止擷取螢幕畫面 防窺畫面 隱私 隱藏通知內容 @@ -790,7 +790,7 @@ 確定要刪除「%s」儲存庫嗎? 刪除儲存庫 新增儲存庫 - 尚未新增任何儲存庫 + 尚未新增任何儲存庫。 擴充套件儲存庫 儲存空間指南 從舊版升級而不確定該如何選擇嗎?請參閱儲存空間指南以取得更多資訊。 From 222e111806aeeec526d1a65e37c1d0002cd6c863 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 28 Jan 2024 00:27:16 +0600 Subject: [PATCH 019/212] Release v0.16.2 --- app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 291377779..a76b9a70b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,8 +22,8 @@ android { defaultConfig { applicationId = "app.mihon" - versionCode = 2 - versionName = "0.16.1" + versionCode = 3 + versionName = "0.16.2" buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"") From 9cc0c4e0359cf06ef6257fc289f05a6f4fb9cf6e Mon Sep 17 00:00:00 2001 From: Soitora Date: Sun, 28 Jan 2024 11:13:18 +0100 Subject: [PATCH 020/212] Hide display cutout setting if fullscreen is off (#241) - make it behave like the one on more -> setting -> reader Co-authored-by: Riztard <16263232+Riztard@users.noreply.github.com> --- .../kanade/presentation/reader/settings/GeneralSettingsPage.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt index 6b631d4ee..2f2832feb 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt @@ -42,7 +42,7 @@ internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) { pref = screenModel.preferences.fullscreen(), ) - if (screenModel.hasDisplayCutout) { + if (screenModel.hasDisplayCutout && screenModel.preferences.fullscreen().get()) { CheckboxItem( label = stringResource(MR.strings.pref_cutout_short), pref = screenModel.preferences.cutoutShort(), From 915a9671514b25fdd62912a5154aca221470c8a7 Mon Sep 17 00:00:00 2001 From: Theodoro Loureiro mota Date: Sun, 28 Jan 2024 11:31:59 -0300 Subject: [PATCH 021/212] Fixing bottom sheet UI bug in non-tablet devices (#182) * replace the windowInsetsPadding for navigationBarsPadding + statusBarsPadding * Setting decorFitsSystemWindows = true to fix ui bug on Android 11 --- .../kanade/presentation/components/AdaptiveSheet.kt | 2 +- .../presentation/core/components/AdaptiveSheet.kt | 13 ++++--------- 2 files changed, 5 insertions(+), 10 deletions(-) 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 b3d07c1c3..2712d9a27 100644 --- a/app/src/main/java/eu/kanade/presentation/components/AdaptiveSheet.kt +++ b/app/src/main/java/eu/kanade/presentation/components/AdaptiveSheet.kt @@ -97,5 +97,5 @@ fun AdaptiveSheet( private val dialogProperties = DialogProperties( usePlatformDefaultWidth = false, - decorFitsSystemWindows = false, + decorFitsSystemWindows = true, ) 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 d36e2593f..515faec14 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 @@ -11,17 +11,14 @@ import androidx.compose.foundation.gestures.anchoredDraggable import androidx.compose.foundation.gestures.animateTo import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredWidthIn -import androidx.compose.foundation.layout.systemBars +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.foundation.layout.widthIn -import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.runtime.Composable @@ -177,10 +174,8 @@ fun AdaptiveSheet( orientation = Orientation.Vertical, enabled = enableSwipeDismiss, ) - .windowInsetsPadding( - WindowInsets.systemBars - .only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), - ), + .navigationBarsPadding() + .statusBarsPadding(), shape = MaterialTheme.shapes.extraLarge, tonalElevation = tonalElevation, content = { From cf33f250ccfe4e1dde92b8942a5f282827d50fb7 Mon Sep 17 00:00:00 2001 From: Jishnu M <83004520+m-jishnu@users.noreply.github.com> Date: Sun, 28 Jan 2024 20:02:23 +0530 Subject: [PATCH 022/212] Change extension update error file name (#253) --- .../java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt index 578743da8..8b9d76a03 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt @@ -384,7 +384,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet private fun writeErrorFile(errors: List>): File { try { if (errors.isNotEmpty()) { - val file = context.createFileInCacheDir("tachiyomi_update_errors.txt") + val file = context.createFileInCacheDir("mihon_update_errors.txt") file.bufferedWriter().use { out -> out.write(context.stringResource(MR.strings.library_errors_help, ERROR_LOG_HELP_URL) + "\n\n") // Error file format: From 4bcba0503ae36aa42384335333b814163c0ed0c8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 28 Jan 2024 20:39:37 +0600 Subject: [PATCH 023/212] Update dependency com.android.tools.build:gradle to v8.2.2 (#256) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 1465a7c75..a1ef4143a 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -1,5 +1,5 @@ [versions] -agp_version = "8.2.1" +agp_version = "8.2.2" lifecycle_version = "2.6.2" paging_version = "3.2.1" From 9c4d2b087f2b511b2cf65b1f06e12dbf09d29146 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 29 Jan 2024 02:03:00 +0600 Subject: [PATCH 024/212] [skip ci] Update issue templates --- .github/ISSUE_TEMPLATE/report_issue.yml | 4 ++-- .github/ISSUE_TEMPLATE/request_feature.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/report_issue.yml b/.github/ISSUE_TEMPLATE/report_issue.yml index 94f0c6f6b..ccf4547e3 100644 --- a/.github/ISSUE_TEMPLATE/report_issue.yml +++ b/.github/ISSUE_TEMPLATE/report_issue.yml @@ -53,7 +53,7 @@ body: label: Mihon version description: You can find your Mihon version in **More → About**. placeholder: | - Example: "0.16.1" + Example: "0.16.2" validations: required: true @@ -96,7 +96,7 @@ body: required: true - label: I have gone through the [FAQ](https://mihon.app/docs/faq/general) and [troubleshooting guide](https://mihon.app/docs/guides/troubleshooting/). required: true - - label: I have updated the app to version **[0.16.1](https://github.com/mihonapp/mihon/releases/latest)**. + - label: I have updated the app to version **[0.16.2](https://github.com/mihonapp/mihon/releases/latest)**. required: true - label: I have updated all installed extensions. required: true diff --git a/.github/ISSUE_TEMPLATE/request_feature.yml b/.github/ISSUE_TEMPLATE/request_feature.yml index f98db1543..35220c01c 100644 --- a/.github/ISSUE_TEMPLATE/request_feature.yml +++ b/.github/ISSUE_TEMPLATE/request_feature.yml @@ -31,7 +31,7 @@ body: required: true - label: I have written a short but informative title. required: true - - label: I have updated the app to version **[0.16.1](https://github.com/mihonapp/mihon/releases/latest)**. + - label: I have updated the app to version **[0.16.2](https://github.com/mihonapp/mihon/releases/latest)**. required: true - label: I will fill out all of the requested information in this form. required: true From cc09230e266c3cd7ed748affe92922d775523695 Mon Sep 17 00:00:00 2001 From: Theodoro Loureiro mota Date: Sun, 28 Jan 2024 18:29:55 -0300 Subject: [PATCH 025/212] Adding Detekt in the project (#216) * Removing ktlint * Removing compose lint * Adding initial Detekt config * Setting up detekt config * Adding detekt baseline * Fixing workflows * Moving to a module based solution * Adding new line * Adding new line * Updating baseline * Addressing PR suggestions * Regenerating baseline.xml * Cleanup --------- Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- app/build.gradle.kts | 1 - build.gradle.kts | 3 +- buildSrc/build.gradle.kts | 3 +- buildSrc/src/main/kotlin/detekt.gradle.kts | 47 + .../src/main/kotlin/tachiyomi.lint.gradle.kts | 22 - config/detekt/baseline.xml | 1327 +++++++++++++++++ config/detekt/detekt.yml | 19 + gradle/compose.versions.toml | 2 - gradle/libs.versions.toml | 6 +- presentation-core/build.gradle.kts | 1 - presentation-widget/build.gradle.kts | 1 - 13 files changed, 1403 insertions(+), 33 deletions(-) create mode 100644 buildSrc/src/main/kotlin/detekt.gradle.kts delete mode 100644 buildSrc/src/main/kotlin/tachiyomi.lint.gradle.kts create mode 100644 config/detekt/baseline.xml create mode 100644 config/detekt/detekt.yml diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index bebc653c0..6ba66a5e7 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -37,4 +37,4 @@ jobs: - name: Build app and run unit tests uses: gradle/gradle-command-action@v2 with: - arguments: ktlintCheck assembleStandardRelease testReleaseUnitTest \ No newline at end of file + arguments: detekt assembleStandardRelease testReleaseUnitTest diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 73106a0ef..1d14c640b 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -35,7 +35,7 @@ jobs: - name: Build app and run unit tests uses: gradle/gradle-command-action@v2 with: - arguments: ktlintCheck assembleStandardRelease testReleaseUnitTest + arguments: detekt assembleStandardRelease testReleaseUnitTest # Sign APK and create release for tags diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a76b9a70b..871b23bdc 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -163,7 +163,6 @@ dependencies { implementation(compose.ui.util) implementation(compose.accompanist.webview) implementation(compose.accompanist.systemuicontroller) - lintChecks(compose.lintchecks) implementation(androidx.paging.runtime) implementation(androidx.paging.compose) diff --git a/build.gradle.kts b/build.gradle.kts index ad3a21a2e..dfaf0816e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -32,8 +32,7 @@ subprojects { } plugins.withType { - plugins.apply("tachiyomi.lint") - + plugins.apply("detekt") configure { compileSdkVersion(AndroidConfig.compileSdk) defaultConfig { diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 2c76035b7..a948efcc7 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -3,9 +3,10 @@ plugins { } dependencies { + implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) implementation(androidxLibs.gradle) implementation(kotlinLibs.gradle) - implementation(libs.ktlint) + implementation(libs.detekt.gradlePlugin) implementation(gradleApi()) } diff --git a/buildSrc/src/main/kotlin/detekt.gradle.kts b/buildSrc/src/main/kotlin/detekt.gradle.kts new file mode 100644 index 000000000..986138ee6 --- /dev/null +++ b/buildSrc/src/main/kotlin/detekt.gradle.kts @@ -0,0 +1,47 @@ +import io.gitlab.arturbosch.detekt.Detekt +import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask +import org.gradle.accessors.dm.LibrariesForLibs + +plugins { + id("io.gitlab.arturbosch.detekt") +} + +val libs = the() +dependencies { + detektPlugins(libs.detekt.rules.formatting) + detektPlugins(libs.detekt.rules.compose) +} + +private val configFile = files("$rootDir/config/detekt/detekt.yml") +private val baselineFile = file("$rootDir/config/detekt/baseline.xml") +private val kotlinFiles = "**/*.kt" +private val resourceFiles = "**/resources/**" +private val buildFiles = "**/build/**" +private val generatedFiles = "**/generated/**" +private val scriptsFiles = "**/*.kts" + +detekt { + buildUponDefaultConfig = true + parallel = true + autoCorrect = false + ignoreFailures = false + config.setFrom(configFile) + baseline = file(baselineFile) +} + +tasks.withType().configureEach { + include(kotlinFiles) + exclude(resourceFiles, buildFiles, generatedFiles, scriptsFiles) + reports { + html.required.set(true) + xml.required.set(false) + txt.required.set(false) + } +} + +tasks.withType().configureEach { + jvmTarget = JavaVersion.VERSION_17.toString() +} +tasks.withType().configureEach { + jvmTarget = JavaVersion.VERSION_17.toString() +} diff --git a/buildSrc/src/main/kotlin/tachiyomi.lint.gradle.kts b/buildSrc/src/main/kotlin/tachiyomi.lint.gradle.kts deleted file mode 100644 index 5216d4962..000000000 --- a/buildSrc/src/main/kotlin/tachiyomi.lint.gradle.kts +++ /dev/null @@ -1,22 +0,0 @@ -import org.jlleitschuh.gradle.ktlint.KtlintExtension -import org.jlleitschuh.gradle.ktlint.KtlintPlugin - -apply() - -extensions.configure("ktlint") { - version.set("0.50.0") - android.set(true) - enableExperimentalRules.set(true) - - filter { - exclude("**/generated/**") - - // For some reason this is needed for Kotlin MPP - exclude { tree -> - val path = tree.file.path - listOf("/generated/").any { - path.contains(it) - } - } - } -} diff --git a/config/detekt/baseline.xml b/config/detekt/baseline.xml new file mode 100644 index 000000000..1845cae53 --- /dev/null +++ b/config/detekt/baseline.xml @@ -0,0 +1,1327 @@ + + + + + ComplexCondition:MangaScreenModel.kt$MangaScreenModel$(selectedItem.selected && selected) || (!selectedItem.selected && !selected) + ComplexCondition:ReaderNavigationOverlayView.kt$ReaderNavigationOverlayView$isVisible || (!showOnStart && firstLaunch) || navigation is DisabledNavigation + ComplexCondition:ReaderPageImageView.kt$ReaderPageImageView$config != null && config!!.landscapeZoom && config!!.minimumScaleType == SCALE_TYPE_CENTER_INSIDE && sWidth > sHeight && scale == minScale + ComposableParamOrder:ChapterSettingsDialog.kt$ChapterSettingsDialog + ComposableParamOrder:LazyLibraryGrid.kt$LazyLibraryGrid + ComposableParamOrder:SwitchPreferenceWidget.kt$SwitchPreferenceWidget + ComposableParamOrder:TrackingPreferenceWidget.kt$TrackingPreferenceWidget + ComposableParamOrder:TriStateListDialog.kt$TriStateListDialog + CompositionLocalAllowlist:Navigator.kt$LocalBackPress + CompositionLocalAllowlist:PreferenceItem.kt$LocalPreferenceHighlighted + CompositionLocalAllowlist:PreferenceItem.kt$LocalPreferenceMinHeight + ConstructorParameterNaming:AnilistModels.kt$ALManga$val average_score: Int + ConstructorParameterNaming:AnilistModels.kt$ALManga$val image_url_lge: String + ConstructorParameterNaming:AnilistModels.kt$ALManga$val publishing_status: String + ConstructorParameterNaming:AnilistModels.kt$ALManga$val remote_id: Long + ConstructorParameterNaming:AnilistModels.kt$ALManga$val start_date_fuzzy: Long + ConstructorParameterNaming:AnilistModels.kt$ALManga$val title_user_pref: String + ConstructorParameterNaming:AnilistModels.kt$ALManga$val total_chapters: Long + ConstructorParameterNaming:AnilistModels.kt$ALUserManga$val chapters_read: Int + ConstructorParameterNaming:AnilistModels.kt$ALUserManga$val completed_date_fuzzy: Long + ConstructorParameterNaming:AnilistModels.kt$ALUserManga$val library_id: Long + ConstructorParameterNaming:AnilistModels.kt$ALUserManga$val list_status: String + ConstructorParameterNaming:AnilistModels.kt$ALUserManga$val score_raw: Int + ConstructorParameterNaming:AnilistModels.kt$ALUserManga$val start_date_fuzzy: Long + ConstructorParameterNaming:AnilistModels.kt$OAuth$val access_token: String + ConstructorParameterNaming:AnilistModels.kt$OAuth$val expires_in: Long + ConstructorParameterNaming:AnilistModels.kt$OAuth$val token_type: String + ConstructorParameterNaming:BackupManga.kt$BackupManga$@ProtoNumber(103) var viewer_flags: Int? = null + ConstructorParameterNaming:BangumiModels.kt$Collection$val ep_status: Int? = 0 + ConstructorParameterNaming:BangumiModels.kt$Collection$val vol_status: Int? = 0 + ConstructorParameterNaming:BangumiModels.kt$OAuth$val access_token: String + ConstructorParameterNaming:BangumiModels.kt$OAuth$val created_at: Long = System.currentTimeMillis() / 1000 + ConstructorParameterNaming:BangumiModels.kt$OAuth$val expires_in: Long + ConstructorParameterNaming:BangumiModels.kt$OAuth$val refresh_token: String? + ConstructorParameterNaming:BangumiModels.kt$OAuth$val token_type: String + ConstructorParameterNaming:BangumiModels.kt$OAuth$val user_id: Long? + ConstructorParameterNaming:ExtensionDetailsScreenModel.kt$ExtensionDetailsScreenModel.State$private val _sources: ImmutableList<ExtensionSourceItem>? = null + ConstructorParameterNaming:KavitaModels.kt$SeriesDto$val thumbnail_url: String? = "" + ConstructorParameterNaming:KitsuModels.kt$OAuth$val access_token: String + ConstructorParameterNaming:KitsuModels.kt$OAuth$val created_at: Long + ConstructorParameterNaming:KitsuModels.kt$OAuth$val expires_in: Long + ConstructorParameterNaming:KitsuModels.kt$OAuth$val refresh_token: String? + ConstructorParameterNaming:KitsuModels.kt$OAuth$val token_type: String + ConstructorParameterNaming:MyAnimeListModels.kt$OAuth$val access_token: String + ConstructorParameterNaming:MyAnimeListModels.kt$OAuth$val created_at: Long = System.currentTimeMillis() + ConstructorParameterNaming:MyAnimeListModels.kt$OAuth$val expires_in: Long + ConstructorParameterNaming:MyAnimeListModels.kt$OAuth$val refresh_token: String + ConstructorParameterNaming:MyAnimeListModels.kt$OAuth$val token_type: String + ConstructorParameterNaming:ShikimoriModels.kt$OAuth$val access_token: String + ConstructorParameterNaming:ShikimoriModels.kt$OAuth$val created_at: Long + ConstructorParameterNaming:ShikimoriModels.kt$OAuth$val expires_in: Long + ConstructorParameterNaming:ShikimoriModels.kt$OAuth$val refresh_token: String? + ConstructorParameterNaming:ShikimoriModels.kt$OAuth$val token_type: String + CyclomaticComplexMethod:AnilistModels.kt$fun DomainTrack.toAnilistScore(): String + CyclomaticComplexMethod:BrowseSourceScreen.kt$@Composable fun BrowseSourceContent( source: Source?, mangaList: LazyPagingItems<StateFlow<Manga>>, columns: GridCells, displayMode: LibraryDisplayMode, snackbarHostState: SnackbarHostState, contentPadding: PaddingValues, onWebViewClick: () -> Unit, onHelpClick: () -> Unit, onLocalSourceHelpClick: () -> Unit, onMangaClick: (Manga) -> Unit, onMangaLongClick: (Manga) -> Unit, ) + CyclomaticComplexMethod:BrowseSourceScreen.kt$BrowseSourceScreen$@Composable override fun Content() + CyclomaticComplexMethod:BrowseSourceScreenModel.kt$BrowseSourceScreenModel$fun searchGenre(genreName: String) + CyclomaticComplexMethod:Button.kt$ButtonElevation$@Composable private fun animateElevation( enabled: Boolean, interactionSource: InteractionSource, ): State<Dp> + CyclomaticComplexMethod:DownloadCache.kt$DownloadCache$private fun renewCache() + CyclomaticComplexMethod:ExtensionLoader.kt$ExtensionLoader$private fun loadExtension(context: Context, extensionInfo: ExtensionInfo): LoadResult + CyclomaticComplexMethod:ExtensionsScreen.kt$@Composable private fun ExtensionContent( state: ExtensionsScreenModel.State, contentPadding: PaddingValues, onLongClickItem: (Extension) -> Unit, onClickItemCancel: (Extension) -> Unit, onOpenWebView: (Extension.Available) -> Unit, onInstallExtension: (Extension.Available) -> Unit, onUninstallExtension: (Extension) -> Unit, onUpdateExtension: (Extension.Installed) -> Unit, onTrustExtension: (Extension.Untrusted) -> Unit, onOpenExtension: (Extension.Installed) -> Unit, onClickUpdateAll: () -> Unit, ) + CyclomaticComplexMethod:ExtensionsScreen.kt$@Composable private fun ExtensionItemContent( extension: Extension, installStep: InstallStep, modifier: Modifier = Modifier, ) + CyclomaticComplexMethod:GetSourcesWithFavoriteCount.kt$GetSourcesWithFavoriteCount$private fun sortFn( direction: SetMigrateSorting.Direction, sorting: SetMigrateSorting.Mode, ): java.util.Comparator<Pair<Source, Long>> + CyclomaticComplexMethod:ImageUtil.kt$ImageUtil$fun chooseBackground(context: Context, imageStream: InputStream): Drawable + CyclomaticComplexMethod:LibraryScreenModel.kt$LibraryScreenModel$private fun LibraryMap.applySort( // Map<MangaId, List<Track>> trackMap: Map<Long, List<Track>>, ): LibraryMap + CyclomaticComplexMethod:LibraryScreenModel.kt$LibraryScreenModel$private suspend fun LibraryMap.applyFilters( trackMap: Map<Long, List<Track>>, loggedInTrackers: Map<Long, TriState>, ): LibraryMap + CyclomaticComplexMethod:LibraryTab.kt$LibraryTab$@Composable override fun Content() + CyclomaticComplexMethod:LibraryUpdateJob.kt$LibraryUpdateJob$private suspend fun addMangaToQueue(categoryId: Long) + CyclomaticComplexMethod:LocalSource.kt$LocalSource$override suspend fun getMangaDetails(manga: SManga): SManga + CyclomaticComplexMethod:LocalSource.kt$LocalSource$private fun updateCover(chapter: SChapter, manga: SManga): UniFile? + CyclomaticComplexMethod:MainActivity.kt$MainActivity$override fun onCreate(savedInstanceState: Bundle?) + CyclomaticComplexMethod:MainActivity.kt$MainActivity$private fun handleIntentAction(intent: Intent, navigator: Navigator): Boolean + CyclomaticComplexMethod:MangaInfoHeader.kt$@Composable private fun ColumnScope.MangaContentInfo( title: String, doSearch: (query: String, global: Boolean) -> Unit, author: String?, artist: String?, status: Long, sourceName: String, isStubSource: Boolean, textAlign: TextAlign? = LocalTextStyle.current.textAlign, ) + CyclomaticComplexMethod:MangaScreen.kt$MangaScreen$@Composable override fun Content() + CyclomaticComplexMethod:MangaScreenModel.kt$MangaScreenModel$fun toggleSelection( item: ChapterList.Item, selected: Boolean, userSelected: Boolean = false, fromLongPress: Boolean = false, ) + CyclomaticComplexMethod:MigrateDialog.kt$MigrateDialogScreenModel$private suspend fun migrateMangaInternal( oldSource: Source?, newSource: Source, oldManga: Manga, newManga: Manga, sourceChapters: List<SChapter>, replace: Boolean, flags: Int, ) + CyclomaticComplexMethod:NotificationReceiver.kt$NotificationReceiver$override fun onReceive(context: Context, intent: Intent) + CyclomaticComplexMethod:PagerViewer.kt$PagerViewer$override fun handleKeyEvent(event: KeyEvent): Boolean + CyclomaticComplexMethod:PagerViewerAdapter.kt$PagerViewerAdapter$fun setChapters(chapters: ViewerChapters, forceTransition: Boolean) + CyclomaticComplexMethod:PreferenceItem.kt$@Composable internal fun PreferenceItem( item: Preference.PreferenceItem<*>, highlightKey: String?, ) + CyclomaticComplexMethod:RateLimitInterceptor.kt$RateLimitInterceptor$override fun intercept(chain: Interceptor.Chain): Response + CyclomaticComplexMethod:ReaderActivity.kt$ReaderActivity$override fun onCreate(savedInstanceState: Bundle?) + CyclomaticComplexMethod:ReaderActivity.kt$ReaderActivity$private fun initializeMenu() + CyclomaticComplexMethod:Scaffold.kt$@Composable private fun ScaffoldLayout( fabPosition: FabPosition, topBar: @Composable () -> Unit, startBar: @Composable () -> Unit, content: @Composable (PaddingValues) -> Unit, snackbar: @Composable () -> Unit, fab: @Composable () -> Unit, contentWindowInsets: WindowInsets, bottomBar: @Composable () -> Unit, ) + CyclomaticComplexMethod:SourcesScreenModel.kt$SourcesScreenModel$private fun collectLatestSources(sources: List<Source>) + CyclomaticComplexMethod:SyncChaptersWithSource.kt$SyncChaptersWithSource$suspend fun await( rawSourceChapters: List<SChapter>, manga: Manga, source: Source, manualFetch: Boolean = false, fetchWindow: Pair<Long, Long> = Pair(0, 0), ): List<Chapter> + CyclomaticComplexMethod:TriStateListDialog.kt$@Composable fun <T> TriStateListDialog( title: String, message: String? = null, items: List<T>, initialChecked: List<T>, initialInversed: List<T>, itemLabel: @Composable (T) -> String, onDismissRequest: () -> Unit, onValueChanged: (newIncluded: List<T>, newExcluded: List<T>) -> Unit, ) + CyclomaticComplexMethod:UpdatesScreenModel.kt$UpdatesScreenModel$fun toggleSelection( item: UpdatesItem, selected: Boolean, userSelected: Boolean = false, fromLongPress: Boolean = false, ) + CyclomaticComplexMethod:VerticalFastScroller.kt$@Composable fun VerticalFastScroller( listState: LazyListState, modifier: Modifier = Modifier, thumbAllowed: () -> Boolean = { true }, thumbColor: Color = MaterialTheme.colorScheme.primary, topContentPadding: Dp = Dp.Hairline, bottomContentPadding: Dp = Dp.Hairline, endContentPadding: Dp = Dp.Hairline, content: @Composable () -> Unit, ) + CyclomaticComplexMethod:VerticalFastScroller.kt$@Composable fun VerticalGridFastScroller( state: LazyGridState, columns: GridCells, arrangement: Arrangement.Horizontal, contentPadding: PaddingValues, modifier: Modifier = Modifier, thumbAllowed: () -> Boolean = { true }, thumbColor: Color = MaterialTheme.colorScheme.primary, topContentPadding: Dp = Dp.Hairline, bottomContentPadding: Dp = Dp.Hairline, endContentPadding: Dp = Dp.Hairline, content: @Composable () -> Unit, ) + CyclomaticComplexMethod:WebtoonRecyclerView.kt$WebtoonRecyclerView.Detector$override fun onTouchEvent(ev: MotionEvent): Boolean + CyclomaticComplexMethod:WebtoonViewer.kt$WebtoonViewer$override fun handleKeyEvent(event: KeyEvent): Boolean + DestructuringDeclarationWithTooManyEntries:ExtensionsScreenModel.kt$ExtensionsScreenModel$(_updates, _installed, _available, _untrusted) + DestructuringDeclarationWithTooManyEntries:MangaInfoHeader.kt$(shrunk, expanded, actual, scrim) + EmptyCatchBlock:RarPageLoader.kt$RarPageLoader${ } + EqualsAlwaysReturnsTrueOrFalse:FilterList.kt$FilterList$override fun equals(other: Any?): Boolean + ExplicitItLambdaParameter:ColorFilterPage.kt${ index, it -> FilterChip( selected = colorFilterMode == index, onClick = { screenModel.preferences.colorFilterMode().set(index) }, label = { Text(stringResource(it.first)) }, ) } + ExplicitItLambdaParameter:LibraryTab.kt$LibraryTab${ it: LibraryManga -> scope.launchIO { val chapter = screenModel.getNextUnreadChapter(it.manga) if (chapter != null) { context.startActivity( ReaderActivity.newIntent(context, chapter.mangaId, chapter.id), ) } else { snackbarHostState.showSnackbar(context.stringResource(MR.strings.no_next_chapter)) } } Unit } + ExplicitItLambdaParameter:ReadingModePage.kt${ index, it -> FilterChip( selected = imageScaleType == index + 1, onClick = { screenModel.preferences.imageScaleType().set(index + 1) }, label = { Text(stringResource(it)) }, ) } + ExplicitItLambdaParameter:ReadingModePage.kt${ index, it -> FilterChip( selected = selected == index, onClick = { onSelect(index) }, label = { Text(stringResource(it)) }, ) } + ExplicitItLambdaParameter:ReadingModePage.kt${ index, it -> FilterChip( selected = zoomStart == index + 1, onClick = { screenModel.preferences.zoomStart().set(index + 1) }, label = { Text(stringResource(it)) }, ) } + ExplicitItLambdaParameter:SettingsReaderScreen.kt$SettingsReaderScreen${ index, it -> index + 1 to stringResource(it) } + ExplicitItLambdaParameter:SettingsReaderScreen.kt$SettingsReaderScreen${ index, it -> index to stringResource(it) } + FinalNewline:Commands.kt$.Commands.kt + ForEachOnRange:MangaBottomActionMenu.kt$0..<5 + ForEachOnRange:MangaBottomActionMenu.kt$0..<7 + ForEachOnRange:UpdatesWidget.kt$0..<rowCount + ForbiddenComment:AddTracks.kt$AddTracks$// TODO: merge into [SyncChapterProgressWithTrack]? + ForbiddenComment:AddTracks.kt$AddTracks$// TODO: update all trackers based on common data + ForbiddenComment:AppTheme.kt$AppTheme.NORD$// TODO: re-enable for preview + ForbiddenComment:BackupRestorer.kt$BackupRestorer$// TODO: optionally trigger online library + tracker update + ForbiddenComment:BangumiApi.kt$BangumiApi$// TODO: get user readed chapter here + ForbiddenComment:BaseTracker.kt$BaseTracker$// TODO: Store all scores as 10 point in the future maybe? + ForbiddenComment:BaselineProfileGenerator.kt$BaselineProfileGenerator$// TODO: automate storage permissions and possibly open manga details screen too? + ForbiddenComment:CategoryDialogs.kt$// TODO: https://issuetracker.google.com/issues/204502668 + ForbiddenComment:Chapter.kt$// TODO: Remove when all deps are migrated + ForbiddenComment:ConfigurableSource.kt$// TODO: use getSourcePreferences once all extensions are on ext-lib 1.5 + ForbiddenComment:DisplayExtensions.kt$// TODO: move the logic to `isTabletUi()` when main activity is rewritten in Compose + ForbiddenComment:ExtensionReposDialogs.kt$// TODO: https://issuetracker.google.com/issues/204502668 + ForbiddenComment:GlobalSearchToolbar.kt$// TODO: make this UX better; it only applies when triggering a new search + ForbiddenComment:HomeScreen.kt$HomeScreen$// TODO: https://issuetracker.google.com/u/0/issues/316327367 + ForbiddenComment:LibrarySettingsDialog.kt$// TODO: re-enable when custom intervals are ready for stable + ForbiddenComment:LibraryTabs.kt$// TODO: use default when width is fixed upstream + ForbiddenComment:LibraryUpdateJob.kt$LibraryUpdateJob$// TODO: surface skipped reasons to user? + ForbiddenComment:LocalSource.kt$LocalSource$// TODO: remove support for this entirely after a while + ForbiddenComment:Localize.kt$// TODO: janky workaround for https://github.com/icerockdev/moko-resources/issues/337 + ForbiddenComment:MainActivity.kt$MainActivity$// TODO: replace with ComponentActivity#enableEdgeToEdge + ForbiddenComment:Manga.kt$// TODO: move these into the domain model + ForbiddenComment:MangaCoverScreenModel.kt$MangaCoverScreenModel$// TODO: Handle animated cover + ForbiddenComment:MangaInfoHeader.kt$// TODO: show something better when using custom interval + ForbiddenComment:PreferenceItem.kt$// TODO: use different composable? + ForbiddenComment:ReaderPreferences.kt$ReaderPreferences$// TODO: default this to true if reader long strip ever goes stable + ForbiddenComment:SettingsAdvancedScreen.kt$SettingsAdvancedScreen$// TODO: allow private option in stable versions once URL handling is more fleshed out + ForbiddenComment:Tracker.kt$Tracker$// TODO: Store all scores as 10 point in the future maybe? + ForbiddenComment:Tracker.kt$Tracker$// TODO: move this to an interactor, and update all trackers based on common data + ForbiddenComment:VerticalFastScroller.kt$// TODO: Sometimes item height is not available when scrolling up + FunctionNaming:Downloader.kt$Downloader$private fun _clearQueue() + FunctionNaming:Pin.kt$fun Pins(vararg pins: Pin) + FunctionNaming:PullRefresh.kt$PullToRefreshStateImpl.Companion$fun Saver( extraVerticalOffset: Float, positionalThreshold: Float, enabled: () -> Boolean, onRefresh: () -> Unit, ) + FunctionNaming:Requests.kt$fun DELETE( url: String, headers: Headers = DEFAULT_HEADERS, body: RequestBody = DEFAULT_BODY, cache: CacheControl = DEFAULT_CACHE_CONTROL, ): Request + FunctionNaming:Requests.kt$fun GET( url: HttpUrl, headers: Headers = DEFAULT_HEADERS, cache: CacheControl = DEFAULT_CACHE_CONTROL, ): Request + FunctionNaming:Requests.kt$fun GET( url: String, headers: Headers = DEFAULT_HEADERS, cache: CacheControl = DEFAULT_CACHE_CONTROL, ): Request + FunctionNaming:Requests.kt$fun POST( url: String, headers: Headers = DEFAULT_HEADERS, body: RequestBody = DEFAULT_BODY, cache: CacheControl = DEFAULT_CACHE_CONTROL, ): Request + FunctionNaming:Requests.kt$fun PUT( url: String, headers: Headers = DEFAULT_HEADERS, body: RequestBody = DEFAULT_BODY, cache: CacheControl = DEFAULT_CACHE_CONTROL, ): Request + FunctionParameterNaming:MangaScreen.kt$MangaScreen$manga_: Manga? + FunctionParameterNaming:MangaScreen.kt$MangaScreen$source_: Source? + FunctionParameterNaming:WebViewInterceptor.kt$_name: String + FunctionParameterNaming:WebViewInterceptor.kt$_value: String + ImportOrdering:Commands.kt$import org.gradle.api.Project import java.io.ByteArrayOutputStream import java.text.SimpleDateFormat import java.util.TimeZone import java.util.Date + Indentation:LocalesConfigPlugin.kt$ + InstanceOfCheckForException:AppUpdateDownloadJob.kt$AppUpdateDownloadJob$e is CancellationException + InstanceOfCheckForException:AppUpdateDownloadJob.kt$AppUpdateDownloadJob$e is StreamResetException + InstanceOfCheckForException:BackupRestoreJob.kt$BackupRestoreJob$e is CancellationException + InstanceOfCheckForException:Downloader.kt$Downloader$e is CancellationException + InstanceOfCheckForException:Downloader.kt$Downloader$error is CancellationException + InstanceOfCheckForException:HttpPageLoader.kt$HttpPageLoader$e is CancellationException + InstanceOfCheckForException:LibraryUpdateJob.kt$LibraryUpdateJob$e is CancellationException + InstanceOfCheckForException:MangaScreenModel.kt$MangaScreenModel$e is HttpException + InstanceOfCheckForException:MangaScreenModel.kt$MangaScreenModel$e is NoChaptersException + InstanceOfCheckForException:MetadataUpdateJob.kt$MetadataUpdateJob$e is CancellationException + InstanceOfCheckForException:ReaderViewModel.kt$ReaderViewModel$e is CancellationException + InstanceOfCheckForException:UncaughtExceptionInterceptor.kt$UncaughtExceptionInterceptor$e is IOException + LargeClass:MangaScreenModel.kt$MangaScreenModel : StateScreenModel + LongMethod:AboutScreen.kt$AboutScreen$@Composable override fun Content() + LongMethod:AdaptiveSheet.kt$@Composable fun AdaptiveSheet( isTabletUi: Boolean, tonalElevation: Dp, enableSwipeDismiss: Boolean, onDismissRequest: () -> Unit, modifier: Modifier = Modifier, content: @Composable () -> Unit, ) + LongMethod:AnilistApi.kt$AnilistApi$suspend fun findLibManga(track: Track, userid: Int): Track? + LongMethod:AppBar.kt$@Composable fun AppBarActions( actions: ImmutableList<AppBar.AppBarAction>, ) + LongMethod:AppBar.kt$@Composable fun SearchToolbar( searchQuery: String?, onChangeSearchQuery: (String?) -> Unit, modifier: Modifier = Modifier, titleContent: @Composable () -> Unit = {}, navigateUp: (() -> Unit)? = null, searchEnabled: Boolean = true, placeholderText: String? = null, onSearch: (String) -> Unit = {}, onClickCloseSearch: () -> Unit = { onChangeSearchQuery(null) }, actions: @Composable RowScope.() -> Unit = {}, scrollBehavior: TopAppBarScrollBehavior? = null, visualTransformation: VisualTransformation = VisualTransformation.None, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, ) + LongMethod:AppModule.kt$AppModule$override fun InjektRegistrar.registerInjectables() + LongMethod:AppThemePreferenceWidget.kt$@Composable fun AppThemePreviewItem( selected: Boolean, onClick: () -> Unit, ) + LongMethod:BrowseSourceScreen.kt$@Composable fun BrowseSourceContent( source: Source?, mangaList: LazyPagingItems<StateFlow<Manga>>, columns: GridCells, displayMode: LibraryDisplayMode, snackbarHostState: SnackbarHostState, contentPadding: PaddingValues, onWebViewClick: () -> Unit, onHelpClick: () -> Unit, onLocalSourceHelpClick: () -> Unit, onMangaClick: (Manga) -> Unit, onMangaLongClick: (Manga) -> Unit, ) + LongMethod:BrowseSourceScreen.kt$BrowseSourceScreen$@Composable override fun Content() + LongMethod:BrowseSourceToolbar.kt$@Composable fun BrowseSourceToolbar( searchQuery: String?, onSearchQueryChange: (String?) -> Unit, source: Source?, displayMode: LibraryDisplayMode, onDisplayModeChange: (LibraryDisplayMode) -> Unit, navigateUp: () -> Unit, onWebViewClick: () -> Unit, onHelpClick: () -> Unit, onSettingsClick: () -> Unit, onSearch: (String) -> Unit, scrollBehavior: TopAppBarScrollBehavior? = null, ) + LongMethod:Button.kt$ButtonElevation$@Composable private fun animateElevation( enabled: Boolean, interactionSource: InteractionSource, ): State<Dp> + LongMethod:CategoryDialogs.kt$@Composable fun ChangeCategoryDialog( initialSelection: ImmutableList<CheckboxState<Category>>, onDismissRequest: () -> Unit, onEditCategories: () -> Unit, onConfirm: (List<Long>, List<Long>) -> Unit, ) + LongMethod:ChapterDownloadIndicator.kt$@Composable private fun DownloadingIndicator( enabled: Boolean, downloadState: Download.State, downloadProgressProvider: () -> Int, onClick: (ChapterDownloadAction) -> Unit, modifier: Modifier = Modifier, ) + LongMethod:ChapterNavigator.kt$@Composable fun ChapterNavigator( isRtl: Boolean, onNextChapter: () -> Unit, enabledNext: Boolean, onPreviousChapter: () -> Unit, enabledPrevious: Boolean, currentPage: Int, totalPages: Int, onSliderValueChange: (Int) -> Unit, ) + LongMethod:ChapterSettingsDialog.kt$@Composable fun ChapterSettingsDialog( onDismissRequest: () -> Unit, manga: Manga? = null, onDownloadFilterChanged: (TriState) -> Unit, onUnreadFilterChanged: (TriState) -> Unit, onBookmarkedFilterChanged: (TriState) -> Unit, scanlatorFilterActive: Boolean, onScanlatorFilterClicked: (() -> Unit), onSortModeChanged: (Long) -> Unit, onDisplayModeChanged: (Long) -> Unit, onSetAsDefault: (applyToExistingManga: Boolean) -> Unit, onResetToDefault: () -> Unit, ) + LongMethod:ClearDatabaseScreen.kt$ClearDatabaseScreen$@Composable override fun Content() + LongMethod:ColorFilterPage.kt$@Composable internal fun ColumnScope.ColorFilterPage(screenModel: ReaderSettingsScreenModel) + LongMethod:CreateBackupScreen.kt$CreateBackupScreen$@Composable override fun Content() + LongMethod:DomainModule.kt$DomainModule$override fun InjektRegistrar.registerInjectables() + LongMethod:DownloadCache.kt$DownloadCache$private fun renewCache() + LongMethod:DownloadQueueScreen.kt$DownloadQueueScreen$@Composable override fun Content() + LongMethod:Downloader.kt$Downloader$private suspend fun downloadChapter(download: Download) + LongMethod:EditTextPreferenceWidget.kt$@Composable fun EditTextPreferenceWidget( title: String, subtitle: String?, icon: ImageVector?, value: String, onConfirm: suspend (String) -> Boolean, ) + LongMethod:ExtensionDetailsScreen.kt$@Composable fun ExtensionDetailsScreen( navigateUp: () -> Unit, state: ExtensionDetailsScreenModel.State, onClickSourcePreferences: (sourceId: Long) -> Unit, onClickEnableAll: () -> Unit, onClickDisableAll: () -> Unit, onClickClearCookies: () -> Unit, onClickUninstall: () -> Unit, onClickSource: (sourceId: Long) -> Unit, ) + LongMethod:ExtensionDetailsScreen.kt$@Composable private fun DetailsHeader( extension: Extension, onClickAgeRating: () -> Unit, onClickUninstall: () -> Unit, onClickAppInfo: (() -> Unit)?, ) + LongMethod:ExtensionLoader.kt$ExtensionLoader$private fun loadExtension(context: Context, extensionInfo: ExtensionInfo): LoadResult + LongMethod:ExtensionsScreen.kt$@Composable private fun ExtensionContent( state: ExtensionsScreenModel.State, contentPadding: PaddingValues, onLongClickItem: (Extension) -> Unit, onClickItemCancel: (Extension) -> Unit, onOpenWebView: (Extension.Available) -> Unit, onInstallExtension: (Extension.Available) -> Unit, onUninstallExtension: (Extension) -> Unit, onUpdateExtension: (Extension.Installed) -> Unit, onTrustExtension: (Extension.Untrusted) -> Unit, onOpenExtension: (Extension.Installed) -> Unit, onClickUpdateAll: () -> Unit, ) + LongMethod:ExtensionsScreen.kt$@Composable private fun ExtensionItemActions( extension: Extension, installStep: InstallStep, modifier: Modifier = Modifier, onClickItemCancel: (Extension) -> Unit = {}, onClickItemAction: (Extension) -> Unit = {}, onClickItemSecondaryAction: (Extension) -> Unit = {}, ) + LongMethod:GlobalSearchToolbar.kt$@Composable fun GlobalSearchToolbar( searchQuery: String?, progress: Int, total: Int, navigateUp: () -> Unit, onChangeSearchQuery: (String?) -> Unit, onSearch: (String) -> Unit, sourceFilter: SourceFilter, onChangeSearchFilter: (SourceFilter) -> Unit, onlyShowHasResults: Boolean, onToggleResults: () -> Unit, scrollBehavior: TopAppBarScrollBehavior, ) + LongMethod:HomeScreen.kt$HomeScreen$@Composable override fun Content() + LongMethod:ImageUtil.kt$ImageUtil$fun chooseBackground(context: Context, imageStream: InputStream): Drawable + LongMethod:InfoScreen.kt$@Composable fun InfoScreen( icon: ImageVector, headingText: String, subtitleText: String, acceptText: String, onAcceptClick: () -> Unit, canAccept: Boolean = true, rejectText: String? = null, onRejectClick: (() -> Unit)? = null, content: @Composable ColumnScope.() -> Unit, ) + LongMethod:LibraryContent.kt$@Composable fun LibraryContent( categories: List<Category>, searchQuery: String?, selection: List<LibraryManga>, contentPadding: PaddingValues, currentPage: () -> Int, hasActiveFilters: Boolean, showPageTabs: Boolean, onChangeCurrentPage: (Int) -> Unit, onMangaClicked: (Long) -> Unit, onContinueReadingClicked: ((LibraryManga) -> Unit)?, onToggleSelection: (LibraryManga) -> Unit, onToggleRangeSelection: (LibraryManga) -> Unit, onRefresh: (Category?) -> Boolean, onGlobalSearchClicked: () -> Unit, getNumberOfMangaForCategory: (Category) -> Int?, getDisplayMode: (Int) -> PreferenceMutableState<LibraryDisplayMode>, getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>, getLibraryForPage: (Int) -> List<LibraryItem>, ) + LongMethod:LibraryPager.kt$@Composable fun LibraryPager( state: PagerState, contentPadding: PaddingValues, hasActiveFilters: Boolean, selectedManga: List<LibraryManga>, searchQuery: String?, onGlobalSearchClicked: () -> Unit, getDisplayMode: (Int) -> PreferenceMutableState<LibraryDisplayMode>, getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>, getLibraryForPage: (Int) -> List<LibraryItem>, onClickManga: (LibraryManga) -> Unit, onLongClickManga: (LibraryManga) -> Unit, onClickContinueReading: ((LibraryManga) -> Unit)?, ) + LongMethod:LibraryScreenModel.kt$LibraryScreenModel$private fun LibraryMap.applySort( // Map<MangaId, List<Track>> trackMap: Map<Long, List<Track>>, ): LibraryMap + LongMethod:LibraryScreenModel.kt$LibraryScreenModel$private suspend fun LibraryMap.applyFilters( trackMap: Map<Long, List<Track>>, loggedInTrackers: Map<Long, TriState>, ): LibraryMap + LongMethod:LibrarySettingsDialog.kt$@Composable private fun ColumnScope.DisplayPage( screenModel: LibrarySettingsScreenModel, ) + LongMethod:LibrarySettingsDialog.kt$@Composable private fun ColumnScope.FilterPage( screenModel: LibrarySettingsScreenModel, ) + LongMethod:LibraryTab.kt$LibraryTab$@Composable override fun Content() + LongMethod:LibraryUpdateJob.kt$LibraryUpdateJob$private suspend fun addMangaToQueue(categoryId: Long) + LongMethod:LibraryUpdateJob.kt$LibraryUpdateJob$private suspend fun updateChapterList() + LongMethod:MainActivity.kt$MainActivity$override fun onCreate(savedInstanceState: Bundle?) + LongMethod:MainActivity.kt$MainActivity$private fun handleIntentAction(intent: Intent, navigator: Navigator): Boolean + LongMethod:MangaBottomActionMenu.kt$@Composable fun LibraryBottomActionMenu( visible: Boolean, onChangeCategoryClicked: () -> Unit, onMarkAsReadClicked: () -> Unit, onMarkAsUnreadClicked: () -> Unit, onDownloadClicked: ((DownloadAction) -> Unit)?, onDeleteClicked: () -> Unit, modifier: Modifier = Modifier, ) + LongMethod:MangaBottomActionMenu.kt$@Composable fun MangaBottomActionMenu( visible: Boolean, modifier: Modifier = Modifier, onBookmarkClicked: (() -> Unit)? = null, onRemoveBookmarkClicked: (() -> Unit)? = null, onMarkAsReadClicked: (() -> Unit)? = null, onMarkAsUnreadClicked: (() -> Unit)? = null, onMarkPreviousAsReadClicked: (() -> Unit)? = null, onDownloadClicked: (() -> Unit)? = null, onDeleteClicked: (() -> Unit)? = null, ) + LongMethod:MangaChapterListItem.kt$@Composable fun MangaChapterListItem( title: String, date: String?, readProgress: String?, scanlator: String?, read: Boolean, bookmark: Boolean, selected: Boolean, downloadIndicatorEnabled: Boolean, downloadStateProvider: () -> Download.State, downloadProgressProvider: () -> Int, chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction, chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, onLongClick: () -> Unit, onClick: () -> Unit, onDownloadClick: ((ChapterDownloadAction) -> Unit)?, onChapterSwipe: (LibraryPreferences.ChapterSwipeAction) -> Unit, modifier: Modifier = Modifier, ) + LongMethod:MangaCoverDialog.kt$@Composable fun MangaCoverDialog( coverDataProvider: () -> Manga, isCustomCover: Boolean, snackbarHostState: SnackbarHostState, onShareClick: () -> Unit, onSaveClick: () -> Unit, onEditClick: ((EditCoverAction) -> Unit)?, onDismissRequest: () -> Unit, ) + LongMethod:MangaDialogs.kt$@Composable fun SetIntervalDialog( interval: Int, nextUpdate: Instant?, onDismissRequest: () -> Unit, onValueChanged: ((Int) -> Unit)? = null, ) + LongMethod:MangaInfoHeader.kt$@Composable fun ExpandableMangaDescription( defaultExpandState: Boolean, description: String?, tagsProvider: () -> List<String>?, onTagSearch: (String) -> Unit, onCopyTagToClipboard: (tag: String) -> Unit, modifier: Modifier = Modifier, ) + LongMethod:MangaInfoHeader.kt$@Composable private fun ColumnScope.MangaContentInfo( title: String, doSearch: (query: String, global: Boolean) -> Unit, author: String?, artist: String?, status: Long, sourceName: String, isStubSource: Boolean, textAlign: TextAlign? = LocalTextStyle.current.textAlign, ) + LongMethod:MangaInfoHeader.kt$@Composable private fun MangaSummary( expandedDescription: String, shrunkDescription: String, expanded: Boolean, modifier: Modifier = Modifier, ) + LongMethod:MangaScreen.kt$@Composable fun MangaScreen( state: MangaScreenModel.State.Success, snackbarHostState: SnackbarHostState, nextUpdate: Instant?, isTabletUi: Boolean, chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction, chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, onBackClicked: () -> Unit, onChapterClicked: (Chapter) -> Unit, onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?, onAddToLibraryClicked: () -> Unit, onWebViewClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?, onTrackingClicked: () -> Unit, // For tags menu onTagSearch: (String) -> Unit, onFilterButtonClicked: () -> Unit, onRefresh: () -> Unit, onContinueReading: () -> Unit, onSearch: (query: String, global: Boolean) -> Unit, // For cover dialog onCoverClicked: () -> Unit, // For top action menu onShareClicked: (() -> Unit)?, onDownloadActionClicked: ((DownloadAction) -> Unit)?, onEditCategoryClicked: (() -> Unit)?, onEditFetchIntervalClicked: (() -> Unit)?, onMigrateClicked: (() -> Unit)?, // For bottom action menu onMultiBookmarkClicked: (List<Chapter>, bookmarked: Boolean) -> Unit, onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit, onMarkPreviousAsReadClicked: (Chapter) -> Unit, onMultiDeleteClicked: (List<Chapter>) -> Unit, // For chapter swipe onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit, // Chapter selection onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit, onAllChapterSelected: (Boolean) -> Unit, onInvertSelection: () -> Unit, ) + LongMethod:MangaScreen.kt$@Composable fun MangaScreenLargeImpl( state: MangaScreenModel.State.Success, snackbarHostState: SnackbarHostState, nextUpdate: Instant?, chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction, chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, onBackClicked: () -> Unit, onChapterClicked: (Chapter) -> Unit, onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?, onAddToLibraryClicked: () -> Unit, onWebViewClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?, onTrackingClicked: () -> Unit, // For tags menu onTagSearch: (String) -> Unit, onCopyTagToClipboard: (tag: String) -> Unit, onFilterButtonClicked: () -> Unit, onRefresh: () -> Unit, onContinueReading: () -> Unit, onSearch: (query: String, global: Boolean) -> Unit, // For cover dialog onCoverClicked: () -> Unit, // For top action menu onShareClicked: (() -> Unit)?, onDownloadActionClicked: ((DownloadAction) -> Unit)?, onEditCategoryClicked: (() -> Unit)?, onEditIntervalClicked: (() -> Unit)?, onMigrateClicked: (() -> Unit)?, // For bottom action menu onMultiBookmarkClicked: (List<Chapter>, bookmarked: Boolean) -> Unit, onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit, onMarkPreviousAsReadClicked: (Chapter) -> Unit, onMultiDeleteClicked: (List<Chapter>) -> Unit, // For swipe actions onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit, // Chapter selection onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit, onAllChapterSelected: (Boolean) -> Unit, onInvertSelection: () -> Unit, ) + LongMethod:MangaScreen.kt$@Composable private fun MangaScreenSmallImpl( state: MangaScreenModel.State.Success, snackbarHostState: SnackbarHostState, nextUpdate: Instant?, chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction, chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, onBackClicked: () -> Unit, onChapterClicked: (Chapter) -> Unit, onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?, onAddToLibraryClicked: () -> Unit, onWebViewClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?, onTrackingClicked: () -> Unit, // For tags menu onTagSearch: (String) -> Unit, onCopyTagToClipboard: (tag: String) -> Unit, onFilterClicked: () -> Unit, onRefresh: () -> Unit, onContinueReading: () -> Unit, onSearch: (query: String, global: Boolean) -> Unit, // For cover dialog onCoverClicked: () -> Unit, // For top action menu onShareClicked: (() -> Unit)?, onDownloadActionClicked: ((DownloadAction) -> Unit)?, onEditCategoryClicked: (() -> Unit)?, onEditIntervalClicked: (() -> Unit)?, onMigrateClicked: (() -> Unit)?, // For bottom action menu onMultiBookmarkClicked: (List<Chapter>, bookmarked: Boolean) -> Unit, onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit, onMarkPreviousAsReadClicked: (Chapter) -> Unit, onMultiDeleteClicked: (List<Chapter>) -> Unit, // For chapter swipe onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit, // Chapter selection onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit, onAllChapterSelected: (Boolean) -> Unit, onInvertSelection: () -> Unit, ) + LongMethod:MangaScreen.kt$MangaScreen$@Composable override fun Content() + LongMethod:MangaScreen.kt$private fun LazyListScope.sharedChapterItems( manga: Manga, chapters: List<ChapterList>, isAnyChapterSelected: Boolean, chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction, chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, onChapterClicked: (Chapter) -> Unit, onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?, onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit, onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit, ) + LongMethod:MangaToolbar.kt$@Composable fun MangaToolbar( title: String, titleAlphaProvider: () -> Float, hasFilters: Boolean, onBackClicked: () -> Unit, onClickFilter: () -> Unit, onClickShare: (() -> Unit)?, onClickDownload: ((DownloadAction) -> Unit)?, onClickEditCategory: (() -> Unit)?, onClickRefresh: () -> Unit, onClickMigrate: (() -> Unit)?, // For action mode actionModeCounter: Int, onSelectAll: () -> Unit, onInvertSelection: () -> Unit, modifier: Modifier = Modifier, backgroundAlphaProvider: () -> Float = titleAlphaProvider, ) + LongMethod:MigrateDialog.kt$@Composable internal fun MigrateDialog( oldManga: Manga, newManga: Manga, screenModel: MigrateDialogScreenModel, onDismissRequest: () -> Unit, onClickTitle: () -> Unit, onPopScreen: () -> Unit, ) + LongMethod:MigrateDialog.kt$MigrateDialogScreenModel$private suspend fun migrateMangaInternal( oldSource: Source?, newSource: Source, oldManga: Manga, newManga: Manga, sourceChapters: List<SChapter>, replace: Boolean, flags: Int, ) + LongMethod:MoreScreen.kt$@Composable fun MoreScreen( downloadQueueStateProvider: () -> DownloadQueueState, downloadedOnly: Boolean, onDownloadedOnlyChange: (Boolean) -> Unit, incognitoMode: Boolean, onIncognitoModeChange: (Boolean) -> Unit, isFDroid: Boolean, onClickDownloadQueue: () -> Unit, onClickCategories: () -> Unit, onClickStats: () -> Unit, onClickDataAndStorage: () -> Unit, onClickSettings: () -> Unit, onClickAbout: () -> Unit, ) + LongMethod:Notifications.kt$Notifications$fun createChannels(context: Context) + LongMethod:PreferenceItem.kt$@Composable internal fun PreferenceItem( item: Preference.PreferenceItem<*>, highlightKey: String?, ) + LongMethod:ReaderActivity.kt$ReaderActivity$override fun onCreate(savedInstanceState: Bundle?) + LongMethod:ReaderActivity.kt$ReaderActivity$private fun initializeMenu() + LongMethod:ReaderAppBars.kt$@Composable fun ReaderAppBars( visible: Boolean, fullscreen: Boolean, mangaTitle: String?, chapterTitle: String?, navigateUp: () -> Unit, onClickTopAppBar: () -> Unit, bookmarked: Boolean, onToggleBookmarked: () -> Unit, onOpenInWebView: (() -> Unit)?, onShare: (() -> Unit)?, viewer: Viewer?, onNextChapter: () -> Unit, enabledNext: Boolean, onPreviousChapter: () -> Unit, enabledPrevious: Boolean, currentPage: Int, totalPages: Int, onSliderValueChange: (Int) -> Unit, readingMode: ReadingMode, onClickReadingMode: () -> Unit, orientation: ReaderOrientation, onClickOrientation: () -> Unit, cropEnabled: Boolean, onClickCropBorder: () -> Unit, onClickSettings: () -> Unit, ) + LongMethod:ReadingModePage.kt$@Composable private fun ColumnScope.PagerViewerSettings(screenModel: ReaderSettingsScreenModel) + LongMethod:Scaffold.kt$@Composable private fun ScaffoldLayout( fabPosition: FabPosition, topBar: @Composable () -> Unit, startBar: @Composable () -> Unit, content: @Composable (PaddingValues) -> Unit, snackbar: @Composable () -> Unit, fab: @Composable () -> Unit, contentWindowInsets: WindowInsets, bottomBar: @Composable () -> Unit, ) + LongMethod:ScanlatorFilterDialog.kt$@Composable fun ScanlatorFilterDialog( availableScanlators: Set<String>, excludedScanlators: Set<String>, onDismissRequest: () -> Unit, onConfirm: (Set<String>) -> Unit, ) + LongMethod:SettingsAdvancedScreen.kt$SettingsAdvancedScreen$@Composable private fun getExtensionsGroup( basePreferences: BasePreferences, ): Preference.PreferenceGroup + LongMethod:SettingsAdvancedScreen.kt$SettingsAdvancedScreen$@Composable private fun getNetworkGroup( networkPreferences: NetworkPreferences, ): Preference.PreferenceGroup + LongMethod:SettingsDataScreen.kt$SettingsDataScreen$@Composable private fun getBackupAndRestoreGroup(backupPreferences: BackupPreferences): Preference.PreferenceGroup + LongMethod:SettingsLibraryScreen.kt$SettingsLibraryScreen$@Composable private fun getGlobalUpdateGroup( allCategories: List<Category>, libraryPreferences: LibraryPreferences, ): Preference.PreferenceGroup + LongMethod:SettingsMainScreen.kt$SettingsMainScreen$@Composable fun Content(twoPane: Boolean) + LongMethod:SettingsReaderScreen.kt$SettingsReaderScreen$@Composable private fun getPagedGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup + LongMethod:SettingsReaderScreen.kt$SettingsReaderScreen$@Composable private fun getWebtoonGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup + LongMethod:SettingsSearchScreen.kt$@Composable private fun SearchResult( searchKey: String, modifier: Modifier = Modifier, listState: LazyListState = rememberLazyListState(), contentPadding: PaddingValues = PaddingValues(), onItemClick: (SearchResultItem) -> Unit, ) + LongMethod:SettingsSearchScreen.kt$SettingsSearchScreen$@Composable override fun Content() + LongMethod:SettingsTrackingScreen.kt$SettingsTrackingScreen$@Composable override fun getPreferences(): List<Preference> + LongMethod:SettingsTrackingScreen.kt$SettingsTrackingScreen$@Composable private fun TrackingLoginDialog( tracker: Tracker, uNameStringRes: StringResource, onDismissRequest: () -> Unit, ) + LongMethod:SourceFilterDialog.kt$@Composable private fun FilterItem(filter: Filter<*>, onUpdate: () -> Unit) + LongMethod:SourceSearchScreen.kt$SourceSearchScreen$@Composable override fun Content() + LongMethod:SyncChaptersWithSource.kt$SyncChaptersWithSource$suspend fun await( rawSourceChapters: List<SChapter>, manga: Manga, source: Source, manualFetch: Boolean = false, fetchWindow: Pair<Long, Long> = Pair(0, 0), ): List<Chapter> + LongMethod:TrackInfoDialog.kt$TrackInfoDialogHomeScreen$@Composable override fun Content() + LongMethod:TrackInfoDialog.kt$TrackerRemoveScreen$@Composable override fun Content() + LongMethod:TrackInfoDialogHome.kt$@Composable private fun TrackInfoItem( title: String, tracker: Tracker, status: StringResource?, onStatusClick: () -> Unit, chapters: String, onChaptersClick: () -> Unit, score: String?, onScoreClick: (() -> Unit)?, startDate: String?, onStartDateClick: (() -> Unit)?, endDate: String?, onEndDateClick: (() -> Unit)?, onNewSearch: () -> Unit, onOpenInBrowser: () -> Unit, onRemoved: () -> Unit, ) + LongMethod:TrackerSearch.kt$@Composable fun TrackerSearch( query: TextFieldValue, onQueryChange: (TextFieldValue) -> Unit, onDispatchQuery: () -> Unit, queryResult: Result<List<TrackSearch>>?, selected: TrackSearch?, onSelectedChange: (TrackSearch) -> Unit, onConfirmSelection: () -> Unit, onDismissRequest: () -> Unit, ) + LongMethod:TrackerSearch.kt$@Composable private fun SearchResultItem( trackSearch: TrackSearch, selected: Boolean, onClick: () -> Unit, ) + LongMethod:TriStateListDialog.kt$@Composable fun <T> TriStateListDialog( title: String, message: String? = null, items: List<T>, initialChecked: List<T>, initialInversed: List<T>, itemLabel: @Composable (T) -> String, onDismissRequest: () -> Unit, onValueChanged: (newIncluded: List<T>, newExcluded: List<T>) -> Unit, ) + LongMethod:UpdatesScreen.kt$@Composable fun UpdateScreen( state: UpdatesScreenModel.State, snackbarHostState: SnackbarHostState, lastUpdated: Long, onClickCover: (UpdatesItem) -> Unit, onSelectAll: (Boolean) -> Unit, onInvertSelection: () -> Unit, onUpdateLibrary: () -> Boolean, onDownloadChapter: (List<UpdatesItem>, ChapterDownloadAction) -> Unit, onMultiBookmarkClicked: (List<UpdatesItem>, bookmark: Boolean) -> Unit, onMultiMarkAsReadClicked: (List<UpdatesItem>, read: Boolean) -> Unit, onMultiDeleteClicked: (List<UpdatesItem>) -> Unit, onUpdateSelected: (UpdatesItem, Boolean, Boolean, Boolean) -> Unit, onOpenChapter: (UpdatesItem) -> Unit, ) + LongMethod:UpdatesTab.kt$UpdatesTab$@Composable override fun Content() + LongMethod:UpdatesUiItem.kt$@Composable private fun UpdatesUiItem( update: UpdatesWithRelations, selected: Boolean, readProgress: String?, onClick: () -> Unit, onLongClick: () -> Unit, onClickCover: (() -> Unit)?, onDownloadChapter: ((ChapterDownloadAction) -> Unit)?, // Download Indicator downloadStateProvider: () -> Download.State, downloadProgressProvider: () -> Int, modifier: Modifier = Modifier, ) + LongMethod:UpdatesWidget.kt$@Composable fun UpdatesWidget( data: ImmutableList<Pair<Long, Bitmap?>>?, contentColor: ColorProvider, topPadding: Dp, bottomPadding: Dp, modifier: GlanceModifier = GlanceModifier, ) + LongMethod:VerticalFastScroller.kt$@Composable fun VerticalFastScroller( listState: LazyListState, modifier: Modifier = Modifier, thumbAllowed: () -> Boolean = { true }, thumbColor: Color = MaterialTheme.colorScheme.primary, topContentPadding: Dp = Dp.Hairline, bottomContentPadding: Dp = Dp.Hairline, endContentPadding: Dp = Dp.Hairline, content: @Composable () -> Unit, ) + LongMethod:VerticalFastScroller.kt$@Composable fun VerticalGridFastScroller( state: LazyGridState, columns: GridCells, arrangement: Arrangement.Horizontal, contentPadding: PaddingValues, modifier: Modifier = Modifier, thumbAllowed: () -> Boolean = { true }, thumbColor: Color = MaterialTheme.colorScheme.primary, topContentPadding: Dp = Dp.Hairline, bottomContentPadding: Dp = Dp.Hairline, endContentPadding: Dp = Dp.Hairline, content: @Composable () -> Unit, ) + LongMethod:WebViewScreenContent.kt$@Composable fun WebViewScreenContent( onNavigateUp: () -> Unit, initialTitle: String?, url: String, onShare: (String) -> Unit, onOpenInBrowser: (String) -> Unit, onClearCookies: (String) -> Unit, headers: Map<String, String> = emptyMap(), onUrlChange: (String) -> Unit = {}, ) + LongMethod:WebtoonRecyclerView.kt$WebtoonRecyclerView.Detector$override fun onTouchEvent(ev: MotionEvent): Boolean + LongMethod:WheelPicker.kt$@Composable private fun <T> WheelPicker( items: ImmutableList<T>, modifier: Modifier = Modifier, startIndex: Int = 0, size: DpSize = DpSize(128.dp, 128.dp), onSelectionChanged: (index: Int) -> Unit = {}, manualInputType: KeyboardType? = null, backgroundContent: (@Composable (size: DpSize) -> Unit)? = { WheelPickerDefaults.Background(size = it) }, itemContent: @Composable LazyItemScope.(item: T) -> Unit, ) + LongParameterList:BottomReaderBar.kt$( backgroundColor: Color, readingMode: ReadingMode, onClickReadingMode: () -> Unit, orientation: ReaderOrientation, onClickOrientation: () -> Unit, cropEnabled: Boolean, onClickCropBorder: () -> Unit, onClickSettings: () -> Unit, ) + LongParameterList:BrowseSourceScreen.kt$( source: Source?, mangaList: LazyPagingItems<StateFlow<Manga>>, columns: GridCells, displayMode: LibraryDisplayMode, snackbarHostState: SnackbarHostState, contentPadding: PaddingValues, onWebViewClick: () -> Unit, onHelpClick: () -> Unit, onLocalSourceHelpClick: () -> Unit, onMangaClick: (Manga) -> Unit, onMangaLongClick: (Manga) -> Unit, ) + LongParameterList:BrowseSourceToolbar.kt$( searchQuery: String?, onSearchQueryChange: (String?) -> Unit, source: Source?, displayMode: LibraryDisplayMode, onDisplayModeChange: (LibraryDisplayMode) -> Unit, navigateUp: () -> Unit, onWebViewClick: () -> Unit, onHelpClick: () -> Unit, onSettingsClick: () -> Unit, onSearch: (String) -> Unit, scrollBehavior: TopAppBarScrollBehavior? = null, ) + LongParameterList:CategoryListItem.kt$( category: Category, canMoveUp: Boolean, canMoveDown: Boolean, onMoveUp: (Category) -> Unit, onMoveDown: (Category) -> Unit, onRename: () -> Unit, onDelete: () -> Unit, modifier: Modifier = Modifier, ) + LongParameterList:CategoryScreen.kt$( categories: List<Category>, lazyListState: LazyListState, paddingValues: PaddingValues, onClickRename: (Category) -> Unit, onClickDelete: (Category) -> Unit, onMoveUp: (Category) -> Unit, onMoveDown: (Category) -> Unit, ) + LongParameterList:CategoryScreen.kt$( state: CategoryScreenState.Success, onClickCreate: () -> Unit, onClickSortAlphabetically: () -> Unit, onClickRename: (Category) -> Unit, onClickDelete: (Category) -> Unit, onClickMoveUp: (Category) -> Unit, onClickMoveDown: (Category) -> Unit, navigateUp: () -> Unit, ) + LongParameterList:ChapterNavigator.kt$( isRtl: Boolean, onNextChapter: () -> Unit, enabledNext: Boolean, onPreviousChapter: () -> Unit, enabledPrevious: Boolean, currentPage: Int, totalPages: Int, onSliderValueChange: (Int) -> Unit, ) + LongParameterList:ChapterRepositoryImpl.kt$ChapterRepositoryImpl$( id: Long, mangaId: Long, url: String, name: String, scanlator: String?, read: Boolean, bookmark: Boolean, lastPageRead: Long, chapterNumber: Double, sourceOrder: Long, dateFetch: Long, dateUpload: Long, lastModifiedAt: Long, ) + LongParameterList:ChapterSettingsDialog.kt$( downloadFilter: TriState, onDownloadFilterChanged: ((TriState) -> Unit)?, unreadFilter: TriState, onUnreadFilterChanged: (TriState) -> Unit, bookmarkedFilter: TriState, onBookmarkedFilterChanged: (TriState) -> Unit, scanlatorFilterActive: Boolean, onScanlatorFilterClicked: (() -> Unit), ) + LongParameterList:ChapterSettingsDialog.kt$( onDismissRequest: () -> Unit, manga: Manga? = null, onDownloadFilterChanged: (TriState) -> Unit, onUnreadFilterChanged: (TriState) -> Unit, onBookmarkedFilterChanged: (TriState) -> Unit, scanlatorFilterActive: Boolean, onScanlatorFilterClicked: (() -> Unit), onSortModeChanged: (Long) -> Unit, onDisplayModeChanged: (Long) -> Unit, onSetAsDefault: (applyToExistingManga: Boolean) -> Unit, onResetToDefault: () -> Unit, ) + LongParameterList:ChapterTransition.kt$( topLabel: String, topChapter: Chapter?, topChapterDownloaded: Boolean, bottomLabel: String, bottomChapter: Chapter?, bottomChapterDownloaded: Boolean, fallbackLabel: String, chapterGap: Int, ) + LongParameterList:ExtensionDetailsScreen.kt$( contentPadding: PaddingValues, extension: Extension.Installed, sources: ImmutableList<ExtensionSourceItem>, onClickSourcePreferences: (sourceId: Long) -> Unit, onClickUninstall: () -> Unit, onClickSource: (sourceId: Long) -> Unit, ) + LongParameterList:ExtensionDetailsScreen.kt$( navigateUp: () -> Unit, state: ExtensionDetailsScreenModel.State, onClickSourcePreferences: (sourceId: Long) -> Unit, onClickEnableAll: () -> Unit, onClickDisableAll: () -> Unit, onClickClearCookies: () -> Unit, onClickUninstall: () -> Unit, onClickSource: (sourceId: Long) -> Unit, ) + LongParameterList:ExtensionsScreen.kt$( item: ExtensionUiModel.Item, onClickItem: (Extension) -> Unit, onLongClickItem: (Extension) -> Unit, onClickItemCancel: (Extension) -> Unit, onClickItemAction: (Extension) -> Unit, onClickItemSecondaryAction: (Extension) -> Unit, modifier: Modifier = Modifier, ) + LongParameterList:ExtensionsScreen.kt$( state: ExtensionsScreenModel.State, contentPadding: PaddingValues, onLongClickItem: (Extension) -> Unit, onClickItemCancel: (Extension) -> Unit, onOpenWebView: (Extension.Available) -> Unit, onInstallExtension: (Extension.Available) -> Unit, onUninstallExtension: (Extension) -> Unit, onUpdateExtension: (Extension.Installed) -> Unit, onTrustExtension: (Extension.Untrusted) -> Unit, onOpenExtension: (Extension.Installed) -> Unit, onClickUpdateAll: () -> Unit, ) + LongParameterList:ExtensionsScreen.kt$( state: ExtensionsScreenModel.State, contentPadding: PaddingValues, searchQuery: String?, onLongClickItem: (Extension) -> Unit, onClickItemCancel: (Extension) -> Unit, onOpenWebView: (Extension.Available) -> Unit, onInstallExtension: (Extension.Available) -> Unit, onUninstallExtension: (Extension) -> Unit, onUpdateExtension: (Extension.Installed) -> Unit, onTrustExtension: (Extension.Untrusted) -> Unit, onOpenExtension: (Extension.Installed) -> Unit, onClickUpdateAll: () -> Unit, onRefresh: () -> Unit, ) + LongParameterList:GlobalSearchScreen.kt$( items: Map<CatalogueSource, SearchItemResult>, contentPadding: PaddingValues, getManga: @Composable (Manga) -> State<Manga>, onClickSource: (CatalogueSource) -> Unit, onClickItem: (Manga) -> Unit, onLongClickItem: (Manga) -> Unit, fromSourceId: Long? = null, ) + LongParameterList:GlobalSearchScreen.kt$( state: SearchScreenModel.State, navigateUp: () -> Unit, onChangeSearchQuery: (String?) -> Unit, onSearch: (String) -> Unit, onChangeSearchFilter: (SourceFilter) -> Unit, onToggleResults: () -> Unit, getManga: @Composable (Manga) -> State<Manga>, onClickSource: (CatalogueSource) -> Unit, onClickItem: (Manga) -> Unit, onLongClickItem: (Manga) -> Unit, ) + LongParameterList:GlobalSearchToolbar.kt$( searchQuery: String?, progress: Int, total: Int, navigateUp: () -> Unit, onChangeSearchQuery: (String?) -> Unit, onSearch: (String) -> Unit, sourceFilter: SourceFilter, onChangeSearchFilter: (SourceFilter) -> Unit, onlyShowHasResults: Boolean, onToggleResults: () -> Unit, scrollBehavior: TopAppBarScrollBehavior, ) + LongParameterList:HistoryMapper.kt$HistoryMapper$( historyId: Long, mangaId: Long, chapterId: Long, title: String, thumbnailUrl: String?, sourceId: Long, isFavorite: Boolean, coverLastModified: Long, chapterNumber: Double, readAt: Date?, readDuration: Long, ) + LongParameterList:HistoryScreen.kt$( state: HistoryScreenModel.State, snackbarHostState: SnackbarHostState, onSearchQueryChange: (String?) -> Unit, onClickCover: (mangaId: Long) -> Unit, onClickResume: (mangaId: Long, chapterId: Long) -> Unit, onDialogChange: (HistoryScreenModel.Dialog?) -> Unit, ) + LongParameterList:InfoScreen.kt$( icon: ImageVector, headingText: String, subtitleText: String, acceptText: String, onAcceptClick: () -> Unit, canAccept: Boolean = true, rejectText: String? = null, onRejectClick: (() -> Unit)? = null, content: @Composable ColumnScope.() -> Unit, ) + LongParameterList:LibraryComfortableGrid.kt$( items: List<LibraryItem>, columns: Int, contentPadding: PaddingValues, selection: List<LibraryManga>, onClick: (LibraryManga) -> Unit, onLongClick: (LibraryManga) -> Unit, onClickContinueReading: ((LibraryManga) -> Unit)?, searchQuery: String?, onGlobalSearchClicked: () -> Unit, ) + LongParameterList:LibraryCompactGrid.kt$( items: List<LibraryItem>, showTitle: Boolean, columns: Int, contentPadding: PaddingValues, selection: List<LibraryManga>, onClick: (LibraryManga) -> Unit, onLongClick: (LibraryManga) -> Unit, onClickContinueReading: ((LibraryManga) -> Unit)?, searchQuery: String?, onGlobalSearchClicked: () -> Unit, ) + LongParameterList:LibraryContent.kt$( categories: List<Category>, searchQuery: String?, selection: List<LibraryManga>, contentPadding: PaddingValues, currentPage: () -> Int, hasActiveFilters: Boolean, showPageTabs: Boolean, onChangeCurrentPage: (Int) -> Unit, onMangaClicked: (Long) -> Unit, onContinueReadingClicked: ((LibraryManga) -> Unit)?, onToggleSelection: (LibraryManga) -> Unit, onToggleRangeSelection: (LibraryManga) -> Unit, onRefresh: (Category?) -> Boolean, onGlobalSearchClicked: () -> Unit, getNumberOfMangaForCategory: (Category) -> Int?, getDisplayMode: (Int) -> PreferenceMutableState<LibraryDisplayMode>, getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>, getLibraryForPage: (Int) -> List<LibraryItem>, ) + LongParameterList:LibraryList.kt$( items: List<LibraryItem>, contentPadding: PaddingValues, selection: List<LibraryManga>, onClick: (LibraryManga) -> Unit, onLongClick: (LibraryManga) -> Unit, onClickContinueReading: ((LibraryManga) -> Unit)?, searchQuery: String?, onGlobalSearchClicked: () -> Unit, ) + LongParameterList:LibraryPager.kt$( state: PagerState, contentPadding: PaddingValues, hasActiveFilters: Boolean, selectedManga: List<LibraryManga>, searchQuery: String?, onGlobalSearchClicked: () -> Unit, getDisplayMode: (Int) -> PreferenceMutableState<LibraryDisplayMode>, getColumnsForOrientation: (Boolean) -> PreferenceMutableState<Int>, getLibraryForPage: (Int) -> List<LibraryItem>, onClickManga: (LibraryManga) -> Unit, onLongClickManga: (LibraryManga) -> Unit, onClickContinueReading: ((LibraryManga) -> Unit)?, ) + LongParameterList:LibraryToolbar.kt$( hasActiveFilters: Boolean, selectedCount: Int, title: LibraryToolbarTitle, onClickUnselectAll: () -> Unit, onClickSelectAll: () -> Unit, onClickInvertSelection: () -> Unit, onClickFilter: () -> Unit, onClickRefresh: () -> Unit, onClickGlobalUpdate: () -> Unit, onClickOpenRandomManga: () -> Unit, searchQuery: String?, onSearchQueryChange: (String?) -> Unit, scrollBehavior: TopAppBarScrollBehavior?, ) + LongParameterList:LibraryToolbar.kt$( title: LibraryToolbarTitle, hasFilters: Boolean, searchQuery: String?, onSearchQueryChange: (String?) -> Unit, onClickFilter: () -> Unit, onClickRefresh: () -> Unit, onClickGlobalUpdate: () -> Unit, onClickOpenRandomManga: () -> Unit, scrollBehavior: TopAppBarScrollBehavior?, ) + LongParameterList:ListPreferenceWidget.kt$( value: T, title: String, subtitle: String?, icon: ImageVector?, entries: Map<out T, String>, onValueChange: (T) -> Unit, ) + LongParameterList:MangaBottomActionMenu.kt$( visible: Boolean, onChangeCategoryClicked: () -> Unit, onMarkAsReadClicked: () -> Unit, onMarkAsUnreadClicked: () -> Unit, onDownloadClicked: ((DownloadAction) -> Unit)?, onDeleteClicked: () -> Unit, modifier: Modifier = Modifier, ) + LongParameterList:MangaChapterListItem.kt$( action: LibraryPreferences.ChapterSwipeAction, read: Boolean, bookmark: Boolean, downloadState: Download.State, background: Color, onSwipe: () -> Unit, ) + LongParameterList:MangaChapterListItem.kt$( title: String, date: String?, readProgress: String?, scanlator: String?, read: Boolean, bookmark: Boolean, selected: Boolean, downloadIndicatorEnabled: Boolean, downloadStateProvider: () -> Download.State, downloadProgressProvider: () -> Int, chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction, chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, onLongClick: () -> Unit, onClick: () -> Unit, onDownloadClick: ((ChapterDownloadAction) -> Unit)?, onChapterSwipe: (LibraryPreferences.ChapterSwipeAction) -> Unit, modifier: Modifier = Modifier, ) + LongParameterList:MangaCoverDialog.kt$( coverDataProvider: () -> Manga, isCustomCover: Boolean, snackbarHostState: SnackbarHostState, onShareClick: () -> Unit, onSaveClick: () -> Unit, onEditClick: ((EditCoverAction) -> Unit)?, onDismissRequest: () -> Unit, ) + LongParameterList:MangaCoverFetcher.kt$MangaCoverFetcher$( private val url: String?, private val isLibraryManga: Boolean, private val options: Options, private val coverFileLazy: Lazy<File?>, private val customCoverFileLazy: Lazy<File>, private val diskCacheKeyLazy: Lazy<String>, private val sourceLazy: Lazy<HttpSource?>, private val callFactoryLazy: Lazy<Call.Factory>, private val diskCacheLazy: Lazy<DiskCache>, ) + LongParameterList:MangaInfoHeader.kt$( appBarPadding: Dp, coverDataProvider: () -> Manga, onCoverClick: () -> Unit, title: String, doSearch: (query: String, global: Boolean) -> Unit, author: String?, artist: String?, status: Long, sourceName: String, isStubSource: Boolean, ) + LongParameterList:MangaInfoHeader.kt$( favorite: Boolean, trackingCount: Int, nextUpdate: Instant?, isUserIntervalMode: Boolean, onAddToLibraryClicked: () -> Unit, onWebViewClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?, onTrackingClicked: () -> Unit, onEditIntervalClicked: (() -> Unit)?, onEditCategory: (() -> Unit)?, modifier: Modifier = Modifier, ) + LongParameterList:MangaInfoHeader.kt$( isTabletUi: Boolean, appBarPadding: Dp, title: String, author: String?, artist: String?, sourceName: String, isStubSource: Boolean, coverDataProvider: () -> Manga, status: Long, onCoverClick: () -> Unit, doSearch: (query: String, global: Boolean) -> Unit, modifier: Modifier = Modifier, ) + LongParameterList:MangaInfoHeader.kt$( title: String, doSearch: (query: String, global: Boolean) -> Unit, author: String?, artist: String?, status: Long, sourceName: String, isStubSource: Boolean, textAlign: TextAlign? = LocalTextStyle.current.textAlign, ) + LongParameterList:MangaMapper.kt$MangaMapper$( id: Long, source: Long, url: String, artist: String?, author: String?, description: String?, genre: List<String>?, title: String, status: Long, thumbnailUrl: String?, favorite: Boolean, lastUpdate: Long?, nextUpdate: Long?, initialized: Boolean, viewerFlags: Long, chapterFlags: Long, coverLastModified: Long, dateAdded: Long, updateStrategy: UpdateStrategy, calculateInterval: Long, lastModifiedAt: Long, favoriteModifiedAt: Long?, ) + LongParameterList:MangaMapper.kt$MangaMapper$( id: Long, source: Long, url: String, artist: String?, author: String?, description: String?, genre: List<String>?, title: String, status: Long, thumbnailUrl: String?, favorite: Boolean, lastUpdate: Long?, nextUpdate: Long?, initialized: Boolean, viewerFlags: Long, chapterFlags: Long, coverLastModified: Long, dateAdded: Long, updateStrategy: UpdateStrategy, calculateInterval: Long, lastModifiedAt: Long, favoriteModifiedAt: Long?, totalCount: Long, readCount: Double, latestUpload: Long, chapterFetchedAt: Long, lastRead: Long, bookmarkCount: Double, category: Long, ) + LongParameterList:MangaRestorer.kt$MangaRestorer$( manga: Manga, chapters: List<BackupChapter>, categories: List<Long>, backupCategories: List<BackupCategory>, history: List<BackupHistory>, tracks: List<BackupTracking>, excludedScanlators: List<String>, ) + LongParameterList:MangaScreen.kt$( manga: Manga, chapters: List<ChapterList>, isAnyChapterSelected: Boolean, chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction, chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, onChapterClicked: (Chapter) -> Unit, onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?, onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit, onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit, ) + LongParameterList:MangaScreen.kt$( selected: List<ChapterList.Item>, onMultiBookmarkClicked: (List<Chapter>, bookmarked: Boolean) -> Unit, onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit, onMarkPreviousAsReadClicked: (Chapter) -> Unit, onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?, onMultiDeleteClicked: (List<Chapter>) -> Unit, fillFraction: Float, modifier: Modifier = Modifier, ) + LongParameterList:MangaScreen.kt$( state: MangaScreenModel.State.Success, snackbarHostState: SnackbarHostState, nextUpdate: Instant?, chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction, chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, onBackClicked: () -> Unit, onChapterClicked: (Chapter) -> Unit, onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?, onAddToLibraryClicked: () -> Unit, onWebViewClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?, onTrackingClicked: () -> Unit, // For tags menu onTagSearch: (String) -> Unit, onCopyTagToClipboard: (tag: String) -> Unit, onFilterButtonClicked: () -> Unit, onRefresh: () -> Unit, onContinueReading: () -> Unit, onSearch: (query: String, global: Boolean) -> Unit, // For cover dialog onCoverClicked: () -> Unit, // For top action menu onShareClicked: (() -> Unit)?, onDownloadActionClicked: ((DownloadAction) -> Unit)?, onEditCategoryClicked: (() -> Unit)?, onEditIntervalClicked: (() -> Unit)?, onMigrateClicked: (() -> Unit)?, // For bottom action menu onMultiBookmarkClicked: (List<Chapter>, bookmarked: Boolean) -> Unit, onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit, onMarkPreviousAsReadClicked: (Chapter) -> Unit, onMultiDeleteClicked: (List<Chapter>) -> Unit, // For swipe actions onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit, // Chapter selection onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit, onAllChapterSelected: (Boolean) -> Unit, onInvertSelection: () -> Unit, ) + LongParameterList:MangaScreen.kt$( state: MangaScreenModel.State.Success, snackbarHostState: SnackbarHostState, nextUpdate: Instant?, chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction, chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, onBackClicked: () -> Unit, onChapterClicked: (Chapter) -> Unit, onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?, onAddToLibraryClicked: () -> Unit, onWebViewClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?, onTrackingClicked: () -> Unit, // For tags menu onTagSearch: (String) -> Unit, onCopyTagToClipboard: (tag: String) -> Unit, onFilterClicked: () -> Unit, onRefresh: () -> Unit, onContinueReading: () -> Unit, onSearch: (query: String, global: Boolean) -> Unit, // For cover dialog onCoverClicked: () -> Unit, // For top action menu onShareClicked: (() -> Unit)?, onDownloadActionClicked: ((DownloadAction) -> Unit)?, onEditCategoryClicked: (() -> Unit)?, onEditIntervalClicked: (() -> Unit)?, onMigrateClicked: (() -> Unit)?, // For bottom action menu onMultiBookmarkClicked: (List<Chapter>, bookmarked: Boolean) -> Unit, onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit, onMarkPreviousAsReadClicked: (Chapter) -> Unit, onMultiDeleteClicked: (List<Chapter>) -> Unit, // For chapter swipe onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit, // Chapter selection onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit, onAllChapterSelected: (Boolean) -> Unit, onInvertSelection: () -> Unit, ) + LongParameterList:MangaScreen.kt$( state: MangaScreenModel.State.Success, snackbarHostState: SnackbarHostState, nextUpdate: Instant?, isTabletUi: Boolean, chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction, chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, onBackClicked: () -> Unit, onChapterClicked: (Chapter) -> Unit, onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?, onAddToLibraryClicked: () -> Unit, onWebViewClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?, onTrackingClicked: () -> Unit, // For tags menu onTagSearch: (String) -> Unit, onFilterButtonClicked: () -> Unit, onRefresh: () -> Unit, onContinueReading: () -> Unit, onSearch: (query: String, global: Boolean) -> Unit, // For cover dialog onCoverClicked: () -> Unit, // For top action menu onShareClicked: (() -> Unit)?, onDownloadActionClicked: ((DownloadAction) -> Unit)?, onEditCategoryClicked: (() -> Unit)?, onEditFetchIntervalClicked: (() -> Unit)?, onMigrateClicked: (() -> Unit)?, // For bottom action menu onMultiBookmarkClicked: (List<Chapter>, bookmarked: Boolean) -> Unit, onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit, onMarkPreviousAsReadClicked: (Chapter) -> Unit, onMultiDeleteClicked: (List<Chapter>) -> Unit, // For chapter swipe onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit, // Chapter selection onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit, onAllChapterSelected: (Boolean) -> Unit, onInvertSelection: () -> Unit, ) + LongParameterList:MangaToolbar.kt$( title: String, titleAlphaProvider: () -> Float, hasFilters: Boolean, onBackClicked: () -> Unit, onClickFilter: () -> Unit, onClickShare: (() -> Unit)?, onClickDownload: ((DownloadAction) -> Unit)?, onClickEditCategory: (() -> Unit)?, onClickRefresh: () -> Unit, onClickMigrate: (() -> Unit)?, // For action mode actionModeCounter: Int, onSelectAll: () -> Unit, onInvertSelection: () -> Unit, modifier: Modifier = Modifier, backgroundAlphaProvider: () -> Float = titleAlphaProvider, ) + LongParameterList:MigrateDialog.kt$( oldManga: Manga, newManga: Manga, screenModel: MigrateDialogScreenModel, onDismissRequest: () -> Unit, onClickTitle: () -> Unit, onPopScreen: () -> Unit, ) + LongParameterList:MigrateDialog.kt$MigrateDialogScreenModel$( oldSource: Source?, newSource: Source, oldManga: Manga, newManga: Manga, sourceChapters: List<SChapter>, replace: Boolean, flags: Int, ) + LongParameterList:MigrateSearchScreen.kt$( state: SearchScreenModel.State, fromSourceId: Long?, navigateUp: () -> Unit, onChangeSearchQuery: (String?) -> Unit, onSearch: (String) -> Unit, onChangeSearchFilter: (SourceFilter) -> Unit, onToggleResults: () -> Unit, getManga: @Composable (Manga) -> State<Manga>, onClickSource: (CatalogueSource) -> Unit, onClickItem: (Manga) -> Unit, onLongClickItem: (Manga) -> Unit, ) + LongParameterList:MigrateSourceScreen.kt$( list: ImmutableList<Pair<Source, Long>>, contentPadding: PaddingValues, onClickItem: (Source) -> Unit, onLongClickItem: (Source) -> Unit, sortingMode: SetMigrateSorting.Mode, onToggleSortingMode: () -> Unit, sortingDirection: SetMigrateSorting.Direction, onToggleSortingDirection: () -> Unit, ) + LongParameterList:MoreScreen.kt$( downloadQueueStateProvider: () -> DownloadQueueState, downloadedOnly: Boolean, onDownloadedOnlyChange: (Boolean) -> Unit, incognitoMode: Boolean, onIncognitoModeChange: (Boolean) -> Unit, isFDroid: Boolean, onClickDownloadQueue: () -> Unit, onClickCategories: () -> Unit, onClickStats: () -> Unit, onClickDataAndStorage: () -> Unit, onClickSettings: () -> Unit, onClickAbout: () -> Unit, ) + LongParameterList:ReaderAppBars.kt$( visible: Boolean, fullscreen: Boolean, mangaTitle: String?, chapterTitle: String?, navigateUp: () -> Unit, onClickTopAppBar: () -> Unit, bookmarked: Boolean, onToggleBookmarked: () -> Unit, onOpenInWebView: (() -> Unit)?, onShare: (() -> Unit)?, viewer: Viewer?, onNextChapter: () -> Unit, enabledNext: Boolean, onPreviousChapter: () -> Unit, enabledPrevious: Boolean, currentPage: Int, totalPages: Int, onSliderValueChange: (Int) -> Unit, readingMode: ReadingMode, onClickReadingMode: () -> Unit, orientation: ReaderOrientation, onClickOrientation: () -> Unit, cropEnabled: Boolean, onClickCropBorder: () -> Unit, onClickSettings: () -> Unit, ) + LongParameterList:Scaffold.kt$( fabPosition: FabPosition, topBar: @Composable () -> Unit, startBar: @Composable () -> Unit, content: @Composable (PaddingValues) -> Unit, snackbar: @Composable () -> Unit, fab: @Composable () -> Unit, contentWindowInsets: WindowInsets, bottomBar: @Composable () -> Unit, ) + LongParameterList:Scrollbar.kt$( orientation: Orientation, reverseDirection: Boolean, atEnd: Boolean, showScrollbar: Boolean, thickness: Float, color: Color, alpha: () -> Float, thumbSize: Float, scrollOffset: Float, positionOffset: Float, ) + LongParameterList:SetMangaChapterFlags.kt$SetMangaChapterFlags$( mangaId: Long, unreadFilter: Long, downloadedFilter: Long, bookmarkedFilter: Long, sortingMode: Long, sortingDirection: Long, displayMode: Long, ) + LongParameterList:SyncChaptersWithSource.kt$SyncChaptersWithSource$( private val downloadManager: DownloadManager, private val downloadProvider: DownloadProvider, private val chapterRepository: ChapterRepository, private val shouldUpdateDbChapter: ShouldUpdateDbChapter, private val updateManga: UpdateManga, private val updateChapter: UpdateChapter, private val getChaptersByMangaId: GetChaptersByMangaId, private val getExcludedScanlators: GetExcludedScanlators, ) + LongParameterList:TrackInfoDialogHome.kt$( title: String, tracker: Tracker, status: StringResource?, onStatusClick: () -> Unit, chapters: String, onChaptersClick: () -> Unit, score: String?, onScoreClick: (() -> Unit)?, startDate: String?, onStartDateClick: (() -> Unit)?, endDate: String?, onEndDateClick: (() -> Unit)?, onNewSearch: () -> Unit, onOpenInBrowser: () -> Unit, onRemoved: () -> Unit, ) + LongParameterList:TrackInfoDialogHome.kt$( trackItems: List<TrackItem>, dateFormat: DateFormat, onStatusClick: (TrackItem) -> Unit, onChapterClick: (TrackItem) -> Unit, onScoreClick: (TrackItem) -> Unit, onStartDateEdit: (TrackItem) -> Unit, onEndDateEdit: (TrackItem) -> Unit, onNewSearch: (TrackItem) -> Unit, onOpenInBrowser: (TrackItem) -> Unit, onRemoved: (TrackItem) -> Unit, ) + LongParameterList:TrackInfoDialogSelector.kt$( title: String, initialSelectedDateMillis: Long, selectableDates: SelectableDates, onConfirm: (Long) -> Unit, onRemove: (() -> Unit)?, onDismissRequest: () -> Unit, ) + LongParameterList:TrackMapper.kt$TrackMapper$( id: Long, mangaId: Long, syncId: Long, remoteId: Long, libraryId: Long?, title: String, lastChapterRead: Double, totalChapters: Long, status: Long, score: Double, remoteUrl: String, startDate: Long, finishDate: Long, ) + LongParameterList:TrackerSearch.kt$( query: TextFieldValue, onQueryChange: (TextFieldValue) -> Unit, onDispatchQuery: () -> Unit, queryResult: Result<List<TrackSearch>>?, selected: TrackSearch?, onSelectedChange: (TrackSearch) -> Unit, onConfirmSelection: () -> Unit, onDismissRequest: () -> Unit, ) + LongParameterList:TriStateListDialog.kt$( title: String, message: String? = null, items: List<T>, initialChecked: List<T>, initialInversed: List<T>, itemLabel: @Composable (T) -> String, onDismissRequest: () -> Unit, onValueChanged: (newIncluded: List<T>, newExcluded: List<T>) -> Unit, ) + LongParameterList:UpdatesRepositoryImpl.kt$UpdatesRepositoryImpl$( mangaId: Long, mangaTitle: String, chapterId: Long, chapterName: String, scanlator: String?, read: Boolean, bookmark: Boolean, lastPageRead: Long, sourceId: Long, favorite: Boolean, thumbnailUrl: String?, coverLastModified: Long, dateUpload: Long, dateFetch: Long, ) + LongParameterList:UpdatesScreen.kt$( onUpdateLibrary: () -> Unit, // For action mode actionModeCounter: Int, onSelectAll: () -> Unit, onInvertSelection: () -> Unit, onCancelActionMode: () -> Unit, scrollBehavior: TopAppBarScrollBehavior, modifier: Modifier = Modifier, ) + LongParameterList:UpdatesScreen.kt$( state: UpdatesScreenModel.State, snackbarHostState: SnackbarHostState, lastUpdated: Long, onClickCover: (UpdatesItem) -> Unit, onSelectAll: (Boolean) -> Unit, onInvertSelection: () -> Unit, onUpdateLibrary: () -> Boolean, onDownloadChapter: (List<UpdatesItem>, ChapterDownloadAction) -> Unit, onMultiBookmarkClicked: (List<UpdatesItem>, bookmark: Boolean) -> Unit, onMultiMarkAsReadClicked: (List<UpdatesItem>, read: Boolean) -> Unit, onMultiDeleteClicked: (List<UpdatesItem>) -> Unit, onUpdateSelected: (UpdatesItem, Boolean, Boolean, Boolean) -> Unit, onOpenChapter: (UpdatesItem) -> Unit, ) + LongParameterList:UpdatesUiItem.kt$( uiModels: List<UpdatesUiModel>, selectionMode: Boolean, onUpdateSelected: (UpdatesItem, Boolean, Boolean, Boolean) -> Unit, onClickCover: (UpdatesItem) -> Unit, onClickUpdate: (UpdatesItem) -> Unit, onDownloadChapter: (List<UpdatesItem>, ChapterDownloadAction) -> Unit, ) + LongParameterList:UpdatesUiItem.kt$( update: UpdatesWithRelations, selected: Boolean, readProgress: String?, onClick: () -> Unit, onLongClick: () -> Unit, onClickCover: (() -> Unit)?, onDownloadChapter: ((ChapterDownloadAction) -> Unit)?, // Download Indicator downloadStateProvider: () -> Download.State, downloadProgressProvider: () -> Int, modifier: Modifier = Modifier, ) + LongParameterList:WebViewScreenContent.kt$( onNavigateUp: () -> Unit, initialTitle: String?, url: String, onShare: (String) -> Unit, onOpenInBrowser: (String) -> Unit, onClearCookies: (String) -> Unit, headers: Map<String, String> = emptyMap(), onUrlChange: (String) -> Unit = {}, ) + LongParameterList:WebtoonRecyclerView.kt$WebtoonRecyclerView$( fromRate: Float, toRate: Float, fromX: Float, toX: Float, fromY: Float, toY: Float, ) + LoopWithTooManyJumpStatements:DownloadStore.kt$DownloadStore$for + LoopWithTooManyJumpStatements:ImageUtil.kt$ImageUtil$for + LoopWithTooManyJumpStatements:Kavita.kt$Kavita$for + LoopWithTooManyJumpStatements:RateLimitInterceptor.kt$RateLimitInterceptor$while + LoopWithTooManyJumpStatements:UpdatesScreenModel.kt$UpdatesScreenModel$for + MagicNumber:AdaptiveSheet.kt$220 + MagicNumber:AdaptiveSheet.kt$90 + MagicNumber:Anilist.kt$Anilist$10 + MagicNumber:Anilist.kt$Anilist$10.0 + MagicNumber:Anilist.kt$Anilist$100 + MagicNumber:Anilist.kt$Anilist$10f + MagicNumber:Anilist.kt$Anilist$18 + MagicNumber:Anilist.kt$Anilist$20 + MagicNumber:Anilist.kt$Anilist$20.0 + MagicNumber:Anilist.kt$Anilist$25 + MagicNumber:Anilist.kt$Anilist$25.0 + MagicNumber:Anilist.kt$Anilist$35 + MagicNumber:Anilist.kt$Anilist$5 + MagicNumber:Anilist.kt$Anilist$60 + MagicNumber:AnilistApi.kt$AnilistApi$31536000000 + MagicNumber:AnilistModels.kt$10 + MagicNumber:AnilistModels.kt$30 + MagicNumber:AnilistModels.kt$35 + MagicNumber:AnilistModels.kt$50 + MagicNumber:AnilistModels.kt$60 + MagicNumber:AnilistModels.kt$70 + MagicNumber:AnilistModels.kt$90 + MagicNumber:App.kt$App$300 + MagicNumber:App.kt$App$8 + MagicNumber:AppThemePreferenceWidget.kt$0.3f + MagicNumber:AppThemePreferenceWidget.kt$0.5f + MagicNumber:AppThemePreferenceWidget.kt$0.6f + MagicNumber:AppThemePreferenceWidget.kt$0.7f + MagicNumber:AppThemePreferenceWidget.kt$0.8f + MagicNumber:AppThemePreferenceWidget.kt$16f + MagicNumber:AppThemePreferenceWidget.kt$9f + MagicNumber:AppUpdateDownloadJob.kt$AppUpdateDownloadJob.<no name provided>$100 + MagicNumber:AppUpdateDownloadJob.kt$AppUpdateDownloadJob.<no name provided>$200 + MagicNumber:AppUpdateNotifier.kt$AppUpdateNotifier$100 + MagicNumber:Backup.kt$Backup$100 + MagicNumber:Backup.kt$Backup$101 + MagicNumber:Backup.kt$Backup$104 + MagicNumber:Backup.kt$Backup$105 + MagicNumber:BackupCategory.kt$BackupCategory$100 + MagicNumber:BackupChapter.kt$BackupChapter$10 + MagicNumber:BackupChapter.kt$BackupChapter$11 + MagicNumber:BackupChapter.kt$BackupChapter$3 + MagicNumber:BackupChapter.kt$BackupChapter$4 + MagicNumber:BackupChapter.kt$BackupChapter$5 + MagicNumber:BackupChapter.kt$BackupChapter$6 + MagicNumber:BackupChapter.kt$BackupChapter$7 + MagicNumber:BackupChapter.kt$BackupChapter$8 + MagicNumber:BackupChapter.kt$BackupChapter$9 + MagicNumber:BackupCreateJob.kt$BackupCreateJob.Companion$10 + MagicNumber:BackupDecoder.kt$BackupDecoder$0x1f8b + MagicNumber:BackupHistory.kt$BackupHistory$3 + MagicNumber:BackupManga.kt$BackupManga$100 + MagicNumber:BackupManga.kt$BackupManga$101 + MagicNumber:BackupManga.kt$BackupManga$102 + MagicNumber:BackupManga.kt$BackupManga$103 + MagicNumber:BackupManga.kt$BackupManga$104 + MagicNumber:BackupManga.kt$BackupManga$105 + MagicNumber:BackupManga.kt$BackupManga$106 + MagicNumber:BackupManga.kt$BackupManga$107 + MagicNumber:BackupManga.kt$BackupManga$108 + MagicNumber:BackupManga.kt$BackupManga$13 + MagicNumber:BackupManga.kt$BackupManga$14 + MagicNumber:BackupManga.kt$BackupManga$16 + MagicNumber:BackupManga.kt$BackupManga$17 + MagicNumber:BackupManga.kt$BackupManga$18 + MagicNumber:BackupManga.kt$BackupManga$3 + MagicNumber:BackupManga.kt$BackupManga$4 + MagicNumber:BackupManga.kt$BackupManga$5 + MagicNumber:BackupManga.kt$BackupManga$6 + MagicNumber:BackupManga.kt$BackupManga$7 + MagicNumber:BackupManga.kt$BackupManga$8 + MagicNumber:BackupManga.kt$BackupManga$9 + MagicNumber:BackupPreferences.kt$BackupPreferences$12 + MagicNumber:BackupTracking.kt$BackupTracking$10 + MagicNumber:BackupTracking.kt$BackupTracking$100 + MagicNumber:BackupTracking.kt$BackupTracking$11 + MagicNumber:BackupTracking.kt$BackupTracking$3 + MagicNumber:BackupTracking.kt$BackupTracking$4 + MagicNumber:BackupTracking.kt$BackupTracking$5 + MagicNumber:BackupTracking.kt$BackupTracking$6 + MagicNumber:BackupTracking.kt$BackupTracking$7 + MagicNumber:BackupTracking.kt$BackupTracking$8 + MagicNumber:BackupTracking.kt$BackupTracking$9 + MagicNumber:Bangumi.kt$Bangumi$145 + MagicNumber:Bangumi.kt$Bangumi$153 + MagicNumber:Bangumi.kt$Bangumi$240 + MagicNumber:BangumiInterceptor.kt$BangumiInterceptor$1000 + MagicNumber:BangumiModels.kt$1000 + MagicNumber:BangumiModels.kt$3600 + MagicNumber:BangumiModels.kt$OAuth$1000 + MagicNumber:BasePreferenceWidget.kt$200 + MagicNumber:BrowseIcons.kt$0x1F888888 + MagicNumber:ChapterDownloadIndicator.kt$0.5f + MagicNumber:ChapterRecognition.kt$ChapterRecognition$10 + MagicNumber:ChapterRecognition.kt$ChapterRecognition$10.0 + MagicNumber:ChapterRecognition.kt$ChapterRecognition$2.0 + MagicNumber:ChapterRecognition.kt$ChapterRecognition$3 + MagicNumber:CircularProgressIndicator.kt$2000 + MagicNumber:ColorFilterPage.kt$16 + MagicNumber:ColorFilterPage.kt$24 + MagicNumber:ColorFilterPage.kt$8 + MagicNumber:CommonMangaItem.kt$0.33f + MagicNumber:CommonMangaItem.kt$0xAA000000 + MagicNumber:ContextExtensions.kt$3 + MagicNumber:ContextExtensions.kt$50 + MagicNumber:DateExtensions.kt$7 + MagicNumber:DelayedTrackingUpdateJob.kt$DelayedTrackingUpdateJob$3 + MagicNumber:DelayedTrackingUpdateJob.kt$DelayedTrackingUpdateJob.Companion$5 + MagicNumber:DeviceUtil.kt$DeviceUtil$1024 + MagicNumber:DeviceUtil.kt$DeviceUtil$3L + MagicNumber:DeviceUtil.kt$DeviceUtil$90000 + MagicNumber:DiskUtil.kt$DiskUtil$240 + MagicNumber:Download.kt$Download.State.DOWNLOADED$3 + MagicNumber:Download.kt$Download.State.ERROR$4 + MagicNumber:DownloadCache.kt$DownloadCache$1000 + MagicNumber:DownloadDropdownMenu.kt$10 + MagicNumber:DownloadDropdownMenu.kt$25 + MagicNumber:DownloadDropdownMenu.kt$5 + MagicNumber:DownloadHolder.kt$DownloadHolder$100 + MagicNumber:DownloadNotifier.kt$DownloadNotifier$15 + MagicNumber:DownloadNotifier.kt$DownloadNotifier$30 + MagicNumber:DownloadProvider.kt$DownloadProvider$4 + MagicNumber:DownloadQueueScreen.kt$DownloadQueueScreen$0.08f + MagicNumber:DownloadQueueScreen.kt$DownloadQueueScreen$0.12f + MagicNumber:DownloadQueueScreenModel.kt$DownloadQueueScreenModel$50 + MagicNumber:Downloader.kt$Downloader$100 + MagicNumber:Downloader.kt$Downloader$1000 + MagicNumber:Downloader.kt$Downloader$3 + MagicNumber:Downloader.kt$Downloader$5 + MagicNumber:DropdownMenu.kt$56 + MagicNumber:ExtensionDetailsScreen.kt$1.5f + MagicNumber:ExtensionInstallService.kt$ExtensionInstallService$100 + MagicNumber:FetchInterval.kt$FetchInterval$10 + MagicNumber:FetchInterval.kt$FetchInterval$1000 + MagicNumber:FetchInterval.kt$FetchInterval$3 + MagicNumber:FetchInterval.kt$FetchInterval$7 + MagicNumber:FetchInterval.kt$FetchInterval$8 + MagicNumber:GetApplicationRelease.kt$GetApplicationRelease$3 + MagicNumber:GlanceUtils.kt$10 + MagicNumber:GlanceUtils.kt$64 + MagicNumber:GlanceUtils.kt$95 + MagicNumber:GlobalSearchItem.kt$99f + MagicNumber:Hash.kt$Hash$15 + MagicNumber:Hash.kt$Hash$240 + MagicNumber:Hash.kt$Hash$4 + MagicNumber:HttpSource.kt$HttpSource$0xff + MagicNumber:HttpSource.kt$HttpSource$7 + MagicNumber:HttpSource.kt$HttpSource$8 + MagicNumber:ImageUtil.kt$ImageUtil$0.01 + MagicNumber:ImageUtil.kt$ImageUtil$0.0275 + MagicNumber:ImageUtil.kt$ImageUtil$100 + MagicNumber:ImageUtil.kt$ImageUtil$14 + MagicNumber:ImageUtil.kt$ImageUtil$18 + MagicNumber:ImageUtil.kt$ImageUtil$200 + MagicNumber:ImageUtil.kt$ImageUtil$22 + MagicNumber:ImageUtil.kt$ImageUtil$25 + MagicNumber:ImageUtil.kt$ImageUtil$3 + MagicNumber:ImageUtil.kt$ImageUtil$30 + MagicNumber:ImageUtil.kt$ImageUtil$32 + MagicNumber:ImageUtil.kt$ImageUtil$40 + MagicNumber:ImageUtil.kt$ImageUtil$5 + MagicNumber:ImageUtil.kt$ImageUtil$50 + MagicNumber:ImageUtil.kt$ImageUtil$6 + MagicNumber:ImageUtil.kt$ImageUtil$740 + MagicNumber:ImageUtil.kt$ImageUtil$9 + MagicNumber:Kavita.kt$Kavita$0xff + MagicNumber:Kavita.kt$Kavita$148 + MagicNumber:Kavita.kt$Kavita$198 + MagicNumber:Kavita.kt$Kavita$3 + MagicNumber:Kavita.kt$Kavita$7 + MagicNumber:Kavita.kt$Kavita$74 + MagicNumber:Kavita.kt$Kavita$8 + MagicNumber:KavitaApi.kt$KavitaApi$200 + MagicNumber:KavitaApi.kt$KavitaApi$204 + MagicNumber:KavitaApi.kt$KavitaApi$401 + MagicNumber:KavitaApi.kt$KavitaApi$500 + MagicNumber:KavitaModels.kt$OAuth$3 + MagicNumber:Kitsu.kt$Kitsu$20 + MagicNumber:Kitsu.kt$Kitsu$37 + MagicNumber:Kitsu.kt$Kitsu$50 + MagicNumber:Kitsu.kt$Kitsu$51 + MagicNumber:KitsuModels.kt$1000 + MagicNumber:KitsuModels.kt$3600 + MagicNumber:Komga.kt$Komga$37 + MagicNumber:Komga.kt$Komga$50 + MagicNumber:Komga.kt$Komga$51 + MagicNumber:LibraryScreenModel.kt$LibraryScreenModel$10 + MagicNumber:LibraryScreenModel.kt$LibraryScreenModel$25 + MagicNumber:LibraryScreenModel.kt$LibraryScreenModel$5 + MagicNumber:LibrarySortMode.kt$LibrarySort.Direction.Ascending$0b01000000 + MagicNumber:LibrarySortMode.kt$LibrarySort.Type.ChapterFetchDate$0b00011000 + MagicNumber:LibrarySortMode.kt$LibrarySort.Type.DateAdded$0b00011100 + MagicNumber:LibrarySortMode.kt$LibrarySort.Type.LastRead$0b00000100 + MagicNumber:LibrarySortMode.kt$LibrarySort.Type.LastUpdate$0b00001000 + MagicNumber:LibrarySortMode.kt$LibrarySort.Type.LatestChapter$0b00010100 + MagicNumber:LibrarySortMode.kt$LibrarySort.Type.TotalChapters$0b00010000 + MagicNumber:LibrarySortMode.kt$LibrarySort.Type.TrackerMean$0b000100000 + MagicNumber:LibrarySortMode.kt$LibrarySort.Type.UnreadCount$0b00001100 + MagicNumber:LibraryToolbar.kt$0.08f + MagicNumber:LibraryToolbar.kt$0.12f + MagicNumber:LibraryUpdateJob.kt$LibraryUpdateJob$5 + MagicNumber:LibraryUpdateJob.kt$LibraryUpdateJob.Companion$10 + MagicNumber:LibraryUpdateNotifier.kt$LibraryUpdateNotifier$40 + MagicNumber:MangaBottomActionMenu.kt$3 + MagicNumber:MangaBottomActionMenu.kt$4 + MagicNumber:MangaBottomActionMenu.kt$5 + MagicNumber:MangaBottomActionMenu.kt$6 + MagicNumber:MangaBottomActionMenu.kt$7 + MagicNumber:MangaCover.kt$MangaCover.Book$3f + MagicNumber:MangaInfoHeader.kt$0.2f + MagicNumber:MangaInfoHeader.kt$0.65f + MagicNumber:MangaScreenModel.kt$MangaScreenModel$10 + MagicNumber:MangaScreenModel.kt$MangaScreenModel$103 + MagicNumber:MangaScreenModel.kt$MangaScreenModel$25 + MagicNumber:MangaScreenModel.kt$MangaScreenModel$5 + MagicNumber:MangaUpdates.kt$MangaUpdates$146 + MagicNumber:MangaUpdates.kt$MangaUpdates$160 + MagicNumber:MangaUpdates.kt$MangaUpdates$173 + MagicNumber:MangaUpdatesApi.kt$MangaUpdatesApi$200 + MagicNumber:MetadataUpdateJob.kt$MetadataUpdateJob$5 + MagicNumber:Modifier.kt$0.16f + MagicNumber:Modifier.kt$0.22f + MagicNumber:MonetColorScheme.kt$MonetColorScheme.Companion$128 + MagicNumber:MyAnimeList.kt$MyAnimeList$162 + MagicNumber:MyAnimeList.kt$MyAnimeList$46 + MagicNumber:MyAnimeList.kt$MyAnimeList$81 + MagicNumber:MyAnimeListApi.kt$MyAnimeListApi$64 + MagicNumber:MyAnimeListInterceptor.kt$MyAnimeListInterceptor$401 + MagicNumber:MyAnimeListModels.kt$1000 + MagicNumber:NetworkHelper.kt$NetworkHelper$30 + MagicNumber:Page.kt$Page$100 + MagicNumber:PageIndicatorText.kt$235 + MagicNumber:PageIndicatorText.kt$45 + MagicNumber:PagerConfig.kt$PagerConfig$3 + MagicNumber:PagerConfig.kt$PagerConfig$4 + MagicNumber:PagerConfig.kt$PagerConfig$5 + MagicNumber:PagerPageHolder.kt$PagerPageHolder$16 + MagicNumber:PagerPageHolder.kt$PagerPageHolder$90f + MagicNumber:PagerViewer.kt$PagerViewer$5 + MagicNumber:PkceUtil.kt$PkceUtil$50 + MagicNumber:PullRefresh.kt$PullToRefreshStateImpl$4 + MagicNumber:ReaderActivity.kt$ReaderActivity$230 + MagicNumber:ReaderActivity.kt$ReaderActivity$242 + MagicNumber:ReaderActivity.kt$ReaderActivity.ReaderConfig$0.01f + MagicNumber:ReaderActivity.kt$ReaderActivity.ReaderConfig$100 + MagicNumber:ReaderActivity.kt$ReaderActivity.ReaderConfig$100f + MagicNumber:ReaderActivity.kt$ReaderActivity.ReaderConfig$255f + MagicNumber:ReaderActivity.kt$ReaderActivity.ReaderConfig$3 + MagicNumber:ReaderContentOverlay.kt$100f + MagicNumber:ReaderOrientation.kt$ReaderOrientation.FREE$0x00000008 + MagicNumber:ReaderOrientation.kt$ReaderOrientation.LANDSCAPE$0x00000018 + MagicNumber:ReaderOrientation.kt$ReaderOrientation.LOCKED_LANDSCAPE$0x00000028 + MagicNumber:ReaderOrientation.kt$ReaderOrientation.LOCKED_PORTRAIT$0x00000020 + MagicNumber:ReaderOrientation.kt$ReaderOrientation.PORTRAIT$0x00000010 + MagicNumber:ReaderOrientation.kt$ReaderOrientation.REVERSE_PORTRAIT$0x00000030 + MagicNumber:ReaderPageImageView.kt$ReaderPageImageView$180 + MagicNumber:ReaderPageImageView.kt$ReaderPageImageView$250 + MagicNumber:ReaderPageImageView.kt$ReaderPageImageView$500 + MagicNumber:ReaderPreferences.kt$ReaderPreferences$500 + MagicNumber:ReaderPreferences.kt$ReaderPreferences.ReaderHideThreshold.HIGH$13 + MagicNumber:ReaderPreferences.kt$ReaderPreferences.ReaderHideThreshold.HIGHEST$5 + MagicNumber:ReaderPreferences.kt$ReaderPreferences.ReaderHideThreshold.LOW$31 + MagicNumber:ReaderPreferences.kt$ReaderPreferences.ReaderHideThreshold.LOWEST$47 + MagicNumber:ReaderProgressIndicator.kt$ReaderProgressIndicator$100f + MagicNumber:ReaderSettingsDialog.kt$0.5f + MagicNumber:ReaderViewModel.kt$ReaderViewModel$0.25 + MagicNumber:ReadingMode.kt$ReadingMode.CONTINUOUS_VERTICAL$0x00000005 + MagicNumber:ReadingMode.kt$ReadingMode.VERTICAL$0x00000003 + MagicNumber:ReadingMode.kt$ReadingMode.WEBTOON$0x00000004 + MagicNumber:ReadingModePage.kt$100f + MagicNumber:ReadingModePage.kt$5 + MagicNumber:SaveImageNotifier.kt$SaveImageNotifier$1280 + MagicNumber:SaveImageNotifier.kt$SaveImageNotifier$720 + MagicNumber:Scaffold.kt$3 + MagicNumber:Scaffold.kt$4 + MagicNumber:Scaffold.kt$5 + MagicNumber:Scaffold.kt$6 + MagicNumber:Scaffold.kt$7 + MagicNumber:Scaffold.kt$8 + MagicNumber:Scaffold.kt$9 + MagicNumber:Scrollbar.kt$100 + MagicNumber:Scrollbar.kt$50 + MagicNumber:SecureActivityDelegate.kt$SecureActivityDelegate.Companion$60_000 + MagicNumber:SettingsDataScreen.kt$SettingsDataScreen$12 + MagicNumber:SettingsDataScreen.kt$SettingsDataScreen$168 + MagicNumber:SettingsDataScreen.kt$SettingsDataScreen$24 + MagicNumber:SettingsDataScreen.kt$SettingsDataScreen$48 + MagicNumber:SettingsDataScreen.kt$SettingsDataScreen$6 + MagicNumber:SettingsDownloadScreen.kt$SettingsDownloadScreen$10 + MagicNumber:SettingsDownloadScreen.kt$SettingsDownloadScreen$3 + MagicNumber:SettingsDownloadScreen.kt$SettingsDownloadScreen$4 + MagicNumber:SettingsDownloadScreen.kt$SettingsDownloadScreen$5 + MagicNumber:SettingsItems.kt$0.5f + MagicNumber:SettingsItems.kt$1.5f + MagicNumber:SettingsLibraryScreen.kt$SettingsLibraryScreen$12 + MagicNumber:SettingsLibraryScreen.kt$SettingsLibraryScreen$168 + MagicNumber:SettingsLibraryScreen.kt$SettingsLibraryScreen$24 + MagicNumber:SettingsLibraryScreen.kt$SettingsLibraryScreen$48 + MagicNumber:SettingsLibraryScreen.kt$SettingsLibraryScreen$72 + MagicNumber:SettingsMainScreen.kt$SettingsMainScreen$0.02f + MagicNumber:SettingsMainScreen.kt$SettingsMainScreen$0.05f + MagicNumber:SettingsMainScreen.kt$SettingsMainScreen$3 + MagicNumber:SettingsReaderScreen.kt$SettingsReaderScreen$100f + MagicNumber:SettingsReaderScreen.kt$SettingsReaderScreen$250 + MagicNumber:SettingsReaderScreen.kt$SettingsReaderScreen$3 + MagicNumber:SettingsReaderScreen.kt$SettingsReaderScreen$500 + MagicNumber:SettingsSearchScreen.kt$10 + MagicNumber:Shikimori.kt$Shikimori$40 + MagicNumber:ShikimoriModels.kt$1000 + MagicNumber:ShikimoriModels.kt$3600 + MagicNumber:Surface.kt$100f + MagicNumber:Surface.kt$4.5f + MagicNumber:Suwayomi.kt$Suwayomi$255 + MagicNumber:Suwayomi.kt$Suwayomi$35 + MagicNumber:Tabs.kt$0.08f + MagicNumber:Tabs.kt$0.12f + MagicNumber:TimeUtils.kt$4 + MagicNumber:TrackInfoDialog.kt$TrackChapterSelectorScreen.Model$10000 + MagicNumber:TrackInfoDialogSelector.kt$3L + MagicNumber:TrackInfoDialogSelector.kt$4L + MagicNumber:TrackInfoDialogSelector.kt$5L + MagicNumber:TrackInfoDialogSelector.kt$6L + MagicNumber:TrackerSearchPreviewProvider.kt$TrackerSearchPreviewProvider$10 + MagicNumber:TrackerSearchPreviewProvider.kt$TrackerSearchPreviewProvider$100 + MagicNumber:TrackerSearchPreviewProvider.kt$TrackerSearchPreviewProvider$1000L + MagicNumber:TrackerSearchPreviewProvider.kt$TrackerSearchPreviewProvider$100L + MagicNumber:TrackerSearchPreviewProvider.kt$TrackerSearchPreviewProvider$30 + MagicNumber:TrackerSearchPreviewProvider.kt$TrackerSearchPreviewProvider$365 + MagicNumber:TrackerSearchPreviewProvider.kt$TrackerSearchPreviewProvider$40 + MagicNumber:TrackingPreferenceWidget.kt$0xFF4CAF50 + MagicNumber:UniFileTempFileManager.kt$UniFileTempFileManager$3 + MagicNumber:UniFileTempFileManager.kt$UniFileTempFileManager$8192 + MagicNumber:UpdatesScreenModel.kt$UpdatesScreenModel$3 + MagicNumber:VerticalFastScroller.kt$100 + MagicNumber:ViewerNavigation.kt$ViewerNavigation.NavigationRegion.LEFT$0x11 + MagicNumber:ViewerNavigation.kt$ViewerNavigation.NavigationRegion.LEFT$0x28 + MagicNumber:ViewerNavigation.kt$ViewerNavigation.NavigationRegion.LEFT$0x7D + MagicNumber:ViewerNavigation.kt$ViewerNavigation.NavigationRegion.LEFT$0xCC + MagicNumber:ViewerNavigation.kt$ViewerNavigation.NavigationRegion.MENU$0x81 + MagicNumber:ViewerNavigation.kt$ViewerNavigation.NavigationRegion.MENU$0x8D + MagicNumber:ViewerNavigation.kt$ViewerNavigation.NavigationRegion.MENU$0x95 + MagicNumber:ViewerNavigation.kt$ViewerNavigation.NavigationRegion.MENU$0xCC + MagicNumber:ViewerNavigation.kt$ViewerNavigation.NavigationRegion.NEXT$0x84 + MagicNumber:ViewerNavigation.kt$ViewerNavigation.NavigationRegion.NEXT$0x96 + MagicNumber:ViewerNavigation.kt$ViewerNavigation.NavigationRegion.NEXT$0xCC + MagicNumber:ViewerNavigation.kt$ViewerNavigation.NavigationRegion.NEXT$0xE2 + MagicNumber:ViewerNavigation.kt$ViewerNavigation.NavigationRegion.PREV$0x33 + MagicNumber:ViewerNavigation.kt$ViewerNavigation.NavigationRegion.PREV$0x77 + MagicNumber:ViewerNavigation.kt$ViewerNavigation.NavigationRegion.PREV$0xCC + MagicNumber:ViewerNavigation.kt$ViewerNavigation.NavigationRegion.PREV$0xFF + MagicNumber:ViewerNavigation.kt$ViewerNavigation.NavigationRegion.RIGHT$0xA6 + MagicNumber:ViewerNavigation.kt$ViewerNavigation.NavigationRegion.RIGHT$0xCC + MagicNumber:ViewerNavigation.kt$ViewerNavigation.NavigationRegion.RIGHT$0xCF + MagicNumber:ViewerNavigation.kt$ViewerNavigation.NavigationRegion.RIGHT$0xD5 + MagicNumber:WebViewInterceptor.kt$WebViewInterceptor$30 + MagicNumber:WebtoonConfig.kt$WebtoonConfig$3 + MagicNumber:WebtoonConfig.kt$WebtoonConfig$4 + MagicNumber:WebtoonConfig.kt$WebtoonConfig$5 + MagicNumber:WebtoonPageHolder.kt$WebtoonPageHolder$0.8 + MagicNumber:WebtoonPageHolder.kt$WebtoonPageHolder$100f + MagicNumber:WebtoonPageHolder.kt$WebtoonPageHolder$16 + MagicNumber:WebtoonPageHolder.kt$WebtoonPageHolder$90f + MagicNumber:WebtoonRecyclerView.kt$WebtoonRecyclerView$0.4f + MagicNumber:WebtoonRecyclerView.kt$WebtoonRecyclerView$400 + MagicNumber:WebtoonRecyclerView.kt$WebtoonRecyclerView.Detector$0.5f + MagicNumber:WebtoonViewer.kt$WebtoonViewer$3 + MagicNumber:WebtoonViewer.kt$WebtoonViewer$5 + MagicNumber:WheelPicker.kt$0.2f + MagicNumber:WheelPicker.kt$1.2f + MagicNumber:WorkManagerExtensions.kt$500 + MatchingDeclarationName:ChapterDownloadIndicator.kt$ChapterDownloadAction + MatchingDeclarationName:CommonMangaItem.kt$CommonMangaItemDefaults + MatchingDeclarationName:EmptyScreen.kt$EmptyScreenAction + MatchingDeclarationName:LibrarySortMode.kt$LibrarySort : FlagWithMask + MatchingDeclarationName:MyAnimeListModels.kt$OAuth + MatchingDeclarationName:NetworkStateTracker.kt$NetworkState + MatchingDeclarationName:SettingsItems.kt$SettingsItemsPaddings + MatchingDeclarationName:ShikimoriModels.kt$OAuth + MatchingDeclarationName:TabbedDialog.kt$TabbedDialogPaddings + MaxLineLength:CoroutinesExtensions.kt$* + MaxLineLength:Downloader.kt$Downloader$. + MaxLineLength:LibraryUpdateJob.kt$LibraryUpdateJob$// failedUpdates will already have the source, don't need to copy it into the message + MaxLineLength:MainActivity.kt$MainActivity$// or the Google-specific search intent (triggered by saying or typing "search *query* on *Tachiyomi*" in Google Search/Google Assistant) + MaxLineLength:PreferenceScreen.kt$* + MaxLineLength:Scaffold.kt$* + MaxLineLength:SpecificHostRateLimitInterceptor.kt$* + MaxLineLength:WebViewUtil.kt$WebViewUtil$* + MemberNameEqualsClassName:ExtensionInstaller.kt$ExtensionInstaller$private val extensionInstaller = Injekt.get<BasePreferences>().extensionInstaller() + ModifierClickableOrder:ScanlatorFilterDialog.kt$clickable { if (isExcluded) { mutableExcludedScanlators.remove(scanlator) } else { mutableExcludedScanlators.add(scanlator) } } + ModifierMissing:AppBar.kt$AppBarActions + ModifierMissing:AppThemePreferenceWidget.kt$AppThemePreviewItem + ModifierMissing:BottomReaderBar.kt$BottomReaderBar + ModifierMissing:BrowseSourceComfortableGrid.kt$BrowseSourceComfortableGrid + ModifierMissing:BrowseSourceCompactGrid.kt$BrowseSourceCompactGrid + ModifierMissing:BrowseSourceList.kt$BrowseSourceList + ModifierMissing:BrowseSourceScreen.kt$BrowseSourceContent + ModifierMissing:BrowseSourceToolbar.kt$BrowseSourceToolbar + ModifierMissing:CategoryScreen.kt$CategoryScreen + ModifierMissing:ChapterNavigator.kt$ChapterNavigator + ModifierMissing:ChapterSettingsDialog.kt$ChapterSettingsDialog + ModifierMissing:ChapterSettingsDialog.kt$ScanlatorFilterItem + ModifierMissing:CollapsibleBox.kt$CollapsibleBox + ModifierMissing:CommonMangaItem.kt$MangaComfortableGridItem + ModifierMissing:CommonMangaItem.kt$MangaCompactGridItem + ModifierMissing:CommonMangaItem.kt$MangaListItem + ModifierMissing:CrashScreen.kt$CrashScreen + ModifierMissing:ExtensionDetailsScreen.kt$ExtensionDetailsScreen + ModifierMissing:ExtensionFilterScreen.kt$ExtensionFilterScreen + ModifierMissing:ExtensionReposScreen.kt$ExtensionReposScreen + ModifierMissing:ExtensionsScreen.kt$ExtensionScreen + ModifierMissing:GlobalSearchCardRow.kt$GlobalSearchCardRow + ModifierMissing:GlobalSearchResultItems.kt$GlobalSearchErrorResultItem + ModifierMissing:GlobalSearchResultItems.kt$GlobalSearchLoadingResultItem + ModifierMissing:GlobalSearchResultItems.kt$GlobalSearchResultItem + ModifierMissing:GlobalSearchScreen.kt$GlobalSearchScreen + ModifierMissing:GlobalSearchToolbar.kt$GlobalSearchToolbar + ModifierMissing:HistoryScreen.kt$HistoryScreen + ModifierMissing:HomeScreen.kt$HomeScreen$NavigationRailItem + ModifierMissing:InfoScreen.kt$InfoScreen + ModifierMissing:LibraryContent.kt$LibraryContent + ModifierMissing:LibraryPager.kt$LibraryPager + ModifierMissing:LibrarySettingsDialog.kt$LibrarySettingsDialog + ModifierMissing:LogoHeader.kt$LogoHeader + ModifierMissing:MangaCoverDialog.kt$MangaCoverDialog + ModifierMissing:MangaScreen.kt$MangaScreenLargeImpl + ModifierMissing:MigrateMangaScreen.kt$MigrateMangaScreen + ModifierMissing:MigrateSearchScreen.kt$MigrateSearchScreen + ModifierMissing:MigrateSourceScreen.kt$MigrateSourceScreen + ModifierMissing:ModeSelectionDialog.kt$ModeSelectionDialog + ModifierMissing:MoreScreen.kt$MoreScreen + ModifierMissing:NewUpdateScreen.kt$NewUpdateScreen + ModifierMissing:OnboardingScreen.kt$OnboardingScreen + ModifierMissing:PageIndicatorText.kt$PageIndicatorText + ModifierMissing:PreferenceGroupHeader.kt$PreferenceGroupHeader + ModifierMissing:PreferenceScaffold.kt$PreferenceScaffold + ModifierMissing:ReaderAppBars.kt$ReaderAppBars + ModifierMissing:ReaderPageActionsDialog.kt$ReaderPageActionsDialog + ModifierMissing:ReaderSettingsDialog.kt$ReaderSettingsDialog + ModifierMissing:SectionCard.kt$SectionCard + ModifierMissing:SettingsItems.kt$CheckboxItem + ModifierMissing:SettingsItems.kt$HeadingItem + ModifierMissing:SettingsItems.kt$IconItem + ModifierMissing:SettingsItems.kt$RadioItem + ModifierMissing:SettingsItems.kt$SelectItem + ModifierMissing:SettingsItems.kt$SettingsChipRow + ModifierMissing:SettingsItems.kt$SettingsIconGrid + ModifierMissing:SettingsItems.kt$SliderItem + ModifierMissing:SettingsItems.kt$SortItem + ModifierMissing:SettingsItems.kt$TextItem + ModifierMissing:SettingsItems.kt$TriStateItem + ModifierMissing:SettingsMainScreen.kt$SettingsMainScreen$Content + ModifierMissing:SourceFilterDialog.kt$SourceFilterDialog + ModifierMissing:SourcesFilterScreen.kt$SourcesFilterScreen + ModifierMissing:SourcesScreen.kt$SourcesScreen + ModifierMissing:StatsScreenContent.kt$StatsScreenContent + ModifierMissing:TabbedScreen.kt$TabbedScreen + ModifierMissing:Tabs.kt$TabText + ModifierMissing:TrackInfoDialogHome.kt$TrackInfoDialogHome + ModifierMissing:TrackInfoDialogSelector.kt$TrackChapterSelector + ModifierMissing:TrackInfoDialogSelector.kt$TrackDateSelector + ModifierMissing:TrackInfoDialogSelector.kt$TrackScoreSelector + ModifierMissing:TrackInfoDialogSelector.kt$TrackStatusSelector + ModifierMissing:TrackLogoIcon.kt$TrackLogoIcon + ModifierMissing:TrackerSearch.kt$TrackerSearch + ModifierMissing:UpdatesScreen.kt$UpdateScreen + ModifierMissing:WebViewScreenContent.kt$WebViewScreenContent + ModifierMissing:WheelPicker.kt$WheelPickerDefaults$Background + ModifierMissing:WheelPicker.kt$WheelPickerDefaults$Item + ModifierNotUsedAtRoot:DropdownMenu.kt$modifier = modifier + ModifierNotUsedAtRoot:MangaChapterListItem.kt$modifier = modifier .selectedBackground(selected) .combinedClickable( onClick = onClick, onLongClick = onLongClick, ) .padding(start = 16.dp, top = 12.dp, end = 8.dp, bottom = 12.dp) + ModifierNotUsedAtRoot:TrackingPreferenceWidget.kt$modifier = modifier .clickable(enabled = onClick != null, onClick = { onClick?.invoke() }) .fillMaxWidth() .padding(horizontal = PrefsHorizontalPadding, vertical = 8.dp) + NestedBlockDepth:Anilist.kt$Anilist$override suspend fun update(track: Track, didReadChapter: Boolean): Track + NestedBlockDepth:AppLanguageScreen.kt$AppLanguageScreen$private fun getLangs(context: Context): ImmutableList<Language> + NestedBlockDepth:BackupRestorer.kt$BackupRestorer$private fun writeErrorLog(): File + NestedBlockDepth:BrowseSourceScreenModel.kt$BrowseSourceScreenModel$fun searchGenre(genreName: String) + NestedBlockDepth:ChapterLoader.kt$ChapterLoader$private fun getPageLoader(chapter: ReaderChapter): PageLoader + NestedBlockDepth:CloseableExtensions.kt$inline fun <T : Closeable?> Array<T>.use(block: () -> Unit) + NestedBlockDepth:ContextExtensions.kt$fun Context.openInBrowser(uri: Uri, forceDefaultBrowser: Boolean = false) + NestedBlockDepth:DownloadQueueScreenModel.kt$DownloadQueueScreenModel.<no name provided>$override fun onMenuItemClick(position: Int, menuItem: MenuItem) + NestedBlockDepth:Downloader.kt$Downloader$private fun archiveChapter( mangaDir: UniFile, dirname: String, tmpDir: UniFile, ) + NestedBlockDepth:ExtensionInstaller.kt$ExtensionInstaller$fun installApk(downloadId: Long, uri: Uri) + NestedBlockDepth:ImageUtil.kt$ImageUtil$fun chooseBackground(context: Context, imageStream: InputStream): Drawable + NestedBlockDepth:KavitaApi.kt$KavitaApi$fun getNewToken(apiUrl: String, apiKey: String): String? + NestedBlockDepth:KavitaApi.kt$KavitaApi$private fun getLatestChapterRead(url: String): Double + NestedBlockDepth:Kitsu.kt$Kitsu$override suspend fun update(track: Track, didReadChapter: Boolean): Track + NestedBlockDepth:LibraryUpdateJob.kt$LibraryUpdateJob$private fun writeErrorFile(errors: List<Pair<Manga, String?>>): File + NestedBlockDepth:LocalSource.kt$LocalSource$private fun copyComicInfoFileFromArchive(chapterArchives: List<UniFile>, folderPath: String?): File? + NestedBlockDepth:LocalSource.kt$LocalSource$private fun updateCover(chapter: SChapter, manga: SManga): UniFile? + NestedBlockDepth:MyAnimeList.kt$MyAnimeList$override suspend fun update(track: Track, didReadChapter: Boolean): Track + NestedBlockDepth:SyncChaptersWithSource.kt$SyncChaptersWithSource$suspend fun await( rawSourceChapters: List<SChapter>, manga: Manga, source: Source, manualFetch: Boolean = false, fetchWindow: Pair<Long, Long> = Pair(0, 0), ): List<Chapter> + NestedBlockDepth:UniFileTempFileManager.kt$UniFileTempFileManager$fun createTempFile(file: UniFile): File + NestedBlockDepth:WebtoonRecyclerView.kt$WebtoonRecyclerView.Detector$override fun onTouchEvent(ev: MotionEvent): Boolean + NewLineAtEndOfFile:Commands.kt$.Commands.kt + NoConsecutiveBlankLines:LocalesConfigPlugin.kt$ + PreviewPublic:Scrollbar.kt$LazyListHorizontalScrollbarPreview + PreviewPublic:Scrollbar.kt$LazyListScrollbarPreview + RethrowCaughtException:LocalSource.kt$LocalSource$throw e + ReturnCount:AndroidDatabaseHandler.kt$AndroidDatabaseHandler$private suspend fun <T> dispatch(inTransaction: Boolean, block: suspend Database.() -> T): T + ReturnCount:AndroidSourceManager.kt$AndroidSourceManager$private suspend fun createStubSource(id: Long): StubSource + ReturnCount:BackupCreateJob.kt$BackupCreateJob$override suspend fun doWork(): Result + ReturnCount:ChapterRecognition.kt$ChapterRecognition$fun parseChapterNumber( mangaTitle: String, chapterName: String, chapterNumber: Double? = null, ): Double + ReturnCount:ChapterRecognition.kt$ChapterRecognition$private fun checkForDecimal(decimal: String?, alpha: String?): Double + ReturnCount:Download.kt$Download.Companion$suspend fun fromChapterId( chapterId: Long, getChapter: GetChapter = Injekt.get(), getManga: GetManga = Injekt.get(), sourceManager: SourceManager = Injekt.get(), ): Download? + ReturnCount:DownloadCache.kt$DownloadCache$fun isChapterDownloaded( chapterName: String, chapterScanlator: String?, mangaTitle: String, sourceId: Long, skipCache: Boolean, ): Boolean + ReturnCount:DownloadManager.kt$DownloadManager$fun renameSource(oldSource: Source, newSource: Source) + ReturnCount:Downloader.kt$Downloader$private fun isDownloadSuccessful( download: Download, tmpDir: UniFile, ): Boolean + ReturnCount:ExtensionInstallReceiver.kt$ExtensionInstallReceiver$override fun onReceive(context: Context, intent: Intent?) + ReturnCount:ExtensionInstallService.kt$ExtensionInstallService$override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int + ReturnCount:ExtensionInstaller.kt$ExtensionInstaller.DownloadCompletionReceiver$override fun onReceive(context: Context, intent: Intent?) + ReturnCount:ExtensionInstallerPreference.kt$ExtensionInstallerPreference$private fun check(value: ExtensionInstaller): ExtensionInstaller + ReturnCount:ExtensionLoader.kt$ExtensionLoader$fun installPrivateExtensionFile(context: Context, file: File): Boolean + ReturnCount:ExtensionLoader.kt$ExtensionLoader$private fun loadExtension(context: Context, extensionInfo: ExtensionInfo): LoadResult + ReturnCount:ExtensionLoader.kt$ExtensionLoader$private fun selectExtensionPackage(shared: ExtensionInfo?, private: ExtensionInfo?): ExtensionInfo? + ReturnCount:HttpPageLoader.kt$HttpPageLoader$private fun preloadNextPages(currentPage: ReaderPage, amount: Int): List<PriorityPage> + ReturnCount:ImageUtil.kt$ImageUtil$fun chooseBackground(context: Context, imageStream: InputStream): Drawable + ReturnCount:ImageUtil.kt$ImageUtil$fun isAnimatedAndSupported(stream: InputStream): Boolean + ReturnCount:ImageUtil.kt$ImageUtil$fun splitTallImage(tmpDir: UniFile, imageFile: UniFile, filenamePrefix: String): Boolean + ReturnCount:KavitaApi.kt$KavitaApi$fun getNewToken(apiUrl: String, apiKey: String): String? + ReturnCount:KavitaApi.kt$KavitaApi$private fun getLatestChapterRead(url: String): Double + ReturnCount:KitsuDateHelper.kt$KitsuDateHelper$fun parse(dateString: String?): Long + ReturnCount:LibraryUpdateJob.kt$LibraryUpdateJob$override suspend fun doWork(): Result + ReturnCount:LocalSource.kt$LocalSource$private fun copyComicInfoFileFromArchive(chapterArchives: List<UniFile>, folderPath: String?): File? + ReturnCount:MainActivity.kt$MainActivity$private fun handleIntentAction(intent: Intent, navigator: Navigator): Boolean + ReturnCount:MangaCoverFetcher.kt$MangaCoverFetcher$private suspend fun httpLoader(): FetchResult + ReturnCount:MangaExtensions.kt$fun Manga.prepUpdateCover(coverCache: CoverCache, remoteManga: SManga, refreshSameUrl: Boolean): Manga + ReturnCount:MangaExtensions.kt$fun Manga.removeCovers(coverCache: CoverCache = Injekt.get()): Manga + ReturnCount:MangaExtensions.kt$fun Manga.shouldDownloadNewChapters(dbCategories: List<Long>, preferences: DownloadPreferences): Boolean + ReturnCount:MangaScreen.kt$MangaScreen$private fun getMangaUrl(manga_: Manga?, source_: Source?): String? + ReturnCount:MissingChapters.kt$fun List<Double>.missingChaptersCount(): Int + ReturnCount:MissingChapters.kt$fun calculateChapterGap(higherChapter: Chapter?, lowerChapter: Chapter?): Int + ReturnCount:MyAnimeList.kt$MyAnimeList$override suspend fun search(query: String): List<TrackSearch> + ReturnCount:NetworkExtensions.kt$fun Context.isConnectedToWifi(): Boolean + ReturnCount:NetworkExtensions.kt$fun Context.isOnline(): Boolean + ReturnCount:PagerPageHolder.kt$PagerPageHolder$private fun process(page: ReaderPage, imageStream: BufferedInputStream): InputStream + ReturnCount:PagerViewer.kt$PagerViewer$override fun handleKeyEvent(event: KeyEvent): Boolean + ReturnCount:PagerViewer.kt$PagerViewer$private fun checkAllowPreload(page: ReaderPage?): Boolean + ReturnCount:PagerViewerAdapter.kt$PagerViewerAdapter$fun onPageSplit(currentPage: Any?, newPage: InsertPage) + ReturnCount:ReaderActivity.kt$ReaderActivity$override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean + ReturnCount:ReaderActivity.kt$ReaderActivity$private fun moveToPageIndex(index: Int) + ReturnCount:ReaderViewModel.kt$ReaderViewModel$fun getChapterUrl(): String? + ReturnCount:ReaderViewModel.kt$ReaderViewModel$fun setAsCover() + ReturnCount:ReaderViewModel.kt$ReaderViewModel$private fun downloadNextChapters() + ReturnCount:ReaderViewModel.kt$ReaderViewModel$private fun updateTrackChapterRead(readerChapter: ReaderChapter) + ReturnCount:ReaderViewModel.kt$ReaderViewModel$suspend fun preload(chapter: ReaderChapter) + ReturnCount:ViewExtensions.kt$fun View?.isVisibleOnScreen(): Boolean + ReturnCount:WebViewInterceptor.kt$WebViewInterceptor$override fun intercept(chain: Interceptor.Chain): Response + ReturnCount:WebViewInterceptor.kt$private fun isRequestHeaderSafe(_name: String, _value: String): Boolean + ReturnCount:WebtoonFrame.kt$WebtoonFrame$override fun dispatchTouchEvent(ev: MotionEvent): Boolean + ReturnCount:WebtoonPageHolder.kt$WebtoonPageHolder$private fun process(imageStream: BufferedInputStream): InputStream + ReturnCount:WebtoonRecyclerView.kt$WebtoonRecyclerView.Detector$override fun onTouchEvent(ev: MotionEvent): Boolean + ReturnCount:WebtoonViewer.kt$WebtoonViewer$override fun handleKeyEvent(event: KeyEvent): Boolean + ReturnCount:WebtoonViewer.kt$WebtoonViewer$private fun checkAllowPreload(page: ReaderPage?): Boolean + SerialVersionUIDInSerializableClass:Category.kt$Category : Serializable + SerialVersionUIDInSerializableClass:Manga.kt$Manga : Serializable + SpreadOperator:ChapterRepositoryImpl.kt$ChapterRepositoryImpl$(*chapterUpdates.toTypedArray()) + SpreadOperator:ChapterSanitizer.kt$ChapterSanitizer$(*CHAPTER_TRIM_CHARS) + SpreadOperator:LibraryScreenModel.kt$LibraryScreenModel$(*prefFlows) + SpreadOperator:MangaRepositoryImpl.kt$MangaRepositoryImpl$(*mangaUpdates.toTypedArray()) + SpreadOperator:SourcesScreenModel.kt$SourcesScreenModel$( SourceUiModel.Header(it.key), *it.value.map { source -> SourceUiModel.Item(source) }.toTypedArray(), ) + SpreadOperator:TrackRepositoryImpl.kt$TrackRepositoryImpl$(*tracks.toTypedArray()) + SwallowedException:AboutScreen.kt$AboutScreen$e: Exception + SwallowedException:AndroidPreference.kt$AndroidPreference$e: ClassCastException + SwallowedException:AndroidPreference.kt$AndroidPreference.Object$e: Exception + SwallowedException:Anilist.kt$Anilist$e: ClassCastException + SwallowedException:Anilist.kt$Anilist$e: Exception + SwallowedException:Anilist.kt$Anilist$e: Throwable + SwallowedException:AnilistModels.kt$ALManga$e: Exception + SwallowedException:BackupRestorer.kt$BackupRestorer$e: Exception + SwallowedException:Bangumi.kt$Bangumi$e: Exception + SwallowedException:Bangumi.kt$Bangumi$e: Throwable + SwallowedException:BrowseIcons.kt$e: Exception + SwallowedException:ChapterCache.kt$ChapterCache$e: IOException + SwallowedException:ChapterLoader.kt$ChapterLoader$e: UnsupportedRarV5Exception + SwallowedException:ContextExtensions.kt$e: Exception + SwallowedException:ContextExtensions.kt$e: PackageManager.NameNotFoundException + SwallowedException:CrashLogUtil.kt$CrashLogUtil$e: Throwable + SwallowedException:CreateBackupScreen.kt$CreateBackupScreen$e: ActivityNotFoundException + SwallowedException:DeviceUtil.kt$DeviceUtil$e: Exception + SwallowedException:DownloadPendingDeleter.kt$DownloadPendingDeleter$e: Exception + SwallowedException:DownloadStore.kt$DownloadStore$e: Exception + SwallowedException:Downloader.kt$Downloader$e: Throwable + SwallowedException:ExtensionLoader.kt$ExtensionLoader$error: PackageManager.NameNotFoundException + SwallowedException:GetChapterByUrlAndMangaId.kt$GetChapterByUrlAndMangaId$e: Exception + SwallowedException:HttpSource.kt$HttpSource$e: URISyntaxException + SwallowedException:ImageUtil.kt$ImageUtil$e: Exception + SwallowedException:Kavita.kt$Kavita$e: Exception + SwallowedException:KavitaApi.kt$KavitaApi$e: SocketTimeoutException + SwallowedException:Kitsu.kt$Kitsu$e: Exception + SwallowedException:KitsuModels.kt$KitsuSearchManga$e: IllegalArgumentException + SwallowedException:Komga.kt$Komga$e: Exception + SwallowedException:LibrarySortMode.kt$LibrarySort.Companion$e: Exception + SwallowedException:LocalSource.kt$LocalSource$e: Format.UnknownFormatException + SwallowedException:MangaScreen.kt$MangaScreen$e: Exception + SwallowedException:MangaUpdates.kt$MangaUpdates$e: Exception + SwallowedException:MangaUpdatesApi.kt$MangaUpdatesApi$e: Exception + SwallowedException:MetadataUpdateJob.kt$MetadataUpdateJob$e: Exception + SwallowedException:MyAnimeList.kt$MyAnimeList$e: Exception + SwallowedException:MyAnimeList.kt$MyAnimeList$e: Throwable + SwallowedException:MyAnimeListApi.kt$MyAnimeListApi$e: Exception + SwallowedException:OkHttpExtensions.kt$ex: Throwable + SwallowedException:Pager.kt$Pager$e: IllegalArgumentException + SwallowedException:Pager.kt$Pager$e: IndexOutOfBoundsException + SwallowedException:Pager.kt$Pager$e: NullPointerException + SwallowedException:PreferenceStore.kt$e: IllegalArgumentException + SwallowedException:RarPageLoader.kt$RarPageLoader$e: Exception + SwallowedException:ReaderActivity.kt$ReaderActivity$e: ArrayIndexOutOfBoundsException + SwallowedException:ReaderViewModel.kt$ReaderViewModel$e: Exception + SwallowedException:SettingsAdvancedScreen.kt$SettingsAdvancedScreen$e: ActivityNotFoundException + SwallowedException:SettingsDataScreen.kt$SettingsDataScreen$e: ActivityNotFoundException + SwallowedException:Shikimori.kt$Shikimori$e: Exception + SwallowedException:Shikimori.kt$Shikimori$e: Throwable + SwallowedException:StorageStep.kt$StorageStep$e: ActivityNotFoundException + SwallowedException:Suwayomi.kt$Suwayomi$e: Exception + SwallowedException:TrackInfoDialog.kt$TrackInfoDialogHomeScreen.Model$e: Exception + ThrowingExceptionsWithoutMessageOrCause:MangaScreenModel.kt$MangaScreenModel$IllegalStateException() + ThrowingExceptionsWithoutMessageOrCause:OkHttpExtensions.kt$Exception() + ThrowingExceptionsWithoutMessageOrCause:PackageInstallerInstaller.kt$PackageInstallerInstaller$IllegalStateException() + ThrowingExceptionsWithoutMessageOrCause:ShizukuInstaller.kt$ShizukuInstaller$IllegalStateException() + ThrowingExceptionsWithoutMessageOrCause:TrackInfoDialog.kt$TrackInfoDialogHomeScreen.Model$Exception() + ThrowsCount:BackupCreator.kt$BackupCreator$suspend fun backup(uri: Uri, options: BackupOptions): String + ThrowsCount:FileExtensions.kt$fun File.copyAndSetReadOnlyTo(target: File, overwrite: Boolean = false, bufferSize: Int = DEFAULT_BUFFER_SIZE): File + ThrowsCount:KavitaApi.kt$KavitaApi$fun getNewToken(apiUrl: String, apiKey: String): String? + ThrowsCount:LocalSource.kt$LocalSource$fun getFormat(chapter: SChapter): Format + TooGenericExceptionCaught:AboutScreen.kt$AboutScreen$e: Exception + TooGenericExceptionCaught:AddTracks.kt$AddTracks$e: Exception + TooGenericExceptionCaught:AndroidPreference.kt$AndroidPreference.Object$e: Exception + TooGenericExceptionCaught:Anilist.kt$Anilist$e: Exception + TooGenericExceptionCaught:Anilist.kt$Anilist$e: Throwable + TooGenericExceptionCaught:AnilistModels.kt$ALManga$e: Exception + TooGenericExceptionCaught:App.kt$App$e: Exception + TooGenericExceptionCaught:AppUpdateDownloadJob.kt$AppUpdateDownloadJob$e: Exception + TooGenericExceptionCaught:BackupCreateJob.kt$BackupCreateJob$e: Exception + TooGenericExceptionCaught:BackupCreator.kt$BackupCreator$e: Exception + TooGenericExceptionCaught:BackupFileValidator.kt$BackupFileValidator$e: Exception + TooGenericExceptionCaught:BackupRestoreJob.kt$BackupRestoreJob$e: Exception + TooGenericExceptionCaught:BackupRestorer.kt$BackupRestorer$e: Exception + TooGenericExceptionCaught:Bangumi.kt$Bangumi$e: Exception + TooGenericExceptionCaught:Bangumi.kt$Bangumi$e: Throwable + TooGenericExceptionCaught:BaseTracker.kt$BaseTracker$e: Exception + TooGenericExceptionCaught:BaseTracker.kt$BaseTracker$e: Throwable + TooGenericExceptionCaught:BrowseIcons.kt$e: Exception + TooGenericExceptionCaught:ChapterCache.kt$ChapterCache$e: Exception + TooGenericExceptionCaught:ChapterLoader.kt$ChapterLoader$e: Throwable + TooGenericExceptionCaught:ChapterRepositoryImpl.kt$ChapterRepositoryImpl$e: Exception + TooGenericExceptionCaught:CloseableExtensions.kt$closeException: Throwable + TooGenericExceptionCaught:CloseableExtensions.kt$e: Throwable + TooGenericExceptionCaught:CloudflareInterceptor.kt$CloudflareInterceptor$e: Exception + TooGenericExceptionCaught:ContextExtensions.kt$e: Exception + TooGenericExceptionCaught:ContextExtensions.kt$e: Throwable + TooGenericExceptionCaught:CrashLogUtil.kt$CrashLogUtil$e: Throwable + TooGenericExceptionCaught:CreateCategoryWithName.kt$CreateCategoryWithName$e: Exception + TooGenericExceptionCaught:DeleteCategory.kt$DeleteCategory$e: Exception + TooGenericExceptionCaught:DeleteTrack.kt$DeleteTrack$e: Exception + TooGenericExceptionCaught:DeviceUtil.kt$DeviceUtil$e: Exception + TooGenericExceptionCaught:DownloadCache.kt$DownloadCache$e: Throwable + TooGenericExceptionCaught:DownloadPendingDeleter.kt$DownloadPendingDeleter$e: Exception + TooGenericExceptionCaught:DownloadProvider.kt$DownloadProvider$e: Throwable + TooGenericExceptionCaught:DownloadStore.kt$DownloadStore$e: Exception + TooGenericExceptionCaught:Downloader.kt$Downloader$e: Exception + TooGenericExceptionCaught:Downloader.kt$Downloader$e: Throwable + TooGenericExceptionCaught:Downloader.kt$Downloader$error: Throwable + TooGenericExceptionCaught:ExtensionApi.kt$ExtensionApi$e: Throwable + TooGenericExceptionCaught:ExtensionDetailsScreenModel.kt$ExtensionDetailsScreenModel$e: Exception + TooGenericExceptionCaught:ExtensionInstallActivity.kt$ExtensionInstallActivity$error: Exception + TooGenericExceptionCaught:ExtensionInstaller.kt$ExtensionInstaller$e: Exception + TooGenericExceptionCaught:ExtensionLoader.kt$ExtensionLoader$e: Exception + TooGenericExceptionCaught:ExtensionLoader.kt$ExtensionLoader$e: Throwable + TooGenericExceptionCaught:ExtensionManager.kt$ExtensionManager$e: Exception + TooGenericExceptionCaught:GetChapter.kt$GetChapter$e: Exception + TooGenericExceptionCaught:GetChapterByUrlAndMangaId.kt$GetChapterByUrlAndMangaId$e: Exception + TooGenericExceptionCaught:GetChaptersByMangaId.kt$GetChaptersByMangaId$e: Exception + TooGenericExceptionCaught:GetManga.kt$GetManga$e: Exception + TooGenericExceptionCaught:GetTracks.kt$GetTracks$e: Exception + TooGenericExceptionCaught:GlobalExceptionHandler.kt$GlobalExceptionHandler.Companion$e: Exception + TooGenericExceptionCaught:HistoryRepositoryImpl.kt$HistoryRepositoryImpl$e: Exception + TooGenericExceptionCaught:HttpPageLoader.kt$HttpPageLoader$e: Throwable + TooGenericExceptionCaught:ImageSaver.kt$ImageSaver$e: Exception + TooGenericExceptionCaught:ImageUtil.kt$ImageUtil$e: Exception + TooGenericExceptionCaught:InsertTrack.kt$InsertTrack$e: Exception + TooGenericExceptionCaught:Kavita.kt$Kavita$e: Exception + TooGenericExceptionCaught:KavitaApi.kt$KavitaApi$e: Exception + TooGenericExceptionCaught:Kitsu.kt$Kitsu$e: Exception + TooGenericExceptionCaught:Komga.kt$Komga$e: Exception + TooGenericExceptionCaught:KomgaApi.kt$KomgaApi$e: Exception + TooGenericExceptionCaught:LibrarySortMode.kt$LibrarySort.Companion$e: Exception + TooGenericExceptionCaught:LibraryUpdateJob.kt$LibraryUpdateJob$e: Exception + TooGenericExceptionCaught:LibraryUpdateJob.kt$LibraryUpdateJob$e: Throwable + TooGenericExceptionCaught:LocalSource.kt$LocalSource$e: Exception + TooGenericExceptionCaught:LocalSource.kt$LocalSource$e: Throwable + TooGenericExceptionCaught:MainActivity.kt$MainActivity$e: Exception + TooGenericExceptionCaught:MangaCoverFetcher.kt$MangaCoverFetcher$e: Exception + TooGenericExceptionCaught:MangaCoverScreenModel.kt$MangaCoverScreenModel$e: Exception + TooGenericExceptionCaught:MangaCoverScreenModel.kt$MangaCoverScreenModel$e: Throwable + TooGenericExceptionCaught:MangaRepositoryImpl.kt$MangaRepositoryImpl$e: Exception + TooGenericExceptionCaught:MangaScreen.kt$MangaScreen$e: Exception + TooGenericExceptionCaught:MangaScreenModel.kt$MangaScreenModel$e: Throwable + TooGenericExceptionCaught:MangaUpdates.kt$MangaUpdates$e: Exception + TooGenericExceptionCaught:MangaUpdatesApi.kt$MangaUpdatesApi$e: Exception + TooGenericExceptionCaught:MetadataUpdateJob.kt$MetadataUpdateJob$e: Exception + TooGenericExceptionCaught:MetadataUpdateJob.kt$MetadataUpdateJob$e: Throwable + TooGenericExceptionCaught:MyAnimeList.kt$MyAnimeList$e: Exception + TooGenericExceptionCaught:MyAnimeList.kt$MyAnimeList$e: Throwable + TooGenericExceptionCaught:MyAnimeListApi.kt$MyAnimeListApi$e: Exception + TooGenericExceptionCaught:OkHttpExtensions.kt$<no name provided>$e: Exception + TooGenericExceptionCaught:OkHttpExtensions.kt$ex: Throwable + TooGenericExceptionCaught:OkioExtensions.kt$e: Exception + TooGenericExceptionCaught:PackageInstallerInstaller.kt$PackageInstallerInstaller$e: Exception + TooGenericExceptionCaught:Pager.kt$Pager$e: IndexOutOfBoundsException + TooGenericExceptionCaught:Pager.kt$Pager$e: NullPointerException + TooGenericExceptionCaught:PagerPageHolder.kt$PagerPageHolder$e: Throwable + TooGenericExceptionCaught:QueryPagingSource.kt$QueryPagingSource$e: Exception + TooGenericExceptionCaught:RarPageLoader.kt$RarPageLoader$e: Exception + TooGenericExceptionCaught:ReaderActivity.kt$ReaderActivity$e: ArrayIndexOutOfBoundsException + TooGenericExceptionCaught:ReaderViewModel.kt$ReaderViewModel$e: Exception + TooGenericExceptionCaught:ReaderViewModel.kt$ReaderViewModel$e: Throwable + TooGenericExceptionCaught:RefreshTracks.kt$RefreshTracks$e: Throwable + TooGenericExceptionCaught:RenameCategory.kt$RenameCategory$e: Exception + TooGenericExceptionCaught:ReorderCategory.kt$ReorderCategory$e: Exception + TooGenericExceptionCaught:RestoreBackupScreen.kt$RestoreBackupScreenModel$e: Exception + TooGenericExceptionCaught:SearchScreenModel.kt$SearchScreenModel$e: Exception + TooGenericExceptionCaught:SetMangaCategories.kt$SetMangaCategories$e: Exception + TooGenericExceptionCaught:SetReadStatus.kt$SetReadStatus$e: Exception + TooGenericExceptionCaught:SettingsAdvancedScreen.kt$SettingsAdvancedScreen$e: Throwable + TooGenericExceptionCaught:SettingsDataScreen.kt$SettingsDataScreen$e: Throwable + TooGenericExceptionCaught:SettingsTrackingScreen.kt$SettingsTrackingScreen$e: Throwable + TooGenericExceptionCaught:Shikimori.kt$Shikimori$e: Exception + TooGenericExceptionCaught:Shikimori.kt$Shikimori$e: Throwable + TooGenericExceptionCaught:ShizukuInstaller.kt$ShizukuInstaller$e: Exception + TooGenericExceptionCaught:SourcePagingSource.kt$SourcePagingSource$e: Exception + TooGenericExceptionCaught:Suwayomi.kt$Suwayomi$e: Exception + TooGenericExceptionCaught:SyncChapterProgressWithTrack.kt$SyncChapterProgressWithTrack$e: Throwable + TooGenericExceptionCaught:TrackChapter.kt$TrackChapter$e: Exception + TooGenericExceptionCaught:TrackInfoDialog.kt$TrackInfoDialogHomeScreen.Model$e: Exception + TooGenericExceptionCaught:TrackInfoDialog.kt$TrackerSearchScreen.Model$e: Throwable + TooGenericExceptionCaught:UncaughtExceptionInterceptor.kt$UncaughtExceptionInterceptor$e: Exception + TooGenericExceptionCaught:UpdateCategory.kt$UpdateCategory$e: Exception + TooGenericExceptionCaught:UpdateChapter.kt$UpdateChapter$e: Exception + TooGenericExceptionCaught:WebViewActivity.kt$WebViewActivity$e: Exception + TooGenericExceptionCaught:WebViewScreenModel.kt$WebViewScreenModel$e: Exception + TooGenericExceptionCaught:WebViewUtil.kt$WebViewUtil$e: Throwable + TooGenericExceptionCaught:WebtoonPageHolder.kt$WebtoonPageHolder$e: Throwable + TooGenericExceptionCaught:WidgetManager.kt$WidgetManager$e: Exception + TooGenericExceptionThrown:Anilist.kt$Anilist$throw Exception("$track not found on user library") + TooGenericExceptionThrown:Anilist.kt$Anilist$throw Exception("Unknown score type") + TooGenericExceptionThrown:AnilistApi.kt$AnilistApi$throw Exception("Could not find manga") + TooGenericExceptionThrown:AnilistInterceptor.kt$AnilistInterceptor$throw Exception("Not authenticated with Anilist") + TooGenericExceptionThrown:AppUpdateDownloadJob.kt$AppUpdateDownloadJob$throw Exception("Unsuccessful response") + TooGenericExceptionThrown:BangumiApi.kt$BangumiApi$throw Exception("Null Response") + TooGenericExceptionThrown:BangumiInterceptor.kt$BangumiInterceptor$throw Exception("Not authenticated with Bangumi") + TooGenericExceptionThrown:ChapterLoader.kt$ChapterLoader$throw Exception(context.stringResource(MR.strings.page_list_empty_error)) + TooGenericExceptionThrown:DownloadManager.kt$DownloadManager$throw Exception(context.stringResource(MR.strings.page_list_empty_error)) + TooGenericExceptionThrown:DownloadProvider.kt$DownloadProvider$throw Exception( context.stringResource( MR.strings.invalid_location, downloadsDir?.displayablePath ?: "", ), ) + TooGenericExceptionThrown:Downloader.kt$Downloader$throw Exception(context.stringResource(MR.strings.page_list_empty_error)) + TooGenericExceptionThrown:ExtensionLoader.kt$ExtensionLoader$throw Exception("Unknown source class type: ${obj.javaClass}") + TooGenericExceptionThrown:HttpSource.kt$HttpSource$throw RuntimeException(e) + TooGenericExceptionThrown:KitsuApi.kt$KitsuApi$throw Exception("Could not find manga") + TooGenericExceptionThrown:KitsuInterceptor.kt$KitsuInterceptor$throw Exception("Not authenticated with Kitsu") + TooGenericExceptionThrown:KitsuModels.kt$KitsuLibManga$throw Exception("Unknown status") + TooGenericExceptionThrown:KitsuModels.kt$throw Exception("Unknown status") + TooGenericExceptionThrown:LocalSource.kt$LocalSource$throw Exception(context.stringResource(MR.strings.chapter_not_found)) + TooGenericExceptionThrown:LocalSource.kt$LocalSource$throw Exception(context.stringResource(MR.strings.local_invalid_format)) + TooGenericExceptionThrown:MangaUpdates.kt$MangaUpdates$throw Throwable("Unable to login") + TooGenericExceptionThrown:ShikimoriApi.kt$ShikimoriApi$throw Exception("Too much mangas in response") + TooGenericExceptionThrown:ShikimoriInterceptor.kt$ShikimoriInterceptor$throw Exception("Not authenticated with Shikimori") + TooGenericExceptionThrown:ShizukuInstaller.kt$ShizukuInstaller$throw RuntimeException("Failed to commit install session $sessionId") + TooGenericExceptionThrown:ShizukuInstaller.kt$ShizukuInstaller$throw RuntimeException("Failed to create install session") + TooGenericExceptionThrown:ShizukuInstaller.kt$ShizukuInstaller$throw RuntimeException("Failed to write APK to session $sessionId") + TooGenericExceptionThrown:TrackInfoDialog.kt$TrackInfoDialogHomeScreen.Model$throw Exception() + TooManyFunctions:AndroidDatabaseHandler.kt$AndroidDatabaseHandler : DatabaseHandler + TooManyFunctions:Anilist.kt$Anilist : BaseTrackerDeletableTracker + TooManyFunctions:AnilistApi.kt$AnilistApi + TooManyFunctions:Bangumi.kt$Bangumi : BaseTracker + TooManyFunctions:BaseTracker.kt$BaseTracker : Tracker + TooManyFunctions:BrowseSourceScreenModel.kt$BrowseSourceScreenModel : StateScreenModel + TooManyFunctions:CategoryRepositoryImpl.kt$CategoryRepositoryImpl : CategoryRepository + TooManyFunctions:ChapterRepository.kt$ChapterRepository + TooManyFunctions:ChapterRepositoryImpl.kt$ChapterRepositoryImpl : ChapterRepository + TooManyFunctions:ChapterTransition.kt$eu.kanade.presentation.reader.ChapterTransition.kt + TooManyFunctions:DohProviders.kt$eu.kanade.tachiyomi.network.DohProviders.kt + TooManyFunctions:DownloadCache.kt$DownloadCache + TooManyFunctions:DownloadManager.kt$DownloadManager + TooManyFunctions:DownloadPreferences.kt$DownloadPreferences + TooManyFunctions:DownloadProvider.kt$DownloadProvider + TooManyFunctions:DownloadQueueScreenModel.kt$DownloadQueueScreenModel : ScreenModel + TooManyFunctions:Downloader.kt$Downloader + TooManyFunctions:EpubFile.kt$EpubFile : Closeable + TooManyFunctions:ExtensionLoader.kt$ExtensionLoader + TooManyFunctions:ExtensionManager.kt$ExtensionManager + TooManyFunctions:ExtensionsScreenModel.kt$ExtensionsScreenModel : StateScreenModel + TooManyFunctions:HttpSource.kt$HttpSource : CatalogueSource + TooManyFunctions:ImageUtil.kt$ImageUtil + TooManyFunctions:Kavita.kt$Kavita : BaseTrackerEnhancedTracker + TooManyFunctions:Kitsu.kt$Kitsu : BaseTrackerDeletableTracker + TooManyFunctions:Komga.kt$Komga : BaseTrackerEnhancedTracker + TooManyFunctions:LibraryPreferences.kt$LibraryPreferences + TooManyFunctions:LibraryScreenModel.kt$LibraryScreenModel : StateScreenModel + TooManyFunctions:LocalSource.kt$LocalSource : CatalogueSourceUnmeteredSource + TooManyFunctions:MangaCoverFetcher.kt$MangaCoverFetcher : Fetcher + TooManyFunctions:MangaRepository.kt$MangaRepository + TooManyFunctions:MangaRepositoryImpl.kt$MangaRepositoryImpl : MangaRepository + TooManyFunctions:MangaRestorer.kt$MangaRestorer + TooManyFunctions:MangaScreenModel.kt$MangaScreenModel : StateScreenModel + TooManyFunctions:MangaUpdates.kt$MangaUpdates : BaseTrackerDeletableTracker + TooManyFunctions:MyAnimeList.kt$MyAnimeList : BaseTrackerDeletableTracker + TooManyFunctions:MyAnimeListApi.kt$MyAnimeListApi + TooManyFunctions:NotificationReceiver.kt$NotificationReceiver : BroadcastReceiver + TooManyFunctions:NotificationReceiver.kt$NotificationReceiver$Companion + TooManyFunctions:PagerPageHolder.kt$PagerPageHolder : ReaderPageImageViewPositionableView + TooManyFunctions:PagerViewer.kt$PagerViewer : Viewer + TooManyFunctions:ParsedHttpSource.kt$ParsedHttpSource : HttpSource + TooManyFunctions:ReaderActivity.kt$ReaderActivity : BaseActivity + TooManyFunctions:ReaderPageImageView.kt$ReaderPageImageView : FrameLayout + TooManyFunctions:ReaderPreferences.kt$ReaderPreferences + TooManyFunctions:ReaderViewModel.kt$ReaderViewModel : ViewModel + TooManyFunctions:SettingsItems.kt$tachiyomi.presentation.core.components.SettingsItems.kt + TooManyFunctions:SharedPreferencesDataStore.kt$SharedPreferencesDataStore : PreferenceDataStore + TooManyFunctions:Shikimori.kt$Shikimori : BaseTrackerDeletableTracker + TooManyFunctions:SourcePreferences.kt$SourcePreferences + TooManyFunctions:Suwayomi.kt$Suwayomi : BaseTrackerEnhancedTracker + TooManyFunctions:Tracker.kt$Tracker + TooManyFunctions:UpdatesScreenModel.kt$UpdatesScreenModel : StateScreenModel + TooManyFunctions:WebtoonPageHolder.kt$WebtoonPageHolder : WebtoonBaseHolder + TooManyFunctions:WebtoonRecyclerView.kt$WebtoonRecyclerView : RecyclerView + TooManyFunctions:WebtoonViewer.kt$WebtoonViewer : Viewer + TopLevelPropertyNaming:App.kt$private const val ACTION_DISABLE_INCOGNITO_MODE = "tachi.action.DISABLE_INCOGNITO_MODE" + TopLevelPropertyNaming:AppBar.kt$const val SEARCH_DEBOUNCE_MILLIS = 250L + TopLevelPropertyNaming:BackupCreateJob.kt$private const val IS_AUTO_BACKUP_KEY = "is_auto_backup" // Boolean + TopLevelPropertyNaming:BackupCreateJob.kt$private const val LOCATION_URI_KEY = "location_uri" // String + TopLevelPropertyNaming:BackupCreateJob.kt$private const val OPTIONS_KEY = "options" // BooleanArray + TopLevelPropertyNaming:BackupCreateJob.kt$private const val TAG_AUTO = "BackupCreator" + TopLevelPropertyNaming:BackupCreateJob.kt$private const val TAG_MANUAL = "$TAG_AUTO:manual" + TopLevelPropertyNaming:BackupRestoreJob.kt$private const val LOCATION_URI_KEY = "location_uri" // String + TopLevelPropertyNaming:BackupRestoreJob.kt$private const val OPTIONS_KEY = "options" // BooleanArray + TopLevelPropertyNaming:BackupRestoreJob.kt$private const val SYNC_KEY = "sync" // Boolean + TopLevelPropertyNaming:ChapterCache.kt$/** Application cache version. */ private const val PARAMETER_APP_VERSION = 1 + TopLevelPropertyNaming:ChapterCache.kt$/** The maximum number of bytes this cache should use to store. */ private const val PARAMETER_CACHE_SIZE = 100L * 1024 * 1024 + TopLevelPropertyNaming:ChapterCache.kt$/** The number of values per cache entry. Must be positive. */ private const val PARAMETER_VALUE_COUNT = 1 + TopLevelPropertyNaming:ColorFilterPage.kt$private const val ALPHA_MASK: Long = 0xFF000000 + TopLevelPropertyNaming:ColorFilterPage.kt$private const val BLUE_MASK: Long = 0x000000FF + TopLevelPropertyNaming:ColorFilterPage.kt$private const val GREEN_MASK: Long = 0x0000FF00 + TopLevelPropertyNaming:ColorFilterPage.kt$private const val RED_MASK: Long = 0x00FF0000 + TopLevelPropertyNaming:ComicInfo.kt$const val COMIC_INFO_FILE = "ComicInfo.xml" + TopLevelPropertyNaming:DatabaseAdapter.kt$private const val LIST_OF_STRINGS_SEPARATOR = ", " + TopLevelPropertyNaming:DateExtensions.kt$private const val MILLISECONDS_IN_DAY = 86_400_000L + TopLevelPropertyNaming:DisplayExtensions.kt$// make sure icons on the nav rail fit private const val TABLET_UI_MIN_SCREEN_WIDTH_LANDSCAPE_DP = 600 + TopLevelPropertyNaming:DisplayExtensions.kt$// some tablets have screen width like 711dp = 1600px / 2.25 private const val TABLET_UI_MIN_SCREEN_WIDTH_PORTRAIT_DP = 700 + TopLevelPropertyNaming:DisplayExtensions.kt$private const val TABLET_UI_REQUIRED_SCREEN_WIDTH_DP = 720 + TopLevelPropertyNaming:DohProviders.kt$/** * Based on https://github.com/square/okhttp/blob/ef5d0c83f7bbd3a0c0534e7ca23cbc4ee7550f3b/okhttp-dnsoverhttps/src/test/java/okhttp3/dnsoverhttps/DohProviders.java */ const val PREF_DOH_CLOUDFLARE = 1 + TopLevelPropertyNaming:DohProviders.kt$const val PREF_DOH_360 = 7 + TopLevelPropertyNaming:DohProviders.kt$const val PREF_DOH_ADGUARD = 3 + TopLevelPropertyNaming:DohProviders.kt$const val PREF_DOH_ALIDNS = 5 + TopLevelPropertyNaming:DohProviders.kt$const val PREF_DOH_CONTROLD = 10 + TopLevelPropertyNaming:DohProviders.kt$const val PREF_DOH_DNSPOD = 6 + TopLevelPropertyNaming:DohProviders.kt$const val PREF_DOH_GOOGLE = 2 + TopLevelPropertyNaming:DohProviders.kt$const val PREF_DOH_MULLVAD = 9 + TopLevelPropertyNaming:DohProviders.kt$const val PREF_DOH_NJALLA = 11 + TopLevelPropertyNaming:DohProviders.kt$const val PREF_DOH_QUAD101 = 8 + TopLevelPropertyNaming:DohProviders.kt$const val PREF_DOH_QUAD9 = 4 + TopLevelPropertyNaming:DohProviders.kt$const val PREF_DOH_SHECAN = 12 + TopLevelPropertyNaming:Downloader.kt$// Arbitrary minimum required space to start a download: 200 MB private const val MIN_DISK_SPACE = 200L * 1024 * 1024 + TopLevelPropertyNaming:ExtensionInstallActivity.kt$private const val INSTALL_REQUEST_CODE = 500 + TopLevelPropertyNaming:GLUtil.kt$// Safe minimum default size private const val IMAGE_MAX_BITMAP_DIMENSION = 2048 + TopLevelPropertyNaming:GuidesStep.kt$const val GETTING_STARTED_URL = "https://mihon.app/docs/guides/getting-started" + TopLevelPropertyNaming:KomgaApi.kt$private const val READLIST_API = "/api/v1/readlists" + TopLevelPropertyNaming:LibraryUpdateNotifier.kt$private const val MANGA_PER_SOURCE_QUEUE_WARNING_THRESHOLD = 60 + TopLevelPropertyNaming:LibraryUpdateNotifier.kt$private const val NOTIF_ICON_SIZE = 192 + TopLevelPropertyNaming:LibraryUpdateNotifier.kt$private const val NOTIF_MAX_CHAPTERS = 5 + TopLevelPropertyNaming:LibraryUpdateNotifier.kt$private const val NOTIF_TITLE_MAX_LEN = 45 + TopLevelPropertyNaming:LocalCoverManager.kt$private const val DEFAULT_COVER_NAME = "cover.jpg" + TopLevelPropertyNaming:MainActivity.kt$// Splash screen private const val SPLASH_MIN_DURATION = 500 // ms + TopLevelPropertyNaming:MainActivity.kt$private const val SPLASH_EXIT_ANIM_DURATION = 400L // ms + TopLevelPropertyNaming:MainActivity.kt$private const val SPLASH_MAX_DURATION = 5000 // ms + TopLevelPropertyNaming:PackageInstallerInstaller.kt$private const val INSTALL_ACTION = "PackageInstallerInstaller.INSTALL_ACTION" + TopLevelPropertyNaming:ReaderNavigationOverlayView.kt$private const val FADE_DURATION = 1000L + TopLevelPropertyNaming:ReaderPageImageView.kt$private const val MAX_ZOOM_SCALE = 5F + TopLevelPropertyNaming:ShizukuInstaller.kt$private const val SHIZUKU_PERMISSION_REQUEST_CODE = 14045 + TopLevelPropertyNaming:StorageManager.kt$private const val AUTOMATIC_BACKUPS_PATH = "autobackup" + TopLevelPropertyNaming:StorageManager.kt$private const val DOWNLOADS_PATH = "downloads" + TopLevelPropertyNaming:StorageManager.kt$private const val LOCAL_SOURCE_PATH = "local" + TopLevelPropertyNaming:SuwayomiApi.kt$private const val ADDRESS_DEFAULT = "" + TopLevelPropertyNaming:SuwayomiApi.kt$private const val ADDRESS_TITLE = "Server URL Address" + TopLevelPropertyNaming:SuwayomiApi.kt$private const val LOGIN_DEFAULT = "" + TopLevelPropertyNaming:SuwayomiApi.kt$private const val LOGIN_TITLE = "Login (Basic Auth)" + TopLevelPropertyNaming:SuwayomiApi.kt$private const val PASSWORD_DEFAULT = "" + TopLevelPropertyNaming:SuwayomiApi.kt$private const val PASSWORD_TITLE = "Password (Basic Auth)" + TopLevelPropertyNaming:WebtoonAdapter.kt$/** * View holder type of a chapter page view. */ private const val PAGE_VIEW = 0 + TopLevelPropertyNaming:WebtoonAdapter.kt$/** * View holder type of a chapter transition view. */ private const val TRANSITION_VIEW = 1 + TopLevelPropertyNaming:WebtoonRecyclerView.kt$private const val ANIMATOR_DURATION_TIME = 200 + TopLevelPropertyNaming:WebtoonRecyclerView.kt$private const val DEFAULT_RATE = 1f + TopLevelPropertyNaming:WebtoonRecyclerView.kt$private const val MAX_SCALE_RATE = 3f + TopLevelPropertyNaming:WebtoonRecyclerView.kt$private const val MIN_RATE = 0.5f + UnstableCollections:CategoryScreen.kt$List<Category> + UnstableCollections:Commons.kt$List<Category> + UnstableCollections:Commons.kt$Set<String> + UnstableCollections:GlobalSearchCardRow.kt$List<Manga> + UnstableCollections:GlobalSearchScreen.kt$Map<CatalogueSource, SearchItemResult> + UnstableCollections:HistoryScreen.kt$List<HistoryUiModel> + UnstableCollections:LibraryComfortableGrid.kt$List<LibraryItem> + UnstableCollections:LibraryComfortableGrid.kt$List<LibraryManga> + UnstableCollections:LibraryCompactGrid.kt$List<LibraryItem> + UnstableCollections:LibraryCompactGrid.kt$List<LibraryManga> + UnstableCollections:LibraryContent.kt$List<Category> + UnstableCollections:LibraryContent.kt$List<LibraryManga> + UnstableCollections:LibraryList.kt$List<LibraryItem> + UnstableCollections:LibraryList.kt$List<LibraryManga> + UnstableCollections:LibraryPager.kt$List<LibraryManga> + UnstableCollections:LibraryTabs.kt$List<Category> + UnstableCollections:ListPreferenceWidget.kt$Map<out T, String> + UnstableCollections:MangaScreen.kt$List<ChapterList.Item> + UnstableCollections:MultiSelectListPreferenceWidget.kt$Set<String> + UnstableCollections:PreferenceScreen.kt$List<Preference> + UnstableCollections:ScanlatorFilterDialog.kt$Set<String> + UnstableCollections:SettingsDownloadScreen.kt$SettingsDownloadScreen$List<Category> + UnstableCollections:SettingsLibraryScreen.kt$SettingsLibraryScreen$List<Category> + UnstableCollections:TrackInfoDialogHome.kt$List<TrackItem> + UnstableCollections:TrackInfoDialogSelector.kt$Map<Long, StringResource?> + UnstableCollections:TriStateListDialog.kt$List<T> + UnstableCollections:UpdatesScreen.kt$List<UpdatesItem> + UnstableCollections:WebViewScreenContent.kt$Map<String, String> + UnusedParameter:UpdatesRepositoryImpl.kt$UpdatesRepositoryImpl$dateUpload: Long + UnusedPrivateMember:AppThemePreferenceWidget.kt$@PreviewLightDark @Composable private fun AppThemesListPreview() + UnusedPrivateMember:ChapterTransition.kt$@PreviewLightDark @Composable private fun TransitionTextLongTitlePreview() + UnusedPrivateMember:ChapterTransition.kt$@PreviewLightDark @Composable private fun TransitionTextNoNextPreview() + UnusedPrivateMember:ChapterTransition.kt$@PreviewLightDark @Composable private fun TransitionTextNoPreviousPreview() + UnusedPrivateMember:ChapterTransition.kt$@PreviewLightDark @Composable private fun TransitionTextPreview() + UnusedPrivateMember:ChapterTransition.kt$@PreviewLightDark @Composable private fun TransitionTextWithGapPreview() + UnusedPrivateMember:CrashScreen.kt$@PreviewLightDark @Composable private fun CrashScreenPreview() + UnusedPrivateMember:EmptyScreen.kt$@PreviewLightDark @Composable private fun NoActionPreview() + UnusedPrivateMember:EmptyScreen.kt$@PreviewLightDark @Composable private fun WithActionPreview() + UnusedPrivateMember:GuidesStep.kt$@PreviewLightDark @Composable private fun GuidesStepPreview() + UnusedPrivateMember:HistoryDialogs.kt$@PreviewLightDark @Composable private fun HistoryDeleteDialogPreview() + UnusedPrivateMember:HistoryItem.kt$@PreviewLightDark @Composable private fun HistoryItemPreviews( @PreviewParameter(HistoryWithRelationsProvider::class) historyWithRelations: HistoryWithRelations, ) + UnusedPrivateMember:InfoScreen.kt$@PreviewLightDark @Composable private fun InfoScaffoldPreview() + UnusedPrivateMember:InfoWidget.kt$@PreviewLightDark @Composable private fun InfoWidgetPreview() + UnusedPrivateMember:LibraryBadges.kt$@PreviewLightDark @Composable private fun BadgePreview() + UnusedPrivateMember:MissingChapterCountListItem.kt$@PreviewLightDark @Composable private fun Preview() + UnusedPrivateMember:ModeSelectionDialog.kt$@PreviewLightDark @Composable private fun Preview() + UnusedPrivateMember:NewUpdateScreen.kt$@PreviewLightDark @Composable private fun NewUpdateScreenPreview() + UnusedPrivateMember:OrientationSelectDialog.kt$@PreviewLightDark @Composable private fun DialogContentPreview() + UnusedPrivateMember:PageIndicatorText.kt$@PreviewLightDark @Composable private fun PageIndicatorTextPreview() + UnusedPrivateMember:PermissionStep.kt$PermissionStep$@Composable private fun SectionHeader( text: String, modifier: Modifier = Modifier, ) + UnusedPrivateMember:ReadingModeSelectDialog.kt$@PreviewLightDark @Composable private fun DialogContentPreview() + UnusedPrivateMember:SwitchPreferenceWidget.kt$@PreviewLightDark @Composable private fun SwitchPreferenceWidgetPreview() + UnusedPrivateMember:TextPreferenceWidget.kt$@PreviewLightDark @Composable private fun TextPreferenceWidgetPreview() + UnusedPrivateMember:TrackInfoDialogHome.kt$@PreviewLightDark @Composable private fun TrackInfoDialogHomePreviews( @PreviewParameter(TrackInfoDialogHomePreviewProvider::class) content: @Composable () -> Unit, ) + UnusedPrivateMember:TrackInfoDialogSelector.kt$@PreviewLightDark @Composable private fun TrackStatusSelectorPreviews() + UnusedPrivateMember:TrackLogoIcon.kt$@PreviewLightDark @Composable private fun TrackLogoIconPreviews( @PreviewParameter(TrackLogoIconPreviewProvider::class) tracker: Tracker, ) + UnusedPrivateMember:TrackerSearch.kt$@PreviewLightDark @Composable private fun TrackerSearchPreviews( @PreviewParameter(TrackerSearchPreviewProvider::class) content: @Composable () -> Unit, ) + UnusedPrivateProperty:JavaScriptEngine.kt$JavaScriptEngine$context: Context + UnusedPrivateProperty:LibraryUpdateJob.kt$LibraryUpdateJob.Companion$private const val MANGA_PER_SOURCE_QUEUE_WARNING_THRESHOLD = 60 + UnusedPrivateProperty:MainActivity.kt$MainActivity$private val sourcePreferences: SourcePreferences by injectLazy() + UnusedPrivateProperty:MainActivity.kt$MainActivity$private val uiPreferences: UiPreferences by injectLazy() + UnusedPrivateProperty:MetadataUpdateJob.kt$MetadataUpdateJob.Companion$private const val MANGA_PER_SOURCE_QUEUE_WARNING_THRESHOLD = 60 + UseCheckOrError:CatalogueSource.kt$CatalogueSource$throw IllegalStateException("Not used") + UseCheckOrError:MangaScreenModel.kt$MangaScreenModel$throw IllegalStateException() + UseCheckOrError:PackageInstallerInstaller.kt$PackageInstallerInstaller$throw IllegalStateException() + UseCheckOrError:ReadingMode.kt$ReadingMode.Companion$throw IllegalStateException("Preference value must be resolved: $preference") + UseCheckOrError:ShizukuInstaller.kt$ShizukuInstaller$throw IllegalStateException() + UseCheckOrError:Source.kt$Source$throw IllegalStateException("Not used") + UseCheckOrError:SourceFilterDialog.kt$throw IllegalStateException("Unknown TriState state: $this") + UseCheckOrError:TransactionContext.kt$TransactionElement$throw IllegalStateException("Transaction was never started or was already released") + VariableNaming:Chapter.kt$Chapter$var date_fetch: Long + VariableNaming:Chapter.kt$Chapter$var last_modified: Long + VariableNaming:Chapter.kt$Chapter$var last_page_read: Int + VariableNaming:Chapter.kt$Chapter$var manga_id: Long? + VariableNaming:Chapter.kt$Chapter$var source_order: Int + VariableNaming:LocalSource.kt$LocalSource$private val LATEST_FILTERS = FilterList(OrderBy.Latest(context)) + VariableNaming:LocalSource.kt$LocalSource$private val POPULAR_FILTERS = FilterList(OrderBy.Popular(context)) + VariableNaming:SChapter.kt$SChapter$var chapter_number: Float + VariableNaming:SChapter.kt$SChapter$var date_upload: Long + VariableNaming:SManga.kt$SManga$var thumbnail_url: String? + VariableNaming:SManga.kt$SManga$var update_strategy: UpdateStrategy + VariableNaming:Track.kt$Track$var finished_reading_date: Long + VariableNaming:Track.kt$Track$var last_chapter_read: Double + VariableNaming:Track.kt$Track$var library_id: Long? + VariableNaming:Track.kt$Track$var manga_id: Long + VariableNaming:Track.kt$Track$var remote_id: Long + VariableNaming:Track.kt$Track$var started_reading_date: Long + VariableNaming:Track.kt$Track$var total_chapters: Long + VariableNaming:Track.kt$Track$var tracker_id: Long + VariableNaming:Track.kt$Track$var tracking_url: String + VariableNaming:TrackSearch.kt$TrackSearch$var cover_url: String = "" + VariableNaming:TrackSearch.kt$TrackSearch$var publishing_status: String = "" + VariableNaming:TrackSearch.kt$TrackSearch$var publishing_type: String = "" + VariableNaming:TrackSearch.kt$TrackSearch$var start_date: String = "" + + diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml new file mode 100644 index 000000000..112fcf3f0 --- /dev/null +++ b/config/detekt/detekt.yml @@ -0,0 +1,19 @@ +naming: + FunctionNaming: + functionPattern: '[a-z][a-zA-Z0-9]*' + ignoreAnnotated: [ 'Composable' ] + TopLevelPropertyNaming: + constantPattern: '[A-Z][A-Za-z0-9]*' + +complexity: + LongParameterList: + functionThreshold: 6 + constructorThreshold: 7 + ignoreDefaultParameters: true + +style: + MagicNumber: + ignorePropertyDeclaration: true + ignoreCompanionObjectPropertyDeclaration: true + UnusedPrivateMember: + ignoreAnnotated: [ 'Preview' ] diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 86338364a..dd963d0d6 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -23,5 +23,3 @@ glance = "androidx.glance:glance-appwidget:1.0.0" accompanist-webview = { module = "com.google.accompanist:accompanist-webview", version.ref = "accompanist" } accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" } - -lintchecks = { module = "com.slack.lint.compose:compose-lint-checks", version = "1.2.0" } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 479c3b44a..576d08176 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,6 +8,8 @@ shizuku_version = "12.2.0" sqldelight = "2.0.0" sqlite = "2.4.0" voyager = "1.0.0" +detekt = "1.23.1" +detektCompose = "0.3.11" [libraries] desugar = "com.android.tools:desugar_jdk_libs:2.0.4" @@ -93,7 +95,9 @@ voyager-screenmodel = { module = "cafe.adriel.voyager:voyager-screenmodel", vers voyager-tab-navigator = { module = "cafe.adriel.voyager:voyager-tab-navigator", version.ref = "voyager" } voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", version.ref = "voyager" } -ktlint = "org.jlleitschuh.gradle:ktlint-gradle:12.0.3" +detekt-gradlePlugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" } +detekt-rules-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } +detekt-rules-compose = { module = "io.nlopez.compose.rules:detekt", version.ref = "detektCompose" } [bundles] okhttp = ["okhttp-core", "okhttp-logging", "okhttp-brotli", "okhttp-dnsoverhttps"] diff --git a/presentation-core/build.gradle.kts b/presentation-core/build.gradle.kts index f36ca3857..59a97d0fe 100644 --- a/presentation-core/build.gradle.kts +++ b/presentation-core/build.gradle.kts @@ -36,7 +36,6 @@ dependencies { debugImplementation(compose.ui.tooling) implementation(compose.ui.tooling.preview) implementation(compose.ui.util) - lintChecks(compose.lintchecks) implementation(kotlinx.immutables) } diff --git a/presentation-widget/build.gradle.kts b/presentation-widget/build.gradle.kts index d3e56e373..21a0a4bbd 100644 --- a/presentation-widget/build.gradle.kts +++ b/presentation-widget/build.gradle.kts @@ -27,7 +27,6 @@ dependencies { api(projects.i18n) implementation(compose.glance) - lintChecks(compose.lintchecks) implementation(kotlinx.immutables) From 5dc6501688d541da990eaa1f687d93dc9e18e20e Mon Sep 17 00:00:00 2001 From: Theodoro Loureiro mota Date: Mon, 29 Jan 2024 03:56:29 -0300 Subject: [PATCH 026/212] Fix #197 - Scroll crashing (#272) Updating compose bom. --- gradle/compose.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index dd963d0d6..e851411b2 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,6 +1,6 @@ [versions] compiler = "1.5.8" -compose-bom = "2023.12.00-alpha04" +compose-bom = "2024.01.00-alpha01" accompanist = "0.33.2-alpha" [libraries] From 47e542152788e587f5f77dea4b2676683f2fba47 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 29 Jan 2024 13:25:01 +0600 Subject: [PATCH 027/212] Upgrade `combose-bom` to latest --- .../manga/components/ChapterDownloadIndicator.kt | 4 ++-- .../presentation/manga/components/MangaBottomActionMenu.kt | 4 ++-- gradle/compose.versions.toml | 4 ++-- .../tachiyomi/presentation/core/components/AdaptiveSheet.kt | 5 ++++- .../java/tachiyomi/presentation/core/components/Pager.kt | 2 +- .../presentation/core/components/material/Surface.kt | 4 ++-- 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt index 162aa92a3..50ddadf0c 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt @@ -10,7 +10,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.outlined.ArrowDownward import androidx.compose.material.icons.outlined.ErrorOutline -import androidx.compose.material.ripple.rememberRipple +import androidx.compose.material.ripple import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon @@ -256,7 +256,7 @@ private fun Modifier.commonClickable( onClick = onClick, role = Role.Button, interactionSource = remember { MutableInteractionSource() }, - indication = rememberRipple( + indication = ripple( bounded = false, radius = IconButtonTokens.StateLayerSize / 2, ), diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt index 8d409dab8..d67a406e7 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt @@ -30,7 +30,7 @@ import androidx.compose.material.icons.outlined.Delete import androidx.compose.material.icons.outlined.DoneAll import androidx.compose.material.icons.outlined.Download import androidx.compose.material.icons.outlined.RemoveDone -import androidx.compose.material.ripple.rememberRipple +import androidx.compose.material.ripple import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface @@ -192,7 +192,7 @@ private fun RowScope.Button( .weight(animatedWeight) .combinedClickable( interactionSource = remember { MutableInteractionSource() }, - indication = rememberRipple(bounded = false), + indication = ripple(bounded = false), onLongClick = onLongClick, onClick = onClick, ), diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index e851411b2..778c43c47 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,7 +1,7 @@ [versions] compiler = "1.5.8" -compose-bom = "2024.01.00-alpha01" -accompanist = "0.33.2-alpha" +compose-bom = "2024.01.00-alpha03" +accompanist = "0.34.0" [libraries] activity = "androidx.activity:activity-compose:1.8.2" 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 515faec14..568332ca7 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,6 +3,7 @@ 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.animation.rememberSplineBasedDecay import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.AnchoredDraggableState import androidx.compose.foundation.gestures.DraggableAnchors @@ -110,12 +111,14 @@ fun AdaptiveSheet( } } } else { + val decayAnimationSpec = rememberSplineBasedDecay() val anchoredDraggableState = remember { AnchoredDraggableState( initialValue = 1, - animationSpec = sheetAnimationSpec, positionalThreshold = { with(density) { 56.dp.toPx() } }, velocityThreshold = { with(density) { 125.dp.toPx() } }, + snapAnimationSpec = sheetAnimationSpec, + decayAnimationSpec = decayAnimationSpec, ) } val internalOnDismissRequest = { diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/Pager.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/Pager.kt index 521f988b8..fb3cbdf74 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/Pager.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/Pager.kt @@ -40,7 +40,7 @@ fun HorizontalPager( modifier = modifier, contentPadding = contentPadding, pageSize = pageSize, - beyondBoundsPageCount = beyondBoundsPageCount, + outOfBoundsPageCount = beyondBoundsPageCount, pageSpacing = pageSpacing, verticalAlignment = verticalAlignment, flingBehavior = PagerDefaults.flingBehavior( diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Surface.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Surface.kt index 71866b61d..e472e5127 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Surface.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Surface.kt @@ -6,7 +6,7 @@ import androidx.compose.foundation.border import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box -import androidx.compose.material.ripple.rememberRipple +import androidx.compose.material.ripple import androidx.compose.material3.ColorScheme import androidx.compose.material3.LocalAbsoluteTonalElevation import androidx.compose.material3.LocalContentColor @@ -70,7 +70,7 @@ fun Surface( ) .combinedClickable( interactionSource = interactionSource, - indication = rememberRipple(), + indication = ripple(), enabled = enabled, role = Role.Button, onLongClick = onLongClick, From b71c793fad46b91309da5d505ff49f99847b4f68 Mon Sep 17 00:00:00 2001 From: MajorTanya <39014446+MajorTanya@users.noreply.github.com> Date: Mon, 29 Jan 2024 08:57:23 +0100 Subject: [PATCH 028/212] Add click-to-copy extension debug info (#271) * Add click-to-copy extension debug info Adds some debug info about an extension to the user's clipboard when tapping the logo/name/package name area at the top of the details screen. Modeled after the debug info from the About screen. Closes #168. * Fix linting failure * Slight cleanup * Address detekt --------- Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- .../browse/ExtensionDetailsScreen.kt | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt index e55576db8..807b68b20 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt @@ -53,6 +53,7 @@ import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.ui.browse.extension.details.ExtensionDetailsScreenModel import eu.kanade.tachiyomi.util.system.LocaleHelper +import eu.kanade.tachiyomi.util.system.copyToClipboard import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import tachiyomi.i18n.MR @@ -224,7 +225,31 @@ private fun DetailsHeader( end = MaterialTheme.padding.medium, top = MaterialTheme.padding.medium, bottom = MaterialTheme.padding.small, - ), + ) + .clickable { + val extDebugInfo = buildString { + append( + """ + Extension name: ${extension.name} (lang: ${extension.lang}; package: ${extension.pkgName}) + Extension version: ${extension.versionName} (lib: ${extension.libVersion}; version code: ${extension.versionCode}) + NSFW: ${extension.isNsfw} + """.trimIndent() + ) + + if (extension is Extension.Installed) { + append("\n\n") + append( + """ + Update available: ${extension.hasUpdate} + Obsolete: ${extension.isObsolete} + Shared: ${extension.isShared} + Repository: ${extension.repoUrl} + """.trimIndent() + ) + } + } + context.copyToClipboard("Extension Debug information", extDebugInfo) + }, horizontalAlignment = Alignment.CenterHorizontally, ) { ExtensionIcon( From 4811cf07cdbf052dc3ddd2916e56ed81b6dc652a Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 29 Jan 2024 14:13:55 +0600 Subject: [PATCH 029/212] Address build warning in `:i18n` And small cleanup --- i18n/build.gradle.kts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/i18n/build.gradle.kts b/i18n/build.gradle.kts index 366e19ad3..9ed5a6cbf 100644 --- a/i18n/build.gradle.kts +++ b/i18n/build.gradle.kts @@ -6,14 +6,18 @@ plugins { kotlin { androidTarget() + + applyDefaultHierarchyTemplate() + sourceSets { val commonMain by getting { dependencies { api(libs.moko.core) } } - val androidMain by getting { - dependsOn(commonMain) // https://github.com/icerockdev/moko-resources/issues/562 + + androidMain { + dependsOn(commonMain) // https://github.com/icerockdev/moko-resources/issues/562 } } } From f03f998b218219254645b0c2c5d591af0240ac76 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 29 Jan 2024 14:31:31 +0600 Subject: [PATCH 030/212] [skip ci] Delete ic_launcher-web.png --- app/src/main/ic_launcher-web.png | Bin 21978 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 app/src/main/ic_launcher-web.png diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png deleted file mode 100644 index 6c7f4495678700efd3700965fa79a7c4634cfaf4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21978 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 R!sb7aOD5NhE6!sg{s$NGhf)9l From aa498360db90350f2642e6320dc55e7d474df1fd Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 29 Jan 2024 15:11:28 +0600 Subject: [PATCH 031/212] Move `:core` to `:core:common` --- app/build.gradle.kts | 2 +- .../eu/kanade/core/preference/CheckboxState.kt | 2 +- .../core/preference/PreferenceMutableState.kt | 2 +- .../eu/kanade/domain/base/BasePreferences.kt | 4 ++-- .../base/ExtensionInstallerPreference.kt | 6 +++--- .../domain/chapter/interactor/SetReadStatus.kt | 4 ++-- .../download/interactor/DeleteDownload.kt | 2 +- .../interactor/CreateExtensionRepo.kt | 2 +- .../interactor/DeleteExtensionRepo.kt | 2 +- .../extension/interactor/TrustExtension.kt | 2 +- .../java/eu/kanade/domain/manga/model/Manga.kt | 2 +- .../interactor/GetSourcesWithFavoriteCount.kt | 2 +- .../domain/source/interactor/ToggleLanguage.kt | 2 +- .../domain/source/interactor/ToggleSource.kt | 2 +- .../source/interactor/ToggleSourcePin.kt | 2 +- .../domain/source/service/SourcePreferences.kt | 6 +++--- .../domain/track/interactor/AddTracks.kt | 6 +++--- .../interactor/SyncChapterProgressWithTrack.kt | 2 +- .../domain/track/interactor/TrackChapter.kt | 4 ++-- .../track/service/DelayedTrackingUpdateJob.kt | 4 ++-- .../domain/track/service/TrackPreferences.kt | 4 ++-- .../domain/track/store/DelayedTrackingStore.kt | 2 +- .../java/eu/kanade/domain/ui/UiPreferences.kt | 4 ++-- .../presentation/browse/BrowseSourceScreen.kt | 2 +- .../browse/components/BrowseIcons.kt | 2 +- .../category/CategoryExtensions.kt | 2 +- .../category/components/CategoryDialogs.kt | 2 +- .../library/DeleteLibraryMangaDialog.kt | 2 +- .../library/LibrarySettingsDialog.kt | 2 +- .../manga/ChapterSettingsDialog.kt | 2 +- .../eu/kanade/presentation/more/MoreScreen.kt | 2 +- .../presentation/more/settings/Preference.kt | 2 +- .../settings/screen/SettingsAdvancedScreen.kt | 6 +++--- .../settings/screen/SettingsBrowseScreen.kt | 2 +- .../more/settings/screen/SettingsDataScreen.kt | 14 +++++++------- .../settings/screen/SettingsSecurityScreen.kt | 2 +- .../settings/screen/SettingsTrackingScreen.kt | 4 ++-- .../more/settings/screen/about/AboutScreen.kt | 6 +++--- .../screen/advanced/ClearDatabaseScreen.kt | 6 +++--- .../screen/appearance/AppLanguageScreen.kt | 2 +- .../screen/browse/ExtensionReposScreenModel.kt | 2 +- .../widget/AppThemePreferenceWidget.kt | 2 +- .../reader/settings/ColorFilterPage.kt | 2 +- .../presentation/util/ExceptionFormatter.kt | 2 +- .../eu/kanade/presentation/util/TimeUtils.kt | 2 +- app/src/main/java/eu/kanade/tachiyomi/App.kt | 4 ++-- .../main/java/eu/kanade/tachiyomi/AppInfo.kt | 2 +- .../java/eu/kanade/tachiyomi/Migrations.kt | 4 ++-- .../tachiyomi/crash/GlobalExceptionHandler.kt | 2 +- .../tachiyomi/data/backup/BackupNotifier.kt | 6 +++--- .../data/backup/create/BackupCreateJob.kt | 2 +- .../data/backup/create/BackupCreator.kt | 4 ++-- .../create/creators/PreferenceBackupCreator.kt | 4 ++-- .../data/backup/restore/BackupRestoreJob.kt | 4 ++-- .../data/backup/restore/BackupRestorer.kt | 2 +- .../restore/restorers/PreferenceRestorer.kt | 4 ++-- .../tachiyomi/data/cache/ChapterCache.kt | 2 +- .../tachiyomi/data/coil/MangaCoverFetcher.kt | 2 +- .../data/coil/TachiyomiImageDecoder.kt | 2 +- .../tachiyomi/data/download/DownloadCache.kt | 10 +++++----- .../tachiyomi/data/download/DownloadManager.kt | 8 ++++---- .../data/download/DownloadNotifier.kt | 2 +- .../data/download/DownloadProvider.kt | 6 +++--- .../tachiyomi/data/download/Downloader.kt | 14 +++++++------- .../tachiyomi/data/library/LibraryUpdateJob.kt | 8 ++++---- .../data/library/LibraryUpdateNotifier.kt | 8 ++++---- .../data/library/MetadataUpdateJob.kt | 4 ++-- .../data/notification/NotificationHandler.kt | 2 +- .../data/notification/NotificationReceiver.kt | 4 ++-- .../data/notification/Notifications.kt | 2 +- .../kanade/tachiyomi/data/saver/ImageSaver.kt | 6 +++--- .../kanade/tachiyomi/data/track/BaseTracker.kt | 6 +++--- .../tachiyomi/data/track/anilist/AnilistApi.kt | 2 +- .../tachiyomi/data/track/bangumi/BangumiApi.kt | 2 +- .../tachiyomi/data/track/kavita/KavitaApi.kt | 4 ++-- .../tachiyomi/data/track/kitsu/KitsuApi.kt | 2 +- .../tachiyomi/data/track/komga/KomgaApi.kt | 4 ++-- .../data/track/mangaupdates/MangaUpdatesApi.kt | 2 +- .../data/track/myanimelist/MyAnimeListApi.kt | 2 +- .../data/track/shikimori/ShikimoriApi.kt | 2 +- .../data/track/suwayomi/SuwayomiApi.kt | 2 +- .../tachiyomi/data/updater/AppUpdateChecker.kt | 2 +- .../data/updater/AppUpdateDownloadJob.kt | 4 ++-- .../data/updater/AppUpdateNotifier.kt | 2 +- .../java/eu/kanade/tachiyomi/di/AppModule.kt | 4 ++-- .../eu/kanade/tachiyomi/di/PreferenceModule.kt | 6 +++--- .../tachiyomi/extension/ExtensionManager.kt | 6 +++--- .../tachiyomi/extension/api/ExtensionApi.kt | 8 ++++---- .../extension/api/ExtensionUpdateNotifier.kt | 2 +- .../installer/PackageInstallerInstaller.kt | 2 +- .../extension/installer/ShizukuInstaller.kt | 2 +- .../extension/util/ExtensionInstallReceiver.kt | 4 ++-- .../extension/util/ExtensionInstallService.kt | 4 ++-- .../extension/util/ExtensionInstaller.kt | 4 ++-- .../extension/util/ExtensionLoader.kt | 2 +- .../browse/extension/ExtensionFilterScreen.kt | 2 +- .../extension/ExtensionFilterScreenModel.kt | 2 +- .../browse/extension/ExtensionsScreenModel.kt | 2 +- .../details/ExtensionDetailsScreenModel.kt | 2 +- .../migration/manga/MigrateMangaScreenModel.kt | 2 +- .../browse/migration/search/MigrateDialog.kt | 8 ++++---- .../migration/search/SourceSearchScreen.kt | 2 +- .../sources/MigrateSourceScreenModel.kt | 4 ++-- .../ui/browse/source/SourcesScreenModel.kt | 4 ++-- .../browse/source/browse/BrowseSourceScreen.kt | 4 ++-- .../source/browse/BrowseSourceScreenModel.kt | 6 +++--- .../browse/source/browse/SourceFilterDialog.kt | 2 +- .../ui/deeplink/DeepLinkScreenModel.kt | 2 +- .../ui/download/DownloadQueueScreen.kt | 2 +- .../tachiyomi/ui/history/HistoryScreenModel.kt | 6 +++--- .../kanade/tachiyomi/ui/history/HistoryTab.kt | 2 +- .../tachiyomi/ui/library/LibraryScreenModel.kt | 12 ++++++------ .../ui/library/LibrarySettingsScreenModel.kt | 8 ++++---- .../kanade/tachiyomi/ui/library/LibraryTab.kt | 4 ++-- .../kanade/tachiyomi/ui/main/MainActivity.kt | 6 +++--- .../ui/manga/MangaCoverScreenModel.kt | 10 +++++----- .../kanade/tachiyomi/ui/manga/MangaScreen.kt | 6 +++--- .../tachiyomi/ui/manga/MangaScreenModel.kt | 18 +++++++++--------- .../ui/manga/track/TrackInfoDialog.kt | 10 +++++----- .../eu/kanade/tachiyomi/ui/more/MoreTab.kt | 2 +- .../tachiyomi/ui/reader/ReaderActivity.kt | 12 ++++++------ .../ui/reader/ReaderNavigationOverlayView.kt | 2 +- .../tachiyomi/ui/reader/ReaderViewModel.kt | 14 +++++++------- .../tachiyomi/ui/reader/SaveImageNotifier.kt | 2 +- .../ui/reader/loader/ChapterLoader.kt | 8 ++++---- .../ui/reader/loader/DirectoryPageLoader.kt | 2 +- .../ui/reader/loader/DownloadPageLoader.kt | 2 +- .../ui/reader/loader/HttpPageLoader.kt | 4 ++-- .../ui/reader/loader/RarPageLoader.kt | 2 +- .../ui/reader/loader/ZipPageLoader.kt | 2 +- .../tachiyomi/ui/reader/model/ReaderChapter.kt | 2 +- .../ui/reader/setting/ReaderPreferences.kt | 4 ++-- .../tachiyomi/ui/reader/viewer/ViewerConfig.kt | 2 +- .../ui/reader/viewer/pager/PagerPageHolder.kt | 10 +++++----- .../viewer/pager/PagerTransitionHolder.kt | 2 +- .../ui/reader/viewer/pager/PagerViewer.kt | 2 +- .../reader/viewer/pager/PagerViewerAdapter.kt | 2 +- .../reader/viewer/webtoon/WebtoonPageHolder.kt | 10 +++++----- .../viewer/webtoon/WebtoonTransitionHolder.kt | 2 +- .../ui/reader/viewer/webtoon/WebtoonViewer.kt | 2 +- .../tachiyomi/ui/security/UnlockActivity.kt | 4 ++-- .../ui/setting/track/TrackLoginActivity.kt | 2 +- .../tachiyomi/ui/stats/StatsScreenModel.kt | 2 +- .../tachiyomi/ui/updates/UpdatesScreenModel.kt | 6 +++--- .../kanade/tachiyomi/ui/updates/UpdatesTab.kt | 2 +- .../tachiyomi/ui/webview/WebViewActivity.kt | 2 +- .../tachiyomi/ui/webview/WebViewScreenModel.kt | 2 +- .../eu/kanade/tachiyomi/util/CrashLogUtil.kt | 4 ++-- .../tachiyomi/util/lang/DateExtensions.kt | 4 ++-- .../tachiyomi/util/system/AuthenticatorUtil.kt | 2 +- .../tachiyomi/util/system/ContextExtensions.kt | 4 ++-- .../tachiyomi/util/system/IntentExtensions.kt | 2 +- .../tachiyomi/util/system/LocaleHelper.kt | 2 +- .../util/system/WorkManagerExtensions.kt | 2 +- core/{ => common}/.gitignore | 0 core/{ => common}/build.gradle.kts | 2 +- core/{ => common}/src/main/AndroidManifest.xml | 0 .../core/security/SecurityPreferences.kt | 6 +++--- .../tachiyomi/network/AndroidCookieJar.kt | 0 .../kanade/tachiyomi/network/DohProviders.kt | 0 .../tachiyomi/network/JavaScriptEngine.kt | 2 +- .../kanade/tachiyomi/network/NetworkHelper.kt | 0 .../tachiyomi/network/NetworkPreferences.kt | 4 ++-- .../tachiyomi/network/OkHttpExtensions.kt | 0 .../tachiyomi/network/ProgressListener.kt | 0 .../tachiyomi/network/ProgressResponseBody.kt | 0 .../eu/kanade/tachiyomi/network/Requests.kt | 0 .../interceptor/CloudflareInterceptor.kt | 2 +- .../interceptor/IgnoreGzipInterceptor.kt | 0 .../interceptor/RateLimitInterceptor.kt | 0 .../SpecificHostRateLimitInterceptor.kt | 0 .../UncaughtExceptionInterceptor.kt | 0 .../interceptor/UserAgentInterceptor.kt | 0 .../network/interceptor/WebViewInterceptor.kt | 2 +- .../eu/kanade/tachiyomi/util/lang/Hash.kt | 0 .../tachiyomi/util/lang/StringExtensions.kt | 0 .../kanade/tachiyomi/util/storage/DiskUtil.kt | 0 .../kanade/tachiyomi/util/storage/EpubFile.kt | 0 .../tachiyomi/util/system/DensityExtensions.kt | 0 .../kanade/tachiyomi/util/system/DeviceUtil.kt | 2 +- .../tachiyomi/util/system/ToastExtensions.kt | 2 +- .../util/system/WebViewClientCompat.kt | 0 .../tachiyomi/util/system/WebViewUtil.kt | 2 +- .../kotlin/tachiyomi/core/common}/Constants.kt | 2 +- .../tachiyomi/core/common}/i18n/Localize.kt | 2 +- .../common}/preference/AndroidPreference.kt | 4 ++-- .../preference/AndroidPreferenceStore.kt | 16 ++++++++-------- .../core/common}/preference/CheckboxState.kt | 2 +- .../preference/InMemoryPreferenceStore.kt | 2 +- .../core/common}/preference/Preference.kt | 6 +++--- .../core/common}/preference/PreferenceStore.kt | 2 +- .../core/common}/preference/TriState.kt | 2 +- .../storage/AndroidStorageFolderProvider.kt | 4 ++-- .../core/common}/storage/FolderProvider.kt | 2 +- .../core/common}/storage/UniFileExtensions.kt | 2 +- .../common}/storage/UniFileTempFileManager.kt | 2 +- .../common}/util/lang/BooleanExtensions.kt | 2 +- .../common}/util/lang/CoroutinesExtensions.kt | 2 +- .../common}/util/lang/RxCoroutineBridge.kt | 2 +- .../core/common}/util/lang/SortUtil.kt | 2 +- .../core/common}/util/system/ImageUtil.kt | 2 +- .../common}/util/system/LogcatExtensions.kt | 2 +- data/build.gradle.kts | 2 +- .../data/chapter/ChapterRepositoryImpl.kt | 4 ++-- .../data/history/HistoryRepositoryImpl.kt | 2 +- .../data/manga/MangaRepositoryImpl.kt | 2 +- .../data/source/SourcePagingSource.kt | 2 +- domain/build.gradle.kts | 2 +- .../domain/backup/service/BackupPreferences.kt | 4 ++-- .../interactor/CreateCategoryWithName.kt | 4 ++-- .../category/interactor/DeleteCategory.kt | 4 ++-- .../category/interactor/RenameCategory.kt | 4 ++-- .../category/interactor/ReorderCategory.kt | 4 ++-- .../category/interactor/SetMangaCategories.kt | 2 +- .../category/interactor/UpdateCategory.kt | 2 +- .../domain/chapter/interactor/GetChapter.kt | 2 +- .../chapter/interactor/GetChaptersByMangaId.kt | 2 +- .../interactor/SetMangaDefaultChapterFlags.kt | 2 +- .../domain/chapter/interactor/UpdateChapter.kt | 2 +- .../domain/chapter/service/ChapterSort.kt | 2 +- .../download/service/DownloadPreferences.kt | 2 +- .../library/service/LibraryPreferences.kt | 8 ++++---- .../domain/manga/interactor/GetManga.kt | 2 +- .../java/tachiyomi/domain/manga/model/Manga.kt | 2 +- .../tachiyomi/domain/manga/model/TriState.kt | 2 +- .../interactor/GetApplicationRelease.kt | 4 ++-- .../storage/service/StoragePreferences.kt | 6 +++--- .../domain/track/interactor/DeleteTrack.kt | 2 +- .../domain/track/interactor/GetTracks.kt | 2 +- .../domain/track/interactor/InsertTrack.kt | 2 +- .../interactor/GetApplicationReleaseTest.kt | 4 ++-- presentation-core/build.gradle.kts | 2 +- .../core/components/SettingsItems.kt | 6 +++--- .../presentation/core/i18n/Localize.kt | 4 ++-- .../presentation/core/util/Preference.kt | 2 +- presentation-widget/build.gradle.kts | 2 +- .../widget/BaseUpdatesGridGlanceWidget.kt | 2 +- .../presentation/widget/WidgetManager.kt | 2 +- .../widget/components/LockedWidget.kt | 2 +- .../widget/components/UpdatesWidget.kt | 2 +- settings.gradle.kts | 12 ++++++------ source-api/build.gradle.kts | 2 +- .../eu/kanade/tachiyomi/util/RxExtension.kt | 2 +- .../kanade/tachiyomi/source/CatalogueSource.kt | 2 +- .../tachiyomi/source/online/HttpSource.kt | 2 +- source-local/build.gradle.kts | 2 +- .../tachiyomi/source/local/LocalSource.kt | 14 +++++++------- .../tachiyomi/source/local/filter/OrderBy.kt | 2 +- .../source/local/image/LocalCoverManager.kt | 4 ++-- .../tachiyomi/source/local/io/Archive.kt | 2 +- .../kotlin/tachiyomi/source/local/io/Format.kt | 2 +- 251 files changed, 416 insertions(+), 416 deletions(-) rename core/{ => common}/.gitignore (100%) rename core/{ => common}/build.gradle.kts (95%) rename core/{ => common}/src/main/AndroidManifest.xml (100%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/core/security/SecurityPreferences.kt (86%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/network/AndroidCookieJar.kt (100%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/network/DohProviders.kt (100%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/network/JavaScriptEngine.kt (92%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/network/NetworkHelper.kt (100%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/network/NetworkPreferences.kt (85%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/network/OkHttpExtensions.kt (100%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/network/ProgressListener.kt (100%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/network/ProgressResponseBody.kt (100%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/network/Requests.kt (100%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt (99%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/network/interceptor/IgnoreGzipInterceptor.kt (100%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/network/interceptor/RateLimitInterceptor.kt (100%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/network/interceptor/SpecificHostRateLimitInterceptor.kt (100%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/network/interceptor/UncaughtExceptionInterceptor.kt (100%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/network/interceptor/UserAgentInterceptor.kt (100%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/network/interceptor/WebViewInterceptor.kt (98%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/util/lang/Hash.kt (100%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/util/lang/StringExtensions.kt (100%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/util/storage/DiskUtil.kt (100%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/util/storage/EpubFile.kt (100%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/util/system/DensityExtensions.kt (100%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/util/system/DeviceUtil.kt (98%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/util/system/ToastExtensions.kt (94%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/util/system/WebViewClientCompat.kt (100%) rename core/{src/main/java => common/src/main/kotlin}/eu/kanade/tachiyomi/util/system/WebViewUtil.kt (98%) rename core/{src/main/java/tachiyomi/core => common/src/main/kotlin/tachiyomi/core/common}/Constants.kt (96%) rename core/{src/main/java/tachiyomi/core => common/src/main/kotlin/tachiyomi/core/common}/i18n/Localize.kt (97%) rename core/{src/main/java/tachiyomi/core => common/src/main/kotlin/tachiyomi/core/common}/preference/AndroidPreference.kt (98%) rename core/{src/main/java/tachiyomi/core => common/src/main/kotlin/tachiyomi/core/common}/preference/AndroidPreferenceStore.kt (81%) rename core/{src/main/java/tachiyomi/core => common/src/main/kotlin/tachiyomi/core/common}/preference/CheckboxState.kt (97%) rename core/{src/main/java/tachiyomi/core => common/src/main/kotlin/tachiyomi/core/common}/preference/InMemoryPreferenceStore.kt (98%) rename core/{src/main/java/tachiyomi/core => common/src/main/kotlin/tachiyomi/core/common}/preference/Preference.kt (92%) rename core/{src/main/java/tachiyomi/core => common/src/main/kotlin/tachiyomi/core/common}/preference/PreferenceStore.kt (96%) rename core/{src/main/java/tachiyomi/core => common/src/main/kotlin/tachiyomi/core/common}/preference/TriState.kt (89%) rename core/{src/main/java/tachiyomi/core => common/src/main/kotlin/tachiyomi/core/common}/storage/AndroidStorageFolderProvider.kt (86%) rename core/{src/main/java/tachiyomi/core => common/src/main/kotlin/tachiyomi/core/common}/storage/FolderProvider.kt (72%) rename core/{src/main/java/tachiyomi/core => common/src/main/kotlin/tachiyomi/core/common}/storage/UniFileExtensions.kt (87%) rename core/{src/main/java/tachiyomi/core => common/src/main/kotlin/tachiyomi/core/common}/storage/UniFileTempFileManager.kt (97%) rename core/{src/main/java/tachiyomi/core => common/src/main/kotlin/tachiyomi/core/common}/util/lang/BooleanExtensions.kt (52%) rename core/{src/main/java/tachiyomi/core => common/src/main/kotlin/tachiyomi/core/common}/util/lang/CoroutinesExtensions.kt (98%) rename core/{src/main/java/tachiyomi/core => common/src/main/kotlin/tachiyomi/core/common}/util/lang/RxCoroutineBridge.kt (98%) rename core/{src/main/java/tachiyomi/core => common/src/main/kotlin/tachiyomi/core/common}/util/lang/SortUtil.kt (88%) rename core/{src/main/java/tachiyomi/core => common/src/main/kotlin/tachiyomi/core/common}/util/system/ImageUtil.kt (99%) rename core/{src/main/java/tachiyomi/core => common/src/main/kotlin/tachiyomi/core/common}/util/system/LogcatExtensions.kt (90%) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 871b23bdc..3c5e630fa 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -140,7 +140,7 @@ android { dependencies { implementation(projects.i18n) - implementation(projects.core) + implementation(projects.core.common) implementation(projects.coreMetadata) implementation(projects.sourceApi) implementation(projects.sourceLocal) diff --git a/app/src/main/java/eu/kanade/core/preference/CheckboxState.kt b/app/src/main/java/eu/kanade/core/preference/CheckboxState.kt index 12635d04f..0f59a885d 100644 --- a/app/src/main/java/eu/kanade/core/preference/CheckboxState.kt +++ b/app/src/main/java/eu/kanade/core/preference/CheckboxState.kt @@ -1,7 +1,7 @@ package eu.kanade.core.preference import androidx.compose.ui.state.ToggleableState -import tachiyomi.core.preference.CheckboxState +import tachiyomi.core.common.preference.CheckboxState fun CheckboxState.TriState.asToggleableState() = when (this) { is CheckboxState.TriState.Exclude -> ToggleableState.Indeterminate diff --git a/app/src/main/java/eu/kanade/core/preference/PreferenceMutableState.kt b/app/src/main/java/eu/kanade/core/preference/PreferenceMutableState.kt index cb91e700a..1c7d9e761 100644 --- a/app/src/main/java/eu/kanade/core/preference/PreferenceMutableState.kt +++ b/app/src/main/java/eu/kanade/core/preference/PreferenceMutableState.kt @@ -5,7 +5,7 @@ import androidx.compose.runtime.mutableStateOf import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import tachiyomi.core.preference.Preference +import tachiyomi.core.common.preference.Preference class PreferenceMutableState( private val preference: Preference, 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 d52a3899c..9fa63d2cd 100644 --- a/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt +++ b/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt @@ -2,8 +2,8 @@ package eu.kanade.domain.base import android.content.Context import dev.icerock.moko.resources.StringResource -import tachiyomi.core.preference.Preference -import tachiyomi.core.preference.PreferenceStore +import tachiyomi.core.common.preference.Preference +import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.i18n.MR class BasePreferences( 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 33f148e95..6dd6ef4fa 100644 --- a/app/src/main/java/eu/kanade/domain/base/ExtensionInstallerPreference.kt +++ b/app/src/main/java/eu/kanade/domain/base/ExtensionInstallerPreference.kt @@ -5,9 +5,9 @@ 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 -import tachiyomi.core.preference.Preference -import tachiyomi.core.preference.PreferenceStore -import tachiyomi.core.preference.getEnum +import tachiyomi.core.common.preference.Preference +import tachiyomi.core.common.preference.PreferenceStore +import tachiyomi.core.common.preference.getEnum class ExtensionInstallerPreference( private val context: Context, diff --git a/app/src/main/java/eu/kanade/domain/chapter/interactor/SetReadStatus.kt b/app/src/main/java/eu/kanade/domain/chapter/interactor/SetReadStatus.kt index 673b686f5..346384f04 100644 --- a/app/src/main/java/eu/kanade/domain/chapter/interactor/SetReadStatus.kt +++ b/app/src/main/java/eu/kanade/domain/chapter/interactor/SetReadStatus.kt @@ -2,8 +2,8 @@ package eu.kanade.domain.chapter.interactor import eu.kanade.domain.download.interactor.DeleteDownload import logcat.LogPriority -import tachiyomi.core.util.lang.withNonCancellableContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.withNonCancellableContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.ChapterUpdate import tachiyomi.domain.chapter.repository.ChapterRepository diff --git a/app/src/main/java/eu/kanade/domain/download/interactor/DeleteDownload.kt b/app/src/main/java/eu/kanade/domain/download/interactor/DeleteDownload.kt index 5ec3b4a7d..18a2bf3f0 100644 --- a/app/src/main/java/eu/kanade/domain/download/interactor/DeleteDownload.kt +++ b/app/src/main/java/eu/kanade/domain/download/interactor/DeleteDownload.kt @@ -1,7 +1,7 @@ package eu.kanade.domain.download.interactor import eu.kanade.tachiyomi.data.download.DownloadManager -import tachiyomi.core.util.lang.withNonCancellableContext +import tachiyomi.core.common.util.lang.withNonCancellableContext import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.service.SourceManager diff --git a/app/src/main/java/eu/kanade/domain/extension/interactor/CreateExtensionRepo.kt b/app/src/main/java/eu/kanade/domain/extension/interactor/CreateExtensionRepo.kt index 66b949fc9..a8083ec00 100644 --- a/app/src/main/java/eu/kanade/domain/extension/interactor/CreateExtensionRepo.kt +++ b/app/src/main/java/eu/kanade/domain/extension/interactor/CreateExtensionRepo.kt @@ -1,7 +1,7 @@ package eu.kanade.domain.extension.interactor import eu.kanade.domain.source.service.SourcePreferences -import tachiyomi.core.preference.plusAssign +import tachiyomi.core.common.preference.plusAssign class CreateExtensionRepo(private val preferences: SourcePreferences) { diff --git a/app/src/main/java/eu/kanade/domain/extension/interactor/DeleteExtensionRepo.kt b/app/src/main/java/eu/kanade/domain/extension/interactor/DeleteExtensionRepo.kt index 8762be0d0..8e50ebeca 100644 --- a/app/src/main/java/eu/kanade/domain/extension/interactor/DeleteExtensionRepo.kt +++ b/app/src/main/java/eu/kanade/domain/extension/interactor/DeleteExtensionRepo.kt @@ -1,7 +1,7 @@ package eu.kanade.domain.extension.interactor import eu.kanade.domain.source.service.SourcePreferences -import tachiyomi.core.preference.minusAssign +import tachiyomi.core.common.preference.minusAssign class DeleteExtensionRepo(private val preferences: SourcePreferences) { diff --git a/app/src/main/java/eu/kanade/domain/extension/interactor/TrustExtension.kt b/app/src/main/java/eu/kanade/domain/extension/interactor/TrustExtension.kt index 0ad6b2c56..c3daec5f8 100644 --- a/app/src/main/java/eu/kanade/domain/extension/interactor/TrustExtension.kt +++ b/app/src/main/java/eu/kanade/domain/extension/interactor/TrustExtension.kt @@ -3,7 +3,7 @@ package eu.kanade.domain.extension.interactor import android.content.pm.PackageInfo import androidx.core.content.pm.PackageInfoCompat import eu.kanade.domain.source.service.SourcePreferences -import tachiyomi.core.preference.getAndSet +import tachiyomi.core.common.preference.getAndSet class TrustExtension( private val preferences: SourcePreferences, diff --git a/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt b/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt index 686b29f43..5334b598e 100644 --- a/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt +++ b/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt @@ -7,7 +7,7 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode import tachiyomi.core.metadata.comicinfo.ComicInfo import tachiyomi.core.metadata.comicinfo.ComicInfoPublishingStatus -import tachiyomi.core.preference.TriState +import tachiyomi.core.common.preference.TriState import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.manga.model.Manga import uy.kohesive.injekt.Injekt diff --git a/app/src/main/java/eu/kanade/domain/source/interactor/GetSourcesWithFavoriteCount.kt b/app/src/main/java/eu/kanade/domain/source/interactor/GetSourcesWithFavoriteCount.kt index 0cf342caf..962fbb8a6 100644 --- a/app/src/main/java/eu/kanade/domain/source/interactor/GetSourcesWithFavoriteCount.kt +++ b/app/src/main/java/eu/kanade/domain/source/interactor/GetSourcesWithFavoriteCount.kt @@ -3,7 +3,7 @@ package eu.kanade.domain.source.interactor import eu.kanade.domain.source.service.SourcePreferences import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine -import tachiyomi.core.util.lang.compareToWithCollator +import tachiyomi.core.common.util.lang.compareToWithCollator import tachiyomi.domain.source.model.Source import tachiyomi.domain.source.repository.SourceRepository import tachiyomi.source.local.isLocal diff --git a/app/src/main/java/eu/kanade/domain/source/interactor/ToggleLanguage.kt b/app/src/main/java/eu/kanade/domain/source/interactor/ToggleLanguage.kt index bc601117b..c813890d2 100644 --- a/app/src/main/java/eu/kanade/domain/source/interactor/ToggleLanguage.kt +++ b/app/src/main/java/eu/kanade/domain/source/interactor/ToggleLanguage.kt @@ -1,7 +1,7 @@ package eu.kanade.domain.source.interactor import eu.kanade.domain.source.service.SourcePreferences -import tachiyomi.core.preference.getAndSet +import tachiyomi.core.common.preference.getAndSet class ToggleLanguage( val preferences: SourcePreferences, diff --git a/app/src/main/java/eu/kanade/domain/source/interactor/ToggleSource.kt b/app/src/main/java/eu/kanade/domain/source/interactor/ToggleSource.kt index fbcb3147c..ec13f8d4b 100644 --- a/app/src/main/java/eu/kanade/domain/source/interactor/ToggleSource.kt +++ b/app/src/main/java/eu/kanade/domain/source/interactor/ToggleSource.kt @@ -1,7 +1,7 @@ package eu.kanade.domain.source.interactor import eu.kanade.domain.source.service.SourcePreferences -import tachiyomi.core.preference.getAndSet +import tachiyomi.core.common.preference.getAndSet import tachiyomi.domain.source.model.Source class ToggleSource( diff --git a/app/src/main/java/eu/kanade/domain/source/interactor/ToggleSourcePin.kt b/app/src/main/java/eu/kanade/domain/source/interactor/ToggleSourcePin.kt index e7ea8bb35..0a49a53ba 100644 --- a/app/src/main/java/eu/kanade/domain/source/interactor/ToggleSourcePin.kt +++ b/app/src/main/java/eu/kanade/domain/source/interactor/ToggleSourcePin.kt @@ -1,7 +1,7 @@ package eu.kanade.domain.source.interactor import eu.kanade.domain.source.service.SourcePreferences -import tachiyomi.core.preference.getAndSet +import tachiyomi.core.common.preference.getAndSet import tachiyomi.domain.source.model.Source class ToggleSourcePin( diff --git a/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt b/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt index 5779020a2..d4d3989c0 100644 --- a/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt +++ b/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt @@ -2,9 +2,9 @@ package eu.kanade.domain.source.service import eu.kanade.domain.source.interactor.SetMigrateSorting import eu.kanade.tachiyomi.util.system.LocaleHelper -import tachiyomi.core.preference.Preference -import tachiyomi.core.preference.PreferenceStore -import tachiyomi.core.preference.getEnum +import tachiyomi.core.common.preference.Preference +import tachiyomi.core.common.preference.PreferenceStore +import tachiyomi.core.common.preference.getEnum import tachiyomi.domain.library.model.LibraryDisplayMode class SourcePreferences( diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/AddTracks.kt b/app/src/main/java/eu/kanade/domain/track/interactor/AddTracks.kt index 70e612c3c..2d31c64e7 100644 --- a/app/src/main/java/eu/kanade/domain/track/interactor/AddTracks.kt +++ b/app/src/main/java/eu/kanade/domain/track/interactor/AddTracks.kt @@ -8,9 +8,9 @@ import eu.kanade.tachiyomi.data.track.Tracker import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.util.lang.convertEpochMillisZone import logcat.LogPriority -import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.lang.withNonCancellableContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.lang.withNonCancellableContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId import tachiyomi.domain.history.interactor.GetHistory import tachiyomi.domain.manga.model.Manga diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/SyncChapterProgressWithTrack.kt b/app/src/main/java/eu/kanade/domain/track/interactor/SyncChapterProgressWithTrack.kt index 6fab0792a..8e6df2289 100644 --- a/app/src/main/java/eu/kanade/domain/track/interactor/SyncChapterProgressWithTrack.kt +++ b/app/src/main/java/eu/kanade/domain/track/interactor/SyncChapterProgressWithTrack.kt @@ -4,7 +4,7 @@ import eu.kanade.domain.track.model.toDbTrack import eu.kanade.tachiyomi.data.track.EnhancedTracker import eu.kanade.tachiyomi.data.track.Tracker import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId import tachiyomi.domain.chapter.interactor.UpdateChapter import tachiyomi.domain.chapter.model.toChapterUpdate diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt b/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt index 557c3be83..a942d5f94 100644 --- a/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt +++ b/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt @@ -9,8 +9,8 @@ import eu.kanade.tachiyomi.data.track.TrackerManager import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import logcat.LogPriority -import tachiyomi.core.util.lang.withNonCancellableContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.withNonCancellableContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.track.interactor.GetTracks import tachiyomi.domain.track.interactor.InsertTrack diff --git a/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt b/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt index b178cf746..1f4e246dc 100644 --- a/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt +++ b/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt @@ -12,8 +12,8 @@ import eu.kanade.domain.track.interactor.TrackChapter import eu.kanade.domain.track.store.DelayedTrackingStore import eu.kanade.tachiyomi.util.system.workManager import logcat.LogPriority -import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.track.interactor.GetTracks import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get diff --git a/app/src/main/java/eu/kanade/domain/track/service/TrackPreferences.kt b/app/src/main/java/eu/kanade/domain/track/service/TrackPreferences.kt index 3e99d5ba0..ab000a9ea 100644 --- a/app/src/main/java/eu/kanade/domain/track/service/TrackPreferences.kt +++ b/app/src/main/java/eu/kanade/domain/track/service/TrackPreferences.kt @@ -2,8 +2,8 @@ package eu.kanade.domain.track.service import eu.kanade.tachiyomi.data.track.Tracker import eu.kanade.tachiyomi.data.track.anilist.Anilist -import tachiyomi.core.preference.Preference -import tachiyomi.core.preference.PreferenceStore +import tachiyomi.core.common.preference.Preference +import tachiyomi.core.common.preference.PreferenceStore class TrackPreferences( private val preferenceStore: PreferenceStore, diff --git a/app/src/main/java/eu/kanade/domain/track/store/DelayedTrackingStore.kt b/app/src/main/java/eu/kanade/domain/track/store/DelayedTrackingStore.kt index 5ec27cbea..10779a219 100644 --- a/app/src/main/java/eu/kanade/domain/track/store/DelayedTrackingStore.kt +++ b/app/src/main/java/eu/kanade/domain/track/store/DelayedTrackingStore.kt @@ -3,7 +3,7 @@ package eu.kanade.domain.track.store import android.content.Context import androidx.core.content.edit import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat class DelayedTrackingStore(context: Context) { diff --git a/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt b/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt index efb27fa02..606a1048d 100644 --- a/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt +++ b/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt @@ -5,8 +5,8 @@ import eu.kanade.domain.ui.model.TabletUiMode import eu.kanade.domain.ui.model.ThemeMode import eu.kanade.tachiyomi.util.system.DeviceUtil import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable -import tachiyomi.core.preference.PreferenceStore -import tachiyomi.core.preference.getEnum +import tachiyomi.core.common.preference.PreferenceStore +import tachiyomi.core.common.preference.getEnum import java.text.DateFormat import java.text.SimpleDateFormat import java.util.Locale diff --git a/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt index 95003f1e4..6a428506f 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt @@ -24,7 +24,7 @@ import eu.kanade.presentation.util.formattedMessage import eu.kanade.tachiyomi.source.Source import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.StateFlow -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.model.StubSource diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseIcons.kt b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseIcons.kt index fbe82ad6e..b4710fc40 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseIcons.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseIcons.kt @@ -31,7 +31,7 @@ import eu.kanade.presentation.util.rememberResourceBitmapPainter import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.extension.util.ExtensionLoader -import tachiyomi.core.util.lang.withIOContext +import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.domain.source.model.Source import tachiyomi.source.local.isLocal diff --git a/app/src/main/java/eu/kanade/presentation/category/CategoryExtensions.kt b/app/src/main/java/eu/kanade/presentation/category/CategoryExtensions.kt index a6c22c240..62c85907d 100644 --- a/app/src/main/java/eu/kanade/presentation/category/CategoryExtensions.kt +++ b/app/src/main/java/eu/kanade/presentation/category/CategoryExtensions.kt @@ -2,7 +2,7 @@ package eu.kanade.presentation.category import android.content.Context import androidx.compose.runtime.Composable -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.domain.category.model.Category import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource diff --git a/app/src/main/java/eu/kanade/presentation/category/components/CategoryDialogs.kt b/app/src/main/java/eu/kanade/presentation/category/components/CategoryDialogs.kt index fd6396b95..7709d960f 100644 --- a/app/src/main/java/eu/kanade/presentation/category/components/CategoryDialogs.kt +++ b/app/src/main/java/eu/kanade/presentation/category/components/CategoryDialogs.kt @@ -30,7 +30,7 @@ import eu.kanade.presentation.category.visualName import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.delay -import tachiyomi.core.preference.CheckboxState +import tachiyomi.core.common.preference.CheckboxState import tachiyomi.domain.category.model.Category import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.padding diff --git a/app/src/main/java/eu/kanade/presentation/library/DeleteLibraryMangaDialog.kt b/app/src/main/java/eu/kanade/presentation/library/DeleteLibraryMangaDialog.kt index 36346f14a..da6faa495 100644 --- a/app/src/main/java/eu/kanade/presentation/library/DeleteLibraryMangaDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/library/DeleteLibraryMangaDialog.kt @@ -10,7 +10,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import dev.icerock.moko.resources.StringResource -import tachiyomi.core.preference.CheckboxState +import tachiyomi.core.common.preference.CheckboxState import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.LabeledCheckbox import tachiyomi.presentation.core.i18n.stringResource diff --git a/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt index 7e9efd716..ad0733606 100644 --- a/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt @@ -19,7 +19,7 @@ import eu.kanade.tachiyomi.ui.library.LibrarySettingsScreenModel import eu.kanade.tachiyomi.util.system.isDevFlavor import eu.kanade.tachiyomi.util.system.isPreviewBuildType import kotlinx.collections.immutable.persistentListOf -import tachiyomi.core.preference.TriState +import tachiyomi.core.common.preference.TriState import tachiyomi.domain.category.model.Category import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibrarySort diff --git a/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt index 64cba7158..5f98dbb86 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt @@ -31,7 +31,7 @@ import eu.kanade.domain.manga.model.forceDownloaded import eu.kanade.presentation.components.TabbedDialog import eu.kanade.presentation.components.TabbedDialogPaddings import kotlinx.collections.immutable.persistentListOf -import tachiyomi.core.preference.TriState +import tachiyomi.core.common.preference.TriState import tachiyomi.domain.manga.model.Manga import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.LabeledCheckbox diff --git a/app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt b/app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt index 974804db1..fc690139a 100644 --- a/app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/MoreScreen.kt @@ -26,7 +26,7 @@ import eu.kanade.presentation.more.settings.widget.SwitchPreferenceWidget import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.more.DownloadQueueState -import tachiyomi.core.Constants +import tachiyomi.core.common.Constants import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.ScrollbarLazyColumn import tachiyomi.presentation.core.components.material.Scaffold diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/Preference.kt b/app/src/main/java/eu/kanade/presentation/more/settings/Preference.kt index fd8d12067..d2ed81c58 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/Preference.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/Preference.kt @@ -8,7 +8,7 @@ import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableMap import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource -import tachiyomi.core.preference.Preference as PreferenceData +import tachiyomi.core.common.preference.Preference as PreferenceData sealed class Preference { abstract val title: String 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 c86492287..509f2196a 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 @@ -57,9 +57,9 @@ import kotlinx.collections.immutable.toImmutableMap import kotlinx.coroutines.launch import logcat.LogPriority import okhttp3.Headers -import tachiyomi.core.util.lang.launchNonCancellable -import tachiyomi.core.util.lang.withUIContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.launchNonCancellable +import tachiyomi.core.common.util.lang.withUIContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.manga.interactor.ResetViewerFlags import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt index 57508ecbd..c8bfd10ca 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt @@ -13,7 +13,7 @@ import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.screen.browse.ExtensionReposScreen import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate import kotlinx.collections.immutable.persistentListOf -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.pluralStringResource import tachiyomi.presentation.core.i18n.stringResource diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt index f82a58aca..91de2993f 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt @@ -50,11 +50,11 @@ import eu.kanade.tachiyomi.util.system.toast import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentMapOf import logcat.LogPriority -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.storage.displayablePath -import tachiyomi.core.util.lang.launchNonCancellable -import tachiyomi.core.util.lang.withUIContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.storage.displayablePath +import tachiyomi.core.common.util.lang.launchNonCancellable +import tachiyomi.core.common.util.lang.withUIContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.backup.service.BackupPreferences import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.storage.service.StoragePreferences @@ -100,7 +100,7 @@ object SettingsDataScreen : SearchableSettings { @Composable fun storageLocationPicker( - storageDirPref: tachiyomi.core.preference.Preference, + storageDirPref: tachiyomi.core.common.preference.Preference, ): ManagedActivityResultLauncher { val context = LocalContext.current @@ -122,7 +122,7 @@ object SettingsDataScreen : SearchableSettings { @Composable fun storageLocationText( - storageDirPref: tachiyomi.core.preference.Preference, + storageDirPref: tachiyomi.core.common.preference.Preference, ): String { val context = LocalContext.current val storageDir by storageDirPref.collectAsState() diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSecurityScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSecurityScreen.kt index fb1e0932c..1d0510025 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSecurityScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsSecurityScreen.kt @@ -12,7 +12,7 @@ import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.isAuthenticationSupported import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableMap -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.pluralStringResource import tachiyomi.presentation.core.i18n.stringResource diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt index 8d508746c..021f0ceb2 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsTrackingScreen.kt @@ -53,8 +53,8 @@ import eu.kanade.tachiyomi.util.system.openInBrowser import eu.kanade.tachiyomi.util.system.toast import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -import tachiyomi.core.util.lang.launchIO -import tachiyomi.core.util.lang.withUIContext +import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.domain.source.service.SourceManager import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.padding diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt index 1315c442b..58f690569 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt @@ -38,9 +38,9 @@ import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.toast import kotlinx.coroutines.launch import logcat.LogPriority -import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.lang.withUIContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.lang.withUIContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.release.interactor.GetApplicationRelease import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.LinkIcon diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/advanced/ClearDatabaseScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/advanced/ClearDatabaseScreen.kt index 9ab8473dd..eaaec9585 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/advanced/ClearDatabaseScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/advanced/ClearDatabaseScreen.kt @@ -37,9 +37,9 @@ import eu.kanade.tachiyomi.util.system.toast import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.update -import tachiyomi.core.util.lang.launchIO -import tachiyomi.core.util.lang.launchUI -import tachiyomi.core.util.lang.withNonCancellableContext +import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.lang.launchUI +import tachiyomi.core.common.util.lang.withNonCancellableContext import tachiyomi.data.Database import tachiyomi.domain.source.interactor.GetSourcesWithNonLibraryManga import tachiyomi.domain.source.model.Source diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/appearance/AppLanguageScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/appearance/AppLanguageScreen.kt index bdba9e15c..b59b26aca 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/appearance/AppLanguageScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/appearance/AppLanguageScreen.kt @@ -30,7 +30,7 @@ import eu.kanade.tachiyomi.util.system.LocaleHelper import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import org.xmlpull.v1.XmlPullParser -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.i18n.stringResource diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionReposScreenModel.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionReposScreenModel.kt index 353ad1bd6..d694618fb 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionReposScreenModel.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionReposScreenModel.kt @@ -13,7 +13,7 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update -import tachiyomi.core.util.lang.launchIO +import tachiyomi.core.common.util.lang.launchIO import tachiyomi.i18n.MR import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt index b9dbbbfb8..db0842d08 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/AppThemePreferenceWidget.kt @@ -48,7 +48,7 @@ import eu.kanade.presentation.manga.components.MangaCover import eu.kanade.presentation.theme.TachiyomiTheme import eu.kanade.tachiyomi.util.system.DeviceUtil import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable -import tachiyomi.core.preference.InMemoryPreferenceStore +import tachiyomi.core.common.preference.InMemoryPreferenceStore import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/ColorFilterPage.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/ColorFilterPage.kt index 1c5a9c6fb..cb8f5578b 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/settings/ColorFilterPage.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/settings/ColorFilterPage.kt @@ -11,7 +11,7 @@ import androidx.core.graphics.green import androidx.core.graphics.red import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences.Companion.ColorFilterMode import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel -import tachiyomi.core.preference.getAndSet +import tachiyomi.core.common.preference.getAndSet import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.SettingsChipRow diff --git a/app/src/main/java/eu/kanade/presentation/util/ExceptionFormatter.kt b/app/src/main/java/eu/kanade/presentation/util/ExceptionFormatter.kt index a76a225b9..be3cfff1a 100644 --- a/app/src/main/java/eu/kanade/presentation/util/ExceptionFormatter.kt +++ b/app/src/main/java/eu/kanade/presentation/util/ExceptionFormatter.kt @@ -4,7 +4,7 @@ import android.content.Context import eu.kanade.tachiyomi.network.HttpException import eu.kanade.tachiyomi.source.online.LicensedMangaChaptersException import eu.kanade.tachiyomi.util.system.isOnline -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.data.source.NoResultsException import tachiyomi.domain.source.model.SourceNotInstalledException import tachiyomi.i18n.MR diff --git a/app/src/main/java/eu/kanade/presentation/util/TimeUtils.kt b/app/src/main/java/eu/kanade/presentation/util/TimeUtils.kt index 0bb5089ab..643f19f6e 100644 --- a/app/src/main/java/eu/kanade/presentation/util/TimeUtils.kt +++ b/app/src/main/java/eu/kanade/presentation/util/TimeUtils.kt @@ -4,7 +4,7 @@ import android.content.Context import android.text.format.DateUtils import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource import java.time.Instant diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index 2b53cee0c..dfe5dad41 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -49,8 +49,8 @@ import logcat.AndroidLogcatLogger import logcat.LogPriority import logcat.LogcatLogger import org.conscrypt.Conscrypt -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.util.system.logcat import tachiyomi.i18n.MR import tachiyomi.presentation.widget.WidgetManager import uy.kohesive.injekt.Injekt diff --git a/app/src/main/java/eu/kanade/tachiyomi/AppInfo.kt b/app/src/main/java/eu/kanade/tachiyomi/AppInfo.kt index 322983606..1a6d3db63 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/AppInfo.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/AppInfo.kt @@ -1,6 +1,6 @@ package eu.kanade.tachiyomi -import tachiyomi.core.util.system.ImageUtil +import tachiyomi.core.common.util.system.ImageUtil /** * Used by extensions. diff --git a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt index 119adf72d..154326514 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt @@ -3,8 +3,8 @@ package eu.kanade.tachiyomi import android.content.Context import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob import eu.kanade.tachiyomi.data.library.LibraryUpdateJob -import tachiyomi.core.preference.Preference -import tachiyomi.core.preference.PreferenceStore +import tachiyomi.core.common.preference.Preference +import tachiyomi.core.common.preference.PreferenceStore object Migrations { diff --git a/app/src/main/java/eu/kanade/tachiyomi/crash/GlobalExceptionHandler.kt b/app/src/main/java/eu/kanade/tachiyomi/crash/GlobalExceptionHandler.kt index 0b64f296c..600dac444 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/crash/GlobalExceptionHandler.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/crash/GlobalExceptionHandler.kt @@ -10,7 +10,7 @@ import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.json.Json import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import kotlin.system.exitProcess class GlobalExceptionHandler private constructor( 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 4ef212999..fc24f245d 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 @@ -12,9 +12,9 @@ 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.notify -import tachiyomi.core.i18n.pluralStringResource -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.storage.displayablePath +import tachiyomi.core.common.i18n.pluralStringResource +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.storage.displayablePath import tachiyomi.i18n.MR import uy.kohesive.injekt.injectLazy import java.io.File diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreateJob.kt index cd607480d..80c11c62f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreateJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreateJob.kt @@ -24,7 +24,7 @@ import eu.kanade.tachiyomi.util.system.isRunning import eu.kanade.tachiyomi.util.system.setForegroundSafely import eu.kanade.tachiyomi.util.system.workManager import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.backup.service.BackupPreferences import tachiyomi.domain.storage.service.StorageManager import uy.kohesive.injekt.Injekt diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt index 8bd718206..14d05e239 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/BackupCreator.kt @@ -21,8 +21,8 @@ import logcat.LogPriority import okio.buffer import okio.gzip import okio.sink -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.backup.service.BackupPreferences import tachiyomi.domain.manga.interactor.GetFavorites import tachiyomi.domain.manga.model.Manga diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/PreferenceBackupCreator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/PreferenceBackupCreator.kt index e0b8f0add..d14cef230 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/PreferenceBackupCreator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/PreferenceBackupCreator.kt @@ -11,8 +11,8 @@ import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue import eu.kanade.tachiyomi.source.ConfigurableSource import eu.kanade.tachiyomi.source.preferenceKey import eu.kanade.tachiyomi.source.sourcePreferences -import tachiyomi.core.preference.Preference -import tachiyomi.core.preference.PreferenceStore +import tachiyomi.core.common.preference.Preference +import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.domain.source.service.SourceManager import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestoreJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestoreJob.kt index 180e8f055..0ecf97e06 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestoreJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestoreJob.kt @@ -19,8 +19,8 @@ import eu.kanade.tachiyomi.util.system.setForegroundSafely import eu.kanade.tachiyomi.util.system.workManager import kotlinx.coroutines.CancellationException import logcat.LogPriority -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.util.system.logcat import tachiyomi.i18n.MR class BackupRestoreJob(private val context: Context, workerParams: WorkerParameters) : diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt index 796d74ae1..a17d33f43 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/BackupRestorer.kt @@ -16,7 +16,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.ensureActive import kotlinx.coroutines.launch -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import java.io.File import java.text.SimpleDateFormat diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/PreferenceRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/PreferenceRestorer.kt index 1062937d4..4871e3949 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/PreferenceRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/PreferenceRestorer.kt @@ -12,8 +12,8 @@ import eu.kanade.tachiyomi.data.backup.models.StringPreferenceValue import eu.kanade.tachiyomi.data.backup.models.StringSetPreferenceValue import eu.kanade.tachiyomi.data.library.LibraryUpdateJob import eu.kanade.tachiyomi.source.sourcePreferences -import tachiyomi.core.preference.AndroidPreferenceStore -import tachiyomi.core.preference.PreferenceStore +import tachiyomi.core.common.preference.AndroidPreferenceStore +import tachiyomi.core.common.preference.PreferenceStore import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt index 56d6262c9..bbdf85c10 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt @@ -12,7 +12,7 @@ import logcat.LogPriority import okhttp3.Response import okio.buffer import okio.sink -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.chapter.model.Chapter import java.io.File import java.io.IOException diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt index 561d32b91..978f65bc9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt @@ -27,7 +27,7 @@ import okio.Source import okio.buffer import okio.sink import okio.source -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.MangaCover import tachiyomi.domain.source.service.SourceManager diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt index fb1278614..acbda8cc5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt @@ -9,7 +9,7 @@ import coil.decode.ImageSource import coil.fetch.SourceResult import coil.request.Options import okio.BufferedSource -import tachiyomi.core.util.system.ImageUtil +import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.decoder.ImageDecoder /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt index b09cca3bc..fb1c8e176 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt @@ -39,11 +39,11 @@ import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.protobuf.ProtoBuf import logcat.LogPriority -import tachiyomi.core.storage.extension -import tachiyomi.core.storage.nameWithoutExtension -import tachiyomi.core.util.lang.launchIO -import tachiyomi.core.util.lang.launchNonCancellable -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.storage.extension +import tachiyomi.core.common.storage.nameWithoutExtension +import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.lang.launchNonCancellable +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.service.SourceManager diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt index 61d0be98e..01a342859 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt @@ -14,10 +14,10 @@ import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.runBlocking import logcat.LogPriority -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.storage.extension -import tachiyomi.core.util.lang.launchIO -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.storage.extension +import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.download.service.DownloadPreferences diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt index 0d9f6152a..4acd8322e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt @@ -14,7 +14,7 @@ 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.notify -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import uy.kohesive.injekt.injectLazy import java.util.regex.Pattern diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt index 469cc0765..001395af1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt @@ -5,9 +5,9 @@ import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.util.storage.DiskUtil import logcat.LogPriority -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.storage.displayablePath -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.storage.displayablePath +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.storage.service.StorageManager diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index 8b54cfabd..8fb9ab14d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -40,15 +40,15 @@ import kotlinx.coroutines.supervisorScope import logcat.LogPriority import nl.adaptivity.xmlutil.serialization.XML import okhttp3.Response -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE import tachiyomi.core.metadata.comicinfo.ComicInfo -import tachiyomi.core.storage.extension -import tachiyomi.core.util.lang.launchIO -import tachiyomi.core.util.lang.launchNow -import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.system.ImageUtil -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.storage.extension +import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.lang.launchNow +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.system.ImageUtil +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.download.service.DownloadPreferences diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt index 8b9d76a03..258b1f754 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt @@ -39,10 +39,10 @@ import kotlinx.coroutines.ensureActive import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit import logcat.LogPriority -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.preference.getAndSet -import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.preference.getAndSet +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.category.model.Category import tachiyomi.domain.chapter.model.Chapter diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt index c21dd7d32..4d13dc724 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt @@ -26,10 +26,10 @@ import eu.kanade.tachiyomi.util.system.cancelNotification import eu.kanade.tachiyomi.util.system.getBitmapOrNull import eu.kanade.tachiyomi.util.system.notificationBuilder import eu.kanade.tachiyomi.util.system.notify -import tachiyomi.core.Constants -import tachiyomi.core.i18n.pluralStringResource -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.util.lang.launchUI +import tachiyomi.core.common.Constants +import tachiyomi.core.common.i18n.pluralStringResource +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.util.lang.launchUI import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.library.model.LibraryManga import tachiyomi.domain.manga.model.Manga diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/MetadataUpdateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/MetadataUpdateJob.kt index 348b41ca3..c6401ef72 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/MetadataUpdateJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/MetadataUpdateJob.kt @@ -27,8 +27,8 @@ import kotlinx.coroutines.ensureActive import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit import logcat.LogPriority -import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.library.model.LibraryManga import tachiyomi.domain.manga.interactor.GetLibraryManga import tachiyomi.domain.manga.model.Manga diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationHandler.kt b/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationHandler.kt index aba7de79d..f8435daec 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationHandler.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationHandler.kt @@ -7,7 +7,7 @@ import android.net.Uri import androidx.core.net.toUri import eu.kanade.tachiyomi.extension.util.ExtensionInstaller import eu.kanade.tachiyomi.ui.main.MainActivity -import tachiyomi.core.Constants +import tachiyomi.core.common.Constants /** * Class that manages [PendingIntent] of activity's 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 7d6e9099e..de9e55803 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 @@ -18,8 +18,8 @@ import eu.kanade.tachiyomi.util.system.notificationManager import eu.kanade.tachiyomi.util.system.toShareIntent import eu.kanade.tachiyomi.util.system.toast import kotlinx.coroutines.runBlocking -import tachiyomi.core.Constants -import tachiyomi.core.util.lang.launchIO +import tachiyomi.core.common.Constants +import tachiyomi.core.common.util.lang.launchIO import tachiyomi.domain.chapter.interactor.GetChapter import tachiyomi.domain.chapter.interactor.UpdateChapter import tachiyomi.domain.chapter.model.Chapter 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 7632d06db..6b34d124d 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 @@ -7,7 +7,7 @@ import androidx.core.app.NotificationManagerCompat.IMPORTANCE_HIGH import androidx.core.app.NotificationManagerCompat.IMPORTANCE_LOW import eu.kanade.tachiyomi.util.system.buildNotificationChannel import eu.kanade.tachiyomi.util.system.buildNotificationChannelGroup -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt index 0bf0aae68..beefba353 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt @@ -15,9 +15,9 @@ import eu.kanade.tachiyomi.util.storage.cacheImageDir import eu.kanade.tachiyomi.util.storage.getUriCompat import logcat.LogPriority import okio.IOException -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.util.system.ImageUtil -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.util.system.ImageUtil +import tachiyomi.core.common.util.system.logcat import tachiyomi.i18n.MR import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt index 3da3c392a..8f88f1051 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/BaseTracker.kt @@ -10,9 +10,9 @@ import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.util.system.toast import logcat.LogPriority import okhttp3.OkHttpClient -import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.lang.withUIContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.lang.withUIContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.track.interactor.InsertTrack import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt index 2917952a0..c808d0f31 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt @@ -25,7 +25,7 @@ import kotlinx.serialization.json.put import kotlinx.serialization.json.putJsonObject import okhttp3.OkHttpClient import okhttp3.RequestBody.Companion.toRequestBody -import tachiyomi.core.util.lang.withIOContext +import tachiyomi.core.common.util.lang.withIOContext import uy.kohesive.injekt.injectLazy import java.time.Instant import java.time.LocalDate diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt index 8609d405a..65d2dc805 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt @@ -21,7 +21,7 @@ import okhttp3.CacheControl import okhttp3.FormBody import okhttp3.OkHttpClient import okhttp3.Request -import tachiyomi.core.util.lang.withIOContext +import tachiyomi.core.common.util.lang.withIOContext import uy.kohesive.injekt.injectLazy import java.net.URLEncoder import java.nio.charset.StandardCharsets diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaApi.kt index 5a23b14d9..fd1b26197 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kavita/KavitaApi.kt @@ -12,8 +12,8 @@ import okhttp3.Dns import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient import okhttp3.RequestBody.Companion.toRequestBody -import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.system.logcat import uy.kohesive.injekt.injectLazy import java.io.IOException import java.net.SocketTimeoutException diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuApi.kt index e6c3acadf..3c9251eaf 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuApi.kt @@ -25,7 +25,7 @@ import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody -import tachiyomi.core.util.lang.withIOContext +import tachiyomi.core.common.util.lang.withIOContext import uy.kohesive.injekt.injectLazy import java.net.URLEncoder import java.nio.charset.StandardCharsets diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt index 2852d8e8e..a8661bf18 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/komga/KomgaApi.kt @@ -14,8 +14,8 @@ import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody -import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.system.logcat import uy.kohesive.injekt.injectLazy private const val READLIST_API = "/api/v1/readlists" diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdatesApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdatesApi.kt index fa7e4556d..5da3b7222 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdatesApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/mangaupdates/MangaUpdatesApi.kt @@ -28,7 +28,7 @@ import logcat.LogPriority import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.RequestBody.Companion.toRequestBody -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import uy.kohesive.injekt.injectLazy import tachiyomi.domain.track.model.Track as DomainTrack diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt index 83a2032d2..2aef56016 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListApi.kt @@ -28,7 +28,7 @@ import okhttp3.Headers import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody -import tachiyomi.core.util.lang.withIOContext +import tachiyomi.core.common.util.lang.withIOContext import uy.kohesive.injekt.injectLazy import java.text.SimpleDateFormat import java.util.Locale 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 46740c595..6eb93a636 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 @@ -25,7 +25,7 @@ import kotlinx.serialization.json.putJsonObject import okhttp3.FormBody import okhttp3.OkHttpClient import okhttp3.RequestBody.Companion.toRequestBody -import tachiyomi.core.util.lang.withIOContext +import tachiyomi.core.common.util.lang.withIOContext import uy.kohesive.injekt.injectLazy import tachiyomi.domain.track.model.Track as DomainTrack diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiApi.kt index ef1d2e1b1..c7cc8a188 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/suwayomi/SuwayomiApi.kt @@ -16,7 +16,7 @@ import okhttp3.Dns import okhttp3.FormBody import okhttp3.Headers import okhttp3.OkHttpClient -import tachiyomi.core.util.lang.withIOContext +import tachiyomi.core.common.util.lang.withIOContext import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt index 2197dd992..d3632767c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateChecker.kt @@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.data.updater import android.content.Context import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.util.system.isInstalledFromFDroid -import tachiyomi.core.util.lang.withIOContext +import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.domain.release.interactor.GetApplicationRelease import uy.kohesive.injekt.injectLazy diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateDownloadJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateDownloadJob.kt index 073f2bb0d..c1aa46626 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateDownloadJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/AppUpdateDownloadJob.kt @@ -23,8 +23,8 @@ import eu.kanade.tachiyomi.util.system.setForegroundSafely import eu.kanade.tachiyomi.util.system.workManager import okhttp3.internal.http2.ErrorCode import okhttp3.internal.http2.StreamResetException -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.util.lang.withIOContext +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.i18n.MR import uy.kohesive.injekt.injectLazy import java.io.File 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 6c20bea64..35147790f 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 @@ -13,7 +13,7 @@ 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.notify -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.domain.release.model.Release import tachiyomi.i18n.MR diff --git a/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt b/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt index ef0f65fff..9fb7b2fb5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt @@ -26,8 +26,8 @@ import kotlinx.serialization.protobuf.ProtoBuf import nl.adaptivity.xmlutil.XmlDeclMode import nl.adaptivity.xmlutil.core.XmlVersion import nl.adaptivity.xmlutil.serialization.XML -import tachiyomi.core.storage.AndroidStorageFolderProvider -import tachiyomi.core.storage.UniFileTempFileManager +import tachiyomi.core.common.storage.AndroidStorageFolderProvider +import tachiyomi.core.common.storage.UniFileTempFileManager import tachiyomi.data.AndroidDatabaseHandler import tachiyomi.data.Database import tachiyomi.data.DatabaseHandler diff --git a/app/src/main/java/eu/kanade/tachiyomi/di/PreferenceModule.kt b/app/src/main/java/eu/kanade/tachiyomi/di/PreferenceModule.kt index 51136226b..b56c16cae 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/di/PreferenceModule.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/di/PreferenceModule.kt @@ -9,9 +9,9 @@ import eu.kanade.tachiyomi.core.security.SecurityPreferences import eu.kanade.tachiyomi.network.NetworkPreferences import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.util.system.isDevFlavor -import tachiyomi.core.preference.AndroidPreferenceStore -import tachiyomi.core.preference.PreferenceStore -import tachiyomi.core.storage.AndroidStorageFolderProvider +import tachiyomi.core.common.preference.AndroidPreferenceStore +import tachiyomi.core.common.preference.PreferenceStore +import tachiyomi.core.common.storage.AndroidStorageFolderProvider import tachiyomi.domain.backup.service.BackupPreferences import tachiyomi.domain.download.service.DownloadPreferences import tachiyomi.domain.library.service.LibraryPreferences diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt index 2c119a18b..d8f56b878 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt @@ -19,9 +19,9 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.emptyFlow import logcat.LogPriority -import tachiyomi.core.util.lang.launchNow -import tachiyomi.core.util.lang.withUIContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.launchNow +import tachiyomi.core.common.util.lang.withUIContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.source.model.StubSource import tachiyomi.i18n.MR import uy.kohesive.injekt.Injekt diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt index 26e105616..0d79fd335 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt @@ -13,10 +13,10 @@ import eu.kanade.tachiyomi.network.parseAs import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import logcat.LogPriority -import tachiyomi.core.preference.Preference -import tachiyomi.core.preference.PreferenceStore -import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.preference.Preference +import tachiyomi.core.common.preference.PreferenceStore +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.system.logcat import uy.kohesive.injekt.injectLazy import java.time.Instant import kotlin.time.Duration.Companion.days diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt index 602ce80f9..ff764271e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionUpdateNotifier.kt @@ -7,7 +7,7 @@ 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.notify -import tachiyomi.core.i18n.pluralStringResource +import tachiyomi.core.common.i18n.pluralStringResource import tachiyomi.i18n.MR class ExtensionUpdateNotifier(private val context: Context) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/installer/PackageInstallerInstaller.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/installer/PackageInstallerInstaller.kt index 222ff02d0..d3aef3b18 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/installer/PackageInstallerInstaller.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/installer/PackageInstallerInstaller.kt @@ -15,7 +15,7 @@ import eu.kanade.tachiyomi.util.lang.use import eu.kanade.tachiyomi.util.system.getParcelableExtraCompat import eu.kanade.tachiyomi.util.system.getUriSize import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat class PackageInstallerInstaller(private val service: Service) : Installer(service) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt index 33ff287c7..c85611acf 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt @@ -12,7 +12,7 @@ import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import logcat.LogPriority import rikka.shizuku.Shizuku -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import tachiyomi.i18n.MR import java.io.BufferedReader import java.io.InputStream diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt index 3d4b360c9..e0a008e1c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt @@ -14,8 +14,8 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.async import logcat.LogPriority -import tachiyomi.core.util.lang.launchNow -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.launchNow +import tachiyomi.core.common.util.system.logcat /** * Broadcast receiver that listens for the system's packages installed, updated or removed, and only diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallService.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallService.kt index 3ffe9f783..12bdb61d0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallService.kt @@ -15,8 +15,8 @@ import eu.kanade.tachiyomi.extension.util.ExtensionInstaller.Companion.EXTRA_DOW import eu.kanade.tachiyomi.util.system.getSerializableExtraCompat import eu.kanade.tachiyomi.util.system.notificationBuilder import logcat.LogPriority -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.util.system.logcat import tachiyomi.i18n.MR class ExtensionInstallService : Service() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstaller.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstaller.kt index ff384e56f..b71974a30 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstaller.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstaller.kt @@ -27,8 +27,8 @@ import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.transformWhile import logcat.LogPriority -import tachiyomi.core.util.lang.withUIContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.withUIContext +import tachiyomi.core.common.util.system.logcat import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.io.File diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt index ef603f934..2d469f260 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt @@ -20,7 +20,7 @@ import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.runBlocking import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import uy.kohesive.injekt.injectLazy import java.io.File diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterScreen.kt index 9fd5df0fd..278c10d36 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterScreen.kt @@ -11,7 +11,7 @@ import cafe.adriel.voyager.navigator.currentOrThrow import eu.kanade.presentation.browse.ExtensionFilterScreen import eu.kanade.presentation.util.Screen import kotlinx.coroutines.flow.collectLatest -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import tachiyomi.presentation.core.screens.LoadingScreen diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterScreenModel.kt index acfeafa04..7cd83923f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionFilterScreenModel.kt @@ -20,7 +20,7 @@ import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt index 5617e506b..e71d65997 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt @@ -27,7 +27,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update -import tachiyomi.core.util.lang.launchIO +import tachiyomi.core.common.util.lang.launchIO import tachiyomi.i18n.MR import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreenModel.kt index 8d8a9f607..4266c4730 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/ExtensionDetailsScreenModel.kt @@ -25,7 +25,7 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import logcat.LogPriority import okhttp3.HttpUrl.Companion.toHttpUrl -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreenModel.kt index e5d886f85..8faf1c320 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/manga/MigrateMangaScreenModel.kt @@ -16,7 +16,7 @@ import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.manga.interactor.GetFavorites import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.service.SourceManager diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt index d6b70381b..d3b35bcf4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateDialog.kt @@ -32,10 +32,10 @@ import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.ui.browse.migration.MigrationFlags import kotlinx.coroutines.flow.update -import tachiyomi.core.preference.Preference -import tachiyomi.core.preference.PreferenceStore -import tachiyomi.core.util.lang.launchIO -import tachiyomi.core.util.lang.withUIContext +import tachiyomi.core.common.preference.Preference +import tachiyomi.core.common.preference.PreferenceStore +import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.category.interactor.SetMangaCategories import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt index 0bbe49b0a..acd8278a0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt @@ -28,7 +28,7 @@ import eu.kanade.tachiyomi.ui.home.HomeScreen import eu.kanade.tachiyomi.ui.manga.MangaScreen import eu.kanade.tachiyomi.ui.webview.WebViewScreen import kotlinx.coroutines.launch -import tachiyomi.core.Constants +import tachiyomi.core.common.Constants import tachiyomi.domain.manga.model.Manga import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceScreenModel.kt index f7659b3e0..8968d62b8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/sources/MigrateSourceScreenModel.kt @@ -17,8 +17,8 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update import logcat.LogPriority -import tachiyomi.core.util.lang.launchIO -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.source.model.Source import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesScreenModel.kt index ce98bf629..0f777f7e5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/SourcesScreenModel.kt @@ -16,8 +16,8 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update import logcat.LogPriority -import tachiyomi.core.util.lang.launchIO -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.source.model.Pin import tachiyomi.domain.source.model.Source import uy.kohesive.injekt.Injekt diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt index 400b5215a..4bbf6d7dc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt @@ -53,8 +53,8 @@ import eu.kanade.tachiyomi.ui.webview.WebViewScreen import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.receiveAsFlow -import tachiyomi.core.Constants -import tachiyomi.core.util.lang.launchIO +import tachiyomi.core.common.Constants +import tachiyomi.core.common.util.lang.launchIO import tachiyomi.domain.source.model.StubSource import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.Scaffold diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt index 4b0835850..a7e8a26ea 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt @@ -35,9 +35,9 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import tachiyomi.core.preference.CheckboxState -import tachiyomi.core.preference.mapAsCheckboxState -import tachiyomi.core.util.lang.launchIO +import tachiyomi.core.common.preference.CheckboxState +import tachiyomi.core.common.preference.mapAsCheckboxState +import tachiyomi.core.common.util.lang.launchIO import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.category.interactor.SetMangaCategories import tachiyomi.domain.category.model.Category diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt index 9c4c5a272..017af7479 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceFilterDialog.kt @@ -18,7 +18,7 @@ import androidx.compose.ui.unit.dp import eu.kanade.presentation.components.AdaptiveSheet import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList -import tachiyomi.core.preference.TriState +import tachiyomi.core.common.preference.TriState import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.CollapsibleBox diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/deeplink/DeepLinkScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/deeplink/DeepLinkScreenModel.kt index e21821430..5bef14675 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/deeplink/DeepLinkScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/deeplink/DeepLinkScreenModel.kt @@ -12,7 +12,7 @@ import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.ResolvableSource import eu.kanade.tachiyomi.source.online.UriType import kotlinx.coroutines.flow.update -import tachiyomi.core.util.lang.launchIO +import tachiyomi.core.common.util.lang.launchIO import tachiyomi.domain.chapter.interactor.GetChapterByUrlAndMangaId import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.manga.interactor.GetMangaByUrlAndSourceId diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt index f8470ef7b..8332c0c97 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadQueueScreen.kt @@ -53,7 +53,7 @@ import eu.kanade.presentation.components.NestedMenuItem import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.databinding.DownloadListBinding import kotlinx.collections.immutable.persistentListOf -import tachiyomi.core.util.lang.launchUI +import tachiyomi.core.common.util.lang.launchUI import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.Pill import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt index 960b66c3a..a344fcda3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt @@ -18,9 +18,9 @@ import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import logcat.LogPriority -import tachiyomi.core.util.lang.launchIO -import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.history.interactor.GetHistory import tachiyomi.domain.history.interactor.GetNextChapters diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryTab.kt index 0c6e283ca..9f91706f1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryTab.kt @@ -27,7 +27,7 @@ import eu.kanade.tachiyomi.ui.reader.ReaderActivity import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.receiveAsFlow -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.domain.chapter.model.Chapter import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt index a1b6c3b1b..b2771d4e2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt @@ -44,12 +44,12 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update -import tachiyomi.core.preference.CheckboxState -import tachiyomi.core.preference.TriState -import tachiyomi.core.util.lang.compareToWithCollator -import tachiyomi.core.util.lang.launchIO -import tachiyomi.core.util.lang.launchNonCancellable -import tachiyomi.core.util.lang.withIOContext +import tachiyomi.core.common.preference.CheckboxState +import tachiyomi.core.common.preference.TriState +import tachiyomi.core.common.util.lang.compareToWithCollator +import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.lang.launchNonCancellable +import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.category.interactor.SetMangaCategories import tachiyomi.domain.category.model.Category diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsScreenModel.kt index ce573de3f..f0856f830 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsScreenModel.kt @@ -4,10 +4,10 @@ import cafe.adriel.voyager.core.model.ScreenModel import cafe.adriel.voyager.core.model.screenModelScope import eu.kanade.domain.base.BasePreferences import eu.kanade.tachiyomi.data.track.TrackerManager -import tachiyomi.core.preference.Preference -import tachiyomi.core.preference.TriState -import tachiyomi.core.preference.getAndSet -import tachiyomi.core.util.lang.launchIO +import tachiyomi.core.common.preference.Preference +import tachiyomi.core.common.preference.TriState +import tachiyomi.core.common.preference.getAndSet +import tachiyomi.core.common.util.lang.launchIO import tachiyomi.domain.category.interactor.SetDisplayMode import tachiyomi.domain.category.interactor.SetSortModeForCategory import tachiyomi.domain.category.model.Category diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt index e54dd97d6..7acdb51f7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryTab.kt @@ -48,8 +48,8 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.util.lang.launchIO +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.util.lang.launchIO import tachiyomi.domain.category.model.Category import tachiyomi.domain.library.model.LibraryManga import tachiyomi.domain.manga.model.Manga diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index c0125bbd8..87ab0b4c8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -89,9 +89,9 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import logcat.LogPriority -import tachiyomi.core.Constants -import tachiyomi.core.util.lang.launchIO -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.Constants +import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.release.interactor.GetApplicationRelease import tachiyomi.i18n.MR diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaCoverScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaCoverScreenModel.kt index 6a4d968a4..3cbfa540b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaCoverScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaCoverScreenModel.kt @@ -19,11 +19,11 @@ import eu.kanade.tachiyomi.util.system.toShareIntent import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import logcat.LogPriority -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.util.lang.launchIO -import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.lang.withUIContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.lang.withUIContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.manga.interactor.GetManga import tachiyomi.domain.manga.model.Manga import tachiyomi.i18n.MR diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt index b73b4bcc3..f7dad1d8c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt @@ -54,9 +54,9 @@ import eu.kanade.tachiyomi.util.system.toShareIntent import eu.kanade.tachiyomi.util.system.toast import kotlinx.coroutines.launch import logcat.LogPriority -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.manga.model.Manga import tachiyomi.i18n.MR diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt index ae61519ee..26ad3a7fc 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt @@ -50,15 +50,15 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import logcat.LogPriority -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.preference.CheckboxState -import tachiyomi.core.preference.TriState -import tachiyomi.core.preference.mapAsCheckboxState -import tachiyomi.core.util.lang.launchIO -import tachiyomi.core.util.lang.launchNonCancellable -import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.lang.withUIContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.preference.CheckboxState +import tachiyomi.core.common.preference.TriState +import tachiyomi.core.common.preference.mapAsCheckboxState +import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.lang.launchNonCancellable +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.lang.withUIContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.category.interactor.SetMangaCategories import tachiyomi.domain.category.model.Category diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt index 980153732..e0ad25182 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt @@ -64,11 +64,11 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import logcat.LogPriority -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.util.lang.launchNonCancellable -import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.lang.withUIContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.util.lang.launchNonCancellable +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.lang.withUIContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.manga.interactor.GetManga import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.track.interactor.DeleteTrack diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt index 3ca8418ad..7e4ac0932 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/more/MoreTab.kt @@ -32,7 +32,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine -import tachiyomi.core.util.lang.launchIO +import tachiyomi.core.common.util.lang.launchIO import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource import uy.kohesive.injekt.Injekt 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 c67a49084..f170f7e1a 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 @@ -82,12 +82,12 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.sample import kotlinx.coroutines.launch import logcat.LogPriority -import tachiyomi.core.Constants -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.util.lang.launchIO -import tachiyomi.core.util.lang.launchNonCancellable -import tachiyomi.core.util.lang.withUIContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.Constants +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.lang.launchNonCancellable +import tachiyomi.core.common.util.lang.withUIContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.i18n.MR import tachiyomi.presentation.core.util.collectAsState import uy.kohesive.injekt.Injekt diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderNavigationOverlayView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderNavigationOverlayView.kt index 70aa7bfb0..08f5f8349 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderNavigationOverlayView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderNavigationOverlayView.kt @@ -13,7 +13,7 @@ import androidx.core.graphics.withTranslation import androidx.core.view.isVisible import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation import eu.kanade.tachiyomi.ui.reader.viewer.navigation.DisabledNavigation -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import kotlin.math.abs class ReaderNavigationOverlayView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet) { 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 b7f4c9c14..c6170f2a9 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 @@ -54,13 +54,13 @@ import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.runBlocking import logcat.LogPriority -import tachiyomi.core.preference.toggle -import tachiyomi.core.storage.UniFileTempFileManager -import tachiyomi.core.util.lang.launchIO -import tachiyomi.core.util.lang.launchNonCancellable -import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.lang.withUIContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.preference.toggle +import tachiyomi.core.common.storage.UniFileTempFileManager +import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.lang.launchNonCancellable +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.lang.withUIContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId import tachiyomi.domain.chapter.interactor.UpdateChapter import tachiyomi.domain.chapter.model.ChapterUpdate 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 2676744ca..ab0146a0e 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 @@ -15,7 +15,7 @@ import eu.kanade.tachiyomi.util.system.cancelNotification import eu.kanade.tachiyomi.util.system.getBitmapOrNull import eu.kanade.tachiyomi.util.system.notificationBuilder import eu.kanade.tachiyomi.util.system.notify -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt index b8f97c5f4..f0edbe7f3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt @@ -7,10 +7,10 @@ import eu.kanade.tachiyomi.data.download.DownloadProvider import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.storage.UniFileTempFileManager -import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.storage.UniFileTempFileManager +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.model.StubSource import tachiyomi.i18n.MR diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DirectoryPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DirectoryPageLoader.kt index 2a11f74e3..8817b0682 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DirectoryPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DirectoryPageLoader.kt @@ -4,7 +4,7 @@ import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder -import tachiyomi.core.util.system.ImageUtil +import tachiyomi.core.common.util.system.ImageUtil /** * Loader used to load a chapter from a directory given on [file]. diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt index 775b493b3..1d18d7b47 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt @@ -10,7 +10,7 @@ import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderPage -import tachiyomi.core.storage.UniFileTempFileManager +import tachiyomi.core.common.storage.UniFileTempFileManager import tachiyomi.domain.manga.model.Manga import uy.kohesive.injekt.injectLazy diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt index 4802708a6..14c27bf95 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/HttpPageLoader.kt @@ -15,8 +15,8 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.flow import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.suspendCancellableCoroutine -import tachiyomi.core.util.lang.launchIO -import tachiyomi.core.util.lang.withIOContext +import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.lang.withIOContext import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.util.concurrent.PriorityBlockingQueue diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt index ad513ec93..868ab92b2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt @@ -5,7 +5,7 @@ import com.github.junrar.rarfile.FileHeader import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder -import tachiyomi.core.util.system.ImageUtil +import tachiyomi.core.common.util.system.ImageUtil import java.io.File import java.io.InputStream import java.io.PipedInputStream diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt index e4fd1c6e5..e839d8a55 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt @@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.ui.reader.loader import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder -import tachiyomi.core.util.system.ImageUtil +import tachiyomi.core.common.util.system.ImageUtil import java.io.File import java.nio.charset.StandardCharsets import java.util.zip.ZipFile diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/model/ReaderChapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/model/ReaderChapter.kt index c0cdfdd7c..cb3bca256 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/model/ReaderChapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/model/ReaderChapter.kt @@ -4,7 +4,7 @@ import eu.kanade.domain.chapter.model.toDbChapter import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.ui.reader.loader.PageLoader import kotlinx.coroutines.flow.MutableStateFlow -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat data class ReaderChapter(val chapter: Chapter) { 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 4de0ecd94..6c079b836 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 @@ -3,8 +3,8 @@ package eu.kanade.tachiyomi.ui.reader.setting import android.os.Build import androidx.compose.ui.graphics.BlendMode import dev.icerock.moko.resources.StringResource -import tachiyomi.core.preference.PreferenceStore -import tachiyomi.core.preference.getEnum +import tachiyomi.core.common.preference.PreferenceStore +import tachiyomi.core.common.preference.getEnum import tachiyomi.i18n.MR class ReaderPreferences( 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 44dd19db4..0d973d239 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 @@ -5,7 +5,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import tachiyomi.core.preference.Preference +import tachiyomi.core.common.preference.Preference /** * Common configuration for all viewers. 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 2c81f1387..7b6d1dd6a 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 @@ -18,11 +18,11 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope import logcat.LogPriority -import tachiyomi.core.util.lang.launchIO -import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.lang.withUIContext -import tachiyomi.core.util.system.ImageUtil -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.lang.withUIContext +import tachiyomi.core.common.util.system.ImageUtil +import tachiyomi.core.common.util.system.logcat import java.io.BufferedInputStream import java.io.ByteArrayInputStream import java.io.InputStream 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 6adb5fcef..4569f4370 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 @@ -20,7 +20,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.MainScope import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR /** 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 5508d1acd..ffa38bfba 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 @@ -21,7 +21,7 @@ import eu.kanade.tachiyomi.ui.reader.viewer.Viewer import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation.NavigationRegion import kotlinx.coroutines.MainScope import kotlinx.coroutines.cancel -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import uy.kohesive.injekt.injectLazy import kotlin.math.min 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 ec78dc8c6..5ff4ecaae 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 @@ -10,7 +10,7 @@ import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters 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 +import tachiyomi.core.common.util.system.logcat /** * Pager adapter used by this [viewer] to where [ViewerChapters] updates are posted. diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt index b491a7363..4f41d3d51 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt @@ -24,11 +24,11 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.suspendCancellableCoroutine import logcat.LogPriority -import tachiyomi.core.util.lang.launchIO -import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.lang.withUIContext -import tachiyomi.core.util.system.ImageUtil -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.lang.withUIContext +import tachiyomi.core.common.util.system.ImageUtil +import tachiyomi.core.common.util.system.logcat import java.io.BufferedInputStream import java.io.InputStream diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonTransitionHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonTransitionHolder.kt index 65629799e..57036d070 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonTransitionHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonTransitionHolder.kt @@ -17,7 +17,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.MainScope import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt index db65836d8..da11e68ff 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt @@ -21,7 +21,7 @@ import eu.kanade.tachiyomi.ui.reader.viewer.Viewer import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation.NavigationRegion import kotlinx.coroutines.MainScope import kotlinx.coroutines.cancel -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/security/UnlockActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/security/UnlockActivity.kt index 557da5bdd..00d1e136a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/security/UnlockActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/security/UnlockActivity.kt @@ -8,8 +8,8 @@ import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate import eu.kanade.tachiyomi.util.system.AuthenticatorUtil import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.startAuthentication import logcat.LogPriority -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.util.system.logcat import tachiyomi.i18n.MR /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/TrackLoginActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/TrackLoginActivity.kt index 47cd88416..3f742cfe0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/TrackLoginActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/TrackLoginActivity.kt @@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.ui.setting.track import android.net.Uri import androidx.lifecycle.lifecycleScope -import tachiyomi.core.util.lang.launchIO +import tachiyomi.core.common.util.lang.launchIO class TrackLoginActivity : BaseOAuthLoginActivity() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreenModel.kt index 9ed04cd78..0d553ab4b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/stats/StatsScreenModel.kt @@ -13,7 +13,7 @@ import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.track.TrackerManager import eu.kanade.tachiyomi.source.model.SManga import kotlinx.coroutines.flow.update -import tachiyomi.core.util.lang.launchIO +import tachiyomi.core.common.util.lang.launchIO import tachiyomi.domain.history.interactor.GetTotalReadDuration import tachiyomi.domain.library.model.LibraryManga import tachiyomi.domain.library.service.LibraryPreferences diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt index 9574d0c7f..f76ac45fe 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt @@ -32,9 +32,9 @@ import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import logcat.LogPriority -import tachiyomi.core.util.lang.launchIO -import tachiyomi.core.util.lang.launchNonCancellable -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.lang.launchNonCancellable +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.chapter.interactor.GetChapter import tachiyomi.domain.chapter.interactor.UpdateChapter import tachiyomi.domain.chapter.model.ChapterUpdate diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesTab.kt index d84bcd38f..a9dc4281a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesTab.kt @@ -26,7 +26,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaScreen import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.ui.updates.UpdatesScreenModel.Event import kotlinx.coroutines.flow.collectLatest -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt index 61d09dc79..040a02f76 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt @@ -18,7 +18,7 @@ import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.view.setComposeContent import logcat.LogPriority import okhttp3.HttpUrl.Companion.toHttpUrl -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.source.service.SourceManager import tachiyomi.i18n.MR import uy.kohesive.injekt.injectLazy diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewScreenModel.kt index 888369050..331d62b59 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewScreenModel.kt @@ -11,7 +11,7 @@ import eu.kanade.tachiyomi.util.system.toShareIntent import eu.kanade.tachiyomi.util.system.toast import logcat.LogPriority import okhttp3.HttpUrl.Companion.toHttpUrl -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.source.service.SourceManager 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 33e21be6b..56e6c278b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt @@ -9,8 +9,8 @@ import eu.kanade.tachiyomi.util.system.WebViewUtil import eu.kanade.tachiyomi.util.system.createFileInCacheDir 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 +import tachiyomi.core.common.util.lang.withNonCancellableContext +import tachiyomi.core.common.util.lang.withUIContext import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt index e7482165f..a313f5a2d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt @@ -1,8 +1,8 @@ package eu.kanade.tachiyomi.util.lang import android.content.Context -import tachiyomi.core.i18n.pluralStringResource -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.pluralStringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import java.text.DateFormat import java.time.Instant diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/AuthenticatorUtil.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/AuthenticatorUtil.kt index ce3a09141..164a1075c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/AuthenticatorUtil.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/AuthenticatorUtil.kt @@ -11,7 +11,7 @@ import androidx.biometric.auth.startClass2BiometricOrCredentialAuthentication import androidx.core.content.ContextCompat import androidx.fragment.app.FragmentActivity import kotlinx.coroutines.suspendCancellableCoroutine -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import kotlin.coroutines.resume 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 92b5f3843..190764631 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 @@ -22,8 +22,8 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences import eu.kanade.tachiyomi.util.lang.truncateCenter import logcat.LogPriority import rikka.sui.Sui -import tachiyomi.core.i18n.stringResource -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.util.system.logcat import tachiyomi.i18n.MR import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/IntentExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/IntentExtensions.kt index ed4ce04f5..65acbed18 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/IntentExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/IntentExtensions.kt @@ -6,7 +6,7 @@ import android.content.Intent import android.net.Uri import android.os.Build import androidx.core.content.IntentCompat -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import java.io.Serializable diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/LocaleHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/LocaleHelper.kt index 71d965cd5..827eb739e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/LocaleHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/LocaleHelper.kt @@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.util.system import android.content.Context import androidx.core.os.LocaleListCompat import eu.kanade.tachiyomi.ui.browse.source.SourcesScreenModel -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import java.util.Locale 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 index e565921db..a31857c0f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/WorkManagerExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/WorkManagerExtensions.kt @@ -6,7 +6,7 @@ import androidx.work.WorkInfo import androidx.work.WorkManager import kotlinx.coroutines.delay import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat val Context.workManager: WorkManager get() = WorkManager.getInstance(this) diff --git a/core/.gitignore b/core/common/.gitignore similarity index 100% rename from core/.gitignore rename to core/common/.gitignore diff --git a/core/build.gradle.kts b/core/common/build.gradle.kts similarity index 95% rename from core/build.gradle.kts rename to core/common/build.gradle.kts index 841217152..560e7285f 100644 --- a/core/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -5,7 +5,7 @@ plugins { } android { - namespace = "eu.kanade.tachiyomi.core" + namespace = "eu.kanade.tachiyomi.core.common" kotlinOptions { freeCompilerArgs += listOf( diff --git a/core/src/main/AndroidManifest.xml b/core/common/src/main/AndroidManifest.xml similarity index 100% rename from core/src/main/AndroidManifest.xml rename to core/common/src/main/AndroidManifest.xml diff --git a/core/src/main/java/eu/kanade/tachiyomi/core/security/SecurityPreferences.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/core/security/SecurityPreferences.kt similarity index 86% rename from core/src/main/java/eu/kanade/tachiyomi/core/security/SecurityPreferences.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/core/security/SecurityPreferences.kt index faf268314..274f0c592 100644 --- a/core/src/main/java/eu/kanade/tachiyomi/core/security/SecurityPreferences.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/core/security/SecurityPreferences.kt @@ -1,9 +1,9 @@ package eu.kanade.tachiyomi.core.security import dev.icerock.moko.resources.StringResource -import tachiyomi.core.preference.Preference -import tachiyomi.core.preference.PreferenceStore -import tachiyomi.core.preference.getEnum +import tachiyomi.core.common.preference.Preference +import tachiyomi.core.common.preference.PreferenceStore +import tachiyomi.core.common.preference.getEnum import tachiyomi.i18n.MR class SecurityPreferences( diff --git a/core/src/main/java/eu/kanade/tachiyomi/network/AndroidCookieJar.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/AndroidCookieJar.kt similarity index 100% rename from core/src/main/java/eu/kanade/tachiyomi/network/AndroidCookieJar.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/network/AndroidCookieJar.kt diff --git a/core/src/main/java/eu/kanade/tachiyomi/network/DohProviders.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/DohProviders.kt similarity index 100% rename from core/src/main/java/eu/kanade/tachiyomi/network/DohProviders.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/network/DohProviders.kt diff --git a/core/src/main/java/eu/kanade/tachiyomi/network/JavaScriptEngine.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/JavaScriptEngine.kt similarity index 92% rename from core/src/main/java/eu/kanade/tachiyomi/network/JavaScriptEngine.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/network/JavaScriptEngine.kt index 4ecf3e09d..5c7894606 100644 --- a/core/src/main/java/eu/kanade/tachiyomi/network/JavaScriptEngine.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/JavaScriptEngine.kt @@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.network import android.content.Context import app.cash.quickjs.QuickJs -import tachiyomi.core.util.lang.withIOContext +import tachiyomi.core.common.util.lang.withIOContext /** * Util for evaluating JavaScript in sources. diff --git a/core/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkHelper.kt similarity index 100% rename from core/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkHelper.kt diff --git a/core/src/main/java/eu/kanade/tachiyomi/network/NetworkPreferences.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkPreferences.kt similarity index 85% rename from core/src/main/java/eu/kanade/tachiyomi/network/NetworkPreferences.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkPreferences.kt index 7a334d00f..4c839b90d 100644 --- a/core/src/main/java/eu/kanade/tachiyomi/network/NetworkPreferences.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkPreferences.kt @@ -1,7 +1,7 @@ package eu.kanade.tachiyomi.network -import tachiyomi.core.preference.Preference -import tachiyomi.core.preference.PreferenceStore +import tachiyomi.core.common.preference.Preference +import tachiyomi.core.common.preference.PreferenceStore class NetworkPreferences( private val preferenceStore: PreferenceStore, diff --git a/core/src/main/java/eu/kanade/tachiyomi/network/OkHttpExtensions.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/OkHttpExtensions.kt similarity index 100% rename from core/src/main/java/eu/kanade/tachiyomi/network/OkHttpExtensions.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/network/OkHttpExtensions.kt diff --git a/core/src/main/java/eu/kanade/tachiyomi/network/ProgressListener.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/ProgressListener.kt similarity index 100% rename from core/src/main/java/eu/kanade/tachiyomi/network/ProgressListener.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/network/ProgressListener.kt diff --git a/core/src/main/java/eu/kanade/tachiyomi/network/ProgressResponseBody.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/ProgressResponseBody.kt similarity index 100% rename from core/src/main/java/eu/kanade/tachiyomi/network/ProgressResponseBody.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/network/ProgressResponseBody.kt diff --git a/core/src/main/java/eu/kanade/tachiyomi/network/Requests.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/Requests.kt similarity index 100% rename from core/src/main/java/eu/kanade/tachiyomi/network/Requests.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/network/Requests.kt diff --git a/core/src/main/java/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt similarity index 99% rename from core/src/main/java/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt index 12a8f0ce0..6a765c680 100644 --- a/core/src/main/java/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/CloudflareInterceptor.kt @@ -14,7 +14,7 @@ import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Interceptor import okhttp3.Request import okhttp3.Response -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import java.io.IOException import java.util.concurrent.CountDownLatch diff --git a/core/src/main/java/eu/kanade/tachiyomi/network/interceptor/IgnoreGzipInterceptor.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/IgnoreGzipInterceptor.kt similarity index 100% rename from core/src/main/java/eu/kanade/tachiyomi/network/interceptor/IgnoreGzipInterceptor.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/IgnoreGzipInterceptor.kt diff --git a/core/src/main/java/eu/kanade/tachiyomi/network/interceptor/RateLimitInterceptor.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/RateLimitInterceptor.kt similarity index 100% rename from core/src/main/java/eu/kanade/tachiyomi/network/interceptor/RateLimitInterceptor.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/RateLimitInterceptor.kt diff --git a/core/src/main/java/eu/kanade/tachiyomi/network/interceptor/SpecificHostRateLimitInterceptor.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/SpecificHostRateLimitInterceptor.kt similarity index 100% rename from core/src/main/java/eu/kanade/tachiyomi/network/interceptor/SpecificHostRateLimitInterceptor.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/SpecificHostRateLimitInterceptor.kt diff --git a/core/src/main/java/eu/kanade/tachiyomi/network/interceptor/UncaughtExceptionInterceptor.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/UncaughtExceptionInterceptor.kt similarity index 100% rename from core/src/main/java/eu/kanade/tachiyomi/network/interceptor/UncaughtExceptionInterceptor.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/UncaughtExceptionInterceptor.kt diff --git a/core/src/main/java/eu/kanade/tachiyomi/network/interceptor/UserAgentInterceptor.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/UserAgentInterceptor.kt similarity index 100% rename from core/src/main/java/eu/kanade/tachiyomi/network/interceptor/UserAgentInterceptor.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/UserAgentInterceptor.kt diff --git a/core/src/main/java/eu/kanade/tachiyomi/network/interceptor/WebViewInterceptor.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/WebViewInterceptor.kt similarity index 98% rename from core/src/main/java/eu/kanade/tachiyomi/network/interceptor/WebViewInterceptor.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/WebViewInterceptor.kt index cd1ffb14c..e18fa4299 100644 --- a/core/src/main/java/eu/kanade/tachiyomi/network/interceptor/WebViewInterceptor.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/interceptor/WebViewInterceptor.kt @@ -13,7 +13,7 @@ import okhttp3.Headers import okhttp3.Interceptor import okhttp3.Request import okhttp3.Response -import tachiyomi.core.util.lang.launchUI +import tachiyomi.core.common.util.lang.launchUI import tachiyomi.i18n.MR import java.util.Locale import java.util.concurrent.CountDownLatch diff --git a/core/src/main/java/eu/kanade/tachiyomi/util/lang/Hash.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/lang/Hash.kt similarity index 100% rename from core/src/main/java/eu/kanade/tachiyomi/util/lang/Hash.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/util/lang/Hash.kt diff --git a/core/src/main/java/eu/kanade/tachiyomi/util/lang/StringExtensions.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/lang/StringExtensions.kt similarity index 100% rename from core/src/main/java/eu/kanade/tachiyomi/util/lang/StringExtensions.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/util/lang/StringExtensions.kt diff --git a/core/src/main/java/eu/kanade/tachiyomi/util/storage/DiskUtil.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/DiskUtil.kt similarity index 100% rename from core/src/main/java/eu/kanade/tachiyomi/util/storage/DiskUtil.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/DiskUtil.kt diff --git a/core/src/main/java/eu/kanade/tachiyomi/util/storage/EpubFile.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt similarity index 100% rename from core/src/main/java/eu/kanade/tachiyomi/util/storage/EpubFile.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt diff --git a/core/src/main/java/eu/kanade/tachiyomi/util/system/DensityExtensions.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/system/DensityExtensions.kt similarity index 100% rename from core/src/main/java/eu/kanade/tachiyomi/util/system/DensityExtensions.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/util/system/DensityExtensions.kt diff --git a/core/src/main/java/eu/kanade/tachiyomi/util/system/DeviceUtil.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/system/DeviceUtil.kt similarity index 98% rename from core/src/main/java/eu/kanade/tachiyomi/util/system/DeviceUtil.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/util/system/DeviceUtil.kt index e2011f0c5..c9f20326d 100644 --- a/core/src/main/java/eu/kanade/tachiyomi/util/system/DeviceUtil.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/system/DeviceUtil.kt @@ -6,7 +6,7 @@ import android.content.Context import android.os.Build import androidx.core.content.getSystemService import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat object DeviceUtil { diff --git a/core/src/main/java/eu/kanade/tachiyomi/util/system/ToastExtensions.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/system/ToastExtensions.kt similarity index 94% rename from core/src/main/java/eu/kanade/tachiyomi/util/system/ToastExtensions.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/util/system/ToastExtensions.kt index 0f4aba9bf..453f9289e 100644 --- a/core/src/main/java/eu/kanade/tachiyomi/util/system/ToastExtensions.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/system/ToastExtensions.kt @@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.util.system import android.content.Context import android.widget.Toast import dev.icerock.moko.resources.StringResource -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource /** * Display a toast in this context. diff --git a/core/src/main/java/eu/kanade/tachiyomi/util/system/WebViewClientCompat.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/system/WebViewClientCompat.kt similarity index 100% rename from core/src/main/java/eu/kanade/tachiyomi/util/system/WebViewClientCompat.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/util/system/WebViewClientCompat.kt diff --git a/core/src/main/java/eu/kanade/tachiyomi/util/system/WebViewUtil.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/system/WebViewUtil.kt similarity index 98% rename from core/src/main/java/eu/kanade/tachiyomi/util/system/WebViewUtil.kt rename to core/common/src/main/kotlin/eu/kanade/tachiyomi/util/system/WebViewUtil.kt index 42b756f74..b059e15d4 100644 --- a/core/src/main/java/eu/kanade/tachiyomi/util/system/WebViewUtil.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/system/WebViewUtil.kt @@ -8,7 +8,7 @@ import android.webkit.WebSettings import android.webkit.WebView import kotlinx.coroutines.suspendCancellableCoroutine import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import kotlin.coroutines.resume object WebViewUtil { diff --git a/core/src/main/java/tachiyomi/core/Constants.kt b/core/common/src/main/kotlin/tachiyomi/core/common/Constants.kt similarity index 96% rename from core/src/main/java/tachiyomi/core/Constants.kt rename to core/common/src/main/kotlin/tachiyomi/core/common/Constants.kt index 46c619b86..649418997 100644 --- a/core/src/main/java/tachiyomi/core/Constants.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/Constants.kt @@ -1,4 +1,4 @@ -package tachiyomi.core +package tachiyomi.core.common object Constants { const val URL_HELP = "https://mihon.app/docs/guides/troubleshooting/" diff --git a/core/src/main/java/tachiyomi/core/i18n/Localize.kt b/core/common/src/main/kotlin/tachiyomi/core/common/i18n/Localize.kt similarity index 97% rename from core/src/main/java/tachiyomi/core/i18n/Localize.kt rename to core/common/src/main/kotlin/tachiyomi/core/common/i18n/Localize.kt index d6f8f3c16..c698e856e 100644 --- a/core/src/main/java/tachiyomi/core/i18n/Localize.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/i18n/Localize.kt @@ -1,4 +1,4 @@ -package tachiyomi.core.i18n +package tachiyomi.core.common.i18n import android.content.Context import dev.icerock.moko.resources.PluralsResource diff --git a/core/src/main/java/tachiyomi/core/preference/AndroidPreference.kt b/core/common/src/main/kotlin/tachiyomi/core/common/preference/AndroidPreference.kt similarity index 98% rename from core/src/main/java/tachiyomi/core/preference/AndroidPreference.kt rename to core/common/src/main/kotlin/tachiyomi/core/common/preference/AndroidPreference.kt index 21f5b1d30..577d83687 100644 --- a/core/src/main/java/tachiyomi/core/preference/AndroidPreference.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/preference/AndroidPreference.kt @@ -1,4 +1,4 @@ -package tachiyomi.core.preference +package tachiyomi.core.common.preference import android.content.SharedPreferences import android.content.SharedPreferences.Editor @@ -12,7 +12,7 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat sealed class AndroidPreference( private val preferences: SharedPreferences, diff --git a/core/src/main/java/tachiyomi/core/preference/AndroidPreferenceStore.kt b/core/common/src/main/kotlin/tachiyomi/core/common/preference/AndroidPreferenceStore.kt similarity index 81% rename from core/src/main/java/tachiyomi/core/preference/AndroidPreferenceStore.kt rename to core/common/src/main/kotlin/tachiyomi/core/common/preference/AndroidPreferenceStore.kt index b24fa5dcc..6bdb120cd 100644 --- a/core/src/main/java/tachiyomi/core/preference/AndroidPreferenceStore.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/preference/AndroidPreferenceStore.kt @@ -1,17 +1,17 @@ -package tachiyomi.core.preference +package tachiyomi.core.common.preference import android.content.Context import android.content.SharedPreferences import androidx.preference.PreferenceManager import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow -import tachiyomi.core.preference.AndroidPreference.BooleanPrimitive -import tachiyomi.core.preference.AndroidPreference.FloatPrimitive -import tachiyomi.core.preference.AndroidPreference.IntPrimitive -import tachiyomi.core.preference.AndroidPreference.LongPrimitive -import tachiyomi.core.preference.AndroidPreference.Object -import tachiyomi.core.preference.AndroidPreference.StringPrimitive -import tachiyomi.core.preference.AndroidPreference.StringSetPrimitive +import tachiyomi.core.common.preference.AndroidPreference.BooleanPrimitive +import tachiyomi.core.common.preference.AndroidPreference.FloatPrimitive +import tachiyomi.core.common.preference.AndroidPreference.IntPrimitive +import tachiyomi.core.common.preference.AndroidPreference.LongPrimitive +import tachiyomi.core.common.preference.AndroidPreference.Object +import tachiyomi.core.common.preference.AndroidPreference.StringPrimitive +import tachiyomi.core.common.preference.AndroidPreference.StringSetPrimitive class AndroidPreferenceStore( context: Context, diff --git a/core/src/main/java/tachiyomi/core/preference/CheckboxState.kt b/core/common/src/main/kotlin/tachiyomi/core/common/preference/CheckboxState.kt similarity index 97% rename from core/src/main/java/tachiyomi/core/preference/CheckboxState.kt rename to core/common/src/main/kotlin/tachiyomi/core/common/preference/CheckboxState.kt index da681e16d..cf7e471c0 100644 --- a/core/src/main/java/tachiyomi/core/preference/CheckboxState.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/preference/CheckboxState.kt @@ -1,4 +1,4 @@ -package tachiyomi.core.preference +package tachiyomi.core.common.preference sealed class CheckboxState(open val value: T) { diff --git a/core/src/main/java/tachiyomi/core/preference/InMemoryPreferenceStore.kt b/core/common/src/main/kotlin/tachiyomi/core/common/preference/InMemoryPreferenceStore.kt similarity index 98% rename from core/src/main/java/tachiyomi/core/preference/InMemoryPreferenceStore.kt rename to core/common/src/main/kotlin/tachiyomi/core/common/preference/InMemoryPreferenceStore.kt index 2fb3ee9ec..96e8644ad 100644 --- a/core/src/main/java/tachiyomi/core/preference/InMemoryPreferenceStore.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/preference/InMemoryPreferenceStore.kt @@ -1,4 +1,4 @@ -package tachiyomi.core.preference +package tachiyomi.core.common.preference import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow diff --git a/core/src/main/java/tachiyomi/core/preference/Preference.kt b/core/common/src/main/kotlin/tachiyomi/core/common/preference/Preference.kt similarity index 92% rename from core/src/main/java/tachiyomi/core/preference/Preference.kt rename to core/common/src/main/kotlin/tachiyomi/core/common/preference/Preference.kt index 1cc0d3d3c..f75384491 100644 --- a/core/src/main/java/tachiyomi/core/preference/Preference.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/preference/Preference.kt @@ -1,4 +1,4 @@ -package tachiyomi.core.preference +package tachiyomi.core.common.preference import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow @@ -30,7 +30,7 @@ interface Preference { return key.startsWith(PRIVATE_PREFIX) } fun privateKey(key: String): String { - return "${PRIVATE_PREFIX}$key" + return "$PRIVATE_PREFIX$key" } /** @@ -41,7 +41,7 @@ interface Preference { return key.startsWith(APP_STATE_PREFIX) } fun appStateKey(key: String): String { - return "${APP_STATE_PREFIX}$key" + return "$APP_STATE_PREFIX$key" } private const val APP_STATE_PREFIX = "__APP_STATE_" diff --git a/core/src/main/java/tachiyomi/core/preference/PreferenceStore.kt b/core/common/src/main/kotlin/tachiyomi/core/common/preference/PreferenceStore.kt similarity index 96% rename from core/src/main/java/tachiyomi/core/preference/PreferenceStore.kt rename to core/common/src/main/kotlin/tachiyomi/core/common/preference/PreferenceStore.kt index 5b0e9da9b..0cd3a21bf 100644 --- a/core/src/main/java/tachiyomi/core/preference/PreferenceStore.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/preference/PreferenceStore.kt @@ -1,4 +1,4 @@ -package tachiyomi.core.preference +package tachiyomi.core.common.preference interface PreferenceStore { diff --git a/core/src/main/java/tachiyomi/core/preference/TriState.kt b/core/common/src/main/kotlin/tachiyomi/core/common/preference/TriState.kt similarity index 89% rename from core/src/main/java/tachiyomi/core/preference/TriState.kt rename to core/common/src/main/kotlin/tachiyomi/core/common/preference/TriState.kt index 68b9173ce..703f069c3 100644 --- a/core/src/main/java/tachiyomi/core/preference/TriState.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/preference/TriState.kt @@ -1,4 +1,4 @@ -package tachiyomi.core.preference +package tachiyomi.core.common.preference enum class TriState { DISABLED, // Disable filter diff --git a/core/src/main/java/tachiyomi/core/storage/AndroidStorageFolderProvider.kt b/core/common/src/main/kotlin/tachiyomi/core/common/storage/AndroidStorageFolderProvider.kt similarity index 86% rename from core/src/main/java/tachiyomi/core/storage/AndroidStorageFolderProvider.kt rename to core/common/src/main/kotlin/tachiyomi/core/common/storage/AndroidStorageFolderProvider.kt index a5d48a49d..33335f345 100644 --- a/core/src/main/java/tachiyomi/core/storage/AndroidStorageFolderProvider.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/storage/AndroidStorageFolderProvider.kt @@ -1,9 +1,9 @@ -package tachiyomi.core.storage +package tachiyomi.core.common.storage import android.content.Context import android.os.Environment import androidx.core.net.toUri -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import java.io.File diff --git a/core/src/main/java/tachiyomi/core/storage/FolderProvider.kt b/core/common/src/main/kotlin/tachiyomi/core/common/storage/FolderProvider.kt similarity index 72% rename from core/src/main/java/tachiyomi/core/storage/FolderProvider.kt rename to core/common/src/main/kotlin/tachiyomi/core/common/storage/FolderProvider.kt index decd1c378..06d3e364f 100644 --- a/core/src/main/java/tachiyomi/core/storage/FolderProvider.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/storage/FolderProvider.kt @@ -1,4 +1,4 @@ -package tachiyomi.core.storage +package tachiyomi.core.common.storage import java.io.File diff --git a/core/src/main/java/tachiyomi/core/storage/UniFileExtensions.kt b/core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileExtensions.kt similarity index 87% rename from core/src/main/java/tachiyomi/core/storage/UniFileExtensions.kt rename to core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileExtensions.kt index afe60ed35..8bbd9b3a7 100644 --- a/core/src/main/java/tachiyomi/core/storage/UniFileExtensions.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileExtensions.kt @@ -1,4 +1,4 @@ -package tachiyomi.core.storage +package tachiyomi.core.common.storage import com.hippo.unifile.UniFile diff --git a/core/src/main/java/tachiyomi/core/storage/UniFileTempFileManager.kt b/core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileTempFileManager.kt similarity index 97% rename from core/src/main/java/tachiyomi/core/storage/UniFileTempFileManager.kt rename to core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileTempFileManager.kt index 0aa9f4b85..4ccc5bf43 100644 --- a/core/src/main/java/tachiyomi/core/storage/UniFileTempFileManager.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileTempFileManager.kt @@ -1,4 +1,4 @@ -package tachiyomi.core.storage +package tachiyomi.core.common.storage import android.content.Context import android.os.Build diff --git a/core/src/main/java/tachiyomi/core/util/lang/BooleanExtensions.kt b/core/common/src/main/kotlin/tachiyomi/core/common/util/lang/BooleanExtensions.kt similarity index 52% rename from core/src/main/java/tachiyomi/core/util/lang/BooleanExtensions.kt rename to core/common/src/main/kotlin/tachiyomi/core/common/util/lang/BooleanExtensions.kt index 853499550..ca59ea7a7 100644 --- a/core/src/main/java/tachiyomi/core/util/lang/BooleanExtensions.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/util/lang/BooleanExtensions.kt @@ -1,3 +1,3 @@ -package tachiyomi.core.util.lang +package tachiyomi.core.common.util.lang fun Boolean.toLong() = if (this) 1L else 0L diff --git a/core/src/main/java/tachiyomi/core/util/lang/CoroutinesExtensions.kt b/core/common/src/main/kotlin/tachiyomi/core/common/util/lang/CoroutinesExtensions.kt similarity index 98% rename from core/src/main/java/tachiyomi/core/util/lang/CoroutinesExtensions.kt rename to core/common/src/main/kotlin/tachiyomi/core/common/util/lang/CoroutinesExtensions.kt index 829207a9b..a76bff2cc 100644 --- a/core/src/main/java/tachiyomi/core/util/lang/CoroutinesExtensions.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/util/lang/CoroutinesExtensions.kt @@ -1,4 +1,4 @@ -package tachiyomi.core.util.lang +package tachiyomi.core.common.util.lang import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart diff --git a/core/src/main/java/tachiyomi/core/util/lang/RxCoroutineBridge.kt b/core/common/src/main/kotlin/tachiyomi/core/common/util/lang/RxCoroutineBridge.kt similarity index 98% rename from core/src/main/java/tachiyomi/core/util/lang/RxCoroutineBridge.kt rename to core/common/src/main/kotlin/tachiyomi/core/common/util/lang/RxCoroutineBridge.kt index 56f58e1d4..d6f521cc0 100644 --- a/core/src/main/java/tachiyomi/core/util/lang/RxCoroutineBridge.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/util/lang/RxCoroutineBridge.kt @@ -1,4 +1,4 @@ -package tachiyomi.core.util.lang +package tachiyomi.core.common.util.lang import kotlinx.coroutines.CancellableContinuation import kotlinx.coroutines.InternalCoroutinesApi diff --git a/core/src/main/java/tachiyomi/core/util/lang/SortUtil.kt b/core/common/src/main/kotlin/tachiyomi/core/common/util/lang/SortUtil.kt similarity index 88% rename from core/src/main/java/tachiyomi/core/util/lang/SortUtil.kt rename to core/common/src/main/kotlin/tachiyomi/core/common/util/lang/SortUtil.kt index 03c15d43b..9efe5f79f 100644 --- a/core/src/main/java/tachiyomi/core/util/lang/SortUtil.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/util/lang/SortUtil.kt @@ -1,4 +1,4 @@ -package tachiyomi.core.util.lang +package tachiyomi.core.common.util.lang import java.text.Collator import java.util.Locale diff --git a/core/src/main/java/tachiyomi/core/util/system/ImageUtil.kt b/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt similarity index 99% rename from core/src/main/java/tachiyomi/core/util/system/ImageUtil.kt rename to core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt index 68da52505..1c13d2be7 100644 --- a/core/src/main/java/tachiyomi/core/util/system/ImageUtil.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt @@ -1,4 +1,4 @@ -package tachiyomi.core.util.system +package tachiyomi.core.common.util.system import android.content.Context import android.content.res.Configuration diff --git a/core/src/main/java/tachiyomi/core/util/system/LogcatExtensions.kt b/core/common/src/main/kotlin/tachiyomi/core/common/util/system/LogcatExtensions.kt similarity index 90% rename from core/src/main/java/tachiyomi/core/util/system/LogcatExtensions.kt rename to core/common/src/main/kotlin/tachiyomi/core/common/util/system/LogcatExtensions.kt index fb587b07d..115f647f5 100644 --- a/core/src/main/java/tachiyomi/core/util/system/LogcatExtensions.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/util/system/LogcatExtensions.kt @@ -1,4 +1,4 @@ -package tachiyomi.core.util.system +package tachiyomi.core.common.util.system import logcat.LogPriority import logcat.asLog diff --git a/data/build.gradle.kts b/data/build.gradle.kts index 2d86bf577..f4ce0d039 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -26,7 +26,7 @@ android { dependencies { implementation(projects.sourceApi) implementation(projects.domain) - implementation(projects.core) + implementation(projects.core.common) api(libs.bundles.sqldelight) } diff --git a/data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt index e156726fa..d2284b612 100644 --- a/data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt @@ -2,8 +2,8 @@ package tachiyomi.data.chapter import kotlinx.coroutines.flow.Flow import logcat.LogPriority -import tachiyomi.core.util.lang.toLong -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.toLong +import tachiyomi.core.common.util.system.logcat import tachiyomi.data.DatabaseHandler import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.ChapterUpdate diff --git a/data/src/main/java/tachiyomi/data/history/HistoryRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/history/HistoryRepositoryImpl.kt index 103a0995b..e9157025c 100644 --- a/data/src/main/java/tachiyomi/data/history/HistoryRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/history/HistoryRepositoryImpl.kt @@ -2,7 +2,7 @@ package tachiyomi.data.history import kotlinx.coroutines.flow.Flow import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import tachiyomi.data.DatabaseHandler import tachiyomi.domain.history.model.History import tachiyomi.domain.history.model.HistoryUpdate diff --git a/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt index 1e5a50273..88ef9b3b7 100644 --- a/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt @@ -2,7 +2,7 @@ package tachiyomi.data.manga import kotlinx.coroutines.flow.Flow import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import tachiyomi.data.DatabaseHandler import tachiyomi.data.StringListColumnAdapter import tachiyomi.data.UpdateStrategyColumnAdapter diff --git a/data/src/main/java/tachiyomi/data/source/SourcePagingSource.kt b/data/src/main/java/tachiyomi/data/source/SourcePagingSource.kt index adc1d26a0..fc091c491 100644 --- a/data/src/main/java/tachiyomi/data/source/SourcePagingSource.kt +++ b/data/src/main/java/tachiyomi/data/source/SourcePagingSource.kt @@ -5,7 +5,7 @@ import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.SManga -import tachiyomi.core.util.lang.withIOContext +import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.domain.source.repository.SourcePagingSourceType class SourceSearchPagingSource(source: CatalogueSource, val query: String, val filters: FilterList) : diff --git a/domain/build.gradle.kts b/domain/build.gradle.kts index 949de1f8e..dc8c33315 100644 --- a/domain/build.gradle.kts +++ b/domain/build.gradle.kts @@ -15,7 +15,7 @@ android { dependencies { implementation(projects.sourceApi) - implementation(projects.core) + implementation(projects.core.common) implementation(platform(kotlinx.coroutines.bom)) implementation(kotlinx.bundles.coroutines) diff --git a/domain/src/main/java/tachiyomi/domain/backup/service/BackupPreferences.kt b/domain/src/main/java/tachiyomi/domain/backup/service/BackupPreferences.kt index ba00faa18..41f29392d 100644 --- a/domain/src/main/java/tachiyomi/domain/backup/service/BackupPreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/backup/service/BackupPreferences.kt @@ -1,7 +1,7 @@ package tachiyomi.domain.backup.service -import tachiyomi.core.preference.Preference -import tachiyomi.core.preference.PreferenceStore +import tachiyomi.core.common.preference.Preference +import tachiyomi.core.common.preference.PreferenceStore class BackupPreferences( private val preferenceStore: PreferenceStore, diff --git a/domain/src/main/java/tachiyomi/domain/category/interactor/CreateCategoryWithName.kt b/domain/src/main/java/tachiyomi/domain/category/interactor/CreateCategoryWithName.kt index b9528950d..b6b15b83e 100644 --- a/domain/src/main/java/tachiyomi/domain/category/interactor/CreateCategoryWithName.kt +++ b/domain/src/main/java/tachiyomi/domain/category/interactor/CreateCategoryWithName.kt @@ -1,8 +1,8 @@ package tachiyomi.domain.category.interactor import logcat.LogPriority -import tachiyomi.core.util.lang.withNonCancellableContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.withNonCancellableContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.repository.CategoryRepository import tachiyomi.domain.library.service.LibraryPreferences diff --git a/domain/src/main/java/tachiyomi/domain/category/interactor/DeleteCategory.kt b/domain/src/main/java/tachiyomi/domain/category/interactor/DeleteCategory.kt index 8fee4d982..bf26d959f 100644 --- a/domain/src/main/java/tachiyomi/domain/category/interactor/DeleteCategory.kt +++ b/domain/src/main/java/tachiyomi/domain/category/interactor/DeleteCategory.kt @@ -1,8 +1,8 @@ package tachiyomi.domain.category.interactor import logcat.LogPriority -import tachiyomi.core.util.lang.withNonCancellableContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.withNonCancellableContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.category.model.CategoryUpdate import tachiyomi.domain.category.repository.CategoryRepository diff --git a/domain/src/main/java/tachiyomi/domain/category/interactor/RenameCategory.kt b/domain/src/main/java/tachiyomi/domain/category/interactor/RenameCategory.kt index 1cbf5e276..59c4c193e 100644 --- a/domain/src/main/java/tachiyomi/domain/category/interactor/RenameCategory.kt +++ b/domain/src/main/java/tachiyomi/domain/category/interactor/RenameCategory.kt @@ -1,8 +1,8 @@ package tachiyomi.domain.category.interactor import logcat.LogPriority -import tachiyomi.core.util.lang.withNonCancellableContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.withNonCancellableContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.model.CategoryUpdate import tachiyomi.domain.category.repository.CategoryRepository diff --git a/domain/src/main/java/tachiyomi/domain/category/interactor/ReorderCategory.kt b/domain/src/main/java/tachiyomi/domain/category/interactor/ReorderCategory.kt index 7561f52c8..a64824801 100644 --- a/domain/src/main/java/tachiyomi/domain/category/interactor/ReorderCategory.kt +++ b/domain/src/main/java/tachiyomi/domain/category/interactor/ReorderCategory.kt @@ -3,8 +3,8 @@ package tachiyomi.domain.category.interactor import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import logcat.LogPriority -import tachiyomi.core.util.lang.withNonCancellableContext -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.lang.withNonCancellableContext +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.model.CategoryUpdate import tachiyomi.domain.category.repository.CategoryRepository diff --git a/domain/src/main/java/tachiyomi/domain/category/interactor/SetMangaCategories.kt b/domain/src/main/java/tachiyomi/domain/category/interactor/SetMangaCategories.kt index 857ea272b..604e3f0d1 100644 --- a/domain/src/main/java/tachiyomi/domain/category/interactor/SetMangaCategories.kt +++ b/domain/src/main/java/tachiyomi/domain/category/interactor/SetMangaCategories.kt @@ -1,7 +1,7 @@ package tachiyomi.domain.category.interactor import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.manga.repository.MangaRepository class SetMangaCategories( diff --git a/domain/src/main/java/tachiyomi/domain/category/interactor/UpdateCategory.kt b/domain/src/main/java/tachiyomi/domain/category/interactor/UpdateCategory.kt index 65d6d31ce..f3f601ceb 100644 --- a/domain/src/main/java/tachiyomi/domain/category/interactor/UpdateCategory.kt +++ b/domain/src/main/java/tachiyomi/domain/category/interactor/UpdateCategory.kt @@ -1,6 +1,6 @@ package tachiyomi.domain.category.interactor -import tachiyomi.core.util.lang.withNonCancellableContext +import tachiyomi.core.common.util.lang.withNonCancellableContext import tachiyomi.domain.category.model.CategoryUpdate import tachiyomi.domain.category.repository.CategoryRepository diff --git a/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChapter.kt b/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChapter.kt index 99c628451..4b4b9d94d 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChapter.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChapter.kt @@ -1,7 +1,7 @@ package tachiyomi.domain.chapter.interactor import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.repository.ChapterRepository diff --git a/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChaptersByMangaId.kt b/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChaptersByMangaId.kt index 66dab15c7..1dee7770e 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChaptersByMangaId.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChaptersByMangaId.kt @@ -1,7 +1,7 @@ package tachiyomi.domain.chapter.interactor import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.repository.ChapterRepository diff --git a/domain/src/main/java/tachiyomi/domain/chapter/interactor/SetMangaDefaultChapterFlags.kt b/domain/src/main/java/tachiyomi/domain/chapter/interactor/SetMangaDefaultChapterFlags.kt index ab8eaaa55..4c968b39a 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/interactor/SetMangaDefaultChapterFlags.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/interactor/SetMangaDefaultChapterFlags.kt @@ -1,6 +1,6 @@ package tachiyomi.domain.chapter.interactor -import tachiyomi.core.util.lang.withNonCancellableContext +import tachiyomi.core.common.util.lang.withNonCancellableContext import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.manga.interactor.GetFavorites import tachiyomi.domain.manga.interactor.SetMangaChapterFlags diff --git a/domain/src/main/java/tachiyomi/domain/chapter/interactor/UpdateChapter.kt b/domain/src/main/java/tachiyomi/domain/chapter/interactor/UpdateChapter.kt index 150f6b223..3daaf9010 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/interactor/UpdateChapter.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/interactor/UpdateChapter.kt @@ -1,7 +1,7 @@ package tachiyomi.domain.chapter.interactor import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.chapter.model.ChapterUpdate import tachiyomi.domain.chapter.repository.ChapterRepository diff --git a/domain/src/main/java/tachiyomi/domain/chapter/service/ChapterSort.kt b/domain/src/main/java/tachiyomi/domain/chapter/service/ChapterSort.kt index d55c51334..795805555 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/service/ChapterSort.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/service/ChapterSort.kt @@ -1,6 +1,6 @@ package tachiyomi.domain.chapter.service -import tachiyomi.core.util.lang.compareToWithCollator +import tachiyomi.core.common.util.lang.compareToWithCollator import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.manga.model.Manga 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 84dfaecfe..a0625e5a7 100644 --- a/domain/src/main/java/tachiyomi/domain/download/service/DownloadPreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/download/service/DownloadPreferences.kt @@ -1,6 +1,6 @@ package tachiyomi.domain.download.service -import tachiyomi.core.preference.PreferenceStore +import tachiyomi.core.common.preference.PreferenceStore class DownloadPreferences( private val preferenceStore: PreferenceStore, 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 22174c0e2..437dc54bc 100644 --- a/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/library/service/LibraryPreferences.kt @@ -1,9 +1,9 @@ package tachiyomi.domain.library.service -import tachiyomi.core.preference.Preference -import tachiyomi.core.preference.PreferenceStore -import tachiyomi.core.preference.TriState -import tachiyomi.core.preference.getEnum +import tachiyomi.core.common.preference.Preference +import tachiyomi.core.common.preference.PreferenceStore +import tachiyomi.core.common.preference.TriState +import tachiyomi.core.common.preference.getEnum import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibrarySort import tachiyomi.domain.manga.model.Manga diff --git a/domain/src/main/java/tachiyomi/domain/manga/interactor/GetManga.kt b/domain/src/main/java/tachiyomi/domain/manga/interactor/GetManga.kt index 816dda7ac..d4cad69d6 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/interactor/GetManga.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/interactor/GetManga.kt @@ -2,7 +2,7 @@ package tachiyomi.domain.manga.interactor import kotlinx.coroutines.flow.Flow import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.repository.MangaRepository diff --git a/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt b/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt index 2b99c29bf..0ffe9856a 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt @@ -2,7 +2,7 @@ package tachiyomi.domain.manga.model import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.UpdateStrategy -import tachiyomi.core.preference.TriState +import tachiyomi.core.common.preference.TriState import java.io.Serializable import java.time.Instant diff --git a/domain/src/main/java/tachiyomi/domain/manga/model/TriState.kt b/domain/src/main/java/tachiyomi/domain/manga/model/TriState.kt index 75a21f959..49b834772 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/model/TriState.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/model/TriState.kt @@ -1,6 +1,6 @@ package tachiyomi.domain.manga.model -import tachiyomi.core.preference.TriState +import tachiyomi.core.common.preference.TriState inline fun applyFilter(filter: TriState, predicate: () -> Boolean): Boolean = when (filter) { TriState.DISABLED -> true diff --git a/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt b/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt index 380da7b1e..e1ba48c79 100644 --- a/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt +++ b/domain/src/main/java/tachiyomi/domain/release/interactor/GetApplicationRelease.kt @@ -1,7 +1,7 @@ package tachiyomi.domain.release.interactor -import tachiyomi.core.preference.Preference -import tachiyomi.core.preference.PreferenceStore +import tachiyomi.core.common.preference.Preference +import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.domain.release.model.Release import tachiyomi.domain.release.service.ReleaseService import java.time.Instant diff --git a/domain/src/main/java/tachiyomi/domain/storage/service/StoragePreferences.kt b/domain/src/main/java/tachiyomi/domain/storage/service/StoragePreferences.kt index a49a54e3c..f29949cff 100644 --- a/domain/src/main/java/tachiyomi/domain/storage/service/StoragePreferences.kt +++ b/domain/src/main/java/tachiyomi/domain/storage/service/StoragePreferences.kt @@ -1,8 +1,8 @@ package tachiyomi.domain.storage.service -import tachiyomi.core.preference.Preference -import tachiyomi.core.preference.PreferenceStore -import tachiyomi.core.storage.FolderProvider +import tachiyomi.core.common.preference.Preference +import tachiyomi.core.common.preference.PreferenceStore +import tachiyomi.core.common.storage.FolderProvider class StoragePreferences( private val folderProvider: FolderProvider, diff --git a/domain/src/main/java/tachiyomi/domain/track/interactor/DeleteTrack.kt b/domain/src/main/java/tachiyomi/domain/track/interactor/DeleteTrack.kt index 2a30e0ffc..4204b3872 100644 --- a/domain/src/main/java/tachiyomi/domain/track/interactor/DeleteTrack.kt +++ b/domain/src/main/java/tachiyomi/domain/track/interactor/DeleteTrack.kt @@ -1,7 +1,7 @@ package tachiyomi.domain.track.interactor import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.track.repository.TrackRepository class DeleteTrack( diff --git a/domain/src/main/java/tachiyomi/domain/track/interactor/GetTracks.kt b/domain/src/main/java/tachiyomi/domain/track/interactor/GetTracks.kt index 018d5ffb3..851ecd0ed 100644 --- a/domain/src/main/java/tachiyomi/domain/track/interactor/GetTracks.kt +++ b/domain/src/main/java/tachiyomi/domain/track/interactor/GetTracks.kt @@ -2,7 +2,7 @@ package tachiyomi.domain.track.interactor import kotlinx.coroutines.flow.Flow import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.track.model.Track import tachiyomi.domain.track.repository.TrackRepository diff --git a/domain/src/main/java/tachiyomi/domain/track/interactor/InsertTrack.kt b/domain/src/main/java/tachiyomi/domain/track/interactor/InsertTrack.kt index c9c95d2f1..3277e9b9c 100644 --- a/domain/src/main/java/tachiyomi/domain/track/interactor/InsertTrack.kt +++ b/domain/src/main/java/tachiyomi/domain/track/interactor/InsertTrack.kt @@ -1,7 +1,7 @@ package tachiyomi.domain.track.interactor import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.track.model.Track import tachiyomi.domain.track.repository.TrackRepository diff --git a/domain/src/test/java/tachiyomi/domain/release/interactor/GetApplicationReleaseTest.kt b/domain/src/test/java/tachiyomi/domain/release/interactor/GetApplicationReleaseTest.kt index f813094fe..fe564050a 100644 --- a/domain/src/test/java/tachiyomi/domain/release/interactor/GetApplicationReleaseTest.kt +++ b/domain/src/test/java/tachiyomi/domain/release/interactor/GetApplicationReleaseTest.kt @@ -8,8 +8,8 @@ import io.mockk.mockk import kotlinx.coroutines.test.runTest import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import tachiyomi.core.preference.Preference -import tachiyomi.core.preference.PreferenceStore +import tachiyomi.core.common.preference.Preference +import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.domain.release.model.Release import tachiyomi.domain.release.service.ReleaseService import java.time.Instant diff --git a/presentation-core/build.gradle.kts b/presentation-core/build.gradle.kts index 59a97d0fe..cfae8ba2a 100644 --- a/presentation-core/build.gradle.kts +++ b/presentation-core/build.gradle.kts @@ -21,7 +21,7 @@ android { } dependencies { - api(projects.core) + api(projects.core.common) api(projects.i18n) // Compose diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt index d29d44b88..abdf61c9f 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/SettingsItems.kt @@ -43,9 +43,9 @@ import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.unit.dp import dev.icerock.moko.resources.StringResource -import tachiyomi.core.preference.Preference -import tachiyomi.core.preference.TriState -import tachiyomi.core.preference.toggle +import tachiyomi.core.common.preference.Preference +import tachiyomi.core.common.preference.TriState +import tachiyomi.core.common.preference.toggle import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.theme.header diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/i18n/Localize.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/i18n/Localize.kt index 081281f7f..93013ba2b 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/i18n/Localize.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/i18n/Localize.kt @@ -5,8 +5,8 @@ import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.platform.LocalContext import dev.icerock.moko.resources.PluralsResource import dev.icerock.moko.resources.StringResource -import tachiyomi.core.i18n.pluralStringResource -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.pluralStringResource +import tachiyomi.core.common.i18n.stringResource @Composable @ReadOnlyComposable diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/util/Preference.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/util/Preference.kt index c68818927..4b7a5018a 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/util/Preference.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/util/Preference.kt @@ -4,7 +4,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState import androidx.compose.runtime.remember -import tachiyomi.core.preference.Preference +import tachiyomi.core.common.preference.Preference @Composable fun Preference.collectAsState(): State { diff --git a/presentation-widget/build.gradle.kts b/presentation-widget/build.gradle.kts index 21a0a4bbd..896979d98 100644 --- a/presentation-widget/build.gradle.kts +++ b/presentation-widget/build.gradle.kts @@ -21,7 +21,7 @@ android { } dependencies { - implementation(projects.core) + implementation(projects.core.common) implementation(projects.domain) implementation(projects.presentationCore) api(projects.i18n) diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt index 76d84aee1..e4977bfd3 100644 --- a/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt +++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt @@ -33,7 +33,7 @@ import eu.kanade.tachiyomi.util.system.dpToPx import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.map -import tachiyomi.core.util.lang.withIOContext +import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.domain.manga.model.MangaCover import tachiyomi.domain.updates.interactor.GetUpdates import tachiyomi.domain.updates.model.UpdatesWithRelations diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/WidgetManager.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/WidgetManager.kt index 285ba7b13..eda0ff0a1 100644 --- a/presentation-widget/src/main/java/tachiyomi/presentation/widget/WidgetManager.kt +++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/WidgetManager.kt @@ -9,7 +9,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import logcat.LogPriority -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.updates.interactor.GetUpdates class WidgetManager( diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/components/LockedWidget.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/components/LockedWidget.kt index 20096ccbf..afa422abf 100644 --- a/presentation-widget/src/main/java/tachiyomi/presentation/widget/components/LockedWidget.kt +++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/components/LockedWidget.kt @@ -15,7 +15,7 @@ import androidx.glance.text.Text import androidx.glance.text.TextAlign import androidx.glance.text.TextStyle import androidx.glance.unit.ColorProvider -import tachiyomi.core.Constants +import tachiyomi.core.common.Constants import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/components/UpdatesWidget.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/components/UpdatesWidget.kt index d31528170..e90e07983 100644 --- a/presentation-widget/src/main/java/tachiyomi/presentation/widget/components/UpdatesWidget.kt +++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/components/UpdatesWidget.kt @@ -22,7 +22,7 @@ import androidx.glance.text.Text import androidx.glance.text.TextStyle import androidx.glance.unit.ColorProvider import kotlinx.collections.immutable.ImmutableList -import tachiyomi.core.Constants +import tachiyomi.core.common.Constants import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.widget.util.calculateRowAndColumnCount diff --git a/settings.gradle.kts b/settings.gradle.kts index 42b473db8..b1a2a2dd5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -39,13 +39,13 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") rootProject.name = "Mihon" include(":app") -include(":i18n") -include(":source-api") -include(":core") -include(":macrobenchmark") +include(":core-metadata") +include(":core:common") include(":data") include(":domain") -include(":presentation-widget") +include(":i18n") +include(":macrobenchmark") include(":presentation-core") +include(":presentation-widget") +include(":source-api") include(":source-local") -include(":core-metadata") diff --git a/source-api/build.gradle.kts b/source-api/build.gradle.kts index 125252def..209136e48 100644 --- a/source-api/build.gradle.kts +++ b/source-api/build.gradle.kts @@ -17,7 +17,7 @@ kotlin { } val androidMain by getting { dependencies { - implementation(projects.core) + implementation(projects.core.common) api(libs.preferencektx) // Workaround for https://youtrack.jetbrains.com/issue/KT-57605 diff --git a/source-api/src/androidMain/kotlin/eu/kanade/tachiyomi/util/RxExtension.kt b/source-api/src/androidMain/kotlin/eu/kanade/tachiyomi/util/RxExtension.kt index 6938bb406..43fe01d83 100644 --- a/source-api/src/androidMain/kotlin/eu/kanade/tachiyomi/util/RxExtension.kt +++ b/source-api/src/androidMain/kotlin/eu/kanade/tachiyomi/util/RxExtension.kt @@ -1,6 +1,6 @@ package eu.kanade.tachiyomi.util import rx.Observable -import tachiyomi.core.util.lang.awaitSingle +import tachiyomi.core.common.util.lang.awaitSingle actual suspend fun Observable.awaitSingle(): T = awaitSingle() diff --git a/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/CatalogueSource.kt b/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/CatalogueSource.kt index 937fa28a9..9be673541 100644 --- a/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/CatalogueSource.kt +++ b/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/CatalogueSource.kt @@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.source import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.MangasPage import rx.Observable -import tachiyomi.core.util.lang.awaitSingle +import tachiyomi.core.common.util.lang.awaitSingle interface CatalogueSource : Source { diff --git a/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt b/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt index 3a079953b..7ffa538b5 100644 --- a/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt +++ b/source-api/src/commonMain/kotlin/eu/kanade/tachiyomi/source/online/HttpSource.kt @@ -16,7 +16,7 @@ import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import rx.Observable -import tachiyomi.core.util.lang.awaitSingle +import tachiyomi.core.common.util.lang.awaitSingle import uy.kohesive.injekt.injectLazy import java.net.URI import java.net.URISyntaxException diff --git a/source-local/build.gradle.kts b/source-local/build.gradle.kts index 75739a8d7..b30b8acd4 100644 --- a/source-local/build.gradle.kts +++ b/source-local/build.gradle.kts @@ -17,7 +17,7 @@ kotlin { } val androidMain by getting { dependencies { - implementation(projects.core) + implementation(projects.core.common) implementation(projects.coreMetadata) // Move ChapterRecognition to separate module? diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt index 93bb2ec6f..e33dbbf5f 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt @@ -18,18 +18,18 @@ import kotlinx.serialization.json.decodeFromStream import logcat.LogPriority import nl.adaptivity.xmlutil.AndroidXmlReader import nl.adaptivity.xmlutil.serialization.XML -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE import tachiyomi.core.metadata.comicinfo.ComicInfo import tachiyomi.core.metadata.comicinfo.copyFromComicInfo import tachiyomi.core.metadata.comicinfo.getComicInfo import tachiyomi.core.metadata.tachiyomi.MangaDetails -import tachiyomi.core.storage.UniFileTempFileManager -import tachiyomi.core.storage.extension -import tachiyomi.core.storage.nameWithoutExtension -import tachiyomi.core.util.lang.withIOContext -import tachiyomi.core.util.system.ImageUtil -import tachiyomi.core.util.system.logcat +import tachiyomi.core.common.storage.UniFileTempFileManager +import tachiyomi.core.common.storage.extension +import tachiyomi.core.common.storage.nameWithoutExtension +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.system.ImageUtil +import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.chapter.service.ChapterRecognition import tachiyomi.domain.manga.model.Manga import tachiyomi.i18n.MR diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/filter/OrderBy.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/filter/OrderBy.kt index 9be063b9a..956cceeee 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/filter/OrderBy.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/filter/OrderBy.kt @@ -2,7 +2,7 @@ package tachiyomi.source.local.filter import android.content.Context import eu.kanade.tachiyomi.source.model.Filter -import tachiyomi.core.i18n.stringResource +import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR sealed class OrderBy(context: Context, selection: Selection) : Filter.Sort( diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt index 0f5a4c343..a54e3e7e4 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/image/LocalCoverManager.kt @@ -4,8 +4,8 @@ import android.content.Context import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.util.storage.DiskUtil -import tachiyomi.core.storage.nameWithoutExtension -import tachiyomi.core.util.system.ImageUtil +import tachiyomi.core.common.storage.nameWithoutExtension +import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.source.local.io.LocalSourceFileSystem import java.io.InputStream diff --git a/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Archive.kt b/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Archive.kt index a8f5a0740..e968adc7d 100644 --- a/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Archive.kt +++ b/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Archive.kt @@ -1,7 +1,7 @@ package tachiyomi.source.local.io import com.hippo.unifile.UniFile -import tachiyomi.core.storage.extension +import tachiyomi.core.common.storage.extension object Archive { diff --git a/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Format.kt b/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Format.kt index 0f29ae8ab..5b22e41e2 100644 --- a/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Format.kt +++ b/source-local/src/commonMain/kotlin/tachiyomi/source/local/io/Format.kt @@ -1,7 +1,7 @@ package tachiyomi.source.local.io import com.hippo.unifile.UniFile -import tachiyomi.core.storage.extension +import tachiyomi.core.common.storage.extension sealed interface Format { data class Directory(val file: UniFile) : Format From 79f46b25f663c7f49fbd4e292f0f5f210e266dc9 Mon Sep 17 00:00:00 2001 From: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com> Date: Mon, 29 Jan 2024 22:08:28 +0500 Subject: [PATCH 032/212] Fix new extension install not registering in app. (#275) * Fix extension install not registering * fix duplicate key compose error on extension update * fix doubling of extension after update * not needed --- .../eu/kanade/tachiyomi/extension/ExtensionManager.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt index d8f56b878..4a5eff1ac 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt @@ -340,9 +340,12 @@ class ExtensionManager( override fun onExtensionUntrusted(extension: Extension.Untrusted) { val installedExtension = _installedExtensionsFlow.value .find { it.pkgName == extension.pkgName } - ?: return - _installedExtensionsFlow.value -= installedExtension - _untrustedExtensionsFlow.value += extension + + if (installedExtension != null) { + _installedExtensionsFlow.value -= installedExtension + } else { + _untrustedExtensionsFlow.value += extension + } } override fun onPackageUninstalled(pkgName: String) { From b2d22f86c647e24eed6d9b19f36ac2c444525c87 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 29 Jan 2024 21:14:42 +0600 Subject: [PATCH 033/212] Lint --- app/src/main/java/eu/kanade/domain/manga/model/Manga.kt | 2 +- .../main/java/eu/kanade/tachiyomi/data/download/Downloader.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt b/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt index 5334b598e..1b9dd62c8 100644 --- a/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt +++ b/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt @@ -5,9 +5,9 @@ import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode +import tachiyomi.core.common.preference.TriState import tachiyomi.core.metadata.comicinfo.ComicInfo import tachiyomi.core.metadata.comicinfo.ComicInfoPublishingStatus -import tachiyomi.core.common.preference.TriState import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.manga.model.Manga import uy.kohesive.injekt.Injekt diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index 8fb9ab14d..facbe605a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -41,14 +41,14 @@ import logcat.LogPriority import nl.adaptivity.xmlutil.serialization.XML import okhttp3.Response import tachiyomi.core.common.i18n.stringResource -import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE -import tachiyomi.core.metadata.comicinfo.ComicInfo import tachiyomi.core.common.storage.extension import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.lang.launchNow import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.logcat +import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE +import tachiyomi.core.metadata.comicinfo.ComicInfo import tachiyomi.domain.category.interactor.GetCategories import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.download.service.DownloadPreferences From 63146e717ba2cf178b79ae3de4b9b15b0ecc788f Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Mon, 29 Jan 2024 21:31:16 +0100 Subject: [PATCH 034/212] Translations update from Hosted Weblate (#269) * Translated using Weblate (Persian) Currently translated at 84.8% (673 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fa/ * Translated using Weblate (Finnish) Currently translated at 85.3% (677 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fi/ * Translated using Weblate (Chuvash) Currently translated at 88.2% (15 of 17 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/cv/ --------- Co-authored-by: Arash Co-authored-by: A Co-authored-by: C201 --- .../commonMain/resources/MR/cv/plurals.xml | 2 +- .../commonMain/resources/MR/fa/strings.xml | 18 +++++++ .../commonMain/resources/MR/fi/strings.xml | 53 +++++++++++++++---- 3 files changed, 62 insertions(+), 11 deletions(-) diff --git a/i18n/src/commonMain/resources/MR/cv/plurals.xml b/i18n/src/commonMain/resources/MR/cv/plurals.xml index 9cc1c822f..b96d0872c 100644 --- a/i18n/src/commonMain/resources/MR/cv/plurals.xml +++ b/i18n/src/commonMain/resources/MR/cv/plurals.xml @@ -41,7 +41,7 @@ %d сӑнану - 1 сыпӑк ҫук + %d сыпӑк ҫук %d сыпӑк ҫук diff --git a/i18n/src/commonMain/resources/MR/fa/strings.xml b/i18n/src/commonMain/resources/MR/fa/strings.xml index 3466ae242..ba003bb23 100644 --- a/i18n/src/commonMain/resources/MR/fa/strings.xml +++ b/i18n/src/commonMain/resources/MR/fa/strings.xml @@ -658,4 +658,22 @@ شما هیچ مخزنی برای تنظیم ندارید. مخزن منبع باز مقدار رد باقی مانده در نمایش گر های E-ink کاهش میابد + انتخاب یک پوشه + یک پوشه باید انتخاب شود + راهنمای زخیره سازی + مجوز نصب برنامه ها + ایجاد کردن + خطای کامل: + تنظیمات برنامه + تنظیمات منبع + حاوی تنظیمات حساس (یه عنوان مثال رمز ورود ناظر) + فایل پشتیبانی ایجاد نشد + استفاده از فظای زخیره سازی + در دسترس: %1$s / کل:%2$s + هنگام راه‌اندازی برنامه، حافظه پنهان قسمت را پاک کنید + درحال همگام سازی کتاب خانه + قابل استفاده برای به روز رسانی خودکار، بارگیری قسمت ها، و منابع محلی. + آخرین پشتیبان گیری خودکار: %s + DNS بیش از HTTPS (DoH) + همگام سازی کتاب خانه کامل شد \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/fi/strings.xml b/i18n/src/commonMain/resources/MR/fi/strings.xml index 1eec93175..24e863234 100644 --- a/i18n/src/commonMain/resources/MR/fi/strings.xml +++ b/i18n/src/commonMain/resources/MR/fi/strings.xml @@ -141,11 +141,9 @@ Luottamaton Poista asennus Luottamattomat laajennokset - Tämä laajennus on allekirjoitettu luottamattomalla sertifikaatilla ja sitä ei ole aktivoitu. + Haitallinen laajennus voi lukea tallennetut kirjautumistiedot Mihonista tai suorittaa luvatonta koodia. \n -\nHaitallinen laajennus voisi lukea mahdolliset kirjautumistiedot Mihonista tai suorittaa luvatonta koodia. -\n -\nLuottamalla tähän sertifikaattiin hyväksyt nämä riskit. +\nLuottamalla tähän laajennokseen hyväksyt nämä riskit. Koko näyttö Animoi sivusiirtymät Tuplanäpäytyksen animaation nopeus @@ -201,7 +199,7 @@ Neljänneksi viimeinen luku Viidenneksi viimeinen luku Lataa uudet luvut - Palvelut + Seurantapalvelut Voidaan käyttää nykyisen kirjaston palauttamiseen Palauta kirjasto varmuuskopiointi-tiedostosta Varmuuskopioinnin tiheys @@ -290,7 +288,7 @@ Päivitä Kirjasto Vanhentunut - Tämä laajennus ei ole enää saatavilla. + Tämä laajennos ei ole enää saatavilla. Se ei välttämättä toimi oikein ja voi aiheuttaa ongelmia Mihonin kanssa. Laajennoksen poistoa suositellaan. Päivämäärän muoto Globaali päivitys Kirjaa ulos kohteesta %1$s\? @@ -374,7 +372,7 @@ Tiedot Puuttuvat lähteet: Varmuuskopio ei sisällä mangaa. - Virheellinen varmuuskopio + Virheellinen varmuuskopio: Tarkista uusi kansi ja tiedot päivitettäessä kirjastoa Päivitä metatiedot automaattisesti Siirrä @@ -412,7 +410,7 @@ Seurantapalvelimet joihin et ole kirjautunut sisään: Poista kirjanmerkityt luvut Poista luvut - Voi sisältää NSFW (18+) -sisältöä + Tämän laajennoksen lähteet voivat sisältää NSFW (18+) -sisältöä 18+ Tämä ei estä epävirallisia tai mahdollisesti väärin merkittyjä laajennuksia näyttämästä NSFW 18+ sisältöä sovelluksessa. Lukuja ei löytynyt @@ -538,7 +536,7 @@ Viimeisin manga päivitys Lukemattomien määrä Poista kategoria - Jäljitä + Seuraa Alhainen Poissuljetut kategoriat Alhaisin @@ -594,7 +592,7 @@ Poista kaikkien sarjojen lukutila ja lukusuunta Peruttu Sivun %d tiedostopolkua ei löytynyt - Parannetut palvelut + Parannetut seurantapalvelut Lukijan asetuksia ei voitu poistaa Järjestä Kansikuva @@ -646,4 +644,39 @@ Haluatko järjestää kategoriat aakkosjärjestykseen? Kopioi leikepöydälle Siirrä sarja pohjimmaiseksi + Ei nyt + Sisäinen virhe: Lisätietoja saat kaatumisen lokitiedoista + Akun käyttö taustalla + Lukutila, näyttö, navigointi + Lähteet, laajennokset, globaali haku + Manuaaliset ja automaattiset varmuuskopiot, tallennustila + Applikaation lukko, turvallinen ruutu + Kaatumislokien vedos, akun optimoinnit + Pohjoismainen + Suhteelliset aikaleimat + Käytä muotoa \"%1$s\", kuin muotoa \"%2$s\" + Laajennosten asentamiseen tarvitaan lupia. Napauta tästä myöntääksesi luvat. + Lisää laajennosten lähde + Laajennosten lähteen URL + Lisää laajennosten lähteitä Mihoniin. URL-osoitteen tulisi päättyä \"index.min.json\". + Tämä laajennosten lähde on jo lisättynä! + Poista laajennosten lähde + Epäkelpo laajennosten lähteen URL-osoite + Haluatko poistaa laajennosten lähteen \"%s\"? + Peilaa käännetyt leveät sivut + Tuplanapauta lähentääksesi + Vähentää haamujälkiä e-paperi näytöillä + Ohita kaksoisluvut + Jaa korkeat kuvat + Luo + Sovita leveät sivut kääntämällä + Valikon piilottamisen herkkyys vierittäessä + Väläytä näyttöä sivunvaihdon yhteydessä + Käytetään automaattisille varmuuskopioille, lukujen latauksille ja paikallisille lähteille. + Vältä keskeytyksiä pitkään jatkuvissa toimenpiteissä, kuten kirjaston päivitys, lataukset, ja varmuuskopion palautukset. + %d per rivi + Teema, ajan ja päivämäärän formaatti + Automaattinen lataus, ennakkolataus + Yksisuuntainen synkronointi, paranneltu synkronointi + Kategoriat, globaalit päivitykset, luvun pyyhkäisy \ No newline at end of file From ddbe8efbc5cb0665be0775b128e70ace8cee2b12 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Tue, 30 Jan 2024 01:49:51 +0600 Subject: [PATCH 035/212] [skip ci] Increase size of the message indicating which apk to download --- .github/workflows/build_push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 1d14c640b..ba6b8638d 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -99,7 +99,7 @@ jobs: | x86 | ${{ env.APK_X86_SHA }} | | x86_64 | ${{ env.APK_X86_64_SHA }} | - If you are unsure which version to choose then go with mihon-${{ env.VERSION_TAG }}.apk + ## If you are unsure which version to choose then go with mihon-${{ env.VERSION_TAG }}.apk files: | mihon-${{ env.VERSION_TAG }}.apk mihon-arm64-v8a-${{ env.VERSION_TAG }}.apk From 0f4de03d7a77b52490dc9a95e96a308b93b26e4f Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Tue, 30 Jan 2024 02:22:29 +0600 Subject: [PATCH 036/212] Don't throw `MALTokenExpired` whenever we fail to refresh MAL token Also cleanup --- .../myanimelist/MyAnimeListInterceptor.kt | 60 +++++++++---------- .../track/myanimelist/MyAnimeListModels.kt | 12 ++-- 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt index caabcf416..b56a10e46 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt @@ -21,9 +21,8 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList) : Interceptor } val originalRequest = chain.request() - // Refresh access token if expired - if (oauth != null && oauth!!.isExpired()) { - setAuth(refreshToken(chain)) + if (oauth?.isExpired() == true) { + refreshToken(chain) } if (oauth == null) { @@ -36,27 +35,7 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList) : Interceptor .header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})") .build() - val response = chain.proceed(authRequest) - val tokenIsExpired = response.headers["www-authenticate"] - ?.contains("The access token expired") ?: false - - // Retry the request once with a new token in case it was not already refreshed - // by the is expired check before. - if (response.code == 401 && tokenIsExpired) { - response.close() - - val newToken = refreshToken(chain) - setAuth(newToken) - - val newRequest = originalRequest.newBuilder() - .addHeader("Authorization", "Bearer ${newToken.access_token}") - .header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})") - .build() - - return chain.proceed(newRequest) - } - - return response + return chain.proceed(authRequest) } /** @@ -68,22 +47,37 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList) : Interceptor myanimelist.saveOAuth(oauth) } - private fun refreshToken(chain: Interceptor.Chain): OAuth { + private fun refreshToken(chain: Interceptor.Chain): OAuth = synchronized(this) { + if (tokenExpired) throw MALTokenExpired() + oauth?.takeUnless { it.isExpired() }?.let { return@synchronized it } + + val response = try { + chain.proceed(MyAnimeListApi.refreshTokenRequest(oauth!!)) + } catch (_: Throwable) { + throw MALTokenRefreshFailed() + } + + if (response.code == 401) { + myanimelist.setAuthExpired() + throw MALTokenExpired() + } + return runCatching { - val oauthResponse = chain.proceed(MyAnimeListApi.refreshTokenRequest(oauth!!)) - if (oauthResponse.code == 401) { - myanimelist.setAuthExpired() - } - if (oauthResponse.isSuccessful) { - with(json) { oauthResponse.parseAs() } + if (response.isSuccessful) { + with(json) { response.parseAs() } } else { - oauthResponse.close() + response.close() null } } .getOrNull() - ?: throw MALTokenExpired() + ?.also { + this.oauth = it + myanimelist.saveOAuth(it) + } + ?: throw MALTokenRefreshFailed() } } +class MALTokenRefreshFailed : IOException("MAL: Failed to refresh account token") class MALTokenExpired : IOException("MAL: Login has expired") diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListModels.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListModels.kt index dea4231b3..1ae02142f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListModels.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListModels.kt @@ -5,14 +5,16 @@ import kotlinx.serialization.Serializable @Serializable data class OAuth( + val token_type: String, val refresh_token: String, val access_token: String, - val token_type: String, - val created_at: Long = System.currentTimeMillis(), val expires_in: Long, -) - -fun OAuth.isExpired() = System.currentTimeMillis() > created_at + (expires_in * 1000) + val created_at: Long = System.currentTimeMillis(), +) { + // Assumes expired a minute earlier + private val adjustedExpiresIn: Long = (expires_in - 60) * 1000 + fun isExpired() = created_at + adjustedExpiresIn < System.currentTimeMillis() +} fun Track.toMyAnimeListStatus() = when (status) { MyAnimeList.READING -> "reading" From 1581b876cfc9d98620e164a48084be88c348457e Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Tue, 30 Jan 2024 02:29:18 +0600 Subject: [PATCH 037/212] Remove old `ISSUE_TEMPLATE.md` --- .github/ISSUE_TEMPLATE.md | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 01cf0e1d0..000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,34 +0,0 @@ -**PLEASE READ THIS** - -I acknowledge that: - -- I have updated: - - To the latest version of the app (stable is v0.15.3) - - All extensions -- I have gone through the FAQ (https://mihon.app/docs/faq/general) and troubleshooting guide (https://mihon.app/docs/guides/troubleshooting/) -- If this is an issue with an official extension, that I should be opening an issue in https://github.com/tachiyomiorg/extensions -- I have searched the existing issues and this is new ticket **NOT** a duplicate or related to another open or closed issue -- I will fill out the title and the information in this template - -Note that the issue will be automatically closed if you do not fill out the title or requested information. - -**DELETE THIS SECTION IF YOU HAVE READ AND ACKNOWLEDGED IT** - ---- - -## Device information -* Tachiyomi version: ? -* Android version: ? -* Device: ? - -## Steps to reproduce -1. First step -2. Second step - -## Issue/Request -? - -## Other details -Additional details and attachments. - -If you're experiencing crashes, share the crash logs from More → Settings → Advanced → Dump crash logs. From 139663acfca72b89bd9f560bf4c0c8a9155ce108 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Tue, 30 Jan 2024 02:31:38 +0600 Subject: [PATCH 038/212] Release v0.16.3 --- .github/ISSUE_TEMPLATE/report_issue.yml | 4 ++-- .github/ISSUE_TEMPLATE/request_feature.yml | 2 +- app/build.gradle.kts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/report_issue.yml b/.github/ISSUE_TEMPLATE/report_issue.yml index ccf4547e3..8fac5785f 100644 --- a/.github/ISSUE_TEMPLATE/report_issue.yml +++ b/.github/ISSUE_TEMPLATE/report_issue.yml @@ -53,7 +53,7 @@ body: label: Mihon version description: You can find your Mihon version in **More → About**. placeholder: | - Example: "0.16.2" + Example: "0.16.3" validations: required: true @@ -96,7 +96,7 @@ body: required: true - label: I have gone through the [FAQ](https://mihon.app/docs/faq/general) and [troubleshooting guide](https://mihon.app/docs/guides/troubleshooting/). required: true - - label: I have updated the app to version **[0.16.2](https://github.com/mihonapp/mihon/releases/latest)**. + - label: I have updated the app to version **[0.16.3](https://github.com/mihonapp/mihon/releases/latest)**. required: true - label: I have updated all installed extensions. required: true diff --git a/.github/ISSUE_TEMPLATE/request_feature.yml b/.github/ISSUE_TEMPLATE/request_feature.yml index 35220c01c..2c6d324c9 100644 --- a/.github/ISSUE_TEMPLATE/request_feature.yml +++ b/.github/ISSUE_TEMPLATE/request_feature.yml @@ -31,7 +31,7 @@ body: required: true - label: I have written a short but informative title. required: true - - label: I have updated the app to version **[0.16.2](https://github.com/mihonapp/mihon/releases/latest)**. + - label: I have updated the app to version **[0.16.3](https://github.com/mihonapp/mihon/releases/latest)**. required: true - label: I will fill out all of the requested information in this form. required: true diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3c5e630fa..705981b4b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,8 +22,8 @@ android { defaultConfig { applicationId = "app.mihon" - versionCode = 3 - versionName = "0.16.2" + versionCode = 4 + versionName = "0.16.3" buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"") From 170daf9fb2e566fea598eb5dc0cd710f979c7b7a Mon Sep 17 00:00:00 2001 From: pabalaba Date: Thu, 1 Feb 2024 21:20:28 +0100 Subject: [PATCH 039/212] Update workflow dependencies (#310) * feat: updated build_pull_request actions version * feat: updated build_push actions version * feat: updated issue_moderator action version * feat: updated deprecated arguments in build_pull_request * feat: updated deprecated arguments in build_push * feat: removed explicit declaration of gradle version --- .github/workflows/build_pull_request.yml | 11 ++++++----- .github/workflows/build_push.yml | 9 +++++---- .github/workflows/issue_moderator.yml | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 6ba66a5e7..56ac46c79 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -23,10 +23,10 @@ jobs: uses: actions/checkout@v4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v1 + uses: gradle/wrapper-validation-action@v2 - name: Dependency Review - uses: actions/dependency-review-action@v3 + uses: actions/dependency-review-action@v4 - name: Set up JDK uses: actions/setup-java@v4 @@ -34,7 +34,8 @@ jobs: java-version: 17 distribution: adopt + - name: Set up gradle + uses: gradle/actions/setup-gradle@v3 + - name: Build app and run unit tests - uses: gradle/gradle-command-action@v2 - with: - arguments: detekt assembleStandardRelease testReleaseUnitTest + run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index ba6b8638d..0f400fbbf 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@v4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v1 + uses: gradle/wrapper-validation-action@v2 - name: Setup Android SDK run: | @@ -32,10 +32,11 @@ jobs: java-version: 17 distribution: adopt + - name: Set up gradle + uses: gradle/actions/setup-gradle@v3 + - name: Build app and run unit tests - uses: gradle/gradle-command-action@v2 - with: - arguments: detekt assembleStandardRelease testReleaseUnitTest + run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest # Sign APK and create release for tags diff --git a/.github/workflows/issue_moderator.yml b/.github/workflows/issue_moderator.yml index 6a069e5c4..c0859d10e 100644 --- a/.github/workflows/issue_moderator.yml +++ b/.github/workflows/issue_moderator.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Moderate issues - uses: tachiyomiorg/issue-moderator-action@v2 + uses: tachiyomiorg/issue-moderator-action@v2.6.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} duplicate-label: Duplicate From 0da7ad6f1a15e8462d8270fc36ea9f135c3b8d29 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Sun, 4 Feb 2024 01:33:18 +0800 Subject: [PATCH 040/212] Don't copy archives to temp files when opening (#326) --- app/build.gradle.kts | 2 +- app/proguard-rules.pro | 3 ++ .../java/eu/kanade/tachiyomi/di/AppModule.kt | 3 -- .../tachiyomi/ui/reader/ReaderViewModel.kt | 5 +- .../ui/reader/loader/ChapterLoader.kt | 10 ++-- .../ui/reader/loader/DownloadPageLoader.kt | 5 +- .../ui/reader/loader/EpubPageLoader.kt | 6 +-- .../ui/reader/loader/RarPageLoader.kt | 5 +- .../ui/reader/loader/ZipPageLoader.kt | 11 ++--- core/common/build.gradle.kts | 1 + .../kanade/tachiyomi/util/storage/EpubFile.kt | 13 +++--- .../core/common/storage/UniFileExtensions.kt | 7 +++ .../common/storage/UniFileTempFileManager.kt | 46 ------------------- gradle/libs.versions.toml | 2 + source-local/build.gradle.kts | 2 +- .../tachiyomi/source/local/LocalSource.kt | 19 ++++---- 16 files changed, 48 insertions(+), 92 deletions(-) delete mode 100644 core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileTempFileManager.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 705981b4b..867e8f851 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -208,7 +208,7 @@ dependencies { // Disk implementation(libs.disklrucache) implementation(libs.unifile) - implementation(libs.junrar) + implementation(libs.bundles.archive) // Preferences implementation(libs.preferencektx) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 64220f2b1..c44a8da74 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -72,6 +72,9 @@ # XmlUtil -keep public enum nl.adaptivity.xmlutil.EventType { *; } +# Apache Commons Compress +-keep class * extends org.apache.commons.compress.archivers.zip.ZipExtraField { (); } + # Firebase -keep class com.google.firebase.installations.** { *; } -keep interface com.google.firebase.installations.** { *; } diff --git a/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt b/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt index 9fb7b2fb5..9997a0711 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt @@ -27,7 +27,6 @@ import nl.adaptivity.xmlutil.XmlDeclMode import nl.adaptivity.xmlutil.core.XmlVersion import nl.adaptivity.xmlutil.serialization.XML import tachiyomi.core.common.storage.AndroidStorageFolderProvider -import tachiyomi.core.common.storage.UniFileTempFileManager import tachiyomi.data.AndroidDatabaseHandler import tachiyomi.data.Database import tachiyomi.data.DatabaseHandler @@ -112,8 +111,6 @@ class AppModule(val app: Application) : InjektModule { ProtoBuf } - addSingletonFactory { UniFileTempFileManager(app) } - addSingletonFactory { ChapterCache(app, get()) } addSingletonFactory { CoverCache(app) } 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 c6170f2a9..a59f0500b 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 @@ -55,7 +55,6 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.runBlocking import logcat.LogPriority import tachiyomi.core.common.preference.toggle -import tachiyomi.core.common.storage.UniFileTempFileManager import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.lang.launchNonCancellable import tachiyomi.core.common.util.lang.withIOContext @@ -86,7 +85,6 @@ class ReaderViewModel @JvmOverloads constructor( private val sourceManager: SourceManager = Injekt.get(), private val downloadManager: DownloadManager = Injekt.get(), private val downloadProvider: DownloadProvider = Injekt.get(), - private val tempFileManager: UniFileTempFileManager = Injekt.get(), private val imageSaver: ImageSaver = Injekt.get(), preferences: BasePreferences = Injekt.get(), val readerPreferences: ReaderPreferences = Injekt.get(), @@ -271,7 +269,7 @@ class ReaderViewModel @JvmOverloads constructor( val context = Injekt.get() val source = sourceManager.getOrStub(manga.source) - loader = ChapterLoader(context, downloadManager, downloadProvider, tempFileManager, manga, source) + loader = ChapterLoader(context, downloadManager, downloadProvider, manga, source) loadChapter(loader!!, chapterList.first { chapterId == it.chapter.id }) Result.success(true) @@ -907,7 +905,6 @@ class ReaderViewModel @JvmOverloads constructor( private fun deletePendingChapters() { viewModelScope.launchNonCancellable { downloadManager.deletePendingChapters() - tempFileManager.deleteTempFiles() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt index f0edbe7f3..1cd18bceb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ChapterLoader.kt @@ -8,7 +8,7 @@ import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import tachiyomi.core.common.i18n.stringResource -import tachiyomi.core.common.storage.UniFileTempFileManager +import tachiyomi.core.common.storage.openReadOnlyChannel import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.manga.model.Manga @@ -24,7 +24,6 @@ class ChapterLoader( private val context: Context, private val downloadManager: DownloadManager, private val downloadProvider: DownloadProvider, - private val tempFileManager: UniFileTempFileManager, private val manga: Manga, private val source: Source, ) { @@ -92,18 +91,17 @@ class ChapterLoader( source, downloadManager, downloadProvider, - tempFileManager, ) source is LocalSource -> source.getFormat(chapter.chapter).let { format -> when (format) { is Format.Directory -> DirectoryPageLoader(format.file) - is Format.Zip -> ZipPageLoader(tempFileManager.createTempFile(format.file)) + is Format.Zip -> ZipPageLoader(format.file.openReadOnlyChannel(context)) is Format.Rar -> try { - RarPageLoader(tempFileManager.createTempFile(format.file)) + RarPageLoader(format.file.openInputStream()) } catch (e: UnsupportedRarV5Exception) { error(context.stringResource(MR.strings.loader_rar5_error)) } - is Format.Epub -> EpubPageLoader(tempFileManager.createTempFile(format.file)) + is Format.Epub -> EpubPageLoader(format.file.openReadOnlyChannel(context)) } } source is HttpSource -> HttpPageLoader(chapter, source) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt index 1d18d7b47..abef28540 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/DownloadPageLoader.kt @@ -10,7 +10,7 @@ import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderPage -import tachiyomi.core.common.storage.UniFileTempFileManager +import tachiyomi.core.common.storage.openReadOnlyChannel import tachiyomi.domain.manga.model.Manga import uy.kohesive.injekt.injectLazy @@ -23,7 +23,6 @@ internal class DownloadPageLoader( private val source: Source, private val downloadManager: DownloadManager, private val downloadProvider: DownloadProvider, - private val tempFileManager: UniFileTempFileManager, ) : PageLoader() { private val context: Application by injectLazy() @@ -48,7 +47,7 @@ internal class DownloadPageLoader( } private suspend fun getPagesFromArchive(file: UniFile): List { - val loader = ZipPageLoader(tempFileManager.createTempFile(file)).also { zipPageLoader = it } + val loader = ZipPageLoader(file.openReadOnlyChannel(context)).also { zipPageLoader = it } return loader.getPages() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt index 324af51bf..baf65324b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/EpubPageLoader.kt @@ -3,14 +3,14 @@ package eu.kanade.tachiyomi.ui.reader.loader import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.util.storage.EpubFile -import java.io.File +import java.nio.channels.SeekableByteChannel /** * Loader used to load a chapter from a .epub file. */ -internal class EpubPageLoader(file: File) : PageLoader() { +internal class EpubPageLoader(channel: SeekableByteChannel) : PageLoader() { - private val epub = EpubFile(file) + private val epub = EpubFile(channel) override var isLocal: Boolean = true diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt index 868ab92b2..b1db3300d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/RarPageLoader.kt @@ -6,7 +6,6 @@ import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder import tachiyomi.core.common.util.system.ImageUtil -import java.io.File import java.io.InputStream import java.io.PipedInputStream import java.io.PipedOutputStream @@ -15,9 +14,9 @@ import java.util.concurrent.Executors /** * Loader used to load a chapter from a .rar or .cbr file. */ -internal class RarPageLoader(file: File) : PageLoader() { +internal class RarPageLoader(inputStream: InputStream) : PageLoader() { - private val rar = Archive(file) + private val rar = Archive(inputStream) override var isLocal: Boolean = true diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt index e839d8a55..fadba25c4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt @@ -3,22 +3,21 @@ package eu.kanade.tachiyomi.ui.reader.loader import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder +import org.apache.commons.compress.archivers.zip.ZipFile import tachiyomi.core.common.util.system.ImageUtil -import java.io.File -import java.nio.charset.StandardCharsets -import java.util.zip.ZipFile +import java.nio.channels.SeekableByteChannel /** * Loader used to load a chapter from a .zip or .cbz file. */ -internal class ZipPageLoader(file: File) : PageLoader() { +internal class ZipPageLoader(channel: SeekableByteChannel) : PageLoader() { - private val zip = ZipFile(file, StandardCharsets.ISO_8859_1) + private val zip = ZipFile(channel) override var isLocal: Boolean = true override suspend fun getPages(): List { - return zip.entries().asSequence() + return zip.entries.asSequence() .filter { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } } .sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) } .mapIndexed { i, entry -> diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index 560e7285f..aaf0f794b 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -32,6 +32,7 @@ dependencies { implementation(libs.image.decoder) implementation(libs.unifile) + implementation(libs.bundles.archive) api(kotlinx.coroutines.core) api(kotlinx.serialization.json) diff --git a/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt index a00ee69e7..f664c175b 100644 --- a/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt @@ -1,22 +1,23 @@ package eu.kanade.tachiyomi.util.storage +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry +import org.apache.commons.compress.archivers.zip.ZipFile import org.jsoup.Jsoup import org.jsoup.nodes.Document import java.io.Closeable import java.io.File import java.io.InputStream -import java.util.zip.ZipEntry -import java.util.zip.ZipFile +import java.nio.channels.SeekableByteChannel /** * Wrapper over ZipFile to load files in epub format. */ -class EpubFile(file: File) : Closeable { +class EpubFile(channel: SeekableByteChannel) : Closeable { /** * Zip file of this epub. */ - private val zip = ZipFile(file) + private val zip = ZipFile(channel) /** * Path separator used by this epub. @@ -33,14 +34,14 @@ class EpubFile(file: File) : Closeable { /** * Returns an input stream for reading the contents of the specified zip file entry. */ - fun getInputStream(entry: ZipEntry): InputStream { + fun getInputStream(entry: ZipArchiveEntry): InputStream { return zip.getInputStream(entry) } /** * Returns the zip file entry for the specified name, or null if not found. */ - fun getEntry(name: String): ZipEntry? { + fun getEntry(name: String): ZipArchiveEntry? { return zip.getEntry(name) } diff --git a/core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileExtensions.kt b/core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileExtensions.kt index 8bbd9b3a7..257fe210d 100644 --- a/core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileExtensions.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileExtensions.kt @@ -1,6 +1,9 @@ package tachiyomi.core.common.storage +import android.content.Context +import android.os.ParcelFileDescriptor import com.hippo.unifile.UniFile +import java.nio.channels.FileChannel val UniFile.extension: String? get() = name?.substringAfterLast('.') @@ -10,3 +13,7 @@ val UniFile.nameWithoutExtension: String? val UniFile.displayablePath: String get() = filePath ?: uri.toString() + +fun UniFile.openReadOnlyChannel(context: Context): FileChannel { + return ParcelFileDescriptor.AutoCloseInputStream(context.contentResolver.openFileDescriptor(uri, "r")).channel +} diff --git a/core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileTempFileManager.kt b/core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileTempFileManager.kt deleted file mode 100644 index 4ccc5bf43..000000000 --- a/core/common/src/main/kotlin/tachiyomi/core/common/storage/UniFileTempFileManager.kt +++ /dev/null @@ -1,46 +0,0 @@ -package tachiyomi.core.common.storage - -import android.content.Context -import android.os.Build -import android.os.FileUtils -import com.hippo.unifile.UniFile -import java.io.BufferedOutputStream -import java.io.File - -class UniFileTempFileManager( - private val context: Context, -) { - - private val dir = File(context.externalCacheDir, "tmp") - - fun createTempFile(file: UniFile): File { - dir.mkdirs() - - val inputStream = context.contentResolver.openInputStream(file.uri)!! - val tempFile = File.createTempFile( - file.nameWithoutExtension.orEmpty().padEnd(3), // Prefix must be 3+ chars - null, - dir, - ) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - FileUtils.copy(inputStream, tempFile.outputStream()) - } else { - BufferedOutputStream(tempFile.outputStream()).use { tmpOut -> - inputStream.use { input -> - val buffer = ByteArray(8192) - var count: Int - while (input.read(buffer).also { count = it } > 0) { - tmpOut.write(buffer, 0, count) - } - } - } - } - - return tempFile - } - - fun deleteTempFiles() { - dir.deleteRecursively() - } -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 576d08176..c6c51a7ab 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -32,6 +32,7 @@ jsoup = "org.jsoup:jsoup:1.17.2" disklrucache = "com.jakewharton:disklrucache:2.0.2" unifile = "com.github.tachiyomiorg:unifile:7c257e1c64" +common-compress = "org.apache.commons:commons-compress:1.25.0" junrar = "com.github.junrar:junrar:7.5.5" sqlite-framework = { module = "androidx.sqlite:sqlite-framework", version.ref = "sqlite" } @@ -100,6 +101,7 @@ detekt-rules-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatt detekt-rules-compose = { module = "io.nlopez.compose.rules:detekt", version.ref = "detektCompose" } [bundles] +archive = ["common-compress", "junrar"] okhttp = ["okhttp-core", "okhttp-logging", "okhttp-brotli", "okhttp-dnsoverhttps"] js-engine = ["quickjs-android"] sqlite = ["sqlite-framework", "sqlite-ktx", "sqlite-android"] diff --git a/source-local/build.gradle.kts b/source-local/build.gradle.kts index b30b8acd4..46de484ce 100644 --- a/source-local/build.gradle.kts +++ b/source-local/build.gradle.kts @@ -12,7 +12,7 @@ kotlin { api(projects.i18n) implementation(libs.unifile) - implementation(libs.junrar) + implementation(libs.bundles.archive) } } val androidMain by getting { diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt index e33dbbf5f..10aa1e464 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt @@ -18,15 +18,16 @@ import kotlinx.serialization.json.decodeFromStream import logcat.LogPriority import nl.adaptivity.xmlutil.AndroidXmlReader import nl.adaptivity.xmlutil.serialization.XML +import org.apache.commons.compress.archivers.zip.ZipFile import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE import tachiyomi.core.metadata.comicinfo.ComicInfo import tachiyomi.core.metadata.comicinfo.copyFromComicInfo import tachiyomi.core.metadata.comicinfo.getComicInfo import tachiyomi.core.metadata.tachiyomi.MangaDetails -import tachiyomi.core.common.storage.UniFileTempFileManager import tachiyomi.core.common.storage.extension import tachiyomi.core.common.storage.nameWithoutExtension +import tachiyomi.core.common.storage.openReadOnlyChannel import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.logcat @@ -43,7 +44,6 @@ import uy.kohesive.injekt.injectLazy import java.io.File import java.io.InputStream import java.nio.charset.StandardCharsets -import java.util.zip.ZipFile import kotlin.time.Duration.Companion.days import com.github.junrar.Archive as JunrarArchive import tachiyomi.domain.source.model.Source as DomainSource @@ -56,7 +56,6 @@ actual class LocalSource( private val json: Json by injectLazy() private val xml: XML by injectLazy() - private val tempFileManager: UniFileTempFileManager by injectLazy() private val POPULAR_FILTERS = FilterList(OrderBy.Popular(context)) private val LATEST_FILTERS = FilterList(OrderBy.Latest(context)) @@ -214,7 +213,7 @@ actual class LocalSource( for (chapter in chapterArchives) { when (Format.valueOf(chapter)) { is Format.Zip -> { - ZipFile(tempFileManager.createTempFile(chapter)).use { zip: ZipFile -> + ZipFile(chapter.openReadOnlyChannel(context)).use { zip: ZipFile -> zip.getEntry(COMIC_INFO_FILE)?.let { comicInfoFile -> zip.getInputStream(comicInfoFile).buffered().use { stream -> return copyComicInfoFile(stream, folderPath) @@ -223,7 +222,7 @@ actual class LocalSource( } } is Format.Rar -> { - JunrarArchive(tempFileManager.createTempFile(chapter)).use { rar -> + JunrarArchive(chapter.openInputStream()).use { rar -> rar.fileHeaders.firstOrNull { it.fileName == COMIC_INFO_FILE }?.let { comicInfoFile -> rar.getInputStream(comicInfoFile).buffered().use { stream -> return copyComicInfoFile(stream, folderPath) @@ -273,7 +272,7 @@ actual class LocalSource( val format = Format.valueOf(chapterFile) if (format is Format.Epub) { - EpubFile(tempFileManager.createTempFile(format.file)).use { epub -> + EpubFile(format.file.openReadOnlyChannel(context)).use { epub -> epub.fillMetadata(manga, this) } } @@ -332,8 +331,8 @@ actual class LocalSource( entry?.let { coverManager.update(manga, it.openInputStream()) } } is Format.Zip -> { - ZipFile(tempFileManager.createTempFile(format.file)).use { zip -> - val entry = zip.entries().toList() + ZipFile(format.file.openReadOnlyChannel(context)).use { zip -> + val entry = zip.entries.toList() .sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) } .find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } } @@ -341,7 +340,7 @@ actual class LocalSource( } } is Format.Rar -> { - JunrarArchive(tempFileManager.createTempFile(format.file)).use { archive -> + JunrarArchive(format.file.openInputStream()).use { archive -> val entry = archive.fileHeaders .sortedWith { f1, f2 -> f1.fileName.compareToCaseInsensitiveNaturalOrder(f2.fileName) } .find { !it.isDirectory && ImageUtil.isImage(it.fileName) { archive.getInputStream(it) } } @@ -350,7 +349,7 @@ actual class LocalSource( } } is Format.Epub -> { - EpubFile(tempFileManager.createTempFile(format.file)).use { epub -> + EpubFile(format.file.openReadOnlyChannel(context)).use { epub -> val entry = epub.getImagesFromPages() .firstOrNull() ?.let { epub.getEntry(it) } From 2ccff8cdde39348538a86e9e88c9e1f134520706 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Sun, 4 Feb 2024 15:27:46 +0800 Subject: [PATCH 041/212] Fix extracting ComicInfo.xml files in local source (#325) --- .../tachiyomi/source/local/LocalSource.kt | 27 +++++++++---------- .../source/local/io/LocalSourceFileSystem.kt | 5 +--- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt index 10aa1e464..b1ae25236 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt @@ -41,7 +41,6 @@ import tachiyomi.source.local.io.Format import tachiyomi.source.local.io.LocalSourceFileSystem import tachiyomi.source.local.metadata.fillMetadata import uy.kohesive.injekt.injectLazy -import java.io.File import java.io.InputStream import java.nio.charset.StandardCharsets import kotlin.time.Duration.Companion.days @@ -145,8 +144,8 @@ actual class LocalSource( // Augment manga details based on metadata files try { - val mangaDir by lazy { fileSystem.getMangaDirectory(manga.url) } - val mangaDirFiles = fileSystem.getFilesInMangaDirectory(manga.url) + val mangaDir = fileSystem.getMangaDirectory(manga.url) ?: error("${manga.url} is not a valid directory") + val mangaDirFiles = mangaDir.listFiles().orEmpty() val comicInfoFile = mangaDirFiles .firstOrNull { it.name == COMIC_INFO_FILE } @@ -176,7 +175,7 @@ actual class LocalSource( // Replace with ComicInfo.xml file val comicInfo = manga.getComicInfo() mangaDir - ?.createFile(COMIC_INFO_FILE) + .createFile(COMIC_INFO_FILE) ?.openOutputStream() ?.use { val comicInfoString = xml.encodeToString(ComicInfo.serializer(), comicInfo) @@ -191,14 +190,12 @@ actual class LocalSource( .filter(Archive::isSupported) .toList() - val folderPath = mangaDir?.filePath - - val copiedFile = copyComicInfoFileFromArchive(chapterArchives, folderPath) + val copiedFile = copyComicInfoFileFromArchive(chapterArchives, mangaDir) if (copiedFile != null) { - setMangaDetailsFromComicInfoFile(copiedFile.inputStream(), manga) + setMangaDetailsFromComicInfoFile(copiedFile.openInputStream(), manga) } else { // Avoid re-scanning - mangaDir?.createFile(".noxml") + mangaDir.createFile(".noxml") } } } @@ -209,14 +206,14 @@ actual class LocalSource( return@withIOContext manga } - private fun copyComicInfoFileFromArchive(chapterArchives: List, folderPath: String?): File? { + private fun copyComicInfoFileFromArchive(chapterArchives: List, folder: UniFile): UniFile? { for (chapter in chapterArchives) { when (Format.valueOf(chapter)) { is Format.Zip -> { ZipFile(chapter.openReadOnlyChannel(context)).use { zip: ZipFile -> zip.getEntry(COMIC_INFO_FILE)?.let { comicInfoFile -> zip.getInputStream(comicInfoFile).buffered().use { stream -> - return copyComicInfoFile(stream, folderPath) + return copyComicInfoFile(stream, folder) } } } @@ -225,7 +222,7 @@ actual class LocalSource( JunrarArchive(chapter.openInputStream()).use { rar -> rar.fileHeaders.firstOrNull { it.fileName == COMIC_INFO_FILE }?.let { comicInfoFile -> rar.getInputStream(comicInfoFile).buffered().use { stream -> - return copyComicInfoFile(stream, folderPath) + return copyComicInfoFile(stream, folder) } } } @@ -236,9 +233,9 @@ actual class LocalSource( return null } - private fun copyComicInfoFile(comicInfoFileStream: InputStream, folderPath: String?): File { - return File("$folderPath/$COMIC_INFO_FILE").apply { - outputStream().use { outputStream -> + private fun copyComicInfoFile(comicInfoFileStream: InputStream, folder: UniFile): UniFile? { + return folder.createFile(COMIC_INFO_FILE)?.apply { + openOutputStream().use { outputStream -> comicInfoFileStream.use { it.copyTo(outputStream) } } } diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt index ad95b39ce..402df7e00 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt @@ -22,9 +22,6 @@ actual class LocalSourceFileSystem( } actual fun getFilesInMangaDirectory(name: String): List { - return getBaseDirectory() - ?.findFile(name, true) - ?.takeIf { it.isDirectory } - ?.listFiles().orEmpty().toList() + return getMangaDirectory(name)?.listFiles().orEmpty().toList() } } From 9bff20cb1a0918d7789b281952624fed890fbab7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 13:31:52 +0600 Subject: [PATCH 042/212] Update dependency gradle to v8.6 (#341) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1af9e0930..a80b22ce5 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.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 78271a54a4899765a77c10a4e51b79beab8ee830 Mon Sep 17 00:00:00 2001 From: Soitora Date: Tue, 6 Feb 2024 22:18:04 +0100 Subject: [PATCH 043/212] [skip ci] Add Dependencies label to Renovate Dependencies (#354) --- .github/renovate.json5 | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index d333b80a6..669bc1e28 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -4,6 +4,7 @@ "config:base" ], "schedule": ["every sunday"], + "labels": ["Dependencies"], "packageRules": [ { // Compiler plugins are tightly coupled to Kotlin version From d5d7065e75ead7e1ca9d3404e91990f1684c83ff Mon Sep 17 00:00:00 2001 From: Soitora Date: Wed, 7 Feb 2024 08:54:37 +0100 Subject: [PATCH 044/212] [skip ci] Tweak README.md (#353) * Center README license * Clean up the drop-downs * Change copyright modification location * Add Bitmap repo --- README.md | 41 +++++++---------------------------------- 1 file changed, 7 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index e0a09a692..b059b312c 100644 --- a/README.md +++ b/README.md @@ -44,40 +44,13 @@ Discover and read manga, webtoons, comics, and more – easier than ever on your Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. -If you got any questions, [join our Discord server](https://discord.gg/mihon). +Before reporting a new issue, take a look at the [FAQ](https://mihon.app/docs/faq/general), the [changelog](https://mihon.app/changelogs/) and the already opened [issues](https://github.com/mihonapp/mihon/issues); if you got any questions, join our [Discord server](https://discord.gg/mihon). -

Issues
- -Before reporting a new issue, take a look at the [FAQ](https://mihon.app/docs/faq/general), the [changelog](https://mihon.app/changelogs/) and the already opened [issues](https://github.com/mihonapp/mihon/issues). - -
- -
Bugs
- -* Include version (**More → About → Version**). -* If not latest, try updating, it may have already been solved. -* Beta version is equal to the number of commits as seen on the main page. -* Include steps to reproduce (if not obvious from description). -* Include screenshot (if needed). -* If it could be device-dependent, try reproducing on another device (if possible). -* Don't group unrelated requests into one issue -- **DO:** [#24](https://git.mihon.dev/tachiyomi/tachiyomi/issues/24), [#71](https://git.mihon.dev/tachiyomi/tachiyomi/issues/71) -- **DON'T:** [#75](https://git.mihon.dev/tachiyomi/tachiyomi/issues/75) - -
- -
Feature requests
- -* Write a detailed issue, explaining what it should do or how. - * Avoid writing just "like X app does"; -* Include screenshot (if needed) -* Source requests are not accepted. - -
### Repositories [![mihonapp/website - GitHub](https://github-readme-stats.vercel.app/api/pin/?username=mihonapp&repo=website&bg_color=161B22&text_color=c9d1d9&title_color=0877d2&icon_color=0877d2&border_radius=8&hide_border=true)](https://github.com/mihonapp/website/) +[![mihonapp/bitmap.kt - GitHub](https://github-readme-stats.vercel.app/api/pin/?username=mihonapp&repo=bitmap.kt&bg_color=161B22&text_color=c9d1d9&title_color=0877d2&icon_color=0877d2&border_radius=8&hide_border=true)](https://github.com/mihonapp/bitmap.kt/) ### Credits @@ -93,8 +66,10 @@ The developer(s) of this application does not have any affiliation with the cont ### License -``` +
 Copyright © 2015 Javier Tomás
+Copyright © 2024 The Mihon Open Source Project
+
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
@@ -106,8 +81,6 @@ distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
+
-Modifications Copyright © 2024 The Mihon Open Source Project -``` - - \ No newline at end of file + From 18beb20aac774a83d6cb13f6dc276c3683aaf9a2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 14 Feb 2024 01:52:32 +0600 Subject: [PATCH 045/212] Update dependency androidx.benchmark:benchmark-macro-junit4 to v1.2.3 (#255) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index a1ef4143a..e114b83eb 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -25,7 +25,7 @@ workmanager = "androidx.work:work-runtime:2.9.0" paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "paging_version" } paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging_version" } -benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.2" +benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.3" test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha02" test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha02" test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0-beta01" From 5b88f1bd94ae3c696681fe34a22453575e747ff6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 14 Feb 2024 01:53:09 +0600 Subject: [PATCH 046/212] Update dependency io.github.fornewid:material-motion-compose-core to v1.2.0 (#257) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c6c51a7ab..3bd4f2597 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -61,7 +61,7 @@ flexible-adapter-core = "com.github.arkon.FlexibleAdapter:flexible-adapter:c8013 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-materialmotion = "io.github.fornewid:material-motion-compose-core:1.1.0" +compose-materialmotion = "io.github.fornewid:material-motion-compose-core:1.2.0" swipe = "me.saket.swipe:swipe:1.2.0" From 088e37b2d8d4d790e926eda34119377c2f94ccf6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 14 Feb 2024 01:53:22 +0600 Subject: [PATCH 047/212] Update dependency com.github.requery:sqlite-android to v3.45.0 (#260) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3bd4f2597..8d7446300 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -37,7 +37,7 @@ junrar = "com.github.junrar:junrar:7.5.5" sqlite-framework = { module = "androidx.sqlite:sqlite-framework", version.ref = "sqlite" } sqlite-ktx = { module = "androidx.sqlite:sqlite-ktx", version.ref = "sqlite" } -sqlite-android = "com.github.requery:sqlite-android:3.43.0" +sqlite-android = "com.github.requery:sqlite-android:3.45.0" preferencektx = "androidx.preference:preference-ktx:1.2.1" From 31e9273b1ff4ecfd1992beaa8ad10fa27f726cc2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 14 Feb 2024 01:53:30 +0600 Subject: [PATCH 048/212] Update lifecycle.version to v2.7.0 (#268) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index e114b83eb..4ab08195e 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -1,6 +1,6 @@ [versions] agp_version = "8.2.2" -lifecycle_version = "2.6.2" +lifecycle_version = "2.7.0" paging_version = "3.2.1" [libraries] From 0780385d2ebc9caca0bda0151d828ea6e036c768 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 14 Feb 2024 01:53:40 +0600 Subject: [PATCH 049/212] Update dependency androidx.test.espresso:espresso-core to v3.6.0-alpha03 (#339) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 4ab08195e..a54bf43a1 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -27,7 +27,7 @@ paging-compose = { module = "androidx.paging:paging-compose", version.ref = "pag benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.3" test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha02" -test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha02" +test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha03" test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0-beta01" [bundles] From 72f3756a3b89de292d75d41fe6b5a59172039c45 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 14 Feb 2024 01:54:37 +0600 Subject: [PATCH 050/212] Update dependency androidx.test.ext:junit-ktx to v1.2.0-alpha03 (#340) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index a54bf43a1..25f71a3a7 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -26,7 +26,7 @@ paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "pag paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging_version" } benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.3" -test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha02" +test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha03" test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha03" test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0-beta01" From 96c236e5c38248c875f2ac7596cd51845aa651ea Mon Sep 17 00:00:00 2001 From: MajorTanya <39014446+MajorTanya@users.noreply.github.com> Date: Wed, 14 Feb 2024 21:35:16 +0100 Subject: [PATCH 051/212] Minor refactor of theming when expressions (#396) * Minor refactor of theming when expressions Avoids triggering detekt's CyclomaticComplexMethod warning because of too many when branches, which would happen with one more theme being added in these two locations. In TachiyomiTheme, the Monet theme is separated because it requires the current Compose context to function. The other themes do not and are delegated to a Map. * Implement requested changes - moved themeResources out of the ThemingDelegate interface - replaced single condition when with if expression --- .../presentation/theme/TachiyomiTheme.kt | 33 +++++----- .../ui/base/delegate/ThemingDelegate.kt | 61 ++++++------------- 2 files changed, 36 insertions(+), 58 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/theme/TachiyomiTheme.kt b/app/src/main/java/eu/kanade/presentation/theme/TachiyomiTheme.kt index 4dd9a9754..af550aa5d 100644 --- a/app/src/main/java/eu/kanade/presentation/theme/TachiyomiTheme.kt +++ b/app/src/main/java/eu/kanade/presentation/theme/TachiyomiTheme.kt @@ -8,6 +8,7 @@ import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.platform.LocalContext import eu.kanade.domain.ui.UiPreferences import eu.kanade.domain.ui.model.AppTheme +import eu.kanade.presentation.theme.colorscheme.BaseColorScheme import eu.kanade.presentation.theme.colorscheme.GreenAppleColorScheme import eu.kanade.presentation.theme.colorscheme.LavenderColorScheme import eu.kanade.presentation.theme.colorscheme.MidnightDuskColorScheme @@ -62,23 +63,27 @@ private fun getThemeColorScheme( appTheme: AppTheme, isAmoled: Boolean, ): ColorScheme { - val colorScheme = when (appTheme) { - AppTheme.DEFAULT -> TachiyomiColorScheme - AppTheme.MONET -> MonetColorScheme(LocalContext.current) - AppTheme.GREEN_APPLE -> GreenAppleColorScheme - AppTheme.LAVENDER -> LavenderColorScheme - AppTheme.MIDNIGHT_DUSK -> MidnightDuskColorScheme - AppTheme.NORD -> NordColorScheme - AppTheme.STRAWBERRY_DAIQUIRI -> StrawberryColorScheme - AppTheme.TAKO -> TakoColorScheme - AppTheme.TEALTURQUOISE -> TealTurqoiseColorScheme - AppTheme.TIDAL_WAVE -> TidalWaveColorScheme - AppTheme.YINYANG -> YinYangColorScheme - AppTheme.YOTSUBA -> YotsubaColorScheme - else -> TachiyomiColorScheme + val colorScheme = if (appTheme == AppTheme.MONET) { + MonetColorScheme(LocalContext.current) + } else { + colorSchemes.getOrDefault(appTheme, TachiyomiColorScheme) } return colorScheme.getColorScheme( isSystemInDarkTheme(), isAmoled, ) } + +private val colorSchemes: Map = mapOf( + AppTheme.DEFAULT to TachiyomiColorScheme, + AppTheme.GREEN_APPLE to GreenAppleColorScheme, + AppTheme.LAVENDER to LavenderColorScheme, + AppTheme.MIDNIGHT_DUSK to MidnightDuskColorScheme, + AppTheme.NORD to NordColorScheme, + AppTheme.STRAWBERRY_DAIQUIRI to StrawberryColorScheme, + AppTheme.TAKO to TakoColorScheme, + AppTheme.TEALTURQUOISE to TealTurqoiseColorScheme, + AppTheme.TIDAL_WAVE to TidalWaveColorScheme, + AppTheme.YINYANG to YinYangColorScheme, + AppTheme.YOTSUBA to YotsubaColorScheme, +) 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 c792333e9..8b3572f08 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 @@ -12,51 +12,10 @@ interface ThemingDelegate { companion object { fun getThemeResIds(appTheme: AppTheme, isAmoled: Boolean): List { - val resIds = mutableListOf() - when (appTheme) { - AppTheme.MONET -> { - resIds += R.style.Theme_Tachiyomi_Monet - } - AppTheme.GREEN_APPLE -> { - resIds += R.style.Theme_Tachiyomi_GreenApple - } - AppTheme.LAVENDER -> { - resIds += R.style.Theme_Tachiyomi_Lavender - } - AppTheme.MIDNIGHT_DUSK -> { - resIds += R.style.Theme_Tachiyomi_MidnightDusk - } - AppTheme.NORD -> { - resIds += R.style.Theme_Tachiyomi_Nord - } - AppTheme.STRAWBERRY_DAIQUIRI -> { - resIds += R.style.Theme_Tachiyomi_StrawberryDaiquiri - } - AppTheme.TAKO -> { - resIds += R.style.Theme_Tachiyomi_Tako - } - AppTheme.TEALTURQUOISE -> { - resIds += R.style.Theme_Tachiyomi_TealTurquoise - } - AppTheme.YINYANG -> { - resIds += R.style.Theme_Tachiyomi_YinYang - } - AppTheme.YOTSUBA -> { - resIds += R.style.Theme_Tachiyomi_Yotsuba - } - AppTheme.TIDAL_WAVE -> { - resIds += R.style.Theme_Tachiyomi_TidalWave - } - else -> { - resIds += R.style.Theme_Tachiyomi - } + return buildList(2) { + add(themeResources.getOrDefault(appTheme, R.style.Theme_Tachiyomi)) + if (isAmoled) add(R.style.ThemeOverlay_Tachiyomi_Amoled) } - - if (isAmoled) { - resIds += R.style.ThemeOverlay_Tachiyomi_Amoled - } - - return resIds } } } @@ -68,3 +27,17 @@ class ThemingDelegateImpl : ThemingDelegate { .forEach(activity::setTheme) } } + +private val themeResources: Map = mapOf( + AppTheme.MONET to R.style.Theme_Tachiyomi_Monet, + AppTheme.GREEN_APPLE to R.style.Theme_Tachiyomi_GreenApple, + AppTheme.LAVENDER to R.style.Theme_Tachiyomi_Lavender, + AppTheme.MIDNIGHT_DUSK to R.style.Theme_Tachiyomi_MidnightDusk, + AppTheme.NORD to R.style.Theme_Tachiyomi_Nord, + AppTheme.STRAWBERRY_DAIQUIRI to R.style.Theme_Tachiyomi_StrawberryDaiquiri, + AppTheme.TAKO to R.style.Theme_Tachiyomi_Tako, + AppTheme.TEALTURQUOISE to R.style.Theme_Tachiyomi_TealTurquoise, + AppTheme.YINYANG to R.style.Theme_Tachiyomi_YinYang, + AppTheme.YOTSUBA to R.style.Theme_Tachiyomi_Yotsuba, + AppTheme.TIDAL_WAVE to R.style.Theme_Tachiyomi_TidalWave, +) From 7ff95e21babda98dd1b479912278d6029cd15f0d Mon Sep 17 00:00:00 2001 From: Maddie Witman Date: Fri, 16 Feb 2024 06:09:00 -0500 Subject: [PATCH 052/212] Refactor use of Java.util.date to Java.time.*, to fix localized date issues. (#402) * Add support for localdate based relative times * Update History Screen to use new localdate based relative times * Update Updates Screen to use new localdate based relative times * Cleaned up date util classes * Updated build time display * Code cleanup * Fixed crash in settings * Updated Preferences item * Worker Info works * Fixed Tracker date display * Code changes to pass detekt --- .../java/eu/kanade/domain/ui/UiPreferences.kt | 10 ++-- .../presentation/components/DateText.kt | 25 +++++---- .../presentation/history/HistoryScreen.kt | 4 +- .../HistoryScreenModelStateProvider.kt | 5 +- .../screen/SettingsAppearanceScreen.kt | 4 +- .../more/settings/screen/about/AboutScreen.kt | 19 ++----- .../settings/screen/debug/WorkerInfoScreen.kt | 20 +++++-- .../presentation/track/TrackInfoDialogHome.kt | 9 +-- .../TrackInfoDialogHomePreviewProvider.kt | 7 ++- .../presentation/updates/UpdatesScreen.kt | 4 +- .../ui/history/HistoryScreenModel.kt | 12 ++-- .../ui/updates/UpdatesScreenModel.kt | 14 ++--- .../tachiyomi/util/lang/DateExtensions.kt | 56 ++++++------------- 13 files changed, 89 insertions(+), 100 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt b/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt index 606a1048d..2efefb517 100644 --- a/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt +++ b/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt @@ -7,8 +7,8 @@ import eu.kanade.tachiyomi.util.system.DeviceUtil import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.core.common.preference.getEnum -import java.text.DateFormat -import java.text.SimpleDateFormat +import java.time.format.DateTimeFormatter +import java.time.format.FormatStyle import java.util.Locale class UiPreferences( @@ -31,9 +31,9 @@ class UiPreferences( fun tabletUiMode() = preferenceStore.getEnum("tablet_ui_mode", TabletUiMode.AUTOMATIC) companion object { - fun dateFormat(format: String): DateFormat = when (format) { - "" -> DateFormat.getDateInstance(DateFormat.SHORT) - else -> SimpleDateFormat(format, Locale.getDefault()) + fun dateFormat(format: String): DateTimeFormatter = when (format) { + "" -> DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT) + else -> DateTimeFormatter.ofPattern(format, Locale.getDefault()) } } } diff --git a/app/src/main/java/eu/kanade/presentation/components/DateText.kt b/app/src/main/java/eu/kanade/presentation/components/DateText.kt index 9fa8c85d1..59708a049 100644 --- a/app/src/main/java/eu/kanade/presentation/components/DateText.kt +++ b/app/src/main/java/eu/kanade/presentation/components/DateText.kt @@ -4,25 +4,31 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import eu.kanade.domain.ui.UiPreferences -import eu.kanade.tachiyomi.util.lang.toRelativeString +import eu.kanade.tachiyomi.util.lang.toRelativeSting import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.util.Date +import java.time.Instant +import java.time.LocalDate +import java.time.ZoneId @Composable fun relativeDateText( dateEpochMillis: Long, ): String { return relativeDateText( - date = Date(dateEpochMillis).takeIf { dateEpochMillis > 0L }, + localDate = LocalDate.ofInstant( + Instant.ofEpochMilli(dateEpochMillis), + ZoneId.systemDefault(), + ) + .takeIf { dateEpochMillis > 0L }, ) } @Composable fun relativeDateText( - date: Date?, + localDate: LocalDate?, ): String { val context = LocalContext.current @@ -30,11 +36,10 @@ fun relativeDateText( val relativeTime = remember { preferences.relativeTime().get() } val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) } - return date - ?.toRelativeString( - context = context, - relative = relativeTime, - dateFormat = dateFormat, - ) + return localDate?.toRelativeSting( + context = context, + relative = relativeTime, + dateFormat = dateFormat, + ) ?: stringResource(MR.strings.not_applicable) } diff --git a/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt b/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt index ec25769dc..c15604829 100644 --- a/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt @@ -28,7 +28,7 @@ import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.LoadingScreen -import java.util.Date +import java.time.LocalDate @Composable fun HistoryScreen( @@ -133,7 +133,7 @@ private fun HistoryScreenContent( } sealed interface HistoryUiModel { - data class Header(val date: Date) : HistoryUiModel + data class Header(val date: LocalDate) : HistoryUiModel data class Item(val item: HistoryWithRelations) : HistoryUiModel } diff --git a/app/src/main/java/eu/kanade/presentation/history/HistoryScreenModelStateProvider.kt b/app/src/main/java/eu/kanade/presentation/history/HistoryScreenModelStateProvider.kt index 2c07b81e0..4ffa15dd1 100644 --- a/app/src/main/java/eu/kanade/presentation/history/HistoryScreenModelStateProvider.kt +++ b/app/src/main/java/eu/kanade/presentation/history/HistoryScreenModelStateProvider.kt @@ -5,6 +5,7 @@ import eu.kanade.tachiyomi.ui.history.HistoryScreenModel import tachiyomi.domain.history.model.HistoryWithRelations import tachiyomi.domain.manga.model.MangaCover import java.time.Instant +import java.time.LocalDate import java.time.temporal.ChronoUnit import java.util.Date import kotlin.random.Random @@ -71,10 +72,10 @@ class HistoryScreenModelStateProvider : PreviewParameterProvider Instant = { it }) = - HistoryUiModel.Header(Date.from(instantBuilder(Instant.now()))) + HistoryUiModel.Header(LocalDate.from(instantBuilder(Instant.now()))) fun items() = sequence { var count = 1 diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt index d1474bf6a..10ad5e7bd 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt @@ -26,7 +26,7 @@ import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.collectAsState import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.time.Instant +import java.time.LocalDate object SettingsAppearanceScreen : SearchableSettings { @@ -101,7 +101,7 @@ object SettingsAppearanceScreen : SearchableSettings { val context = LocalContext.current val navigator = LocalNavigator.currentOrThrow - val now = remember { Instant.now().toEpochMilli() } + val now = remember { LocalDate.now() } val dateFormat by uiPreferences.dateFormat().collectAsState() val formattedNow = remember(dateFormat) { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt index 58f690569..615687aff 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt @@ -55,10 +55,10 @@ import tachiyomi.presentation.core.icons.Reddit import tachiyomi.presentation.core.icons.X import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.text.DateFormat -import java.text.SimpleDateFormat +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter import java.util.Locale -import java.util.TimeZone object AboutScreen : Screen() { @@ -269,16 +269,9 @@ object AboutScreen : Screen() { internal fun getFormattedBuildTime(): String { return try { - val inputDf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'", Locale.US) - inputDf.timeZone = TimeZone.getTimeZone("UTC") - val buildTime = inputDf.parse(BuildConfig.BUILD_TIME) - - val outputDf = DateFormat.getDateTimeInstance( - DateFormat.MEDIUM, - DateFormat.SHORT, - Locale.getDefault(), - ) - outputDf.timeZone = TimeZone.getDefault() + val df = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm'Z'", Locale.US) + .withZone(ZoneId.of("UTC")) + val buildTime = LocalDateTime.from(df.parse(BuildConfig.BUILD_TIME)) buildTime!!.toDateTimestampString(UiPreferences.dateFormat(Injekt.get().dateFormat().get())) } catch (e: Exception) { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt index ea8db81e8..dbe82830d 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt @@ -42,7 +42,9 @@ import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.plus import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.util.Date +import java.time.Instant +import java.time.LocalDateTime +import java.time.ZoneId class WorkerInfoScreen : Screen() { @@ -149,11 +151,17 @@ class WorkerInfoScreen : Screen() { appendLine("State: ${workInfo.state}") if (workInfo.state == WorkInfo.State.ENQUEUED) { appendLine( - "Next scheduled run: ${Date(workInfo.nextScheduleTimeMillis).toDateTimestampString( - UiPreferences.dateFormat( - Injekt.get().dateFormat().get(), - ), - )}", + "Next scheduled run: ${ + LocalDateTime.ofInstant( + Instant.ofEpochMilli(workInfo.nextScheduleTimeMillis), + ZoneId.systemDefault(), + ) + .toDateTimestampString( + UiPreferences.dateFormat( + Injekt.get().dateFormat().get(), + ), + ) + }", ) appendLine("Attempt #${workInfo.runAttemptCount + 1}") } diff --git a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHome.kt b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHome.kt index 11f2b9a06..993532180 100644 --- a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHome.kt +++ b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHome.kt @@ -52,17 +52,18 @@ import eu.kanade.presentation.theme.TachiyomiPreviewTheme import eu.kanade.presentation.track.components.TrackLogoIcon import eu.kanade.tachiyomi.data.track.Tracker import eu.kanade.tachiyomi.ui.manga.track.TrackItem +import eu.kanade.tachiyomi.util.lang.toLocalDate import eu.kanade.tachiyomi.util.system.copyToClipboard import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource -import java.text.DateFormat +import java.time.format.DateTimeFormatter private const val UnsetStatusTextAlpha = 0.5F @Composable fun TrackInfoDialogHome( trackItems: List, - dateFormat: DateFormat, + dateFormat: DateTimeFormatter, onStatusClick: (TrackItem) -> Unit, onChapterClick: (TrackItem) -> Unit, onScoreClick: (TrackItem) -> Unit, @@ -104,11 +105,11 @@ fun TrackInfoDialogHome( .takeIf { supportsScoring && item.track.score != 0.0 }, onScoreClick = { onScoreClick(item) } .takeIf { supportsScoring }, - startDate = remember(item.track.startDate) { dateFormat.format(item.track.startDate) } + startDate = remember(item.track.startDate) { dateFormat.format(item.track.startDate.toLocalDate()) } .takeIf { supportsReadingDates && item.track.startDate != 0L }, onStartDateClick = { onStartDateEdit(item) } // TODO .takeIf { supportsReadingDates }, - endDate = dateFormat.format(item.track.finishDate) + endDate = dateFormat.format(item.track.finishDate.toLocalDate()) .takeIf { supportsReadingDates && item.track.finishDate != 0L }, onEndDateClick = { onEndDateEdit(item) } .takeIf { supportsReadingDates }, diff --git a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHomePreviewProvider.kt b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHomePreviewProvider.kt index 51b7ca3f8..aa58403ea 100644 --- a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHomePreviewProvider.kt +++ b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHomePreviewProvider.kt @@ -5,7 +5,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import eu.kanade.tachiyomi.ui.manga.track.TrackItem import eu.kanade.test.DummyTracker import tachiyomi.domain.track.model.Track -import java.text.DateFormat +import java.time.format.DateTimeFormatter +import java.time.format.FormatStyle internal class TrackInfoDialogHomePreviewProvider : PreviewParameterProvider<@Composable () -> Unit> { @@ -46,7 +47,7 @@ internal class TrackInfoDialogHomePreviewProvider : trackItemWithoutTrack, trackItemWithTrack, ), - dateFormat = DateFormat.getDateInstance(), + dateFormat = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM), onStatusClick = {}, onChapterClick = {}, onScoreClick = {}, @@ -61,7 +62,7 @@ internal class TrackInfoDialogHomePreviewProvider : private val noTrackers = @Composable { TrackInfoDialogHome( trackItems = listOf(), - dateFormat = DateFormat.getDateInstance(), + dateFormat = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM), onStatusClick = {}, onChapterClick = {}, onScoreClick = {}, diff --git a/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt b/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt index ca1e5632c..ac97de83f 100644 --- a/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt @@ -36,7 +36,7 @@ import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.LoadingScreen -import java.util.Date +import java.time.LocalDate import kotlin.time.Duration.Companion.seconds @Composable @@ -206,6 +206,6 @@ private fun UpdatesBottomBar( } sealed interface UpdatesUiModel { - data class Header(val date: Date) : UpdatesUiModel + data class Header(val date: LocalDate) : UpdatesUiModel data class Item(val item: UpdatesItem) : UpdatesUiModel } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt index a344fcda3..7b0cdcf72 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt @@ -5,7 +5,7 @@ import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.screenModelScope import eu.kanade.core.util.insertSeparators import eu.kanade.presentation.history.HistoryUiModel -import eu.kanade.tachiyomi.util.lang.toDateKey +import eu.kanade.tachiyomi.util.lang.toLocalDate import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow @@ -28,7 +28,7 @@ import tachiyomi.domain.history.interactor.RemoveHistory import tachiyomi.domain.history.model.HistoryWithRelations import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.util.Date +import java.time.LocalDate class HistoryScreenModel( private val getHistory: GetHistory = Injekt.get(), @@ -60,10 +60,12 @@ class HistoryScreenModel( private fun List.toHistoryUiModels(): List { return map { HistoryUiModel.Item(it) } .insertSeparators { before, after -> - val beforeDate = before?.item?.readAt?.time?.toDateKey() ?: Date(0) - val afterDate = after?.item?.readAt?.time?.toDateKey() ?: Date(0) + val beforeDate = before?.item?.readAt?.time?.toLocalDate() ?: LocalDate.MIN + val afterDate = after?.item?.readAt?.time?.toLocalDate() ?: LocalDate.MIN when { - beforeDate.time != afterDate.time && afterDate.time != 0L -> HistoryUiModel.Header(afterDate) + beforeDate.isAfter(afterDate) + or afterDate.equals(LocalDate.MIN) + or beforeDate.equals(LocalDate.MIN) -> HistoryUiModel.Header(afterDate) // Return null to avoid adding a separator between two items. else -> null } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt index f76ac45fe..2b2c1c25a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt @@ -16,7 +16,7 @@ import eu.kanade.tachiyomi.data.download.DownloadCache import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.library.LibraryUpdateJob -import eu.kanade.tachiyomi.util.lang.toDateKey +import eu.kanade.tachiyomi.util.lang.toLocalDate import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.mutate import kotlinx.collections.immutable.persistentListOf @@ -45,8 +45,8 @@ import tachiyomi.domain.updates.interactor.GetUpdates import tachiyomi.domain.updates.model.UpdatesWithRelations import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import java.time.LocalDate import java.time.ZonedDateTime -import java.util.Date class UpdatesScreenModel( private val sourceManager: SourceManager = Injekt.get(), @@ -374,12 +374,12 @@ class UpdatesScreenModel( return items .map { UpdatesUiModel.Item(it) } .insertSeparators { before, after -> - val beforeDate = before?.item?.update?.dateFetch?.toDateKey() ?: Date(0) - val afterDate = after?.item?.update?.dateFetch?.toDateKey() ?: Date(0) + val beforeDate = before?.item?.update?.dateFetch?.toLocalDate() ?: LocalDate.MIN + val afterDate = after?.item?.update?.dateFetch?.toLocalDate() ?: LocalDate.MIN when { - beforeDate.time != afterDate.time && afterDate.time != 0L -> { - UpdatesUiModel.Header(afterDate) - } + beforeDate.isAfter(afterDate) + or afterDate.equals(LocalDate.MIN) + or beforeDate.equals(LocalDate.MIN) -> UpdatesUiModel.Header(afterDate) // Return null to avoid adding a separator between two items. else -> null } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt index a313f5a2d..e1a1ea62f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt @@ -6,15 +6,17 @@ import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import java.text.DateFormat import java.time.Instant +import java.time.LocalDate import java.time.LocalDateTime import java.time.ZoneId +import java.time.format.DateTimeFormatter +import java.time.format.FormatStyle import java.time.temporal.ChronoUnit -import java.util.Calendar import java.util.Date -fun Date.toDateTimestampString(dateFormatter: DateFormat): String { - val date = dateFormatter.format(this) - val time = DateFormat.getTimeInstance(DateFormat.SHORT).format(this) +fun LocalDateTime.toDateTimestampString(dateTimeFormatter: DateTimeFormatter): String { + val date = dateTimeFormatter.format(this) + val time = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).format(this) return "$date $time" } @@ -32,52 +34,28 @@ fun Long.convertEpochMillisZone( .toEpochMilli() } -/** - * Get date as time key - * - * @param date desired date - * @return date as time key - */ -fun Long.toDateKey(): Date { - val instant = Instant.ofEpochMilli(this) - return Date.from(instant.truncatedTo(ChronoUnit.DAYS)) +fun Long.toLocalDate(): LocalDate { + return LocalDate.ofInstant(Instant.ofEpochMilli(this), ZoneId.systemDefault()) } -fun Date.toRelativeString( +fun LocalDate.toRelativeSting( context: Context, relative: Boolean = true, - dateFormat: DateFormat = DateFormat.getDateInstance(DateFormat.SHORT), + dateFormat: DateTimeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT), ): String { if (!relative) { return dateFormat.format(this) } - val now = Date() - val difference = now.timeWithOffset.floorNearest(MILLISECONDS_IN_DAY) - - this.timeWithOffset.floorNearest(MILLISECONDS_IN_DAY) - val days = difference.floorDiv(MILLISECONDS_IN_DAY).toInt() + val now = LocalDate.now() + val difference = ChronoUnit.DAYS.between(this, now) return when { - difference < 0 -> dateFormat.format(this) - difference < MILLISECONDS_IN_DAY -> context.stringResource(MR.strings.relative_time_today) - difference < MILLISECONDS_IN_DAY.times(7) -> context.pluralStringResource( + difference < 0 -> difference.toString() + difference < 1 -> context.stringResource(MR.strings.relative_time_today) + difference < 7 -> context.pluralStringResource( MR.plurals.relative_time, - days, - days, + difference.toInt(), + difference.toInt(), ) else -> dateFormat.format(this) } } - -private const val MILLISECONDS_IN_DAY = 86_400_000L - -private val Date.timeWithOffset: Long - get() { - return Calendar.getInstance().run { - time = this@timeWithOffset - val dstOffset = get(Calendar.DST_OFFSET) - this@timeWithOffset.time + timeZone.rawOffset + dstOffset - } - } - -private fun Long.floorNearest(to: Long): Long { - return this.floorDiv(to) * to -} From 6bdb37be65757ca903c5c2a249ca03331b04d673 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Fri, 16 Feb 2024 17:18:53 +0600 Subject: [PATCH 053/212] Ignore detekt [LongParameterList] for composables --- config/detekt/detekt.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 112fcf3f0..0e21d99d4 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -10,6 +10,7 @@ complexity: functionThreshold: 6 constructorThreshold: 7 ignoreDefaultParameters: true + ignoreAnnotated: [ 'Composable' ] style: MagicNumber: From e183cbb231c6d48a17c573dbd3f2c8ce04ff7031 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 17 Feb 2024 03:28:48 +0600 Subject: [PATCH 054/212] Revert a mishap in 7ff95e21babda98dd1b479912278d6029cd15f0d --- .../main/java/eu/kanade/presentation/components/DateText.kt | 4 ++-- .../main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/components/DateText.kt b/app/src/main/java/eu/kanade/presentation/components/DateText.kt index 59708a049..4f2d988af 100644 --- a/app/src/main/java/eu/kanade/presentation/components/DateText.kt +++ b/app/src/main/java/eu/kanade/presentation/components/DateText.kt @@ -4,7 +4,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import eu.kanade.domain.ui.UiPreferences -import eu.kanade.tachiyomi.util.lang.toRelativeSting +import eu.kanade.tachiyomi.util.lang.toRelativeString import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource import uy.kohesive.injekt.Injekt @@ -36,7 +36,7 @@ fun relativeDateText( val relativeTime = remember { preferences.relativeTime().get() } val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) } - return localDate?.toRelativeSting( + return localDate?.toRelativeString( context = context, relative = relativeTime, dateFormat = dateFormat, diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt index e1a1ea62f..9e61555ec 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt @@ -38,7 +38,7 @@ fun Long.toLocalDate(): LocalDate { return LocalDate.ofInstant(Instant.ofEpochMilli(this), ZoneId.systemDefault()) } -fun LocalDate.toRelativeSting( +fun LocalDate.toRelativeString( context: Context, relative: Boolean = true, dateFormat: DateTimeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT), From 5aec8f8018236a38106483da08f9cbc28261ac9b Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 17 Feb 2024 03:29:15 +0600 Subject: [PATCH 055/212] Small cleanup in `WorkerInfoScreen` --- .../settings/screen/debug/WorkerInfoScreen.kt | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt index dbe82830d..4a57103d8 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt @@ -150,19 +150,16 @@ class WorkerInfoScreen : Screen() { } appendLine("State: ${workInfo.state}") if (workInfo.state == WorkInfo.State.ENQUEUED) { - appendLine( - "Next scheduled run: ${ - LocalDateTime.ofInstant( - Instant.ofEpochMilli(workInfo.nextScheduleTimeMillis), - ZoneId.systemDefault(), - ) - .toDateTimestampString( - UiPreferences.dateFormat( - Injekt.get().dateFormat().get(), - ), - ) - }", + val timestamp = LocalDateTime.ofInstant( + Instant.ofEpochMilli(workInfo.nextScheduleTimeMillis), + ZoneId.systemDefault(), ) + .toDateTimestampString( + UiPreferences.dateFormat( + Injekt.get().dateFormat().get(), + ), + ) + appendLine("Next scheduled run: $timestamp",) appendLine("Attempt #${workInfo.runAttemptCount + 1}") } appendLine() From fb86c470f6cfcb0b6c1bb7b2790366e0d0c0662e Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 17 Feb 2024 03:35:46 +0600 Subject: [PATCH 056/212] Update gradle.properties --- gradle.properties | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/gradle.properties b/gradle.properties index 00f048f04..22e7dfed5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,26 +1,10 @@ -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. - -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html - -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -# Default value: -Xmx10248m -XX:MaxPermSize=256m -# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -org.gradle.jvmargs=-Xmx5120m - -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -org.gradle.parallel=true - -org.gradle.caching=true +android.nonTransitiveRClass=false +android.useAndroidX=true +kotlin.code.style=official kotlin.mpp.androidSourceSetLayoutVersion=2 -android.useAndroidX=true -android.nonTransitiveRClass=false \ No newline at end of file +org.gradle.caching=true +org.gradle.configureondemand=true +org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8 +org.gradle.parallel=true From 95d4df9ca80a88e87e633fc24c7fec677bc9d9b6 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 17 Feb 2024 04:07:06 +0600 Subject: [PATCH 057/212] Cleanup [BaseColorScheme.getColorScheme] --- .../theme/colorscheme/BaseColorScheme.kt | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/BaseColorScheme.kt b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/BaseColorScheme.kt index 605d5e5a5..97455fd9e 100644 --- a/app/src/main/java/eu/kanade/presentation/theme/colorscheme/BaseColorScheme.kt +++ b/app/src/main/java/eu/kanade/presentation/theme/colorscheme/BaseColorScheme.kt @@ -9,18 +9,15 @@ internal abstract class BaseColorScheme { abstract val lightScheme: ColorScheme fun getColorScheme(isDark: Boolean, isAmoled: Boolean): ColorScheme { - return (if (isDark) darkScheme else lightScheme) - .let { - if (isDark && isAmoled) { - it.copy( - background = Color.Black, - onBackground = Color.White, - surface = Color.Black, - onSurface = Color.White, - ) - } else { - it - } - } + if (!isDark) return lightScheme + + if (!isAmoled) return darkScheme + + return darkScheme.copy( + background = Color.Black, + onBackground = Color.White, + surface = Color.Black, + onSurface = Color.White, + ) } } From 1b0bbb84401005801e61107a0c36443af691b8e6 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 17 Feb 2024 04:25:41 +0600 Subject: [PATCH 058/212] Tweak detekt config --- config/detekt/detekt.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 0e21d99d4..11c42e80d 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -6,9 +6,9 @@ naming: constantPattern: '[A-Z][A-Za-z0-9]*' complexity: + LongMethod: + ignoreAnnotated: [ 'Composable' ] LongParameterList: - functionThreshold: 6 - constructorThreshold: 7 ignoreDefaultParameters: true ignoreAnnotated: [ 'Composable' ] @@ -16,5 +16,7 @@ style: MagicNumber: ignorePropertyDeclaration: true ignoreCompanionObjectPropertyDeclaration: true + ReturnCount: + excludeGuardClauses: true UnusedPrivateMember: ignoreAnnotated: [ 'Preview' ] From 840b647b4b4e738fac546b7437dd5449679232a1 Mon Sep 17 00:00:00 2001 From: MajorTanya <39014446+MajorTanya@users.noreply.github.com> Date: Sat, 17 Feb 2024 16:26:47 +0100 Subject: [PATCH 059/212] Address `overridePendingTransition` deprecation (#410) This function is deprecated starting with API 34 "UpsideDownCake" and should be replaced with `overrideActivityTransition`. --- .../base/delegate/SecureActivityDelegate.kt | 9 ++++++- .../tachiyomi/ui/reader/ReaderActivity.kt | 23 ++++++++++++++++-- .../tachiyomi/ui/webview/WebViewActivity.kt | 24 +++++++++++++++++-- 3 files changed, 51 insertions(+), 5 deletions(-) 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 02589cec9..c3b94d639 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 @@ -1,6 +1,8 @@ package eu.kanade.tachiyomi.ui.base.delegate +import android.app.Activity import android.content.Intent +import android.os.Build import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner @@ -106,7 +108,12 @@ class SecureActivityDelegateImpl : SecureActivityDelegate, DefaultLifecycleObser if (activity.isAuthenticationSupported()) { if (!SecureActivityDelegate.requireUnlock) return activity.startActivity(Intent(activity, UnlockActivity::class.java)) - activity.overridePendingTransition(0, 0) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + activity.overrideActivityTransition(Activity.OVERRIDE_TRANSITION_OPEN, 0, 0) + } else { + @Suppress("DEPRECATION") + activity.overridePendingTransition(0, 0) + } } else { securityPreferences.useAuthenticator().set(false) } 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 f170f7e1a..e21fa11c6 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 @@ -1,6 +1,7 @@ package eu.kanade.tachiyomi.ui.reader import android.annotation.SuppressLint +import android.app.Activity import android.app.assist.AssistContent import android.content.Context import android.content.Intent @@ -136,7 +137,16 @@ class ReaderActivity : BaseActivity() { */ override fun onCreate(savedInstanceState: Bundle?) { registerSecureActivity(this) - overridePendingTransition(R.anim.shared_axis_x_push_enter, R.anim.shared_axis_x_push_exit) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + overrideActivityTransition( + Activity.OVERRIDE_TRANSITION_OPEN, + R.anim.shared_axis_x_push_enter, + R.anim.shared_axis_x_push_exit, + ) + } else { + @Suppress("DEPRECATION") + overridePendingTransition(R.anim.shared_axis_x_push_enter, R.anim.shared_axis_x_push_exit) + } super.onCreate(savedInstanceState) @@ -267,7 +277,16 @@ class ReaderActivity : BaseActivity() { override fun finish() { viewModel.onActivityFinish() super.finish() - overridePendingTransition(R.anim.shared_axis_x_pop_enter, R.anim.shared_axis_x_pop_exit) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + overrideActivityTransition( + Activity.OVERRIDE_TRANSITION_CLOSE, + R.anim.shared_axis_x_pop_enter, + R.anim.shared_axis_x_pop_exit, + ) + } else { + @Suppress("DEPRECATION") + overridePendingTransition(R.anim.shared_axis_x_pop_enter, R.anim.shared_axis_x_pop_exit) + } } override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt index 040a02f76..e21fc0b76 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/webview/WebViewActivity.kt @@ -1,8 +1,10 @@ package eu.kanade.tachiyomi.ui.webview +import android.app.Activity import android.app.assist.AssistContent import android.content.Context import android.content.Intent +import android.os.Build import android.os.Bundle import android.widget.Toast import androidx.core.net.toUri @@ -35,7 +37,16 @@ class WebViewActivity : BaseActivity() { } override fun onCreate(savedInstanceState: Bundle?) { - overridePendingTransition(R.anim.shared_axis_x_push_enter, R.anim.shared_axis_x_push_exit) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + overrideActivityTransition( + Activity.OVERRIDE_TRANSITION_OPEN, + R.anim.shared_axis_x_push_enter, + R.anim.shared_axis_x_push_exit, + ) + } else { + @Suppress("DEPRECATION") + overridePendingTransition(R.anim.shared_axis_x_push_enter, R.anim.shared_axis_x_push_exit) + } super.onCreate(savedInstanceState) if (!WebViewUtil.supportsWebView(this)) { @@ -77,7 +88,16 @@ class WebViewActivity : BaseActivity() { override fun finish() { super.finish() - overridePendingTransition(R.anim.shared_axis_x_pop_enter, R.anim.shared_axis_x_pop_exit) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + overrideActivityTransition( + Activity.OVERRIDE_TRANSITION_CLOSE, + R.anim.shared_axis_x_pop_enter, + R.anim.shared_axis_x_pop_exit, + ) + } else { + @Suppress("DEPRECATION") + overridePendingTransition(R.anim.shared_axis_x_pop_enter, R.anim.shared_axis_x_pop_exit) + } } private fun shareWebpage(url: String) { From 617bf491eee1d1010dc23c3f6df5d148700feb44 Mon Sep 17 00:00:00 2001 From: MajorTanya <39014446+MajorTanya@users.noreply.github.com> Date: Sun, 18 Feb 2024 19:57:50 +0100 Subject: [PATCH 060/212] Fix DelayedTrackingUpdateJob spam on update errors (#411) * Fix DelayedTrackingUpdateJob spam on update errors DelayedTrackingUpdateJob would start spamming when it encountered an error (e.g. a tracker has an issue) and never stop. This seems to stem from a circular dependency between the Job's `doWork` and TrackChapter's `await`. TrackChapter sets up a completely new instance of the DelayedTrackingUpdateJob if any Exception was thrown during the track update. This causes the Job to get replaced (as per the WorkManager's set ExistingWorkPolicy). Because of this, the guard clause at the start of doWork would never trigger, as all instances of the Job would report being the 0th try (because they were completely new instances). This simple fix introduces a boolean `isRetry` parameter to TrackChapter's await method, which is set to `false` by default. DelayedTrackingUpdateJob however sets this parameter to `true`, which means TrackChapter won't try to set up the Job again. * Rename isRetry parameter to setupJobOnFailure This also inverts the logic, so true & false were swapped. --- .../java/eu/kanade/domain/track/interactor/TrackChapter.kt | 6 ++++-- .../kanade/domain/track/service/DelayedTrackingUpdateJob.kt | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt b/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt index a942d5f94..fdf24ec4e 100644 --- a/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt +++ b/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt @@ -21,7 +21,7 @@ class TrackChapter( private val delayedTrackingStore: DelayedTrackingStore, ) { - suspend fun await(context: Context, mangaId: Long, chapterNumber: Double) { + suspend fun await(context: Context, mangaId: Long, chapterNumber: Double, setupJobOnFailure: Boolean = true) { withNonCancellableContext { val tracks = getTracks.await(mangaId) if (tracks.isEmpty()) return@withNonCancellableContext @@ -43,7 +43,9 @@ class TrackChapter( delayedTrackingStore.remove(track.id) } catch (e: Exception) { delayedTrackingStore.add(track.id, chapterNumber) - DelayedTrackingUpdateJob.setupTask(context) + if (setupJobOnFailure) { + DelayedTrackingUpdateJob.setupTask(context) + } throw e } } diff --git a/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt b/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt index 1f4e246dc..50589ae9d 100644 --- a/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt +++ b/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt @@ -45,7 +45,7 @@ class DelayedTrackingUpdateJob(private val context: Context, workerParams: Worke logcat(LogPriority.DEBUG) { "Updating delayed track item: ${track.mangaId}, last chapter read: ${track.lastChapterRead}" } - trackChapter.await(context, track.mangaId, track.lastChapterRead) + trackChapter.await(context, track.mangaId, track.lastChapterRead, setupJobOnFailure = false) } } From ab02568ac6e9dabc7a41036bb3d8c77138125544 Mon Sep 17 00:00:00 2001 From: beerpsi <92439990+beerpiss@users.noreply.github.com> Date: Sun, 25 Feb 2024 00:54:30 +0700 Subject: [PATCH 061/212] [ExtensionLoader] Prioritize extension classpath over app classpath (#433) --- .../extension/util/ExtensionLoader.kt | 4 +- .../util/system/ChildFirstPathClassLoader.kt | 86 +++++++++++++++++++ 2 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/util/system/ChildFirstPathClassLoader.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt index 2d469f260..0468c45e5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt @@ -6,7 +6,6 @@ import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.os.Build import androidx.core.content.pm.PackageInfoCompat -import dalvik.system.PathClassLoader import eu.kanade.domain.extension.interactor.TrustExtension import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.tachiyomi.extension.model.Extension @@ -16,6 +15,7 @@ import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.SourceFactory import eu.kanade.tachiyomi.util.lang.Hash import eu.kanade.tachiyomi.util.storage.copyAndSetReadOnlyTo +import eu.kanade.tachiyomi.util.system.ChildFirstPathClassLoader import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.runBlocking @@ -272,7 +272,7 @@ internal object ExtensionLoader { } val classLoader = try { - PathClassLoader(appInfo.sourceDir, null, context.classLoader) + ChildFirstPathClassLoader(appInfo.sourceDir, null, context.classLoader) } catch (e: Exception) { logcat(LogPriority.ERROR, e) { "Extension load error: $extName ($pkgName)" } return LoadResult.Error diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/ChildFirstPathClassLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/ChildFirstPathClassLoader.kt new file mode 100644 index 000000000..b63dfd032 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/ChildFirstPathClassLoader.kt @@ -0,0 +1,86 @@ +package eu.kanade.tachiyomi.util.system + +import dalvik.system.PathClassLoader +import java.io.IOException +import java.io.InputStream +import java.net.URL +import java.util.Enumeration + +/** + * A parent-last class loader that will try in order: + * - the system class loader + * - the child class loader + * - the parent class loader. + */ +class ChildFirstPathClassLoader( + dexPath: String, + librarySearchPath: String?, + parent: ClassLoader +) : PathClassLoader(dexPath, librarySearchPath, parent) { + + private val systemClassLoader: ClassLoader? = getSystemClassLoader() + + override fun loadClass(name: String?, resolve: Boolean): Class<*> { + var c = findLoadedClass(name) + + if (c == null && systemClassLoader != null) { + try { + c = systemClassLoader.loadClass(name) + } catch (_: ClassNotFoundException) {} + } + + if (c == null) { + c = try { + findClass(name) + } catch (_: ClassNotFoundException) { + super.loadClass(name, resolve) + } + } + + if (resolve) { + resolveClass(c) + } + + return c + } + + override fun getResource(name: String?): URL? { + return systemClassLoader?.getResource(name) + ?: findResource(name) + ?: super.getResource(name) + } + + override fun getResources(name: String?): Enumeration { + val systemUrls = systemClassLoader?.getResources(name) + val localUrls = findResources(name) + val parentUrls = parent?.getResources(name) + val urls = buildList { + while (systemUrls?.hasMoreElements() == true) { + add(systemUrls.nextElement()) + } + + while (localUrls?.hasMoreElements() == true) { + add(localUrls.nextElement()) + } + + while (parentUrls?.hasMoreElements() == true) { + add(parentUrls.nextElement()) + } + } + + return object : Enumeration { + val iterator = urls.iterator() + + override fun hasMoreElements() = iterator.hasNext() + override fun nextElement() = iterator.next() + } + } + + override fun getResourceAsStream(name: String?): InputStream? { + return try { + getResource(name)?.openStream() + } catch (_: IOException) { + return null + } + } +} From 07f963d5ae16c3c8d7be025a7e9439ad2110ac71 Mon Sep 17 00:00:00 2001 From: Maddie Witman Date: Sat, 24 Feb 2024 14:33:46 -0500 Subject: [PATCH 062/212] Fix some issues from 7ff95e2 (#415) * Fixed extra header introduced in 7ff95e2 * Removed parentheses to make detekt happy * Updated relative date display for dates in the future * Small cleanup for header creation logic * replaced "and" with "&&" for better formatting --- .../kanade/tachiyomi/ui/history/HistoryScreenModel.kt | 9 +++------ .../kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt | 9 +++------ .../eu/kanade/tachiyomi/util/lang/DateExtensions.kt | 10 +++++++++- i18n/src/commonMain/resources/MR/base/plurals.xml | 5 +++++ 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt index 7b0cdcf72..c0c2b555f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt @@ -28,7 +28,6 @@ import tachiyomi.domain.history.interactor.RemoveHistory import tachiyomi.domain.history.model.HistoryWithRelations import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.time.LocalDate class HistoryScreenModel( private val getHistory: GetHistory = Injekt.get(), @@ -60,12 +59,10 @@ class HistoryScreenModel( private fun List.toHistoryUiModels(): List { return map { HistoryUiModel.Item(it) } .insertSeparators { before, after -> - val beforeDate = before?.item?.readAt?.time?.toLocalDate() ?: LocalDate.MIN - val afterDate = after?.item?.readAt?.time?.toLocalDate() ?: LocalDate.MIN + val beforeDate = before?.item?.readAt?.time?.toLocalDate() + val afterDate = after?.item?.readAt?.time?.toLocalDate() when { - beforeDate.isAfter(afterDate) - or afterDate.equals(LocalDate.MIN) - or beforeDate.equals(LocalDate.MIN) -> HistoryUiModel.Header(afterDate) + beforeDate != afterDate && afterDate != null -> HistoryUiModel.Header(afterDate) // Return null to avoid adding a separator between two items. else -> null } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt index 2b2c1c25a..c5385d1f0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt @@ -45,7 +45,6 @@ import tachiyomi.domain.updates.interactor.GetUpdates import tachiyomi.domain.updates.model.UpdatesWithRelations import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.time.LocalDate import java.time.ZonedDateTime class UpdatesScreenModel( @@ -374,12 +373,10 @@ class UpdatesScreenModel( return items .map { UpdatesUiModel.Item(it) } .insertSeparators { before, after -> - val beforeDate = before?.item?.update?.dateFetch?.toLocalDate() ?: LocalDate.MIN - val afterDate = after?.item?.update?.dateFetch?.toLocalDate() ?: LocalDate.MIN + val beforeDate = before?.item?.update?.dateFetch?.toLocalDate() + val afterDate = after?.item?.update?.dateFetch?.toLocalDate() when { - beforeDate.isAfter(afterDate) - or afterDate.equals(LocalDate.MIN) - or beforeDate.equals(LocalDate.MIN) -> UpdatesUiModel.Header(afterDate) + beforeDate != afterDate && afterDate != null -> UpdatesUiModel.Header(afterDate) // Return null to avoid adding a separator between two items. else -> null } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt index 9e61555ec..8326fe2f8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt @@ -13,6 +13,7 @@ import java.time.format.DateTimeFormatter import java.time.format.FormatStyle import java.time.temporal.ChronoUnit import java.util.Date +import kotlin.math.absoluteValue fun LocalDateTime.toDateTimestampString(dateTimeFormatter: DateTimeFormatter): String { val date = dateTimeFormatter.format(this) @@ -49,13 +50,20 @@ fun LocalDate.toRelativeString( val now = LocalDate.now() val difference = ChronoUnit.DAYS.between(this, now) return when { - difference < 0 -> difference.toString() + difference < -7 -> dateFormat.format(this) + difference < 0 -> context.pluralStringResource( + MR.plurals.upcoming_relative_time, + difference.toInt().absoluteValue, + difference.toInt().absoluteValue, + ) + difference < 1 -> context.stringResource(MR.strings.relative_time_today) difference < 7 -> context.pluralStringResource( MR.plurals.relative_time, difference.toInt(), difference.toInt(), ) + else -> dateFormat.format(this) } } diff --git a/i18n/src/commonMain/resources/MR/base/plurals.xml b/i18n/src/commonMain/resources/MR/base/plurals.xml index 2f10b004c..d9c958afc 100644 --- a/i18n/src/commonMain/resources/MR/base/plurals.xml +++ b/i18n/src/commonMain/resources/MR/base/plurals.xml @@ -10,6 +10,11 @@ %1$d days ago + + Tomorrow + In %1$d days + + %d category %d categories From 7edecae57f77ece7a5a3b457620c61e225fdc906 Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Sat, 24 Feb 2024 20:34:52 +0100 Subject: [PATCH 063/212] Translations update from Hosted Weblate (#301) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Translated using Weblate (Spanish) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/ * Translated using Weblate (Indonesian) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/id/ * Translated using Weblate (Chuvash) Currently translated at 75.7% (601 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/cv/ * Translated using Weblate (Spanish) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/ * Translated using Weblate (Chuvash) Currently translated at 100.0% (17 of 17 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/cv/ * Translated using Weblate (Esperanto) Currently translated at 100.0% (17 of 17 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/eo/ * Translated using Weblate (Turkish) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/ * Translated using Weblate (Turkish) Currently translated at 100.0% (17 of 17 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/tr/ * Translated using Weblate (Turkish) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/ * Translated using Weblate (Turkish) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/ * Translated using Weblate (Japanese) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/ * Translated using Weblate (Polish) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pl/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/ * Translated using Weblate (Hungarian) Currently translated at 97.3% (772 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/ * Translated using Weblate (Hungarian) Currently translated at 94.1% (16 of 17 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/hu/ * Translated using Weblate (Spanish) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/ * Translated using Weblate (Romanian) Currently translated at 99.3% (788 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ro/ * Translated using Weblate (Romanian) Currently translated at 100.0% (17 of 17 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ro/ * Translated using Weblate (Ukrainian) Currently translated at 100.0% (17 of 17 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/uk/ * Translated using Weblate (Spanish) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/ * Translated using Weblate (Spanish) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/ * Translated using Weblate (Nepali) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/ * Translated using Weblate (Spanish) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/ * Translated using Weblate (Spanish) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/ * Translated using Weblate (French) Currently translated at 100.0% (17 of 17 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/fr/ * Translated using Weblate (Esperanto) Currently translated at 62.0% (492 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/eo/ * Translated using Weblate (Esperanto) Currently translated at 100.0% (17 of 17 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/eo/ * Translated using Weblate (Esperanto) Currently translated at 63.6% (505 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/eo/ --------- Co-authored-by: bapeey <90949336+bapeey@users.noreply.github.com> Co-authored-by: Eji-san Co-authored-by: C201 Co-authored-by: Swyter Co-authored-by: Radoŝ Porka Co-authored-by: Deniz Co-authored-by: Uzuki Shimamura Co-authored-by: sebastians17 Co-authored-by: ɴᴇᴋᴏ Co-authored-by: B4LiN7 Co-authored-by: Saft Octavian Co-authored-by: Сергій Co-authored-by: gallegonovato Co-authored-by: FateXBlood Co-authored-by: Naga --- .../commonMain/resources/MR/cv/plurals.xml | 24 ++-- .../commonMain/resources/MR/cv/strings.xml | 79 +++++++++---- .../commonMain/resources/MR/eo/plurals.xml | 16 ++- .../commonMain/resources/MR/eo/strings.xml | 24 ++++ .../commonMain/resources/MR/es/strings.xml | 10 +- .../commonMain/resources/MR/fr/plurals.xml | 4 +- .../commonMain/resources/MR/hu/plurals.xml | 16 +-- .../commonMain/resources/MR/hu/strings.xml | 49 +++++++- .../commonMain/resources/MR/in/strings.xml | 2 +- .../commonMain/resources/MR/ja/strings.xml | 2 +- .../commonMain/resources/MR/ne/strings.xml | 4 +- .../commonMain/resources/MR/pl/strings.xml | 2 +- .../commonMain/resources/MR/ro/plurals.xml | 15 ++- .../commonMain/resources/MR/ro/strings.xml | 46 ++++++++ .../commonMain/resources/MR/tr/plurals.xml | 4 +- .../commonMain/resources/MR/tr/strings.xml | 108 +++++++++--------- .../commonMain/resources/MR/uk/plurals.xml | 10 +- .../resources/MR/zh-rTW/strings.xml | 4 +- 18 files changed, 289 insertions(+), 130 deletions(-) diff --git a/i18n/src/commonMain/resources/MR/cv/plurals.xml b/i18n/src/commonMain/resources/MR/cv/plurals.xml index b96d0872c..21a249060 100644 --- a/i18n/src/commonMain/resources/MR/cv/plurals.xml +++ b/i18n/src/commonMain/resources/MR/cv/plurals.xml @@ -5,28 +5,28 @@ %d пухмӑш - 1 минут хыҫҫӑн + %1$s минут хыҫҫӑн %1$s минут хыҫҫӑн - %1$s,%2$s йӑнӑшпа тӑвӑннӑ - %1$s, %2$s йӑнӑшпа тӑвӑннӑ + %1$s хушши %2$s йӑнӑшпа тӑвӑннӑ + %1$s хушши %2$s йӑнӑшпа тӑвӑннӑ Хушма валли ҫӗнетӳ пур %d хушма валли ҫӗнетӳ пур - %1$s сыпӑкӗсем - %1$s сыпӑкӗсем тата ытти %2$d + %1$s сыпӑкӗсем тата тепӗр 1 + %1$s сыпӑкӗсем тата тепӗр %2$d - 1 ҫӗнӗ сыпӑк + %1$d ҫӗнӗ сыпӑк %1$d ҫӗнӗ сыпӑк - Ҫӗнӗ сыпӑксем 1 хайлав валли тупӑннӑ - Ҫӗнӗ сыпӑксем %d хайлав валли тупӑннӑ + %d хайлав валли + %d хайлав валли %1$s сыпӑк @@ -37,8 +37,8 @@ %1$s йулчӗ - 1 сӑнану - %d сӑнану + %d йӗрлев + %d йӗрлев %d сыпӑк ҫук @@ -64,4 +64,8 @@ Тепӗр сыпӑк Тепӗр %d сыпӑк + + %d усрав + %d усрав + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/cv/strings.xml b/i18n/src/commonMain/resources/MR/cv/strings.xml index d10ebf33d..617a009ed 100644 --- a/i18n/src/commonMain/resources/MR/cv/strings.xml +++ b/i18n/src/commonMain/resources/MR/cv/strings.xml @@ -1,6 +1,6 @@ - Вулавӑшри серилӗхсем + Вулавӑшри хайлавсем Усӑ курнӑ: %1$s Куккисем катертнӗ Кукки тасат @@ -49,8 +49,8 @@ Ыкран сыхлавӗ Сыхлав тата вӑрттӑнлӑх Систерӳсене ӗнер - Ҫул-кун хармачӗ - Ҫутнӑ + Вӑхӑт хармачӗ + Тӗттӗм Сӳнтернӗ Тата Тийевсем @@ -72,8 +72,8 @@ Пӑрахӑҫла Ҫаклат Сӳнтер - Тийене сыпӑксем шучӗ - Йат-йыш + Тийенӗ сыпӑксен шучӗ + Йат йышӗ Кӑтарт Кӑтарту тытӑмӗ WebView-ра уҫ @@ -83,12 +83,12 @@ Катерт Ҫӗнӗрен Чар - Сыпӑксем пӑх - Хуплашка улӑштар - Пухмӑша хуш + Сыпӑксене пӑх + Хуплашкана улӑштар + Пухмӑша кӗрт Пухмӑш йатне улӑштар Пухмӑшсене улӑштар - Пухмӑш хуш + Пухмӑша хуш Хуш Умӗнхине вуланӑ пек паллӑ ту Улӑштар @@ -198,7 +198,7 @@ Шанчӑклӑ мар хушма Ним туман чух ҫаклатни %1$s-мӗш сыпӑк - Ҫаклатӑва уҫма пӳрне йӗрӗ ыйтни + Ҫаклатӑва уҫма пӳрне йӗрре ыйтни Аяккинчи чаку Сыпӑксем урлӑ каҫнине яланах кӑтарт Тӑрӑлӑх мар @@ -400,7 +400,7 @@ Янтӑв ту v%1$s верссиччен ҫӗнетнӗ Мӗн ҫӗнни - Темӑ + Темӗ Хушни вӑхӑчӗпе Тиск ҫинче вырӑн ҫитмен пирки сыпӑксем тийенеймерӗҫ \"%1$s\" пур ҫӗрте шыра @@ -436,7 +436,7 @@ NSFW (18+) ҫӑл куҫӗсем Файлсене суйламалли хушӑм тупӑнман Тархасшӑн MAL-а ҫӗнӗрен кӗр - Ҫӑл куҫсен тата хушмасен йат-йышӗнче кӑтартни + Ҫӑл куҫсен тата хушмасен йат йышӗнче кӑтартни Вулама вӗҫленӗ вӑхӑчӗ Вулама пуҫланӑ вӑхӑчӗ Хӗрри @@ -477,31 +477,31 @@ Йӑнӑшсене кӑтарт Ҫак серилӗх валли пурне те пӑрахӑҫла Хӑй-хальлӗн тийесе илни, малтанах тийени - Йулашки ҫӗнетӳ тӗрӗсленипе + Йулашки хут ҫӗнетӳ пуррине тӗрӗсленипе Йулнӑ сыпӑксемпе Ҫырав шучӗпе Пурне те катерт - «%s» пухмӑш катертесшӗнех-и\? - Пухмӑш катерт + «%s» пухмӑша катертесшӗнех-и? + Пухмӑша катерт Вырӑнти ҫӑл куҫран Асӑрхаттару Йатсӑр сетке - Серилӗхе пуҫламӑша куҫар + Хайлава пуҫламӑша куҫар Улшӑнӑва ҫирӗплетме есӗлӗхе ҫирӗплет - Ҫырава кӑтарт + Хайлава кӑтарт Чӗлхе Шыра… Хуп Тийеве халех пуҫла - InternalError: Хушма пӗлӗме пӑхма тӑвӑм-пулӑм кӗнекине пӑх + InternalError: Хушма хыпар-пӗлӳ пӑхма тӑвӑм-пулӑм кӗнекине пӑх Кӑтартӑну Ҫутнӑ Сӳнтернӗ - Темӑ, кун тата вӑхӑт хармачӗ + Темӗ, кун тата вӑхӑт хармачӗ Пухмӑшсем, пӗтӗмӗшле ҫӗнетӳ, сыпӑксене туртни Вулав тытӑмӗ, кӑтартӑнни, куҫӑм Йаланхилле - Ӑнсӑрт ҫырав уҫ + Ӑнсӑрт ҫырава уҫ Ҫӗр ҫырли тайккирийӗ Ҫур ҫӗр ӗнтрӗкӗ Пӗр йенлӗ ӳсӗм килӗштерӗвӗ, анлӑлатнӑ килӗштерӳ @@ -524,7 +524,7 @@ Чараксӑр тетел урлӑ ҫеҫ Серилӗхе пуҫламан Такку - Хуп-хура темӑ + Хуп-хура темӗ Ап чӗлхи Халь мар Кашни 3 кун @@ -533,8 +533,8 @@ Шутлавсем Пайлашу асне ӑт Хӑй тӗллӗн тата хӑй-хальлӗн йантӑлав - Пухмӑш ҫӗнет - Йӑнӑш кӗнекине тийесе йани, петтерей лайӑхлатни + Пухмӑша ҫӗнет + Йӑнӑш кӗнекине тийесе йани, петтерейе лайӑхлатни Вуламан сыпӑксен шутне «Ҫӗнӗлӗх» ыккун ҫинче кӑтартни Сӑна тӑрӑх пысӑклатни Нумай чӗлхеллӗ @@ -568,6 +568,37 @@ Shizuku-н хушма ларткӑча усӑ курма Shizuku ларт тата ҫут. Йурӗ %s уҫ - Серилӗхе вӗҫе куҫар + Хайлава вӗҫе куҫар Кӗртнӗ пухмӑшсенче пулнӑ пулсан та кӑларса пӑрахнӑ пухмӑшсенче пулнӑ серилӗхсем ҫӗнелмӗҫ. + Суйланӑ + Суйламан + Ҫӳлелле куҫ + Куҫаруҫӑ + Пӗлӗмсем тата усрав + Йӗрлевҫӗн хаклавӗ + Пултмӑшсене сас паллисен йӗркипе аласшӑн-им? + Кӳр + Йаланхилле тавӑр + Пуҫлама пӗлкӗч + Килӗрех! + Айтӑр темиҫе йапала ӗнерӗпӗр. Есӗ вӗсене йаланах кайран ӗнерӳсенче улӑштарма пултаратӑн. + Малалла + Пуҫла + Ирттер + Папка суйла + Папка суйламалла + Усрав пӗлкӗчӗ + Апсене лартма ирӗк + Ҫӑл куҫсен хушмисене лартма ирӗк парӗ. + Систерӳсем килме ирӗк + Вулавӑшри ҫӗнетӳсем пурри пирки тата ытти пирки пӗлесси. + Петтерейе хыҫра усӑ курма ирӗк + Тӑсӑлакан вулавӑш ҫӗнелни, тийени тата йантӑ ӑтава тавӑрни чарӑнасран хӑтӑлтарӗ. + Ирӗк пар + %s-ра ҫӗнни? Епӗр пуҫлав пӗлкӗчӗпе паллашма сӗнетпӗр. + Ҫур ҫӗр + Путмӑшсене ала + Кивӗ верссирен ҫӗнӗлетӗн те мӗн суламаллине пӗлместӗн? Нумайрах пӗлме усрав пӗлкӗчне кӗрсе пӑх. + %s ҫӗнӗрен лартатӑн? + «%1$s» вырӑнне «%2$s» \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/eo/plurals.xml b/i18n/src/commonMain/resources/MR/eo/plurals.xml index 1daf79fc5..90b28e925 100644 --- a/i18n/src/commonMain/resources/MR/eo/plurals.xml +++ b/i18n/src/commonMain/resources/MR/eo/plurals.xml @@ -1,7 +1,7 @@ - Post 1 minuto + Post %1$s minuto Post %1$s minutoj @@ -17,7 +17,7 @@ Ĉapitroj %1$s kaj %2$d pli - 1 nova ĉapitro + %1$d nova ĉapitro %1$d novaj ĉapitroj @@ -25,8 +25,8 @@ Por %d titoloj - Mankas 1 ĉapitron - Mankas %d ĉapitrojn + Preterpasas %d ĉapitron, aŭ ĝi mankas ĉe la fonto aŭ ĝi estis elfiltrita + Preterpasas %d ĉapitrojn, aŭ ili mankas ĉe la fonto aŭ ili estis elfiltritaj 1 ŝanĝspurilo @@ -60,4 +60,12 @@ Sekva nelegita ĉapitro Sekvaj %d nelegitaj ĉapitroj + + %d deponejo + %d deponejoj + + + Disponebla ĝisdatigo de etendaĵo + Disponeblaj ĝisdatigoj de %d etendaĵoj + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/eo/strings.xml b/i18n/src/commonMain/resources/MR/eo/strings.xml index 657166ba7..57643acd9 100644 --- a/i18n/src/commonMain/resources/MR/eo/strings.xml +++ b/i18n/src/commonMain/resources/MR/eo/strings.xml @@ -481,4 +481,28 @@ Forigi ĉiojn Bonvenon! Preterpasi + Elektitaj + Ne elektitaj + Pli da opcioj + Forigi elŝutitaĵn + Datumoj kaj konservejo + Ĝisdatigi kategorion + Sekva + Dosierujo devas esti elektita + Eloso, dato & tempoformo + Apa ŝlosado, sekura ekrano + Malŝlosi %s + Malŝaltita + Tuta eraro: + Agordoj de fonto + Apa agordoj + Norda + Cunami + \"%1$s\" anstataŭ \"%2$s\" + Apa lingvo + Ĝisdatigi ĉiujn + Versio + Lingvo + Instalilo + Malmoderna \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/es/strings.xml b/i18n/src/commonMain/resources/MR/es/strings.xml index 9a9163403..5b10363da 100644 --- a/i18n/src/commonMain/resources/MR/es/strings.xml +++ b/i18n/src/commonMain/resources/MR/es/strings.xml @@ -506,7 +506,7 @@ El formato del capítulo no es correcto No se ha encontrado el capítulo Desactivar el modo incógnito - Ofrece funciones mejoradas para ciertas fuentes. Se hace un seguimiento automático de los elementos al añadirlos a la biblioteca. + Ofrecen funciones mejoradas para ciertas fuentes. Se hace un seguimiento automático de los elementos al añadirlos a la biblioteca. Servicios de seguimiento mejorados Guía de seguimiento Automático @@ -669,7 +669,7 @@ %dh %dm En uso - ¿? + N/D En servicios de seguimiento Descargados Estadísticas @@ -681,7 +681,7 @@ Ver número de capítulos por leer en el icono de actualizaciones Se ha copiado al portapapeles Saltarse los capítulos repetidos - Está disponible, pero la fuente todavía no se ha instalado: %s + Están disponibles, pero las fuentes todavía no se han instalado: %s Ya tienes un elemento en la biblioteca con este mismo nombre. \n \n¿Seguro que quieres continuar\? @@ -742,7 +742,7 @@ Almacenamiento utilizado Puntuación del rastreador Aplicar - Volver a la configuración predeterminada + Restablecer vista Crear No se ha encontrado ningún equipo de traducción Equipo de traducción @@ -780,7 +780,7 @@ Toca aquí para conceder los permisos necesarios para instalar extensiones. Incluir datos privados, como las claves de inicio de sesión en plataformas de seguimiento Descripción completa del problema: - Se espera que se publiquen nuevos capítulos en torno a %1$s, el ciclo aproximado de comprobación entre números es de %2$s. + Se espera que el siguiente número salga en aproximadamente %1$s, la aplicación busca actualizaciones cada %2$s. Frecuencia de actualización personalizada: El repositorio ya existe Actualizaciones inteligentes diff --git a/i18n/src/commonMain/resources/MR/fr/plurals.xml b/i18n/src/commonMain/resources/MR/fr/plurals.xml index 9fc87129c..21e4e5eab 100644 --- a/i18n/src/commonMain/resources/MR/fr/plurals.xml +++ b/i18n/src/commonMain/resources/MR/fr/plurals.xml @@ -62,8 +62,8 @@ Chapitre suivant non lu - Les %d suivants non lus - Les %d suivants non lus + Les %d chapitres suivants non lus + Les %d chapitres suivants non lus Chapitre suivant diff --git a/i18n/src/commonMain/resources/MR/hu/plurals.xml b/i18n/src/commonMain/resources/MR/hu/plurals.xml index 535bbf3a1..469179e28 100644 --- a/i18n/src/commonMain/resources/MR/hu/plurals.xml +++ b/i18n/src/commonMain/resources/MR/hu/plurals.xml @@ -5,15 +5,15 @@ %d bővítményfrissítés érhető el - 1 perc után - %1$s percek után + %1$s perc múlva + %1$s perc múlva %d kategória - %d kategóriák + %d kategória - 1 fejezet + %1$s fejezet %1$s fejezet @@ -26,7 +26,7 @@ %d tracker - %d trackerek + %d tracker %d-nak/nek @@ -54,11 +54,11 @@ Következő fejezet - Következő %d fejezetek + Következő %d fejezet - Hiányzó %1$s fejezet - Hiányzó %1$s fejezetek + %1$s fejezet hiányzik + %1$s fejezet hiányzik 1 nap diff --git a/i18n/src/commonMain/resources/MR/hu/strings.xml b/i18n/src/commonMain/resources/MR/hu/strings.xml index 3a9eff415..1224c75ad 100644 --- a/i18n/src/commonMain/resources/MR/hu/strings.xml +++ b/i18n/src/commonMain/resources/MR/hu/strings.xml @@ -130,8 +130,8 @@ Több Források Dátum formátum - Be - Ki + Sötét + Világos Könyvtár Frissít Előre @@ -175,7 +175,7 @@ Mindig Biztonság Értesítések kezelése - Rendszerbeállítás követése + Rendszer Téma Ugrás legalulra Ugrás legfelülre @@ -597,7 +597,7 @@ Segít háttérbeli könyvtár frissítésben és a biztonsági mentésekben Akkumulátor optimalizálás már ki van kapcsolva Bő feljegyzés - Bő feljegyzések rendszer feljegyzésbe való kiírása (csökkenti az app teljesítményét) + Részletes naplók irása a rendszernaplóba (csökkenti az alkalmazás teljesítményét) Újdonságok Segíts fordítani Adatvédelmi irányelvek @@ -740,4 +740,45 @@ \"%1$s\", ahelyett hogy \"%2$s\" %s nem elérhető Szkennelő-fordítók tiltása + Alkalmazás + Bevezetési útmutató + Üdvözöljük! + Következő + Kezdjünk hozzá + Kihagyás + Válasszon ki egy mappát, ahol a(z) %1$s a fejezetek letöltéseit, biztonsági mentéseket és egyebeket tárolja. +\n +\nAjánlott egy külön mappa használata. +\n +\nKiválasztott mappa: %2$s + Válasszon ki egy mappát + Ki kell választani egy mappát + Régebbi verzióról frissít, és nem tudja, mit válasszon? További információkért olvassa el a tárolási útmutatót. + Tárolási útmutató + Alkalmazások telepítésének engedélyezése + A forrásbővítmények telepítéséhez. + Értesítési engedély + Értesüljön a könyvtári frissítésekről és egyéb információkról. + Háttérben lévő akkumulátor-használat + Kerülje el a hosszan tartó könyvtárfrissítések, letöltések és biztonsági mentések helyreállításának megszakítását. + Engedélyezés + Új a(z) %s az ön számára? Javasoljuk, hogy olvassa el a Kezdő útmutatót. + Újratelepíti a(z) %s? + Intelligens frissítés + A bővítmények telepítéséhez engedélyek szükségesek. Az engedélyezéshez koppintson ide. + Megbízható ismeretlen kiterjesztések visszavonása + Bővítmény tárolók + Nincsenek beállítva tárolók. + Tároló felvétele + Tároló URL + További tárolók hozzáadása a Mihonhoz. Ennek az URL-nek \"index.min.json\"-ra végződő URL-nek kell lennie. + Ez a tároló már létezik! + Tároló törlése + Érvénytelen tároló URL + Szeretné törölni a(z) \"%s\" tárolót? + Nyílt forráskódú tároló + Nincs tárolási hely beállítva + Létrehoz + Teljes hiba: + Kezdetnek állítsunk be néhány dolgot. Ezeket később bármikor módosíthatja a beállításokban. \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/in/strings.xml b/i18n/src/commonMain/resources/MR/in/strings.xml index 9859b62c0..1bc7301b2 100644 --- a/i18n/src/commonMain/resources/MR/in/strings.xml +++ b/i18n/src/commonMain/resources/MR/in/strings.xml @@ -492,7 +492,7 @@ Gagal menyimpan sampul Sampul disimpan Sampul - Nonaktif + Matikan Aktif Panduan pelacakan Pengaturan per-kategori untuk urutan diff --git a/i18n/src/commonMain/resources/MR/ja/strings.xml b/i18n/src/commonMain/resources/MR/ja/strings.xml index 9c7429d80..126f6bdb0 100644 --- a/i18n/src/commonMain/resources/MR/ja/strings.xml +++ b/i18n/src/commonMain/resources/MR/ja/strings.xml @@ -785,7 +785,7 @@ リポジトリを削除 リポジトリ「%s」を削除してもよろしいですか? ストレージ ガイド - トラッカーログイン情報などの機密性の高い情報を含みます + トラッカーログイン情報などの機密性の高い情報を含む 間もなく 不明な拡張機能の信頼を取り消す 拡張機能リポジトリ diff --git a/i18n/src/commonMain/resources/MR/ne/strings.xml b/i18n/src/commonMain/resources/MR/ne/strings.xml index 955f018de..f7b46424b 100644 --- a/i18n/src/commonMain/resources/MR/ne/strings.xml +++ b/i18n/src/commonMain/resources/MR/ne/strings.xml @@ -111,7 +111,7 @@ उल्टो चयन गर्नुहोस् राखिएको मिती अध्याय ल्याएको मिति - सबैभन्दा नयाँ अध्याय + नवीनतम अध्याय पुस्तकालय अपडेट गर्दा नयाँ आवरण र विवरणहरूको लागि जाँच गर्नुहोस् मेटाडेटा स्वतः रिफ्रेस गर्नुहोस् \"समाप्त\" स्थिति भएको @@ -409,7 +409,7 @@ अन्य पुस्तकालयबाट ट्याबहरू - सबैभन्दा नयाँ + नवीनतम द्वारा अर्डर गर्नुहोस् मिति %1$s मा लगइन गर्नुहोस् diff --git a/i18n/src/commonMain/resources/MR/pl/strings.xml b/i18n/src/commonMain/resources/MR/pl/strings.xml index f079535ed..a2569acc1 100644 --- a/i18n/src/commonMain/resources/MR/pl/strings.xml +++ b/i18n/src/commonMain/resources/MR/pl/strings.xml @@ -728,7 +728,7 @@ Przenieś serię na dół Nigdy Wybierz folder - Następna aktualizacja spodziewana za około %1$s, sprawdzanie co %2$s + Następna aktualizacja spodziewana za około %1$s, sprawdzanie co %2$s. Folder musi być wybrany Uprawnienia powiadomień Sortuj kategorie diff --git a/i18n/src/commonMain/resources/MR/ro/plurals.xml b/i18n/src/commonMain/resources/MR/ro/plurals.xml index 2bda6def5..17e794c1c 100644 --- a/i18n/src/commonMain/resources/MR/ro/plurals.xml +++ b/i18n/src/commonMain/resources/MR/ro/plurals.xml @@ -3,7 +3,7 @@ După %1$s minut După %1$s minute - După %1$s minute + După %1$s de minute %1$d capitol nou @@ -33,7 +33,7 @@ %d categorie %d categorii - %d categorii + %d de categorii Gata în %1$s cu eroarea %2$s @@ -48,7 +48,7 @@ %d tracker %d trackere - %d trackere + %d de trackere Omiterea %d capitol, fie că sursa lipsește, fie că a fost filtrată @@ -58,12 +58,12 @@ Ieri Acum %1$d zile - Acum %1$d zile + Acum %1$d de zile Următorul capitol necitit Următoarele %d capitole necitite - Următoarele %d capitole necitite + Următoarele %d de capitole necitite Următorul capitol @@ -80,4 +80,9 @@ %d zile %d zile + + %d repozitoriu + %d repozitorii + %d de repozitorii + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ro/strings.xml b/i18n/src/commonMain/resources/MR/ro/strings.xml index 8e7e8d7cb..5bc15ead1 100644 --- a/i18n/src/commonMain/resources/MR/ro/strings.xml +++ b/i18n/src/commonMain/resources/MR/ro/strings.xml @@ -753,4 +753,50 @@ Înregistrări monitorizate Exclude scanlator Glisare capitol + Ghid de îmbarcare + Bine ați venit! + Următorul + Omite + Selectează un folder + Un folder trebuie selectat + Actualizați de la o versiune mai veche și nu sunteți sigur ce să selectați? Pentru mai multe informații, consultați ghidul de stocare. + Ghid de stocare + Permisiunea de a instala aplicații + Pentru a instala extensii sursă. + Utilizarea bateriei în fundal + Evitați întreruperile actualizărilor bibliotecii, descărcărilor, și restaurărilor de lungă durată. + Acordă + Reinstalare %s? + Actualizare inteligentă + Repozitoriu extensii + Nu aveți nici un repozitoriu configurat. + Adăugați repozitoriu + Adăugați repozitorii adiționale pentru Mihon. Acestea ar trebuii să fie URL-uri care se încheie cu \"index.min.json\". + Doriți să ștergeți repozitoriul \"%s\"? + Repozitoriu open source + Nici o locație pentru stocare nu a fost setată + Eroarea completă: + Includeți setări sensibile (ex. tokeni de urmărire al logări) + Disponibil: %1$s / Total: %2$s + Estimează la fiecare + Configurează actualizări la fiecare + Se preconizează că vor fi lansate noi capitole în aproximativ %1$s, verifică la fiecare %2$s. + În curând + Frecvența personalizată de actualizare: + Să configurăm câteva lucruri mai întâi. Le puteți schimba oricând în setări și mai târziu. + Începe + Selectează un folder unde %1$s va păstra capitolele descărcate, copiile de rezervă, și altele. +\n +\nSe recomanda folosirea unui folder dedicat. +\n +\nFolder-ul selectat: %2$s + Permisiune de notificare + Primiți notificări pentru actualizările bibliotecii și altele. + Nou pe %s? Vă recomandăm să consultați ghidul introductiv. + Sunt necesare permisiuni pentru instalarea de extensii. Atingeți aici pentru a permite. + Revocați încrederea extensiilor necunoscute + URL repozitoriu + Acest repozitoriu există deja! + Șterge repozitoriu + URL repozitoriu invalid \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/tr/plurals.xml b/i18n/src/commonMain/resources/MR/tr/plurals.xml index 3a2a47d45..7e40a5df6 100644 --- a/i18n/src/commonMain/resources/MR/tr/plurals.xml +++ b/i18n/src/commonMain/resources/MR/tr/plurals.xml @@ -25,8 +25,8 @@ %1$s kaldı - %d ulam - %d ulam + %d kategori + %d kategoriler %1$s içinde %2$s hatayla tamamlandı diff --git a/i18n/src/commonMain/resources/MR/tr/strings.xml b/i18n/src/commonMain/resources/MR/tr/strings.xml index d08383a88..fdd88b422 100644 --- a/i18n/src/commonMain/resources/MR/tr/strings.xml +++ b/i18n/src/commonMain/resources/MR/tr/strings.xml @@ -36,10 +36,10 @@ Kitaplığı güncelle Düzenle Ekle - Ulam ekle - Ulamları düzenle - Ulamı yeniden adlandır - Ulamları ayarla + Kategori ekle + Kategorileri düzenle + Kategoriyi yeniden adlandır + Kategorileri ayarla Kapağı düzenle Duraklat Önceki bölüm @@ -73,7 +73,7 @@ Izgara boyutu Dikey Yatay - Kendiliğinden güncellemeler + Otomatik güncellemeler Kapalı 6 saatte bir 12 saatte bir @@ -85,7 +85,7 @@ Şarj olurken \"Bitirilmiş\" durumda olan girdileri atla Okuyunca ilerlemeyi güncelle - Varsayılan ulam + Varsayılan kategori Her zaman sor Güncelle Kur @@ -106,15 +106,15 @@ Sayfa numarası göster Kenarları kırp Özel parlaklık - Özel renk süzgeci + Özel renk filtresi Ekranı açık tut Gezinme Ses düğmeleri - Ters ses düğmeleri + Ses düğmelerini tersine çevir Arka plan rengi - Ak - Kara - Öntanımlı okuma kipi + Beyaz + Siyah + Varsayılan okuma modu Sayfalanmış (soldan sağa) Sayfalanmış (soldan sağa) Sayfalanmış (dikey) @@ -125,8 +125,8 @@ Uzat Genişliği sığdır Yüksekliği sığdır - Özgün boyut - Uslu sığdırma + Orijinal boyut + Akıllı sığdırma Yakınlaştırma başlangıç konumu Kendiliğinden Sol @@ -135,7 +135,7 @@ Animasyon yok Normal Hızlı - Ön tanımlı döndürme + Varsayılan döndürme Bağımsız Kilitli dikey Kilitli yatay @@ -143,7 +143,7 @@ Y M A - Elle okundu olarak imlenince + Elle okundu olarak işaretlenince Okunduktan sonra kendiliğinden sil Devre dışı Son okunan bölüm @@ -182,7 +182,7 @@ Giriş Giriş yapıldı Bilinmeyen sorun - Ulam güncelleniyor + Kategori güncelleniyor Başka sonuç yok Yerel kaynak Diğer @@ -222,8 +222,8 @@ Durum Durum Tür - Bu adla bir ulam zaten var! - Ulamlar silindi + Bu adla bir kategori zaten var! + Kategoriler silindi Bu, bu bölümün okunma tarihini kaldıracak. Emin misiniz? Bu girdi için tüm bölümleri sıfırla Kitaplığa eklensin mi\? @@ -254,7 +254,7 @@ Yeni güncelleme yok Yakında okunan yok Kitaplığınız boş - Ulamınız yok. Kitaplığınızı düzenlemek için artı düğmesine basarak bir tane oluşturun. + Tanımlı kategoriniz yok. Kitaplığınızı düzenlemek için artı düğmesine basarak bir tane oluşturun. İndirici Hata Bölüm, beklenmeyen hata sonucu indirilemedi @@ -273,8 +273,8 @@ Uzun dokununca eylemleri göster WebView ile Aç 32-bit renk - Okundu olarak imli bölümleri atla - Renk filtresi karışım kipi + Okundu olarak işaretli bölümleri atla + Renk filtresi karışım modu Kaplama Çarpma Ekran @@ -325,7 +325,7 @@ Pil iyileştirme zaten devre dışı E-posta adresi Bölüm geçişini her zaman göster - Komut-Seçenek Dizelgesi + Menü En yeni En eski En üste taşı @@ -333,7 +333,7 @@ Uzantı güncellemeleri Kitaplık güncelleniyor Okunan - Süzgeçlenmiş bölümleri geç + Filtrelenmiş bölümleri geç Kaynaklar Tersini seç Aralıklı uzun şerit @@ -362,7 +362,7 @@ %02d dk, %02d sn Kitaplığınızdaki tüm girdileri süzer Gri - Renk şeritlerini azaltır, ancak başarımı etkileyebilir + Renk şeritlenmesini azaltır, ancak performansı etkileyebilir Okuma kipi Bu dizi için Aygıt ayarları açılamadı @@ -378,12 +378,12 @@ Geçiş yap Rahat ızgara Sekmeler - Ulam sekmelerini göster + Kategori sekmelerini göster Sayfa bulunamadı Tümünü devre dışı bırak Tümünü etkinleştir - Okuyucu açılınca, kısaca o anki kipi göster - Okuma kipini göster + Okuyucu açılınca, kısaca o anki modu göster + Okuma modunu göster Başlat Kaynak bulunamadı Devre dışı bırak @@ -401,18 +401,18 @@ Saklama alanı az olduğundan bölümler indirilemedi Eklendiği tarih \"%1$s\" için genel arama - Okuma kipi + Okuma modu Tema İğnelenmiş kaynağınız yok Bitirildi İlerleme Hatalar İzleyiciler giriş yapmadı: - İmli bölümleri silmeye izin ver + İşaretli bölümleri silmeye izin ver Bölümleri sil Bu uzantıdaki kaynaklar yetişkin (18+) içerik içerebilir 18+ - Bu, resmi olmayan veya yanlış imlenmiş uzantıların, uygulama içinde yetişkin (18+) içeriği göstermesini engellemez. + Bu, resmi olmayan veya yanlış işaretlenmiş uzantıların, uygulama içinde yetişkin (18+) içeriği göstermesini engellemez. Bölüm bulunamadı Öntanımlı bölüm ayarları güncellendi Öntanımlı olarak ayarla @@ -432,7 +432,7 @@ Önceki sayfa Kaynak geçiş yapma kılavuzu Yetişkin içerik (18+) kaynakları - Kaynaklar ve uzantılar dizelgesinde göster + Kaynaklar ve uzantılar listesinde göster Dosya seçme uygulaması bulunamadı Lütfen MAL\'ye tekrar giriş yapın Dokunma bölgeleri @@ -467,17 +467,17 @@ Bölümün alındığı tarih Hariç tutulan ulamlardaki girdiler, dahil edilen ulamlarda olsa bile indirilmeyecektir. Kendiliğinden indir - Hariç tutulan ulamlardaki girdiler, dahil edilen ulamlarda olsa bile güncellenmeyecektir. + Hariç tutulan kategorilerdeki girdiler, dahil edilen kategorilerde olsa bile güncellenmeyecektir. Ayrıntıları görmek için dokun Bu Android sürümü artık desteklenmiyor Panoya kopyalanamadı Yatay Dikey Döndürme - Girdilerin başlığına göre sıralaç oluşturur - Sayfaları ayrı sıralaçlara kaydet + Girdilerin başlığına göre dosya oluşturur + Sayfaları ayrı dosyalara kaydet Eylemler - Boz tonlama + Gri tonlama Gizli kipi devre dışı bırak Kendiliğinden Bu dizi için tümünü iptal et @@ -496,15 +496,15 @@ Kapak kaydedilemedi Kapak kaydedildi Kapak - Ulam kapsamında sıralama ayarları + Kategori bazlı sıralama ayarları İzleme kılavuzu - Henüz bir ulamınız yok. + Henüz bir kategoriniz yok. Şimdi indirmeye başla Bazı üreticilerin arka plan hizmetlerini durduran ek uygulama kısıtlamaları vardır. Bu web sitesinde durumun nasıl düzeltileceği hakkında daha fazla bilgi var. Yedekleme/geri yükleme, MIUI iyileştirmesi devre dışıysa düzgün çalışmayabilir. Belirli kaynaklar için gelişmiş özellikler sağlar. Girdiler, kitaplığınıza eklendiğinde kendiliğinden izlenir. Gelişmiş izleyiciler - Arı kara karanlık kip + Saf siyah karanlık mod Yotsuba Yin & Yang Tako @@ -521,14 +521,14 @@ Kaydırmada komut-seçenek dizelgesini gizleme duyarlılığı Ters Çevrilmiş Bugün - Deniz kökçesi & Türk kökçesi + Ördekbaşı & Turkuaz Değişikliği onaylamak için kimlik doğrula Varsayılan Görünüm İzle Başlangıç kılavuzu Tablet arayüzü - Hariç tutulan ulamlar + Hariç tutulan kategoriler Çeviriye yardım edin Uygulama bilgisi Uzantı kurucusu olarak kullanmak için Shizuku\'yu kurun ve başlatın. @@ -561,7 +561,7 @@ Kitaplık güncelleme hatalarının nasıl düzeltileceği konusunda yardım için bkz. %1$s Yalnızca kapağa ızgara Okunmayan bölümü olan girdileri atla - Başlamayan girdileri atla + Başlanmayan girdileri atla Dizi bitirildiği için atlandı Okunmamış bölümler olduğu için atlandı Hiçbir bölüm okunmadığı için atlandı @@ -606,8 +606,8 @@ Uygulama dili Açıklama yok Lavanta - Ulamı sil - \"%s\" ulamını silmek istiyor musunuz? + Kategoriyi sil + \"%s\" Kategorisini silmek istiyor musunuz? İçsel sorun: Daha çok bilgi için çökme günlüklerine bakın Öntanımlı kullanıcı aracısı dizgesi Öntanımlı kullanıcı aracısı dizgesini sıfırla @@ -632,7 +632,7 @@ Elle ve kendiliğinden yedeklemeler, depolama alanı Uygulama kilidi, güvenli ekran Çökme günlükleri dökümü, pil iyileştirmeleri - Ulamlar, genel güncelleme, bölüm kaydırma + Kategoriler, genel güncelleme, bölüm kaydırma Kaynaklar, uzantılar, genel arama Kendiliğinden indir, önceden indir Okuma kipi, görüntüleme, gezinme @@ -647,7 +647,7 @@ Şimdi İndirilenler denetleniyor Gelişigüzel girdi aç - Bu ulamda girdi bulunamadı + Bu kategoride girdi bulunamadı F-Droid derlemeleri resmi olarak desteklenmemektedir. \nDaha fazla bilgi edinmek için dokunun. Okumayı sürdür düğmesi @@ -677,11 +677,11 @@ %ddak %dsn Şimdi değil - Ulam boş + Kategori boş Güncellemeler simgesinde okunmayan sayısını göster Panoya kopyalandı Kullanılabilir ancak kaynak kurulu değil: %s - Yinelenen bölümleri atla + Tekrarlanan bölümleri atla Kitaplığınızda aynı ada sahip bir girdiniz var. \n \nYine de devam etmek istiyor musunuz\? @@ -689,7 +689,7 @@ *gerekli Zaten kitaplıkta bulunan girdileri gizle Panoya kopyala - Ulamı güncelle + Kategoriyi güncelle Uzun görselleri otomatik böl Kaplama Geniş sayfaları sığdırmak için döndür @@ -704,7 +704,7 @@ Özelleştirilmiş güncelleme aralığı Zaman aralıkları Sonraki beklenen güncelleme - Bir sonraki yayın zamanını tahmin et + Bir sonraki yayınlama zamanını tahmin et TAMAM Hepsini tahmin et %s izlemesi kaldırılsın mı\? @@ -726,21 +726,21 @@ Kitaplık eşleştiriliyor Aç: %s Diziyi en alta taşı - Ulamları sırala + Kategorileri sırala Kitaplık güncelleniyor… (%s) - Ulamları alfabetik sıralamak ister misiniz\? + Kategorileri alfabetik sıralamak ister misiniz? Seçilen dosya yok Kaynak ayarları Uygulama ayarları Göreli zaman damgaları \"%1$s\" yerine \"%2$s Asla - E-ink görüntülerinde gölgelenmeyi azaltır + E-ink ekranlarda gölgelenmeyi azaltır Uygula Varsayılana dön En son şu tarihte kendi yedekledi: %s Tareviriler bulunamadı - Tareviri + Scanlator Sayfa değişiminde ışık çak Depolama kullanımı İzleyici notu @@ -748,7 +748,7 @@ Tarevirileri hariç tut Oluştur Seçilmedi - Diğer seçenekler + Daha fazla seçenek Seçili Depolama yeri Kendiliğinden yedeklemeler, bölüm indirmeleri ve yerel kaynak için kullanılır. diff --git a/i18n/src/commonMain/resources/MR/uk/plurals.xml b/i18n/src/commonMain/resources/MR/uk/plurals.xml index ad4538677..362a57bd1 100644 --- a/i18n/src/commonMain/resources/MR/uk/plurals.xml +++ b/i18n/src/commonMain/resources/MR/uk/plurals.xml @@ -36,8 +36,8 @@ %d категорій - %1$s залишилось - %1$s залишилось + %1$s залишився + %1$s залишились %1$s залишилось %1$s залишилось @@ -67,15 +67,15 @@ Учора - %1$d днів тому + %1$d дні тому %1$d днів тому %1$d днів тому Наступний непрочитаний розділ Наступні %d непрочитані розділи - Наступні %d непрочитані розділи - Наступні %d непрочитані розділи + Наступні %d непрочитаних розділів + Наступні %d непрочитаних розділів Наступний розділ diff --git a/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml b/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml index 84b680379..25937d48c 100644 --- a/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml +++ b/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml @@ -191,7 +191,7 @@ 已清除快取,刪除了 %1$d 個檔案 清除時發生錯誤 已清除 Cookie - 協助我們修復錯誤,傳送的資料將不包含個人敏感訊息 + 協助我們修復錯誤,傳送的資料將不包含個人敏感資訊 登入 %1$s 使用者名稱 正在更新類別 @@ -460,7 +460,7 @@ 左邊 上一頁 下一頁 - 章節獲取日期 + 章節擷取日期 右邊 橫向 快顯選單 From 21020e1797ae9c0373e5132e1dbcd9a64455c17f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 25 Feb 2024 01:35:17 +0600 Subject: [PATCH 064/212] Update dependency com.google.gms:google-services to v4.4.1 (#418) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8d7446300..b52abc654 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ detektCompose = "0.3.11" [libraries] desugar = "com.android.tools:desugar_jdk_libs:2.0.4" android-shortcut-gradle = "com.github.zellius:android-shortcut-gradle-plugin:0.1.2" -google-services-gradle = "com.google.gms:google-services:4.4.0" +google-services-gradle = "com.google.gms:google-services:4.4.1" rxjava = "io.reactivex:rxjava:1.3.8" From c15f3f2fd5b11cc9c2088ae2fa444f4fe35ea740 Mon Sep 17 00:00:00 2001 From: Splintor <55398298+Splintorien@users.noreply.github.com> Date: Sat, 24 Feb 2024 21:40:06 +0200 Subject: [PATCH 065/212] Allow disabling reader's zoom out (#302) * Allow disabling reader's zoom out (#62) * Renamed disable zoom out pref and string * Zoom to default rate if the scale is inferior * Fixed null value check and formatting * Fixed detekt --- .../more/settings/screen/SettingsReaderScreen.kt | 5 ++++- .../reader/settings/ReadingModePage.kt | 4 ++++ .../ui/reader/setting/ReaderPreferences.kt | 2 ++ .../ui/reader/viewer/webtoon/WebtoonConfig.kt | 11 +++++++++++ .../ui/reader/viewer/webtoon/WebtoonFrame.kt | 6 ++++++ .../reader/viewer/webtoon/WebtoonRecyclerView.kt | 15 ++++++++++++--- .../ui/reader/viewer/webtoon/WebtoonViewer.kt | 4 ++++ i18n/src/commonMain/resources/MR/base/strings.xml | 1 + 8 files changed, 44 insertions(+), 4 deletions(-) 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 bc60037e9..a0a2e8f97 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsReaderScreen.kt @@ -341,7 +341,10 @@ object SettingsReaderScreen : SearchableSettings { Preference.PreferenceItem.SwitchPreference( pref = readerPreferences.webtoonDoubleTapZoomEnabled(), title = stringResource(MR.strings.pref_double_tap_zoom), - enabled = true, + ), + Preference.PreferenceItem.SwitchPreference( + pref = readerPreferences.webtoonDisableZoomOut(), + title = stringResource(MR.strings.pref_webtoon_disable_zoom_out), ), ), ) diff --git a/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt b/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt index ee2eb854f..fb3e16a93 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/settings/ReadingModePage.kt @@ -197,6 +197,10 @@ private fun ColumnScope.WebtoonViewerSettings(screenModel: ReaderSettingsScreenM label = stringResource(MR.strings.pref_double_tap_zoom), pref = screenModel.preferences.webtoonDoubleTapZoomEnabled(), ) + CheckboxItem( + label = stringResource(MR.strings.pref_webtoon_disable_zoom_out), + pref = screenModel.preferences.webtoonDisableZoomOut(), + ) } @Composable diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt index 6c079b836..53e342e76 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderPreferences.kt @@ -72,6 +72,8 @@ class ReaderPreferences( fun skipDupe() = preferenceStore.getBoolean("skip_dupe", false) + fun webtoonDisableZoomOut() = preferenceStore.getBoolean("webtoon_disable_zoom_out", false) + // endregion // region Split two page spread diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonConfig.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonConfig.kt index 28ad91a51..f55478862 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonConfig.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonConfig.kt @@ -29,6 +29,11 @@ class WebtoonConfig( var imageCropBorders = false private set + var zoomOutDisabled = false + private set + + var zoomPropertyChangedListener: ((Boolean) -> Unit)? = null + var sidePadding = 0 private set @@ -74,6 +79,12 @@ class WebtoonConfig( { imagePropertyChangedListener?.invoke() }, ) + readerPreferences.webtoonDisableZoomOut() + .register( + { zoomOutDisabled = it }, + { zoomPropertyChangedListener?.invoke(it) } + ) + readerPreferences.webtoonDoubleTapZoomEnabled() .register( { doubleTapZoom = it }, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonFrame.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonFrame.kt index 572335b25..17aafc6fd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonFrame.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonFrame.kt @@ -33,6 +33,12 @@ class WebtoonFrame(context: Context) : FrameLayout(context) { scaleDetector.isQuickScaleEnabled = value } + var zoomOutDisabled = false + set(value) { + field = value + recycler?.zoomOutDisabled = value + } + /** * Recycler view added in this frame. */ diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonRecyclerView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonRecyclerView.kt index d86619cdf..20c18c622 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonRecyclerView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonRecyclerView.kt @@ -33,6 +33,15 @@ class WebtoonRecyclerView @JvmOverloads constructor( private var firstVisibleItemPosition = 0 private var lastVisibleItemPosition = 0 private var currentScale = DEFAULT_RATE + var zoomOutDisabled = false + set(value) { + field = value + if (value && currentScale < DEFAULT_RATE) { + zoom(currentScale, DEFAULT_RATE, x, 0f, y, 0f) + } + } + private val minRate + get() = if (zoomOutDisabled) DEFAULT_RATE else MIN_RATE private val listener = GestureListener() private val detector = Detector() @@ -166,7 +175,7 @@ class WebtoonRecyclerView @JvmOverloads constructor( fun onScale(scaleFactor: Float) { currentScale *= scaleFactor currentScale = currentScale.coerceIn( - MIN_RATE, + minRate, MAX_SCALE_RATE, ) @@ -193,8 +202,8 @@ class WebtoonRecyclerView @JvmOverloads constructor( } fun onScaleEnd() { - if (scaleX < MIN_RATE) { - zoom(currentScale, MIN_RATE, x, 0f, y, 0f) + if (scaleX < minRate) { + zoom(currentScale, minRate, x, 0f, y, 0f) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt index da11e68ff..1e370a8c7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt @@ -152,6 +152,10 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr frame.doubleTapZoom = it } + config.zoomPropertyChangedListener = { + frame.zoomOutDisabled = it + } + config.navigationModeChangedListener = { val showOnStart = config.navigationOverlayOnStart || config.forceNavigationOverlay activity.binding.navigationOverlay.setNavigation(config.navigator, showOnStart) diff --git a/i18n/src/commonMain/resources/MR/base/strings.xml b/i18n/src/commonMain/resources/MR/base/strings.xml index 5214e41a8..6c6e1681a 100644 --- a/i18n/src/commonMain/resources/MR/base/strings.xml +++ b/i18n/src/commonMain/resources/MR/base/strings.xml @@ -453,6 +453,7 @@ High Low Lowest + Disable zoom out Delete chapters From 2247f6004a47748b4c2fd75c6cc8358859a85ab6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 25 Feb 2024 18:18:24 +0600 Subject: [PATCH 066/212] Update dependency org.apache.commons:commons-compress to v1.26.0 (#448) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b52abc654..5c8d29b92 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -32,7 +32,7 @@ jsoup = "org.jsoup:jsoup:1.17.2" disklrucache = "com.jakewharton:disklrucache:2.0.2" unifile = "com.github.tachiyomiorg:unifile:7c257e1c64" -common-compress = "org.apache.commons:commons-compress:1.25.0" +common-compress = "org.apache.commons:commons-compress:1.26.0" junrar = "com.github.junrar:junrar:7.5.5" sqlite-framework = { module = "androidx.sqlite:sqlite-framework", version.ref = "sqlite" } From 1521c359412518731ef7338255cdd280321faa70 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 25 Feb 2024 18:19:22 +0600 Subject: [PATCH 067/212] Update dependency com.squareup.okio:okio to v3.8.0 (#423) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5c8d29b92..bdc4c9571 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,7 +22,7 @@ okhttp-core = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp_ve okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp_version" } okhttp-brotli = { module = "com.squareup.okhttp3:okhttp-brotli", version.ref = "okhttp_version" } okhttp-dnsoverhttps = { module = "com.squareup.okhttp3:okhttp-dnsoverhttps", version.ref = "okhttp_version" } -okio = "com.squareup.okio:okio:3.7.0" +okio = "com.squareup.okio:okio:3.8.0" conscrypt-android = "org.conscrypt:conscrypt-android:2.5.2" From 7974a1fc0c2b0e237d5704a033a60ec0fd60cdbc Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 26 Feb 2024 21:19:55 +0600 Subject: [PATCH 068/212] Don't add custom User Agent for MAL Closes #298 --- .../tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt index b56a10e46..22db7a473 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt @@ -32,7 +32,8 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList) : Interceptor // Add the authorization header to the original request val authRequest = originalRequest.newBuilder() .addHeader("Authorization", "Bearer ${oauth!!.access_token}") - .header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})") + // TODO: Add back custom user agent when they stop blocking us for no apparent reason + // .header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})") .build() return chain.proceed(authRequest) From 9254079957e383e4aa5c914ccd9705612e0892d0 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 26 Feb 2024 21:54:24 +0600 Subject: [PATCH 069/212] Fix detekt issue --- .../tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt index 22db7a473..d67c2cabe 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.data.track.myanimelist -import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.network.parseAs import kotlinx.serialization.json.Json import okhttp3.Interceptor @@ -32,7 +31,7 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList) : Interceptor // Add the authorization header to the original request val authRequest = originalRequest.newBuilder() .addHeader("Authorization", "Bearer ${oauth!!.access_token}") - // TODO: Add back custom user agent when they stop blocking us for no apparent reason + // TODO(antsy): Add back custom user agent when they stop blocking us for no apparent reason // .header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})") .build() From 085ad8d44637c375a8ed24aba3a6f75f5b0cc9ee Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 26 Feb 2024 21:19:55 +0600 Subject: [PATCH 070/212] Don't add custom User Agent for MAL Closes #298 --- .../tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt index b56a10e46..22db7a473 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt @@ -32,7 +32,8 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList) : Interceptor // Add the authorization header to the original request val authRequest = originalRequest.newBuilder() .addHeader("Authorization", "Bearer ${oauth!!.access_token}") - .header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})") + // TODO: Add back custom user agent when they stop blocking us for no apparent reason + // .header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})") .build() return chain.proceed(authRequest) From 085147b15ba66e09c01ddfff9800efcd34763dc3 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 26 Feb 2024 21:54:24 +0600 Subject: [PATCH 071/212] Fix detekt issue --- .../tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt index 22db7a473..d67c2cabe 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.data.track.myanimelist -import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.network.parseAs import kotlinx.serialization.json.Json import okhttp3.Interceptor @@ -32,7 +31,7 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList) : Interceptor // Add the authorization header to the original request val authRequest = originalRequest.newBuilder() .addHeader("Authorization", "Bearer ${oauth!!.access_token}") - // TODO: Add back custom user agent when they stop blocking us for no apparent reason + // TODO(antsy): Add back custom user agent when they stop blocking us for no apparent reason // .header("User-Agent", "Mihon v${BuildConfig.VERSION_NAME} (${BuildConfig.APPLICATION_ID})") .build() From a4f5dfab1a874f4875d3bbfd67472a6803e7a7cf Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 26 Feb 2024 22:13:45 +0600 Subject: [PATCH 072/212] Release v0.16.4 --- .github/ISSUE_TEMPLATE/report_issue.yml | 4 ++-- .github/ISSUE_TEMPLATE/request_feature.yml | 2 +- app/build.gradle.kts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/report_issue.yml b/.github/ISSUE_TEMPLATE/report_issue.yml index 8fac5785f..0835f86cb 100644 --- a/.github/ISSUE_TEMPLATE/report_issue.yml +++ b/.github/ISSUE_TEMPLATE/report_issue.yml @@ -53,7 +53,7 @@ body: label: Mihon version description: You can find your Mihon version in **More → About**. placeholder: | - Example: "0.16.3" + Example: "0.16.4" validations: required: true @@ -96,7 +96,7 @@ body: required: true - label: I have gone through the [FAQ](https://mihon.app/docs/faq/general) and [troubleshooting guide](https://mihon.app/docs/guides/troubleshooting/). required: true - - label: I have updated the app to version **[0.16.3](https://github.com/mihonapp/mihon/releases/latest)**. + - label: I have updated the app to version **[0.16.4](https://github.com/mihonapp/mihon/releases/latest)**. required: true - label: I have updated all installed extensions. required: true diff --git a/.github/ISSUE_TEMPLATE/request_feature.yml b/.github/ISSUE_TEMPLATE/request_feature.yml index 2c6d324c9..48071fc91 100644 --- a/.github/ISSUE_TEMPLATE/request_feature.yml +++ b/.github/ISSUE_TEMPLATE/request_feature.yml @@ -31,7 +31,7 @@ body: required: true - label: I have written a short but informative title. required: true - - label: I have updated the app to version **[0.16.3](https://github.com/mihonapp/mihon/releases/latest)**. + - label: I have updated the app to version **[0.16.4](https://github.com/mihonapp/mihon/releases/latest)**. required: true - label: I will fill out all of the requested information in this form. required: true diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 705981b4b..00dc1b691 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,8 +22,8 @@ android { defaultConfig { applicationId = "app.mihon" - versionCode = 4 - versionName = "0.16.3" + versionCode = 5 + versionName = "0.16.4" buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"") From 1e40199b7d7cb0e059d44cfffc48f6ca4432e631 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:12:20 +0600 Subject: [PATCH 073/212] Update sqldelight to v2.0.1 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bdc4c9571..79d2ec8b6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ moko = "0.23.0" okhttp_version = "5.0.0-alpha.12" richtext = "0.20.0" shizuku_version = "12.2.0" -sqldelight = "2.0.0" +sqldelight = "2.0.1" sqlite = "2.4.0" voyager = "1.0.0" detekt = "1.23.1" From e50358dc4be7cbd866a79d052edc62689f0e4ca5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:12:43 +0600 Subject: [PATCH 074/212] Update detekt to v1.23.5 (#267) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 79d2ec8b6..ab295786a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ shizuku_version = "12.2.0" sqldelight = "2.0.1" sqlite = "2.4.0" voyager = "1.0.0" -detekt = "1.23.1" +detekt = "1.23.5" detektCompose = "0.3.11" [libraries] From 55637ddfe154c2ed60efb5d9d1466d2106c97f9f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:12:51 +0600 Subject: [PATCH 075/212] Update dependency androidx.test.uiautomator:uiautomator to v2.3.0 (#416) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 25f71a3a7..b8767c1a0 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -28,7 +28,7 @@ paging-compose = { module = "androidx.paging:paging-compose", version.ref = "pag benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.3" test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha03" test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha03" -test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0-beta01" +test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0" [bundles] lifecycle = ["lifecycle-common", "lifecycle-process", "lifecycle-runtimektx"] From 16969193c718cc8e6cabb473ecb1d0d7bc02f33e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:13:01 +0600 Subject: [PATCH 076/212] Update dependency com.google.firebase:firebase-analytics-ktx to v21.5.1 (#417) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ab295786a..86a1f96e0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -70,7 +70,7 @@ moko-gradle = { module = "dev.icerock.moko:resources-generator", version.ref = " logcat = "com.squareup.logcat:logcat:0.1" -firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.5.0" +firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.5.1" aboutLibraries-gradle = { module = "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin", version.ref = "aboutlib_version" } aboutLibraries-compose = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "aboutlib_version" } From 4fcbd80a8ec72f4113d0ee9d457c000dcd8e7440 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:13:08 +0600 Subject: [PATCH 077/212] Update dependency org.junit.jupiter:junit-jupiter to v5.10.2 (#419) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 86a1f96e0..b290203c8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -87,7 +87,7 @@ sqldelight-android-paging = { module = "app.cash.sqldelight:androidx-paging3-ext sqldelight-dialects-sql = { module = "app.cash.sqldelight:sqlite-3-38-dialect", version.ref = "sqldelight" } sqldelight-gradle = { module = "app.cash.sqldelight:gradle-plugin", version.ref = "sqldelight" } -junit = "org.junit.jupiter:junit-jupiter:5.10.1" +junit = "org.junit.jupiter:junit-jupiter:5.10.2" kotest-assertions = "io.kotest:kotest-assertions-core:5.8.0" mockk = "io.mockk:mockk:1.13.9" From c1c174698525e982107cc955d8dff711c4498989 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:13:36 +0600 Subject: [PATCH 078/212] Update dependency io.coil-kt:coil-bom to v2.6.0 (#447) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b290203c8..6c161a1e4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -43,7 +43,7 @@ preferencektx = "androidx.preference:preference-ktx:1.2.1" injekt-core = "com.github.inorichi.injekt:injekt-core:65b0440" -coil-bom = { module = "io.coil-kt:coil-bom", version = "2.5.0" } +coil-bom = { module = "io.coil-kt:coil-bom", version = "2.6.0" } coil-core = { module = "io.coil-kt:coil" } coil-gif = { module = "io.coil-kt:coil-gif" } coil-compose = { module = "io.coil-kt:coil-compose" } From 802a2c5c1ea73c79967fb5b535b6249506df1870 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:13:49 +0600 Subject: [PATCH 079/212] Update Kotlin (#422) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/kotlinx.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/kotlinx.versions.toml b/gradle/kotlinx.versions.toml index cc75dd895..0cdec4d59 100644 --- a/gradle/kotlinx.versions.toml +++ b/gradle/kotlinx.versions.toml @@ -1,6 +1,6 @@ [versions] kotlin_version = "1.9.22" -serialization_version = "1.6.2" +serialization_version = "1.6.3" xml_serialization_version = "0.86.3" [libraries] @@ -9,7 +9,7 @@ gradle = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = " immutables = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable", version = "0.3.7" } -coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version = "1.7.3" } +coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version = "1.8.0" } coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core" } coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android" } coroutines-guava = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-guava" } From 5859a8bbf6d63bd0fb6b05860f3d85dcca1cf821 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:22:40 +0600 Subject: [PATCH 080/212] Revert "Update sqldelight to v2.0.1" This reverts commit 1e40199b7d7cb0e059d44cfffc48f6ca4432e631. --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6c161a1e4..5c94f3789 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ moko = "0.23.0" okhttp_version = "5.0.0-alpha.12" richtext = "0.20.0" shizuku_version = "12.2.0" -sqldelight = "2.0.1" +sqldelight = "2.0.0" sqlite = "2.4.0" voyager = "1.0.0" detekt = "1.23.5" From 4bfc5e7b51452a1a9cac3a3644e6343d1c47aa95 Mon Sep 17 00:00:00 2001 From: Shamicen <84282253+Shamicen@users.noreply.github.com> Date: Thu, 29 Feb 2024 18:57:39 +0100 Subject: [PATCH 081/212] Made some changes to ComicInfo metadata (#459) * Made some changes to ComicInfo metadata The web field now contains a " " separated list of source and tracker urls. The translator field will now use the source name if the scanlator field is empty. * lint * use already existing source instance * made translator not nullable * implemented requested changes created new Mihon exclusive ComicInfo source field and populated it with SourceName reverted previous changes to translator field * Update core-metadata/src/main/java/tachiyomi/core/metadata/comicinfo/ComicInfo.kt Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> * Update app/src/main/java/eu/kanade/domain/manga/model/Manga.kt Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> * Update app/src/main/java/eu/kanade/domain/manga/model/Manga.kt Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> * Update app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> * Update app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt --------- Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- .../eu/kanade/domain/manga/model/Manga.kt | 11 +++++++++-- .../tachiyomi/data/download/Downloader.kt | 19 +++++++++++++++++-- .../core/metadata/comicinfo/ComicInfo.kt | 6 ++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt b/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt index 1b9dd62c8..ebd2ad3e9 100644 --- a/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt +++ b/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt @@ -95,7 +95,13 @@ fun Manga.hasCustomCover(coverCache: CoverCache = Injekt.get()): Boolean { /** * Creates a ComicInfo instance based on the manga and chapter metadata. */ -fun getComicInfo(manga: Manga, chapter: Chapter, chapterUrl: String, categories: List?) = ComicInfo( +fun getComicInfo( + manga: Manga, + chapter: Chapter, + urls: List, + categories: List?, + sourceName: String, +) = ComicInfo( title = ComicInfo.Title(chapter.name), series = ComicInfo.Series(manga.title), number = chapter.chapterNumber.takeIf { it >= 0 }?.let { @@ -105,7 +111,7 @@ fun getComicInfo(manga: Manga, chapter: Chapter, chapterUrl: String, categories: ComicInfo.Number(it.toString()) } }, - web = ComicInfo.Web(chapterUrl), + web = ComicInfo.Web(urls.joinToString(" ")), summary = manga.description?.let { ComicInfo.Summary(it) }, writer = manga.author?.let { ComicInfo.Writer(it) }, penciller = manga.artist?.let { ComicInfo.Penciller(it) }, @@ -115,6 +121,7 @@ fun getComicInfo(manga: Manga, chapter: Chapter, chapterUrl: String, categories: ComicInfoPublishingStatus.toComicInfoValue(manga.status), ), categories = categories?.let { ComicInfo.CategoriesTachiyomi(it.joinToString()) }, + source = ComicInfo.SourceMihon(sourceName), inker = null, colorist = null, letterer = null, diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index facbe605a..7c8238d5a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -54,6 +54,7 @@ import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.download.service.DownloadPreferences import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.service.SourceManager +import tachiyomi.domain.track.interactor.GetTracks import tachiyomi.i18n.MR import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -78,6 +79,7 @@ class Downloader( private val downloadPreferences: DownloadPreferences = Injekt.get(), private val xml: XML = Injekt.get(), private val getCategories: GetCategories = Injekt.get(), + private val getTracks: GetTracks = Injekt.get(), ) { /** @@ -626,9 +628,22 @@ class Downloader( chapter: Chapter, source: HttpSource, ) { - val chapterUrl = source.getChapterUrl(chapter.toSChapter()) val categories = getCategories.await(manga.id).map { it.name.trim() }.takeUnless { it.isEmpty() } - val comicInfo = getComicInfo(manga, chapter, chapterUrl, categories) + val urls = getTracks.await(manga.id) + .mapNotNull { track -> + track.remoteUrl.takeUnless { url -> url.isBlank() }?.trim() + } + .plus(source.getChapterUrl(chapter.toSChapter()).trim()) + .distinct() + + val comicInfo = getComicInfo( + manga, + chapter, + urls, + categories, + source.name + ) + // Remove the old file dir.findFile(COMIC_INFO_FILE, true)?.delete() dir.createFile(COMIC_INFO_FILE)!!.openOutputStream().use { diff --git a/core-metadata/src/main/java/tachiyomi/core/metadata/comicinfo/ComicInfo.kt b/core-metadata/src/main/java/tachiyomi/core/metadata/comicinfo/ComicInfo.kt index 681b4819b..5d4d92277 100644 --- a/core-metadata/src/main/java/tachiyomi/core/metadata/comicinfo/ComicInfo.kt +++ b/core-metadata/src/main/java/tachiyomi/core/metadata/comicinfo/ComicInfo.kt @@ -27,6 +27,7 @@ fun SManga.getComicInfo() = ComicInfo( coverArtist = null, tags = null, categories = null, + source = null, ) fun SManga.copyFromComicInfo(comicInfo: ComicInfo) { @@ -81,6 +82,7 @@ data class ComicInfo( val web: Web?, val publishingStatus: PublishingStatusTachiyomi?, val categories: CategoriesTachiyomi?, + val source: SourceMihon?, ) { @XmlElement(false) @XmlSerialName("xmlns:xsd", "", "") @@ -154,6 +156,10 @@ data class ComicInfo( @Serializable @XmlSerialName("Categories", "http://www.w3.org/2001/XMLSchema", "ty") data class CategoriesTachiyomi(@XmlValue(true) val value: String = "") + + @Serializable + @XmlSerialName("SourceMihon", "http://www.w3.org/2001/XMLSchema", "mh") + data class SourceMihon(@XmlValue(true) val value: String = "") } enum class ComicInfoPublishingStatus( From 9d5978aca015499502f47e432494abf089319895 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 2 Mar 2024 18:27:19 +0600 Subject: [PATCH 082/212] Address `ZipFile` deprecation warning --- .../eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt | 4 ++-- .../kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt | 4 ++-- .../mihon/core/common/extensions/SeekableByteChannel.kt | 8 ++++++++ .../kotlin/tachiyomi/source/local/LocalSource.kt | 6 +++--- 4 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 core/common/src/main/kotlin/mihon/core/common/extensions/SeekableByteChannel.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt index fadba25c4..89856bf22 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/ZipPageLoader.kt @@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.ui.reader.loader import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder -import org.apache.commons.compress.archivers.zip.ZipFile +import mihon.core.common.extensions.toZipFile import tachiyomi.core.common.util.system.ImageUtil import java.nio.channels.SeekableByteChannel @@ -12,7 +12,7 @@ import java.nio.channels.SeekableByteChannel */ internal class ZipPageLoader(channel: SeekableByteChannel) : PageLoader() { - private val zip = ZipFile(channel) + private val zip = channel.toZipFile() override var isLocal: Boolean = true diff --git a/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt index f664c175b..29cea5824 100644 --- a/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/util/storage/EpubFile.kt @@ -1,7 +1,7 @@ package eu.kanade.tachiyomi.util.storage +import mihon.core.common.extensions.toZipFile import org.apache.commons.compress.archivers.zip.ZipArchiveEntry -import org.apache.commons.compress.archivers.zip.ZipFile import org.jsoup.Jsoup import org.jsoup.nodes.Document import java.io.Closeable @@ -17,7 +17,7 @@ class EpubFile(channel: SeekableByteChannel) : Closeable { /** * Zip file of this epub. */ - private val zip = ZipFile(channel) + private val zip = channel.toZipFile() /** * Path separator used by this epub. diff --git a/core/common/src/main/kotlin/mihon/core/common/extensions/SeekableByteChannel.kt b/core/common/src/main/kotlin/mihon/core/common/extensions/SeekableByteChannel.kt new file mode 100644 index 000000000..69e2d7201 --- /dev/null +++ b/core/common/src/main/kotlin/mihon/core/common/extensions/SeekableByteChannel.kt @@ -0,0 +1,8 @@ +package mihon.core.common.extensions + +import org.apache.commons.compress.archivers.zip.ZipFile +import java.nio.channels.SeekableByteChannel + +fun SeekableByteChannel.toZipFile(): ZipFile { + return ZipFile.Builder().setSeekableByteChannel(this).get() +} diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt index b1ae25236..9e2aa8406 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt @@ -16,9 +16,9 @@ import kotlinx.coroutines.awaitAll import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromStream import logcat.LogPriority +import mihon.core.common.extensions.toZipFile import nl.adaptivity.xmlutil.AndroidXmlReader import nl.adaptivity.xmlutil.serialization.XML -import org.apache.commons.compress.archivers.zip.ZipFile import tachiyomi.core.common.i18n.stringResource import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE import tachiyomi.core.metadata.comicinfo.ComicInfo @@ -210,7 +210,7 @@ actual class LocalSource( for (chapter in chapterArchives) { when (Format.valueOf(chapter)) { is Format.Zip -> { - ZipFile(chapter.openReadOnlyChannel(context)).use { zip: ZipFile -> + chapter.openReadOnlyChannel(context).toZipFile().use { zip -> zip.getEntry(COMIC_INFO_FILE)?.let { comicInfoFile -> zip.getInputStream(comicInfoFile).buffered().use { stream -> return copyComicInfoFile(stream, folder) @@ -328,7 +328,7 @@ actual class LocalSource( entry?.let { coverManager.update(manga, it.openInputStream()) } } is Format.Zip -> { - ZipFile(format.file.openReadOnlyChannel(context)).use { zip -> + format.file.openReadOnlyChannel(context).toZipFile().use { zip -> val entry = zip.entries.toList() .sortedWith { f1, f2 -> f1.name.compareToCaseInsensitiveNaturalOrder(f2.name) } .find { !it.isDirectory && ImageUtil.isImage(it.name) { zip.getInputStream(it) } } From 0301362430af6f74678dcae801b70d6aeb371a56 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 2 Mar 2024 18:41:56 +0600 Subject: [PATCH 083/212] Upgrade Compose --- gradle/compose.versions.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 778c43c47..54a808a29 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,7 +1,7 @@ [versions] -compiler = "1.5.8" -compose-bom = "2024.01.00-alpha03" -accompanist = "0.34.0" +compiler = "1.5.10" +compose-bom = "2024.02.00-alpha02" +accompanist = "0.35.0-alpha" [libraries] activity = "androidx.activity:activity-compose:1.8.2" From e83bfb0d3511f3c049d1dfe6ca13e74467655e08 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 2 Mar 2024 18:41:42 +0600 Subject: [PATCH 084/212] ChapterDownloadIndicator: Remove composed modifier usage Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> --- .../components/ChapterDownloadIndicator.kt | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt index 50ddadf0c..11314eaf1 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.composed import androidx.compose.ui.graphics.Color +import androidx.compose.ui.hapticfeedback.HapticFeedback import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.painterResource @@ -86,11 +87,13 @@ private fun NotDownloadedIndicator( modifier: Modifier = Modifier, onClick: (ChapterDownloadAction) -> Unit, ) { + val hapticFeedback = LocalHapticFeedback.current Box( modifier = modifier .size(IconButtonTokens.StateLayerSize) .commonClickable( enabled = enabled, + hapticFeedback = hapticFeedback, onLongClick = { onClick(ChapterDownloadAction.START_NOW) }, onClick = { onClick(ChapterDownloadAction.START) }, ) @@ -114,12 +117,14 @@ private fun DownloadingIndicator( onClick: (ChapterDownloadAction) -> Unit, modifier: Modifier = Modifier, ) { + val hapticFeedback = LocalHapticFeedback.current var isMenuExpanded by remember { mutableStateOf(false) } Box( modifier = modifier .size(IconButtonTokens.StateLayerSize) .commonClickable( enabled = enabled, + hapticFeedback = hapticFeedback, onLongClick = { onClick(ChapterDownloadAction.CANCEL) }, onClick = { isMenuExpanded = true }, ), @@ -186,12 +191,14 @@ private fun DownloadedIndicator( modifier: Modifier = Modifier, onClick: (ChapterDownloadAction) -> Unit, ) { + val hapticFeedback = LocalHapticFeedback.current var isMenuExpanded by remember { mutableStateOf(false) } Box( modifier = modifier .size(IconButtonTokens.StateLayerSize) .commonClickable( enabled = enabled, + hapticFeedback = hapticFeedback, onLongClick = { isMenuExpanded = true }, onClick = { isMenuExpanded = true }, ), @@ -221,11 +228,13 @@ private fun ErrorIndicator( modifier: Modifier = Modifier, onClick: (ChapterDownloadAction) -> Unit, ) { + val hapticFeedback = LocalHapticFeedback.current Box( modifier = modifier .size(IconButtonTokens.StateLayerSize) .commonClickable( enabled = enabled, + hapticFeedback = hapticFeedback, onLongClick = { onClick(ChapterDownloadAction.START) }, onClick = { onClick(ChapterDownloadAction.START) }, ), @@ -242,26 +251,23 @@ private fun ErrorIndicator( private fun Modifier.commonClickable( enabled: Boolean, + hapticFeedback: HapticFeedback, onLongClick: () -> Unit, onClick: () -> Unit, -) = composed { - val haptic = LocalHapticFeedback.current - - Modifier.combinedClickable( - enabled = enabled, - onLongClick = { - onLongClick() - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - }, - onClick = onClick, - role = Role.Button, - interactionSource = remember { MutableInteractionSource() }, - indication = ripple( - bounded = false, - radius = IconButtonTokens.StateLayerSize / 2, - ), - ) -} +) = this.combinedClickable( + enabled = enabled, + onLongClick = { + onLongClick() + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) + }, + onClick = onClick, + role = Role.Button, + interactionSource = null, + indication = ripple( + bounded = false, + radius = IconButtonTokens.StateLayerSize / 2, + ), +) private val IndicatorSize = 26.dp private val IndicatorPadding = 2.dp From 9f48def1e2718abd5b4aad3cb6ee8af6b39e76cc Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 2 Mar 2024 19:00:27 +0600 Subject: [PATCH 085/212] Enable experimental Compose compiler optimization Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> --- app/build.gradle.kts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 15751e0e5..c250b0f31 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -304,6 +304,12 @@ tasks { project.layout.buildDirectory.dir("compose_metrics").get().asFile.absolutePath, ) } + + // https://developer.android.com/jetpack/androidx/releases/compose-compiler#1.5.9 + kotlinOptions.freeCompilerArgs += listOf( + "-P", + "plugin:androidx.compose.compiler.plugins.kotlin:nonSkippingGroupOptimization=true", + ) } } From 84984ef7e1d7242924120cd2f171cb9dd75bc916 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 2 Mar 2024 19:02:53 +0600 Subject: [PATCH 086/212] Remove custom Pager Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> --- .../presentation/components/TabbedDialog.kt | 7 +-- .../presentation/components/TabbedScreen.kt | 2 +- .../library/components/LibraryPager.kt | 2 +- .../presentation/core/components/Pager.kt | 56 ------------------- 4 files changed, 5 insertions(+), 62 deletions(-) delete mode 100644 presentation-core/src/main/java/tachiyomi/presentation/core/components/Pager.kt diff --git a/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt b/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt index dfba8cd61..4ef2e9771 100644 --- a/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material.icons.Icons @@ -29,7 +30,6 @@ import androidx.compose.ui.util.fastForEachIndexed import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.launch import tachiyomi.i18n.MR -import tachiyomi.presentation.core.components.HorizontalPager import tachiyomi.presentation.core.components.material.TabText import tachiyomi.presentation.core.i18n.stringResource @@ -78,9 +78,8 @@ fun TabbedDialog( modifier = Modifier.animateContentSize(), state = pagerState, verticalAlignment = Alignment.Top, - ) { page -> - content(page) - } + pageContent = { page -> content(page) } + ) } } } diff --git a/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt b/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt index 9dae3de14..efb1e2e04 100644 --- a/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/components/TabbedScreen.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.calculateEndPadding import androidx.compose.foundation.layout.calculateStartPadding import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material3.MaterialTheme import androidx.compose.material3.PrimaryTabRow @@ -24,7 +25,6 @@ import dev.icerock.moko.resources.StringResource import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.launch -import tachiyomi.presentation.core.components.HorizontalPager import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.TabText import tachiyomi.presentation.core.i18n.stringResource diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt b/app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt index db7af5875..6487ab39f 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll @@ -22,7 +23,6 @@ import eu.kanade.tachiyomi.ui.library.LibraryItem import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibraryManga import tachiyomi.i18n.MR -import tachiyomi.presentation.core.components.HorizontalPager import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.util.plus diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/Pager.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/Pager.kt deleted file mode 100644 index fb3cbdf74..000000000 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/Pager.kt +++ /dev/null @@ -1,56 +0,0 @@ -package tachiyomi.presentation.core.components - -import androidx.compose.foundation.gestures.Orientation -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.pager.PageSize -import androidx.compose.foundation.pager.PagerDefaults -import androidx.compose.foundation.pager.PagerScope -import androidx.compose.foundation.pager.PagerSnapDistance -import androidx.compose.foundation.pager.PagerState -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.input.nestedscroll.NestedScrollConnection -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp - -/** - * Horizontal Pager with custom SnapFlingBehavior for a more natural swipe feeling - */ -@Composable -fun HorizontalPager( - state: PagerState, - modifier: Modifier = Modifier, - contentPadding: PaddingValues = PaddingValues(0.dp), - pageSize: PageSize = PageSize.Fill, - beyondBoundsPageCount: Int = 0, - pageSpacing: Dp = 0.dp, - verticalAlignment: Alignment.Vertical = Alignment.CenterVertically, - userScrollEnabled: Boolean = true, - reverseLayout: Boolean = false, - key: ((index: Int) -> Any)? = null, - pageNestedScrollConnection: NestedScrollConnection = PagerDefaults.pageNestedScrollConnection( - state = state, - orientation = Orientation.Horizontal, - ), - pageContent: @Composable PagerScope.(page: Int) -> Unit, -) { - androidx.compose.foundation.pager.HorizontalPager( - state = state, - modifier = modifier, - contentPadding = contentPadding, - pageSize = pageSize, - outOfBoundsPageCount = beyondBoundsPageCount, - pageSpacing = pageSpacing, - verticalAlignment = verticalAlignment, - flingBehavior = PagerDefaults.flingBehavior( - state = state, - pagerSnapDistance = PagerSnapDistance.atMost(0), - ), - userScrollEnabled = userScrollEnabled, - reverseLayout = reverseLayout, - key = key, - pageNestedScrollConnection = pageNestedScrollConnection, - pageContent = pageContent, - ) -} From f72b6e4d7c1f2f93d705402e4d80c94160bef54d Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 2 Mar 2024 20:08:15 +0600 Subject: [PATCH 087/212] Switch to Coil3 Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> --- app/build.gradle.kts | 2 +- .../browse/components/BrowseIcons.kt | 2 +- .../manga/components/MangaCover.kt | 2 +- .../manga/components/MangaCoverDialog.kt | 12 ++-- .../manga/components/MangaInfoHeader.kt | 2 +- app/src/main/java/eu/kanade/tachiyomi/App.kt | 36 +++++----- .../tachiyomi/data/coil/MangaCoverFetcher.kt | 70 +++++++++++-------- .../tachiyomi/data/coil/MangaCoverKeyer.kt | 4 +- .../data/coil/TachiyomiImageDecoder.kt | 22 +++--- .../data/library/LibraryUpdateNotifier.kt | 9 +-- .../ui/manga/MangaCoverScreenModel.kt | 8 +-- .../tachiyomi/ui/reader/SaveImageNotifier.kt | 8 +-- .../ui/reader/viewer/ReaderPageImageView.kt | 11 +-- .../util/system/DrawableExtensions.kt | 2 +- gradle.properties | 2 +- gradle/libs.versions.toml | 11 +-- presentation-core/build.gradle.kts | 2 +- .../widget/BaseUpdatesGridGlanceWidget.kt | 23 +++--- 18 files changed, 124 insertions(+), 104 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c250b0f31..4147d11af 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -284,7 +284,7 @@ tasks { "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi", "-opt-in=androidx.compose.animation.ExperimentalAnimationApi", "-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi", - "-opt-in=coil.annotation.ExperimentalCoilApi", + "-opt-in=coil3.annotation.ExperimentalCoilApi", "-opt-in=com.google.accompanist.permissions.ExperimentalPermissionsApi", "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", "-opt-in=kotlinx.coroutines.FlowPreview", diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseIcons.kt b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseIcons.kt index b4710fc40..950b55192 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseIcons.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseIcons.kt @@ -25,7 +25,7 @@ import androidx.compose.ui.res.imageResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import androidx.core.graphics.drawable.toBitmap -import coil.compose.AsyncImage +import coil3.compose.AsyncImage import eu.kanade.domain.source.model.icon import eu.kanade.presentation.util.rememberResourceBitmapPainter import eu.kanade.tachiyomi.R diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCover.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCover.kt index cfb4f5fcb..4fb1cf87b 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCover.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCover.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.painter.ColorPainter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.semantics.Role -import coil.compose.AsyncImage +import coil3.compose.AsyncImage import eu.kanade.presentation.util.rememberResourceBitmapPainter import eu.kanade.tachiyomi.R diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt index a71d08674..a70511326 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCoverDialog.kt @@ -37,10 +37,10 @@ import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import androidx.core.view.updatePadding -import coil.imageLoader -import coil.request.CachePolicy -import coil.request.ImageRequest -import coil.size.Size +import coil3.imageLoader +import coil3.request.CachePolicy +import coil3.request.ImageRequest +import coil3.size.Size import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.components.DropdownMenu @@ -168,7 +168,9 @@ fun MangaCoverDialog( .data(coverDataProvider()) .size(Size.ORIGINAL) .memoryCachePolicy(CachePolicy.DISABLED) - .target { drawable -> + .target { image -> + val drawable = image.asDrawable(view.context.resources) + // Copy bitmap in case it came from memory cache // Because SSIV needs to thoroughly read the image val copy = (drawable as? BitmapDrawable)?.let { diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt index 0ad66a9a8..ac4946c99 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt @@ -73,7 +73,7 @@ import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import coil.compose.AsyncImage +import coil3.compose.AsyncImage import eu.kanade.presentation.components.DropdownMenu import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.model.SManga diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index dfe5dad41..6c29060c1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -15,12 +15,14 @@ import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner import androidx.lifecycle.lifecycleScope -import coil.ImageLoader -import coil.ImageLoaderFactory -import coil.decode.GifDecoder -import coil.decode.ImageDecoderDecoder -import coil.disk.DiskCache -import coil.util.DebugLogger +import coil3.ImageLoader +import coil3.SingletonImageLoader +import coil3.disk.DiskCache +import coil3.disk.directory +import coil3.network.okhttp.OkHttpNetworkFetcherFactory +import coil3.request.allowRgb565 +import coil3.request.crossfade +import coil3.util.DebugLogger import eu.kanade.domain.DomainModule import eu.kanade.domain.base.BasePreferences import eu.kanade.domain.ui.UiPreferences @@ -58,7 +60,7 @@ import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy import java.security.Security -class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory { +class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factory { private val basePreferences: BasePreferences by injectLazy() private val networkPreferences: NetworkPreferences by injectLazy() @@ -131,24 +133,19 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory { } } - override fun newImageLoader(): ImageLoader { + override fun newImageLoader(context: Context): ImageLoader { return ImageLoader.Builder(this).apply { - val callFactoryInit = { Injekt.get().client } - val diskCacheInit = { CoilDiskCache.get(this@App) } + val callFactoryLazy = lazy { Injekt.get().client } + val diskCacheLazy = lazy { CoilDiskCache.get(this@App) } components { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - add(ImageDecoderDecoder.Factory()) - } else { - add(GifDecoder.Factory()) - } + add(OkHttpNetworkFetcherFactory(callFactoryLazy::value)) add(TachiyomiImageDecoder.Factory()) - add(MangaCoverFetcher.MangaFactory(lazy(callFactoryInit), lazy(diskCacheInit))) - add(MangaCoverFetcher.MangaCoverFactory(lazy(callFactoryInit), lazy(diskCacheInit))) + add(MangaCoverFetcher.MangaFactory(callFactoryLazy, diskCacheLazy)) + add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy, diskCacheLazy)) add(MangaKeyer()) add(MangaCoverKeyer()) } - callFactory(callFactoryInit) - diskCache(diskCacheInit) + diskCache(diskCacheLazy::value) crossfade((300 * this@App.animatorDurationScale).toInt()) allowRgb565(DeviceUtil.isLowRamDevice(this@App)) if (networkPreferences.verboseLogging().get()) logger(DebugLogger()) @@ -156,7 +153,6 @@ class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory { // Coil spawns a new thread for every image load by default fetcherDispatcher(Dispatchers.IO.limitedParallelism(8)) decoderDispatcher(Dispatchers.IO.limitedParallelism(2)) - transformationDispatcher(Dispatchers.IO.limitedParallelism(2)) }.build() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt index 978f65bc9..a3d1061ef 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt @@ -1,19 +1,18 @@ package eu.kanade.tachiyomi.data.coil import androidx.core.net.toUri -import coil.ImageLoader -import coil.decode.DataSource -import coil.decode.ImageSource -import coil.disk.DiskCache -import coil.fetch.FetchResult -import coil.fetch.Fetcher -import coil.fetch.SourceResult -import coil.network.HttpException -import coil.request.Options -import coil.request.Parameters +import coil3.Extras +import coil3.ImageLoader +import coil3.decode.DataSource +import coil3.decode.ImageSource +import coil3.disk.DiskCache +import coil3.fetch.FetchResult +import coil3.fetch.Fetcher +import coil3.fetch.SourceFetchResult +import coil3.getOrDefault +import coil3.request.Options import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.data.cache.CoverCache -import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher.Companion.USE_CUSTOM_COVER import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.source.online.HttpSource import logcat.LogPriority @@ -22,6 +21,7 @@ import okhttp3.Call import okhttp3.Request import okhttp3.Response import okhttp3.internal.http.HTTP_NOT_MODIFIED +import okio.FileSystem import okio.Path.Companion.toOkioPath import okio.Source import okio.buffer @@ -42,7 +42,7 @@ import java.io.File * handled by Coil's [DiskCache]. * * Available request parameter: - * - [USE_CUSTOM_COVER]: Use custom cover if set by user, default is true + * - [USE_CUSTOM_COVER_KEY]: Use custom cover if set by user, default is true */ class MangaCoverFetcher( private val url: String?, @@ -61,7 +61,7 @@ class MangaCoverFetcher( override suspend fun fetch(): FetchResult { // Use custom cover if exists - val useCustomCover = options.parameters.value(USE_CUSTOM_COVER) ?: true + val useCustomCover = options.extras.getOrDefault(USE_CUSTOM_COVER_KEY) if (useCustomCover) { val customCoverFile = customCoverFileLazy.value if (customCoverFile.exists()) { @@ -80,8 +80,12 @@ class MangaCoverFetcher( } private fun fileLoader(file: File): FetchResult { - return SourceResult( - source = ImageSource(file = file.toOkioPath(), diskCacheKey = diskCacheKey), + return SourceFetchResult( + source = ImageSource( + file = file.toOkioPath(), + fileSystem = FileSystem.SYSTEM, + diskCacheKey = diskCacheKey + ), mimeType = "image/*", dataSource = DataSource.DISK, ) @@ -92,8 +96,8 @@ class MangaCoverFetcher( .openInputStream() .source() .buffer() - return SourceResult( - source = ImageSource(source = source, context = options.context), + return SourceFetchResult( + source = ImageSource(source = source, fileSystem = FileSystem.SYSTEM), mimeType = "image/*", dataSource = DataSource.DISK, ) @@ -121,7 +125,7 @@ class MangaCoverFetcher( } // Read from snapshot - return SourceResult( + return SourceFetchResult( source = snapshot.toImageSource(), mimeType = "image/*", dataSource = DataSource.DISK, @@ -141,7 +145,7 @@ class MangaCoverFetcher( // Read from disk cache snapshot = writeToDiskCache(response) if (snapshot != null) { - return SourceResult( + return SourceFetchResult( source = snapshot.toImageSource(), mimeType = "image/*", dataSource = DataSource.NETWORK, @@ -149,8 +153,8 @@ class MangaCoverFetcher( } // Read from response if cache is unused or unusable - return SourceResult( - source = ImageSource(source = responseBody.source(), context = options.context), + return SourceFetchResult( + source = ImageSource(source = responseBody.source(), fileSystem = FileSystem.SYSTEM), mimeType = "image/*", dataSource = if (response.cacheResponse != null) DataSource.DISK else DataSource.NETWORK, ) @@ -169,17 +173,20 @@ class MangaCoverFetcher( val response = client.newCall(newRequest()).await() if (!response.isSuccessful && response.code != HTTP_NOT_MODIFIED) { response.close() - throw HttpException(response) + throw Exception(response.message) } return response } private fun newRequest(): Request { - val request = Request.Builder() - .url(url!!) - .headers(sourceLazy.value?.headers ?: options.headers) - // Support attaching custom data to the network request. - .tag(Parameters::class.java, options.parameters) + val request = Request.Builder().apply { + url(url!!) + + val sourceHeaders = sourceLazy.value?.headers + if (sourceHeaders != null) { + headers(sourceHeaders) + } + } when { options.networkCachePolicy.readEnabled -> { @@ -264,7 +271,12 @@ class MangaCoverFetcher( } private fun DiskCache.Snapshot.toImageSource(): ImageSource { - return ImageSource(file = data, diskCacheKey = diskCacheKey, closeable = this) + return ImageSource( + file = data, + fileSystem = FileSystem.SYSTEM, + diskCacheKey = diskCacheKey, + closeable = this, + ) } private fun getResourceType(cover: String?): Type? { @@ -330,7 +342,7 @@ class MangaCoverFetcher( } companion object { - const val USE_CUSTOM_COVER = "use_custom_cover" + val USE_CUSTOM_COVER_KEY = Extras.Key(true) private val CACHE_CONTROL_NO_STORE = CacheControl.Builder().noStore().build() private val CACHE_CONTROL_NO_NETWORK_NO_CACHE = CacheControl.Builder().noCache().onlyIfCached().build() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverKeyer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverKeyer.kt index bc85b22f4..56e0291aa 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverKeyer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverKeyer.kt @@ -1,7 +1,7 @@ package eu.kanade.tachiyomi.data.coil -import coil.key.Keyer -import coil.request.Options +import coil3.key.Keyer +import coil3.request.Options import eu.kanade.domain.manga.model.hasCustomCover import eu.kanade.tachiyomi.data.cache.CoverCache import tachiyomi.domain.manga.model.MangaCover diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt index acbda8cc5..3f6c13906 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt @@ -1,13 +1,13 @@ package eu.kanade.tachiyomi.data.coil -import androidx.core.graphics.drawable.toDrawable -import coil.ImageLoader -import coil.decode.DecodeResult -import coil.decode.Decoder -import coil.decode.ImageDecoderDecoder -import coil.decode.ImageSource -import coil.fetch.SourceResult -import coil.request.Options +import coil3.ImageLoader +import coil3.asCoilImage +import coil3.decode.DecodeResult +import coil3.decode.Decoder +import coil3.decode.ImageSource +import coil3.fetch.SourceFetchResult +import coil3.request.Options +import coil3.request.allowRgb565 import okio.BufferedSource import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.decoder.ImageDecoder @@ -30,14 +30,14 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti check(bitmap != null) { "Failed to decode image" } return DecodeResult( - drawable = bitmap.toDrawable(options.context.resources), + image = bitmap.asCoilImage(), isSampled = false, ) } class Factory : Decoder.Factory { - override fun create(result: SourceResult, options: Options, imageLoader: ImageLoader): Decoder? { + override fun create(result: SourceFetchResult, options: Options, imageLoader: ImageLoader): Decoder? { if (!isApplicable(result.source.source())) return null return TachiyomiImageDecoder(result.source, options) } @@ -52,7 +52,7 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti } } - override fun equals(other: Any?) = other is ImageDecoderDecoder.Factory + override fun equals(other: Any?) = other is Factory override fun hashCode() = javaClass.hashCode() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt index 4d13dc724..e18bf76c4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt @@ -9,9 +9,10 @@ import android.graphics.BitmapFactory import android.net.Uri import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat -import coil.imageLoader -import coil.request.ImageRequest -import coil.transform.CircleCropTransformation +import coil3.imageLoader +import coil3.request.ImageRequest +import coil3.request.transformations +import coil3.transform.CircleCropTransformation import eu.kanade.presentation.util.formatChapterNumber import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.core.security.SecurityPreferences @@ -294,7 +295,7 @@ class LibraryUpdateNotifier( .transformations(CircleCropTransformation()) .size(NOTIF_ICON_SIZE) .build() - val drawable = context.imageLoader.execute(request).drawable + val drawable = context.imageLoader.execute(request).image?.asDrawable(context.resources) return drawable?.getBitmapOrNull() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaCoverScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaCoverScreenModel.kt index 3cbfa540b..75a98c5df 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaCoverScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaCoverScreenModel.kt @@ -5,9 +5,9 @@ import android.net.Uri import androidx.compose.material3.SnackbarHostState import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.screenModelScope -import coil.imageLoader -import coil.request.ImageRequest -import coil.size.Size +import coil3.imageLoader +import coil3.request.ImageRequest +import coil3.size.Size import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.saver.Image @@ -96,7 +96,7 @@ class MangaCoverScreenModel( .build() return withIOContext { - val result = context.imageLoader.execute(req).drawable + val result = context.imageLoader.execute(req).image?.asDrawable(context.resources) // TODO: Handle animated cover val bitmap = result?.getBitmapOrNull() ?: return@withIOContext null diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt index ab0146a0e..373ed97a8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt @@ -4,9 +4,9 @@ import android.content.Context import android.graphics.Bitmap import android.net.Uri import androidx.core.app.NotificationCompat -import coil.imageLoader -import coil.request.CachePolicy -import coil.request.ImageRequest +import coil3.imageLoader +import coil3.request.CachePolicy +import coil3.request.ImageRequest import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.notification.NotificationHandler import eu.kanade.tachiyomi.data.notification.NotificationReceiver @@ -37,7 +37,7 @@ class SaveImageNotifier(private val context: Context) { .memoryCachePolicy(CachePolicy.DISABLED) .size(720, 1280) .target( - onSuccess = { showCompleteNotification(uri, it.getBitmapOrNull()) }, + onSuccess = { showCompleteNotification(uri, it.asDrawable(context.resources).getBitmapOrNull()) }, onError = { onError(null) }, ) .build() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt index c60e404e7..fbb1302e8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt @@ -18,10 +18,11 @@ import androidx.annotation.StyleRes import androidx.appcompat.widget.AppCompatImageView import androidx.core.os.postDelayed import androidx.core.view.isVisible -import coil.dispose -import coil.imageLoader -import coil.request.CachePolicy -import coil.request.ImageRequest +import coil3.dispose +import coil3.imageLoader +import coil3.request.CachePolicy +import coil3.request.ImageRequest +import coil3.request.crossfade import com.davemorrissey.labs.subscaleview.ImageSource import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_IN_OUT_QUAD @@ -348,7 +349,7 @@ open class ReaderPageImageView @JvmOverloads constructor( .diskCachePolicy(CachePolicy.DISABLED) .target( onSuccess = { result -> - setImageDrawable(result) + setImageDrawable(result.asDrawable(context.resources)) (result as? Animatable)?.start() isVisible = true this@ReaderPageImageView.onImageLoaded() diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/DrawableExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/DrawableExtensions.kt index 2a09aeca9..2877768af 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/DrawableExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/DrawableExtensions.kt @@ -4,7 +4,7 @@ import android.graphics.Bitmap import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import androidx.core.graphics.drawable.toBitmap -import coil.drawable.ScaleDrawable +import coil3.gif.ScaleDrawable fun Drawable.getBitmapOrNull(): Bitmap? = when (this) { is BitmapDrawable -> bitmap diff --git a/gradle.properties b/gradle.properties index 22e7dfed5..2eec6929d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ kotlin.mpp.androidSourceSetLayoutVersion=2 org.gradle.caching=true org.gradle.configureondemand=true -org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx3g -Dfile.encoding=UTF-8 org.gradle.parallel=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5c94f3789..97be75a27 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -43,10 +43,11 @@ preferencektx = "androidx.preference:preference-ktx:1.2.1" injekt-core = "com.github.inorichi.injekt:injekt-core:65b0440" -coil-bom = { module = "io.coil-kt:coil-bom", version = "2.6.0" } -coil-core = { module = "io.coil-kt:coil" } -coil-gif = { module = "io.coil-kt:coil-gif" } -coil-compose = { module = "io.coil-kt:coil-compose" } +coil-bom = { module = "io.coil-kt.coil3:coil-bom", version = "3.0.0-alpha06" } +coil-core = { module = "io.coil-kt.coil3:coil" } +coil-gif = { module = "io.coil-kt.coil3:coil-gif" } +coil-compose = { module = "io.coil-kt.coil3:coil-compose" } +coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp" } subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:7e57335" image-decoder = "com.github.tachiyomiorg:image-decoder:fbd6601290" @@ -105,7 +106,7 @@ archive = ["common-compress", "junrar"] okhttp = ["okhttp-core", "okhttp-logging", "okhttp-brotli", "okhttp-dnsoverhttps"] js-engine = ["quickjs-android"] sqlite = ["sqlite-framework", "sqlite-ktx", "sqlite-android"] -coil = ["coil-core", "coil-gif", "coil-compose"] +coil = ["coil-core", "coil-gif", "coil-compose", "coil-network-okhttp"] shizuku = ["shizuku-api", "shizuku-provider"] sqldelight = ["sqldelight-android-driver", "sqldelight-coroutines", "sqldelight-android-paging"] voyager = ["voyager-navigator", "voyager-screenmodel", "voyager-tab-navigator", "voyager-transitions"] diff --git a/presentation-core/build.gradle.kts b/presentation-core/build.gradle.kts index cfae8ba2a..e30c5869c 100644 --- a/presentation-core/build.gradle.kts +++ b/presentation-core/build.gradle.kts @@ -52,7 +52,7 @@ tasks { "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi", "-opt-in=androidx.compose.animation.ExperimentalAnimationApi", "-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi", - "-opt-in=coil.annotation.ExperimentalCoilApi", + "-opt-in=coil3.annotation.ExperimentalCoilApi", "-opt-in=kotlinx.coroutines.FlowPreview", ) } diff --git a/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt b/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt index e4977bfd3..4fa7850a8 100644 --- a/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt +++ b/presentation-widget/src/main/java/tachiyomi/presentation/widget/BaseUpdatesGridGlanceWidget.kt @@ -21,13 +21,15 @@ import androidx.glance.background import androidx.glance.layout.fillMaxSize import androidx.glance.layout.padding import androidx.glance.unit.ColorProvider -import coil.executeBlocking -import coil.imageLoader -import coil.request.CachePolicy -import coil.request.ImageRequest -import coil.size.Precision -import coil.size.Scale -import coil.transform.RoundedCornersTransformation +import coil3.annotation.ExperimentalCoilApi +import coil3.executeBlocking +import coil3.imageLoader +import coil3.request.CachePolicy +import coil3.request.ImageRequest +import coil3.request.transformations +import coil3.size.Precision +import coil3.size.Scale +import coil3.transform.RoundedCornersTransformation import eu.kanade.tachiyomi.core.security.SecurityPreferences import eu.kanade.tachiyomi.util.system.dpToPx import kotlinx.collections.immutable.ImmutableList @@ -105,6 +107,7 @@ abstract class BaseUpdatesGridGlanceWidget( } } + @OptIn(ExperimentalCoilApi::class) private suspend fun List.prepareData( rowCount: Int, columnCount: Int, @@ -140,7 +143,11 @@ abstract class BaseUpdatesGridGlanceWidget( } } .build() - Pair(updatesView.mangaId, context.imageLoader.executeBlocking(request).drawable?.toBitmap()) + val bitmap = context.imageLoader.executeBlocking(request) + .image + ?.asDrawable(context.resources) + ?.toBitmap() + Pair(updatesView.mangaId, bitmap) } .toImmutableList() } From b51a0a38bd9849b573bc2de0d936d6ab2becb83c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 2 Mar 2024 20:11:03 +0600 Subject: [PATCH 088/212] Update dependency me.saket.swipe:swipe to v1.3.0 (#343) * Update dependency me.saket.swipe:swipe to v1.3.0 * Update MangaChapterListItem.kt --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> --- .../manga/components/MangaChapterListItem.kt | 233 ++++++++---------- gradle/libs.versions.toml | 2 +- 2 files changed, 103 insertions(+), 132 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt index 9c9f07c1a..505478715 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt @@ -24,36 +24,27 @@ import androidx.compose.material3.ProvideTextStyle import androidx.compose.material3.Text import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.platform.LocalHapticFeedback -import androidx.compose.ui.platform.LocalViewConfiguration -import androidx.compose.ui.platform.ViewConfiguration import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import eu.kanade.tachiyomi.data.download.model.Download import me.saket.swipe.SwipeableActionsBox -import me.saket.swipe.rememberSwipeableActionsState import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.ReadItemAlpha import tachiyomi.presentation.core.components.material.SecondaryItemAlpha import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.selectedBackground -import kotlin.math.absoluteValue @Composable fun MangaChapterListItem( @@ -75,142 +66,122 @@ fun MangaChapterListItem( onChapterSwipe: (LibraryPreferences.ChapterSwipeAction) -> Unit, modifier: Modifier = Modifier, ) { - val haptic = LocalHapticFeedback.current - val density = LocalDensity.current - val textAlpha = if (read) ReadItemAlpha else 1f val textSubtitleAlpha = if (read) ReadItemAlpha else SecondaryItemAlpha - // Increase touch slop of swipe action to reduce accidental trigger - val configuration = LocalViewConfiguration.current - CompositionLocalProvider( - LocalViewConfiguration provides object : ViewConfiguration by configuration { - override val touchSlop: Float = configuration.touchSlop * 3f - }, + val start = getSwipeAction( + action = chapterSwipeStartAction, + read = read, + bookmark = bookmark, + downloadState = downloadStateProvider(), + background = MaterialTheme.colorScheme.primaryContainer, + onSwipe = { onChapterSwipe(chapterSwipeStartAction) }, + ) + val end = getSwipeAction( + action = chapterSwipeEndAction, + read = read, + bookmark = bookmark, + downloadState = downloadStateProvider(), + background = MaterialTheme.colorScheme.primaryContainer, + onSwipe = { onChapterSwipe(chapterSwipeEndAction) }, + ) + + SwipeableActionsBox( + modifier = Modifier.clipToBounds(), + startActions = listOfNotNull(start), + endActions = listOfNotNull(end), + swipeThreshold = swipeActionThreshold, + backgroundUntilSwipeThreshold = MaterialTheme.colorScheme.surfaceContainerLowest, ) { - val start = getSwipeAction( - action = chapterSwipeStartAction, - read = read, - bookmark = bookmark, - downloadState = downloadStateProvider(), - background = MaterialTheme.colorScheme.primaryContainer, - onSwipe = { onChapterSwipe(chapterSwipeStartAction) }, - ) - val end = getSwipeAction( - action = chapterSwipeEndAction, - read = read, - bookmark = bookmark, - downloadState = downloadStateProvider(), - background = MaterialTheme.colorScheme.primaryContainer, - onSwipe = { onChapterSwipe(chapterSwipeEndAction) }, - ) - - val swipeableActionsState = rememberSwipeableActionsState() - LaunchedEffect(Unit) { - // Haptic effect when swipe over threshold - val swipeActionThresholdPx = with(density) { swipeActionThreshold.toPx() } - snapshotFlow { swipeableActionsState.offset.value.absoluteValue > swipeActionThresholdPx } - .collect { if (it) haptic.performHapticFeedback(HapticFeedbackType.LongPress) } - } - - SwipeableActionsBox( - modifier = Modifier.clipToBounds(), - state = swipeableActionsState, - startActions = listOfNotNull(start), - endActions = listOfNotNull(end), - swipeThreshold = swipeActionThreshold, - backgroundUntilSwipeThreshold = MaterialTheme.colorScheme.surfaceContainerLowest, + Row( + modifier = modifier + .selectedBackground(selected) + .combinedClickable( + onClick = onClick, + onLongClick = onLongClick, + ) + .padding(start = 16.dp, top = 12.dp, end = 8.dp, bottom = 12.dp), ) { - Row( - modifier = modifier - .selectedBackground(selected) - .combinedClickable( - onClick = onClick, - onLongClick = onLongClick, - ) - .padding(start = 16.dp, top = 12.dp, end = 8.dp, bottom = 12.dp), + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(6.dp), ) { - Column( - modifier = Modifier.weight(1f), - verticalArrangement = Arrangement.spacedBy(6.dp), + Row( + horizontalArrangement = Arrangement.spacedBy(2.dp), + verticalAlignment = Alignment.CenterVertically, ) { - Row( - horizontalArrangement = Arrangement.spacedBy(2.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - var textHeight by remember { mutableIntStateOf(0) } - if (!read) { - Icon( - imageVector = Icons.Filled.Circle, - contentDescription = stringResource(MR.strings.unread), - modifier = Modifier - .height(8.dp) - .padding(end = 4.dp), - tint = MaterialTheme.colorScheme.primary, - ) - } - if (bookmark) { - Icon( - imageVector = Icons.Filled.Bookmark, - contentDescription = stringResource(MR.strings.action_filter_bookmarked), - modifier = Modifier - .sizeIn(maxHeight = with(LocalDensity.current) { textHeight.toDp() - 2.dp }), - tint = MaterialTheme.colorScheme.primary, - ) - } - Text( - text = title, - style = MaterialTheme.typography.bodyMedium, - color = LocalContentColor.current.copy(alpha = textAlpha), - maxLines = 1, - overflow = TextOverflow.Ellipsis, - onTextLayout = { textHeight = it.size.height }, + var textHeight by remember { mutableIntStateOf(0) } + if (!read) { + Icon( + imageVector = Icons.Filled.Circle, + contentDescription = stringResource(MR.strings.unread), + modifier = Modifier + .height(8.dp) + .padding(end = 4.dp), + tint = MaterialTheme.colorScheme.primary, ) } + if (bookmark) { + Icon( + imageVector = Icons.Filled.Bookmark, + contentDescription = stringResource(MR.strings.action_filter_bookmarked), + modifier = Modifier + .sizeIn(maxHeight = with(LocalDensity.current) { textHeight.toDp() - 2.dp }), + tint = MaterialTheme.colorScheme.primary, + ) + } + Text( + text = title, + style = MaterialTheme.typography.bodyMedium, + color = LocalContentColor.current.copy(alpha = textAlpha), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + onTextLayout = { textHeight = it.size.height }, + ) + } - Row { - ProvideTextStyle( - value = MaterialTheme.typography.bodyMedium.copy( - fontSize = 12.sp, - color = LocalContentColor.current.copy(alpha = textSubtitleAlpha), - ), - ) { - if (date != null) { - Text( - text = date, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - if (readProgress != null || scanlator != null) DotSeparatorText() - } - if (readProgress != null) { - Text( - text = readProgress, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - color = LocalContentColor.current.copy(alpha = ReadItemAlpha), - ) - if (scanlator != null) DotSeparatorText() - } - if (scanlator != null) { - Text( - text = scanlator, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - ) - } + Row { + ProvideTextStyle( + value = MaterialTheme.typography.bodyMedium.copy( + fontSize = 12.sp, + color = LocalContentColor.current.copy(alpha = textSubtitleAlpha), + ), + ) { + if (date != null) { + Text( + text = date, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) + if (readProgress != null || scanlator != null) DotSeparatorText() + } + if (readProgress != null) { + Text( + text = readProgress, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + color = LocalContentColor.current.copy(alpha = ReadItemAlpha), + ) + if (scanlator != null) DotSeparatorText() + } + if (scanlator != null) { + Text( + text = scanlator, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + ) } } } - - ChapterDownloadIndicator( - enabled = downloadIndicatorEnabled, - modifier = Modifier.padding(start = 4.dp), - downloadStateProvider = downloadStateProvider, - downloadProgressProvider = downloadProgressProvider, - onClick = { onDownloadClick?.invoke(it) }, - ) } + + ChapterDownloadIndicator( + enabled = downloadIndicatorEnabled, + modifier = Modifier.padding(start = 4.dp), + downloadStateProvider = downloadStateProvider, + downloadProgressProvider = downloadProgressProvider, + onClick = { onDownloadClick?.invoke(it) }, + ) } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 97be75a27..f2cb8f1f9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -64,7 +64,7 @@ directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0" insetter = "dev.chrisbanes.insetter:insetter:0.6.1" compose-materialmotion = "io.github.fornewid:material-motion-compose-core:1.2.0" -swipe = "me.saket.swipe:swipe:1.2.0" +swipe = "me.saket.swipe:swipe:1.3.0" moko-core = { module = "dev.icerock.moko:resources", version.ref = "moko" } moko-gradle = { module = "dev.icerock.moko:resources-generator", version.ref = "moko" } From c56f4665ef0276c54f5abebd9ab93e2e283739a6 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 2 Mar 2024 20:24:54 +0600 Subject: [PATCH 089/212] `detekt` my beloved --- .../presentation/manga/components/ChapterDownloadIndicator.kt | 2 -- .../java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt | 4 +++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt index 11314eaf1..3066fe5ed 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt @@ -2,7 +2,6 @@ package eu.kanade.presentation.manga.components import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.combinedClickable -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -24,7 +23,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.composed import androidx.compose.ui.graphics.Color import androidx.compose.ui.hapticfeedback.HapticFeedback import androidx.compose.ui.hapticfeedback.HapticFeedbackType diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt index a3d1061ef..0556d9d1f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt @@ -13,6 +13,7 @@ import coil3.getOrDefault import coil3.request.Options import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.data.cache.CoverCache +import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher.Companion.USE_CUSTOM_COVER_KEY import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.source.online.HttpSource import logcat.LogPriority @@ -33,6 +34,7 @@ import tachiyomi.domain.manga.model.MangaCover import tachiyomi.domain.source.service.SourceManager import uy.kohesive.injekt.injectLazy import java.io.File +import java.io.IOException /** * A [Fetcher] that fetches cover image for [Manga] object. @@ -173,7 +175,7 @@ class MangaCoverFetcher( val response = client.newCall(newRequest()).await() if (!response.isSuccessful && response.code != HTTP_NOT_MODIFIED) { response.close() - throw Exception(response.message) + throw IOException(response.message) } return response } From d6ba3c824972c8a2196516edd42e9e8be6385f36 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 2 Mar 2024 20:25:38 +0600 Subject: [PATCH 090/212] Revert changes to gradle.properties --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 2eec6929d..22e7dfed5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,5 @@ kotlin.mpp.androidSourceSetLayoutVersion=2 org.gradle.caching=true org.gradle.configureondemand=true -org.gradle.jvmargs=-Xmx3g -Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8 org.gradle.parallel=true From 60480686daa9456c78d7cb45c5ad2e23644d1bd2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 3 Mar 2024 13:53:47 +0600 Subject: [PATCH 091/212] Update dependency io.mockk:mockk to v1.13.10 (#470) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f2cb8f1f9..f1472b443 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -90,7 +90,7 @@ sqldelight-gradle = { module = "app.cash.sqldelight:gradle-plugin", version.ref junit = "org.junit.jupiter:junit-jupiter:5.10.2" kotest-assertions = "io.kotest:kotest-assertions-core:5.8.0" -mockk = "io.mockk:mockk:1.13.9" +mockk = "io.mockk:mockk:1.13.10" voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" } voyager-screenmodel = { module = "cafe.adriel.voyager:voyager-screenmodel", version.ref = "voyager" } From 31e052ac15679496f9f2c3882e53096119e0e6cf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 3 Mar 2024 13:53:57 +0600 Subject: [PATCH 092/212] Update dependency com.android.tools.build:gradle to v8.3.0 (#471) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index b8767c1a0..26e6db019 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -1,5 +1,5 @@ [versions] -agp_version = "8.2.2" +agp_version = "8.3.0" lifecycle_version = "2.7.0" paging_version = "3.2.1" From b7e091d5d039e00cababc7daf555280df6cf9c03 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 3 Mar 2024 05:16:35 +0600 Subject: [PATCH 093/212] Small cleanup --- .../manga/components/ChapterDownloadIndicator.kt | 12 ++++-------- .../manga/components/MangaChapterListItem.kt | 11 +++-------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt index 3066fe5ed..85b54eac0 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt @@ -85,13 +85,12 @@ private fun NotDownloadedIndicator( modifier: Modifier = Modifier, onClick: (ChapterDownloadAction) -> Unit, ) { - val hapticFeedback = LocalHapticFeedback.current Box( modifier = modifier .size(IconButtonTokens.StateLayerSize) .commonClickable( enabled = enabled, - hapticFeedback = hapticFeedback, + hapticFeedback = LocalHapticFeedback.current, onLongClick = { onClick(ChapterDownloadAction.START_NOW) }, onClick = { onClick(ChapterDownloadAction.START) }, ) @@ -115,14 +114,13 @@ private fun DownloadingIndicator( onClick: (ChapterDownloadAction) -> Unit, modifier: Modifier = Modifier, ) { - val hapticFeedback = LocalHapticFeedback.current var isMenuExpanded by remember { mutableStateOf(false) } Box( modifier = modifier .size(IconButtonTokens.StateLayerSize) .commonClickable( enabled = enabled, - hapticFeedback = hapticFeedback, + hapticFeedback = LocalHapticFeedback.current, onLongClick = { onClick(ChapterDownloadAction.CANCEL) }, onClick = { isMenuExpanded = true }, ), @@ -189,14 +187,13 @@ private fun DownloadedIndicator( modifier: Modifier = Modifier, onClick: (ChapterDownloadAction) -> Unit, ) { - val hapticFeedback = LocalHapticFeedback.current var isMenuExpanded by remember { mutableStateOf(false) } Box( modifier = modifier .size(IconButtonTokens.StateLayerSize) .commonClickable( enabled = enabled, - hapticFeedback = hapticFeedback, + hapticFeedback = LocalHapticFeedback.current, onLongClick = { isMenuExpanded = true }, onClick = { isMenuExpanded = true }, ), @@ -226,13 +223,12 @@ private fun ErrorIndicator( modifier: Modifier = Modifier, onClick: (ChapterDownloadAction) -> Unit, ) { - val hapticFeedback = LocalHapticFeedback.current Box( modifier = modifier .size(IconButtonTokens.StateLayerSize) .commonClickable( enabled = enabled, - hapticFeedback = hapticFeedback, + hapticFeedback = LocalHapticFeedback.current, onLongClick = { onClick(ChapterDownloadAction.START) }, onClick = { onClick(ChapterDownloadAction.START) }, ), diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt index 505478715..12720957e 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt @@ -30,13 +30,13 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import eu.kanade.tachiyomi.data.download.model.Download import me.saket.swipe.SwipeableActionsBox import tachiyomi.domain.library.service.LibraryPreferences @@ -140,13 +140,8 @@ fun MangaChapterListItem( ) } - Row { - ProvideTextStyle( - value = MaterialTheme.typography.bodyMedium.copy( - fontSize = 12.sp, - color = LocalContentColor.current.copy(alpha = textSubtitleAlpha), - ), - ) { + Row(modifier = Modifier.alpha(textSubtitleAlpha)) { + ProvideTextStyle(value = MaterialTheme.typography.bodySmall) { if (date != null) { Text( text = date, From d8b9a9f593911569ff2bceb49b4f020978d0d2e1 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 4 Mar 2024 02:03:59 +0600 Subject: [PATCH 094/212] Fix ChapterDownloadIndicator Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> --- .../manga/components/ChapterDownloadIndicator.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt index 85b54eac0..b1a1474ec 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt @@ -24,6 +24,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.hapticfeedback.HapticFeedback import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalHapticFeedback @@ -137,6 +138,8 @@ private fun DownloadingIndicator( modifier = IndicatorModifier, color = strokeColor, strokeWidth = IndicatorStrokeWidth, + trackColor = Color.Transparent, + strokeCap = StrokeCap.Butt, ) } else { val animatedProgress by animateFloatAsState( @@ -154,6 +157,9 @@ private fun DownloadingIndicator( modifier = IndicatorModifier, color = strokeColor, strokeWidth = IndicatorSize / 2, + trackColor = Color.Transparent, + strokeCap = StrokeCap.Butt, + gapSize = 0.dp, ) } DropdownMenu(expanded = isMenuExpanded, onDismissRequest = { isMenuExpanded = false }) { From 154f4d327caea9ceef6a53e739324ee0a9ed959d Mon Sep 17 00:00:00 2001 From: az4521 <18432684+az4521@users.noreply.github.com> Date: Sat, 9 Mar 2024 10:08:57 -0500 Subject: [PATCH 095/212] Update image-decoder (#466) Use newer image-decoder lib fixes crashing when trying to load corrupt images below 12 bytes in size --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f1472b443..756a03dc7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -50,7 +50,7 @@ coil-compose = { module = "io.coil-kt.coil3:coil-compose" } coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp" } subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:7e57335" -image-decoder = "com.github.tachiyomiorg:image-decoder:fbd6601290" +image-decoder = "com.github.tachiyomiorg:image-decoder:398d3c074f" natural-comparator = "com.github.gpanther:java-nat-sort:natural-comparator-1.1" @@ -111,4 +111,4 @@ shizuku = ["shizuku-api", "shizuku-provider"] sqldelight = ["sqldelight-android-driver", "sqldelight-coroutines", "sqldelight-android-paging"] voyager = ["voyager-navigator", "voyager-screenmodel", "voyager-tab-navigator", "voyager-transitions"] richtext = ["richtext-commonmark", "richtext-m3"] -test = ["junit", "kotest-assertions", "mockk"] \ No newline at end of file +test = ["junit", "kotest-assertions", "mockk"] From d0e64d3a66d227ca61fc8d956b03cab5ac3b84f0 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Sat, 9 Mar 2024 23:09:06 +0800 Subject: [PATCH 096/212] Fix dual page split for local source (#485) `InputStream.available()` is implementation-dependent, should never assume it will return the total number of bytes in the stream. --- .../main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt b/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt index 1c13d2be7..b01f4e882 100644 --- a/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt @@ -551,7 +551,7 @@ object ImageUtil { imageStream: InputStream, resetAfterExtraction: Boolean = true, ): BitmapFactory.Options { - imageStream.mark(imageStream.available() + 1) + imageStream.mark(Int.MAX_VALUE) val imageBytes = imageStream.readBytes() val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } From 402e579a69391e7633754066eb8e6480bd9f247d Mon Sep 17 00:00:00 2001 From: Redjard Date: Sat, 9 Mar 2024 19:48:43 +0100 Subject: [PATCH 097/212] Fix shizuku being buggy for multi user setups (#494) * Fix #493 Fetch the current userid separately because shizuku always runs as the main user and would otherwise install and update for the main user * Update app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt --------- Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- .../kanade/tachiyomi/extension/installer/ShizukuInstaller.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt index c85611acf..24a8cb377 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/installer/ShizukuInstaller.kt @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.extension.installer import android.app.Service import android.content.pm.PackageManager +import android.os.Process import eu.kanade.tachiyomi.extension.model.InstallStep import eu.kanade.tachiyomi.util.system.getUriSize import eu.kanade.tachiyomi.util.system.toast @@ -49,7 +50,8 @@ class ShizukuInstaller(private val service: Service) : Installer(service) { try { val size = service.getUriSize(entry.uri) ?: throw IllegalStateException() service.contentResolver.openInputStream(entry.uri)!!.use { - val createCommand = "pm install-create --user current -r -i ${service.packageName} -S $size" + val userId = Process.myUserHandle().hashCode() + val createCommand = "pm install-create --user $userId -r -i ${service.packageName} -S $size" val createResult = exec(createCommand) sessionId = SESSION_ID_REGEX.find(createResult.out)?.value ?: throw RuntimeException("Failed to create install session") From 4ae9dbe52487185ef9ee25f58fabe5025bb2278b Mon Sep 17 00:00:00 2001 From: KaiserBh <41852205+kaiserbh@users.noreply.github.com> Date: Sun, 10 Mar 2024 06:45:41 +1100 Subject: [PATCH 098/212] feat: db changes to accommodate new cross device syncing logic. (#450) * feat: db changes to accommodate new syncing logic. Using timestamp to sync is a bit skewed due to system clock etc and therefore there was a lot of issues with it such as removing a manga that shouldn't have been removed. Marking chapters as unread even though it was marked as a read. Hopefully by using versioning system it should eliminate those issues. * chore: add new line. * chore: remove isSyncing from Chapter/Manga model. * chore: remove isSyncing leftover. * chore: remove isSyncing. * refactor: remove isSync guard. Just use it directly to 1 now since we don't have the isSyncing field in Manga or Chapter. * Lint and stuff * Add missing , --------- Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- app/build.gradle.kts | 2 +- .../create/creators/MangaBackupCreator.kt | 1 + .../data/backup/models/BackupChapter.kt | 6 +++ .../data/backup/models/BackupManga.kt | 7 ++- .../backup/restore/restorers/MangaRestorer.kt | 12 ++++- .../tachiyomi/data/database/models/Chapter.kt | 3 ++ .../data/database/models/ChapterImpl.kt | 2 + .../data/chapter/ChapterRepositoryImpl.kt | 8 ++++ .../java/tachiyomi/data/manga/MangaMapper.kt | 10 ++++ .../data/manga/MangaRepositoryImpl.kt | 3 ++ .../sqldelight/tachiyomi/data/chapters.sq | 31 +++++++++++-- .../main/sqldelight/tachiyomi/data/mangas.sq | 27 +++++++++-- .../tachiyomi/data/mangas_categories.sq | 15 +++--- .../sqldelight/tachiyomi/migrations/2.sqm | 46 +++++++++++++++++++ .../tachiyomi/domain/chapter/model/Chapter.kt | 2 + .../domain/chapter/model/ChapterUpdate.kt | 2 + .../tachiyomi/domain/manga/model/Manga.kt | 2 + .../domain/manga/model/MangaUpdate.kt | 2 + 18 files changed, 161 insertions(+), 20 deletions(-) create mode 100644 data/src/main/sqldelight/tachiyomi/migrations/2.sqm diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4147d11af..8b63752a0 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,7 +22,7 @@ android { defaultConfig { applicationId = "app.mihon" - versionCode = 5 + versionCode = 6 versionName = "0.16.4" buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/MangaBackupCreator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/MangaBackupCreator.kt index 9f51a0d55..fd10293a9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/MangaBackupCreator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/MangaBackupCreator.kt @@ -98,4 +98,5 @@ private fun Manga.toBackupManga() = updateStrategy = this.updateStrategy, lastModifiedAt = this.lastModifiedAt, favoriteModifiedAt = this.favoriteModifiedAt, + version = this.version, ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt index 567ca372c..d729efe16 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupChapter.kt @@ -4,6 +4,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import tachiyomi.domain.chapter.model.Chapter +@Suppress("MagicNumber") @Serializable data class BackupChapter( // in 1.x some of these values have different names @@ -21,6 +22,7 @@ data class BackupChapter( @ProtoNumber(9) var chapterNumber: Float = 0F, @ProtoNumber(10) var sourceOrder: Long = 0, @ProtoNumber(11) var lastModifiedAt: Long = 0, + @ProtoNumber(12) var version: Long = 0, ) { fun toChapterImpl(): Chapter { return Chapter.create().copy( @@ -35,6 +37,7 @@ data class BackupChapter( dateUpload = this@BackupChapter.dateUpload, sourceOrder = this@BackupChapter.sourceOrder, lastModifiedAt = this@BackupChapter.lastModifiedAt, + version = this@BackupChapter.version, ) } } @@ -53,6 +56,8 @@ val backupChapterMapper = { dateFetch: Long, dateUpload: Long, lastModifiedAt: Long, + version: Long, + _: Long, -> BackupChapter( url = url, @@ -66,5 +71,6 @@ val backupChapterMapper = { dateUpload = dateUpload, sourceOrder = sourceOrder, lastModifiedAt = lastModifiedAt, + version = version, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt index da202ae55..410ecc67a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt @@ -5,7 +5,10 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber import tachiyomi.domain.manga.model.Manga -@Suppress("DEPRECATION") +@Suppress( + "DEPRECATION", + "MagicNumber", +) @Serializable data class BackupManga( // in 1.x some of these values have different names @@ -39,6 +42,7 @@ data class BackupManga( @ProtoNumber(106) var lastModifiedAt: Long = 0, @ProtoNumber(107) var favoriteModifiedAt: Long? = null, @ProtoNumber(108) var excludedScanlators: List = emptyList(), + @ProtoNumber(109) var version: Long = 0, ) { fun getMangaImpl(): Manga { return Manga.create().copy( @@ -58,6 +62,7 @@ data class BackupManga( updateStrategy = this@BackupManga.updateStrategy, lastModifiedAt = this@BackupManga.lastModifiedAt, favoriteModifiedAt = this@BackupManga.favoriteModifiedAt, + version = this@BackupManga.version, ) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt index 30dc44368..b0c86e2ab 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaRestorer.kt @@ -83,7 +83,7 @@ class MangaRestorer( } private suspend fun restoreExistingManga(manga: Manga, dbManga: Manga): Manga { - return if (manga.lastModifiedAt > dbManga.lastModifiedAt) { + return if (manga.version > dbManga.version) { updateManga(dbManga.copyFrom(manga).copy(id = dbManga.id)) } else { updateManga(manga.copyFrom(dbManga).copy(id = dbManga.id)) @@ -100,6 +100,7 @@ class MangaRestorer( thumbnailUrl = newer.thumbnailUrl, status = newer.status, initialized = this.initialized || newer.initialized, + version = newer.version, ) } @@ -126,6 +127,8 @@ class MangaRestorer( dateAdded = manga.dateAdded, mangaId = manga.id, updateStrategy = manga.updateStrategy.let(UpdateStrategyColumnAdapter::encode), + version = manga.version, + isSyncing = 1, ) } return manga @@ -137,6 +140,7 @@ class MangaRestorer( return manga.copy( initialized = manga.description != null, id = insertManga(manga), + version = manga.version, ) } @@ -183,7 +187,7 @@ class MangaRestorer( } private fun Chapter.forComparison() = - this.copy(id = 0L, mangaId = 0L, dateFetch = 0L, dateUpload = 0L, lastModifiedAt = 0L) + this.copy(id = 0L, mangaId = 0L, dateFetch = 0L, dateUpload = 0L, lastModifiedAt = 0L, version = 0L) private suspend fun insertNewChapters(chapters: List) { handler.await(true) { @@ -200,6 +204,7 @@ class MangaRestorer( chapter.sourceOrder, chapter.dateFetch, chapter.dateUpload, + chapter.version, ) } } @@ -221,6 +226,8 @@ class MangaRestorer( dateFetch = null, dateUpload = null, chapterId = chapter.id, + version = chapter.version, + isSyncing = 0, ) } } @@ -253,6 +260,7 @@ class MangaRestorer( coverLastModified = manga.coverLastModified, dateAdded = manga.dateAdded, updateStrategy = manga.updateStrategy, + version = manga.version, ) mangasQueries.selectLastInsertedRowId() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Chapter.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Chapter.kt index 4ff50483e..f91368084 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Chapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Chapter.kt @@ -21,6 +21,8 @@ interface Chapter : SChapter, Serializable { var source_order: Int var last_modified: Long + + var version: Long } fun Chapter.toDomainChapter(): DomainChapter? { @@ -39,5 +41,6 @@ fun Chapter.toDomainChapter(): DomainChapter? { chapterNumber = chapter_number.toDouble(), scanlator = scanlator, lastModifiedAt = last_modified, + version = version, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/ChapterImpl.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/ChapterImpl.kt index 58ba41dec..a92dd56df 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/ChapterImpl.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/ChapterImpl.kt @@ -28,6 +28,8 @@ class ChapterImpl : Chapter { override var last_modified: Long = 0 + override var version: Long = 0 + override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || javaClass != other.javaClass) return false diff --git a/data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt index d2284b612..099ccb0da 100644 --- a/data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt @@ -29,6 +29,7 @@ class ChapterRepositoryImpl( chapter.sourceOrder, chapter.dateFetch, chapter.dateUpload, + chapter.version, ) val lastInsertId = chaptersQueries.selectLastInsertedRowId().executeAsOne() chapter.copy(id = lastInsertId) @@ -64,6 +65,8 @@ class ChapterRepositoryImpl( dateFetch = chapterUpdate.dateFetch, dateUpload = chapterUpdate.dateUpload, chapterId = chapterUpdate.id, + version = chapterUpdate.version, + isSyncing = 0, ) } } @@ -124,6 +127,7 @@ class ChapterRepositoryImpl( } } + @Suppress("LongParameterList") private fun mapChapter( id: Long, mangaId: Long, @@ -138,6 +142,9 @@ class ChapterRepositoryImpl( dateFetch: Long, dateUpload: Long, lastModifiedAt: Long, + version: Long, + @Suppress("UNUSED_PARAMETER") + isSyncing: Long, ): Chapter = Chapter( id = id, mangaId = mangaId, @@ -152,5 +159,6 @@ class ChapterRepositoryImpl( chapterNumber = chapterNumber, scanlator = scanlator, lastModifiedAt = lastModifiedAt, + version = version, ) } diff --git a/data/src/main/java/tachiyomi/data/manga/MangaMapper.kt b/data/src/main/java/tachiyomi/data/manga/MangaMapper.kt index d9937b285..8f0a68d43 100644 --- a/data/src/main/java/tachiyomi/data/manga/MangaMapper.kt +++ b/data/src/main/java/tachiyomi/data/manga/MangaMapper.kt @@ -5,6 +5,7 @@ import tachiyomi.domain.library.model.LibraryManga import tachiyomi.domain.manga.model.Manga object MangaMapper { + @Suppress("LongParameterList") fun mapManga( id: Long, source: Long, @@ -28,6 +29,9 @@ object MangaMapper { calculateInterval: Long, lastModifiedAt: Long, favoriteModifiedAt: Long?, + version: Long, + @Suppress("UNUSED_PARAMETER") + isSyncing: Long, ): Manga = Manga( id = id, source = source, @@ -51,8 +55,10 @@ object MangaMapper { initialized = initialized, lastModifiedAt = lastModifiedAt, favoriteModifiedAt = favoriteModifiedAt, + version = version, ) + @Suppress("LongParameterList") fun mapLibraryManga( id: Long, source: Long, @@ -76,6 +82,8 @@ object MangaMapper { calculateInterval: Long, lastModifiedAt: Long, favoriteModifiedAt: Long?, + version: Long, + isSyncing: Long, totalCount: Long, readCount: Double, latestUpload: Long, @@ -107,6 +115,8 @@ object MangaMapper { calculateInterval, lastModifiedAt, favoriteModifiedAt, + version, + isSyncing, ), category = category, totalChapters = totalCount, diff --git a/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt index 88ef9b3b7..d7ac06abf 100644 --- a/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt @@ -106,6 +106,7 @@ class MangaRepositoryImpl( coverLastModified = manga.coverLastModified, dateAdded = manga.dateAdded, updateStrategy = manga.updateStrategy, + version = manga.version, ) mangasQueries.selectLastInsertedRowId() } @@ -155,6 +156,8 @@ class MangaRepositoryImpl( dateAdded = value.dateAdded, mangaId = value.id, updateStrategy = value.updateStrategy?.let(UpdateStrategyColumnAdapter::encode), + version = value.version, + isSyncing = 0, ) } } diff --git a/data/src/main/sqldelight/tachiyomi/data/chapters.sq b/data/src/main/sqldelight/tachiyomi/data/chapters.sq index 4c341793f..c69de987d 100644 --- a/data/src/main/sqldelight/tachiyomi/data/chapters.sq +++ b/data/src/main/sqldelight/tachiyomi/data/chapters.sq @@ -14,6 +14,8 @@ CREATE TABLE chapters( date_fetch INTEGER NOT NULL, date_upload INTEGER NOT NULL, last_modified_at INTEGER NOT NULL DEFAULT 0, + version INTEGER NOT NULL DEFAULT 0, + is_syncing INTEGER NOT NULL DEFAULT 0, FOREIGN KEY(manga_id) REFERENCES mangas (_id) ON DELETE CASCADE ); @@ -30,6 +32,22 @@ BEGIN WHERE _id = new._id; END; +CREATE TRIGGER update_chapter_and_manga_version AFTER UPDATE ON chapters +WHEN new.is_syncing = 0 AND ( + new.read != old.read OR + new.bookmark != old.bookmark OR + new.last_page_read != old.last_page_read +) +BEGIN + -- Update the chapter version + UPDATE chapters SET version = version + 1 + WHERE _id = new._id; + + -- Update the manga version + UPDATE mangas SET version = version + 1 + WHERE _id = new.manga_id AND (SELECT is_syncing FROM mangas WHERE _id = new.manga_id) = 0; +END; + getChapterById: SELECT * FROM chapters @@ -73,9 +91,14 @@ removeChaptersWithIds: DELETE FROM chapters WHERE _id IN :chapterIds; +resetIsSyncing: +UPDATE chapters +SET is_syncing = 0 +WHERE is_syncing = 1; + insert: -INSERT INTO chapters(manga_id, url, name, scanlator, read, bookmark, last_page_read, chapter_number, source_order, date_fetch, date_upload, last_modified_at) -VALUES (:mangaId, :url, :name, :scanlator, :read, :bookmark, :lastPageRead, :chapterNumber, :sourceOrder, :dateFetch, :dateUpload, 0); +INSERT INTO chapters(manga_id, url, name, scanlator, read, bookmark, last_page_read, chapter_number, source_order, date_fetch, date_upload, last_modified_at, version, is_syncing) +VALUES (:mangaId, :url, :name, :scanlator, :read, :bookmark, :lastPageRead, :chapterNumber, :sourceOrder, :dateFetch, :dateUpload, 0, :version, 0); update: UPDATE chapters @@ -89,7 +112,9 @@ SET manga_id = coalesce(:mangaId, manga_id), chapter_number = coalesce(:chapterNumber, chapter_number), source_order = coalesce(:sourceOrder, source_order), date_fetch = coalesce(:dateFetch, date_fetch), - date_upload = coalesce(:dateUpload, date_upload) + date_upload = coalesce(:dateUpload, date_upload), + version = coalesce(:version, version), + is_syncing = coalesce(:isSyncing, is_syncing) WHERE _id = :chapterId; selectLastInsertedRowId: diff --git a/data/src/main/sqldelight/tachiyomi/data/mangas.sq b/data/src/main/sqldelight/tachiyomi/data/mangas.sq index 07ef12eba..6bc655a76 100644 --- a/data/src/main/sqldelight/tachiyomi/data/mangas.sq +++ b/data/src/main/sqldelight/tachiyomi/data/mangas.sq @@ -25,7 +25,9 @@ CREATE TABLE mangas( update_strategy INTEGER AS UpdateStrategy NOT NULL DEFAULT 0, calculate_interval INTEGER DEFAULT 0 NOT NULL, last_modified_at INTEGER NOT NULL DEFAULT 0, - favorite_modified_at INTEGER + favorite_modified_at INTEGER, + version INTEGER NOT NULL DEFAULT 0, + is_syncing INTEGER NOT NULL DEFAULT 0 ); CREATE INDEX library_favorite_index ON mangas(favorite) WHERE favorite = 1; @@ -48,6 +50,16 @@ BEGIN WHERE _id = new._id; END; +CREATE TRIGGER update_manga_version AFTER UPDATE ON mangas +BEGIN + UPDATE mangas SET version = version + 1 + WHERE _id = new._id AND new.is_syncing = 0 AND ( + new.url != old.url OR + new.description != old.description OR + new.favorite != old.favorite + ); +END; + getMangaById: SELECT * FROM mangas @@ -104,6 +116,11 @@ resetViewerFlags: UPDATE mangas SET viewer = 0; +resetIsSyncing: +UPDATE mangas +SET is_syncing = 0 +WHERE is_syncing = 1; + getSourceIdsWithNonLibraryManga: SELECT source, COUNT(*) AS manga_count FROM mangas @@ -116,8 +133,8 @@ WHERE favorite = 0 AND source IN :sourceIds; insert: -INSERT INTO mangas(source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added, update_strategy, calculate_interval, last_modified_at) -VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :dateAdded, :updateStrategy, :calculateInterval, 0); +INSERT INTO mangas(source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added, update_strategy, calculate_interval, last_modified_at, version) +VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :dateAdded, :updateStrategy, :calculateInterval, 0, :version); update: UPDATE mangas SET @@ -139,7 +156,9 @@ UPDATE mangas SET cover_last_modified = coalesce(:coverLastModified, cover_last_modified), date_added = coalesce(:dateAdded, date_added), update_strategy = coalesce(:updateStrategy, update_strategy), - calculate_interval = coalesce(:calculateInterval, calculate_interval) + calculate_interval = coalesce(:calculateInterval, calculate_interval), + version = coalesce(:version, version), + is_syncing = coalesce(:isSyncing, is_syncing) WHERE _id = :mangaId; selectLastInsertedRowId: diff --git a/data/src/main/sqldelight/tachiyomi/data/mangas_categories.sq b/data/src/main/sqldelight/tachiyomi/data/mangas_categories.sq index b908e3f86..3d8c815c9 100644 --- a/data/src/main/sqldelight/tachiyomi/data/mangas_categories.sq +++ b/data/src/main/sqldelight/tachiyomi/data/mangas_categories.sq @@ -2,25 +2,22 @@ CREATE TABLE mangas_categories( _id INTEGER NOT NULL PRIMARY KEY, manga_id INTEGER NOT NULL, category_id INTEGER NOT NULL, - last_modified_at INTEGER NOT NULL DEFAULT 0, FOREIGN KEY(category_id) REFERENCES categories (_id) ON DELETE CASCADE, FOREIGN KEY(manga_id) REFERENCES mangas (_id) ON DELETE CASCADE ); -CREATE TRIGGER update_last_modified_at_mangas_categories -AFTER UPDATE ON mangas_categories -FOR EACH ROW +CREATE TRIGGER insert_manga_category_update_version AFTER INSERT ON mangas_categories BEGIN - UPDATE mangas_categories - SET last_modified_at = strftime('%s', 'now') - WHERE _id = new._id; + UPDATE mangas + SET version = version + 1 + WHERE _id = new.manga_id AND (SELECT is_syncing FROM mangas WHERE _id = new.manga_id) = 0; END; insert: -INSERT INTO mangas_categories(manga_id, category_id, last_modified_at) -VALUES (:mangaId, :categoryId, 0); +INSERT INTO mangas_categories(manga_id, category_id) +VALUES (:mangaId, :categoryId); deleteMangaCategoryByMangaId: DELETE FROM mangas_categories diff --git a/data/src/main/sqldelight/tachiyomi/migrations/2.sqm b/data/src/main/sqldelight/tachiyomi/migrations/2.sqm new file mode 100644 index 000000000..1a05f72e4 --- /dev/null +++ b/data/src/main/sqldelight/tachiyomi/migrations/2.sqm @@ -0,0 +1,46 @@ +-- Mangas table +ALTER TABLE mangas ADD COLUMN version INTEGER NOT NULL DEFAULT 0; +ALTER TABLE mangas ADD COLUMN is_syncing INTEGER NOT NULL DEFAULT 0; + +-- Chapters table +ALTER TABLE chapters ADD COLUMN version INTEGER NOT NULL DEFAULT 0; +ALTER TABLE chapters ADD COLUMN is_syncing INTEGER NOT NULL DEFAULT 0; + +-- Mangas triggers +DROP TRIGGER IF EXISTS update_manga_version; +CREATE TRIGGER update_manga_version AFTER UPDATE ON mangas +BEGIN + UPDATE mangas SET version = version + 1 + WHERE _id = new._id AND new.is_syncing = 0 AND ( + new.url != old.url OR + new.description != old.description OR + new.favorite != old.favorite + ); +END; + +-- Chapters triggers +DROP TRIGGER IF EXISTS update_chapter_and_manga_version; +CREATE TRIGGER update_chapter_and_manga_version AFTER UPDATE ON chapters +WHEN new.is_syncing = 0 AND ( + new.read != old.read OR + new.bookmark != old.bookmark OR + new.last_page_read != old.last_page_read +) +BEGIN + -- Update the chapter version + UPDATE chapters SET version = version + 1 + WHERE _id = new._id; + + -- Update the manga version + UPDATE mangas SET version = version + 1 + WHERE _id = new.manga_id AND (SELECT is_syncing FROM mangas WHERE _id = new.manga_id) = 0; +END; + +-- manga_categories table +DROP TRIGGER IF EXISTS insert_manga_category_update_version; +CREATE TRIGGER insert_manga_category_update_version AFTER INSERT ON mangas_categories +BEGIN + UPDATE mangas + SET version = version + 1 + WHERE _id = new.manga_id AND (SELECT is_syncing FROM mangas WHERE _id = new.manga_id) = 0; +END; diff --git a/domain/src/main/java/tachiyomi/domain/chapter/model/Chapter.kt b/domain/src/main/java/tachiyomi/domain/chapter/model/Chapter.kt index 3a4a8c4a4..f993e0256 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/model/Chapter.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/model/Chapter.kt @@ -14,6 +14,7 @@ data class Chapter( val chapterNumber: Double, val scanlator: String?, val lastModifiedAt: Long, + val version: Long, ) { val isRecognizedNumber: Boolean get() = chapterNumber >= 0f @@ -43,6 +44,7 @@ data class Chapter( chapterNumber = -1.0, scanlator = null, lastModifiedAt = 0, + version = 1, ) } } diff --git a/domain/src/main/java/tachiyomi/domain/chapter/model/ChapterUpdate.kt b/domain/src/main/java/tachiyomi/domain/chapter/model/ChapterUpdate.kt index 33d1d4fba..5a9193dc6 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/model/ChapterUpdate.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/model/ChapterUpdate.kt @@ -13,6 +13,7 @@ data class ChapterUpdate( val dateUpload: Long? = null, val chapterNumber: Double? = null, val scanlator: String? = null, + val version: Long? = null, ) fun Chapter.toChapterUpdate(): ChapterUpdate { @@ -29,5 +30,6 @@ fun Chapter.toChapterUpdate(): ChapterUpdate { dateUpload, chapterNumber, scanlator, + version, ) } diff --git a/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt b/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt index 0ffe9856a..1cfc01f80 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt @@ -29,6 +29,7 @@ data class Manga( val initialized: Boolean, val lastModifiedAt: Long, val favoriteModifiedAt: Long?, + val version: Long, ) : Serializable { val expectedNextUpdate: Instant? @@ -122,6 +123,7 @@ data class Manga( initialized = false, lastModifiedAt = 0L, favoriteModifiedAt = null, + version = 0L, ) } } diff --git a/domain/src/main/java/tachiyomi/domain/manga/model/MangaUpdate.kt b/domain/src/main/java/tachiyomi/domain/manga/model/MangaUpdate.kt index 28a374e87..7f1cc2f87 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/model/MangaUpdate.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/model/MangaUpdate.kt @@ -23,6 +23,7 @@ data class MangaUpdate( val thumbnailUrl: String? = null, val updateStrategy: UpdateStrategy? = null, val initialized: Boolean? = null, + val version: Long? = null, ) fun Manga.toMangaUpdate(): MangaUpdate { @@ -47,5 +48,6 @@ fun Manga.toMangaUpdate(): MangaUpdate { thumbnailUrl = thumbnailUrl, updateStrategy = updateStrategy, initialized = initialized, + version = version, ) } From edd7d0522c305a8aec8ab524214d3a26106dac31 Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Mon, 11 Mar 2024 12:51:54 +0100 Subject: [PATCH 099/212] Translations update from Hosted Weblate (#445) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Translated using Weblate (Esperanto) Currently translated at 65.1% (517 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/eo/ * Translated using Weblate (Spanish) Currently translated at 100.0% (794 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/ * Translated using Weblate (Croatian) Currently translated at 100.0% (794 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/ * Translated using Weblate (Serbian) Currently translated at 100.0% (794 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sr/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (794 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (794 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/ * Translated using Weblate (Spanish) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/es/ * Translated using Weblate (Croatian) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/hr/ * Translated using Weblate (Serbian) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/sr/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/zh_Hans/ * Translated using Weblate (Filipino) Currently translated at 100.0% (794 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/ * Translated using Weblate (Filipino) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/fil/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/zh_Hant/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (794 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/hu/ * Translated using Weblate (Nepali) Currently translated at 100.0% (794 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/ * Translated using Weblate (Polish) Currently translated at 100.0% (794 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pl/ * Translated using Weblate (Polish) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/pl/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (794 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/ * Translated using Weblate (Russian) Currently translated at 100.0% (794 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/ * Translated using Weblate (Russian) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ru/ * Translated using Weblate (Indonesian) Currently translated at 100.0% (794 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/id/ * Translated using Weblate (Japanese) Currently translated at 100.0% (794 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/ * Translated using Weblate (Indonesian) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/id/ * Translated using Weblate (Dutch) Currently translated at 94.2% (748 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/nl/ * Translated using Weblate (Dutch) Currently translated at 94.4% (17 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/nl/ * Translated using Weblate (German) Currently translated at 100.0% (794 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/ --------- Co-authored-by: Radoŝ Porka Co-authored-by: gallegonovato Co-authored-by: Milo Ivir Co-authored-by: David Katrinka Co-authored-by: gekka <1778962971@qq.com> Co-authored-by: ɴᴇᴋᴏ Co-authored-by: Infy's Tagalog Translations Co-authored-by: Lzmxya Co-authored-by: B4LiN7 Co-authored-by: FateXBlood Co-authored-by: Eryk Michalak Co-authored-by: Dexroneum Co-authored-by: TheKingTermux Co-authored-by: Tim Bolhoeve Co-authored-by: Lyfja <45209212+lyfja@users.noreply.github.com> --- .../commonMain/resources/MR/de/strings.xml | 1 + .../commonMain/resources/MR/eo/strings.xml | 12 +++ .../commonMain/resources/MR/es/plurals.xml | 5 + .../commonMain/resources/MR/es/strings.xml | 11 +- .../commonMain/resources/MR/fil/plurals.xml | 6 +- .../commonMain/resources/MR/fil/strings.xml | 7 +- .../commonMain/resources/MR/hr/plurals.xml | 5 + .../commonMain/resources/MR/hr/strings.xml | 3 +- .../commonMain/resources/MR/hu/plurals.xml | 16 ++- .../commonMain/resources/MR/hu/strings.xml | 100 +++++++++++------- .../commonMain/resources/MR/in/plurals.xml | 5 +- .../commonMain/resources/MR/in/strings.xml | 1 + .../commonMain/resources/MR/ja/strings.xml | 1 + .../commonMain/resources/MR/ne/strings.xml | 1 + .../commonMain/resources/MR/nl/plurals.xml | 4 + .../commonMain/resources/MR/nl/strings.xml | 9 ++ .../commonMain/resources/MR/pl/plurals.xml | 6 ++ .../commonMain/resources/MR/pl/strings.xml | 1 + .../commonMain/resources/MR/ru/plurals.xml | 6 ++ .../commonMain/resources/MR/ru/strings.xml | 1 + .../commonMain/resources/MR/sr/plurals.xml | 11 +- .../commonMain/resources/MR/sr/strings.xml | 62 +++++++---- .../resources/MR/zh-rCN/plurals.xml | 3 + .../resources/MR/zh-rCN/strings.xml | 1 + .../resources/MR/zh-rTW/plurals.xml | 3 + .../resources/MR/zh-rTW/strings.xml | 3 +- 26 files changed, 202 insertions(+), 82 deletions(-) diff --git a/i18n/src/commonMain/resources/MR/de/strings.xml b/i18n/src/commonMain/resources/MR/de/strings.xml index a402087fb..e038287cd 100644 --- a/i18n/src/commonMain/resources/MR/de/strings.xml +++ b/i18n/src/commonMain/resources/MR/de/strings.xml @@ -798,4 +798,5 @@ Open-Source-Repository Bald Zertifikatsbestätigungen zurücknehmen + Rauszoomen deaktivieren \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/eo/strings.xml b/i18n/src/commonMain/resources/MR/eo/strings.xml index 57643acd9..991249e0e 100644 --- a/i18n/src/commonMain/resources/MR/eo/strings.xml +++ b/i18n/src/commonMain/resources/MR/eo/strings.xml @@ -505,4 +505,16 @@ Lingvo Instalilo Malmoderna + Libera: %1$s / Tuta: %2$s + Popularaj + Havantaj rezultoj + Nekonata titolo + Sen priskribo + Baldaŭ + Ĉu vi certas? + Ĵus + Neniam + %1$s eraro: %2$s + Neniu fonto trovita + Ups! \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/es/plurals.xml b/i18n/src/commonMain/resources/MR/es/plurals.xml index 31c76cb44..08f01feea 100644 --- a/i18n/src/commonMain/resources/MR/es/plurals.xml +++ b/i18n/src/commonMain/resources/MR/es/plurals.xml @@ -85,4 +85,9 @@ %d repositorios %d repositorios + + Mañana + Dentro de %1$d días + Dentro de %1$d días + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/es/strings.xml b/i18n/src/commonMain/resources/MR/es/strings.xml index 5b10363da..0aedfb7b4 100644 --- a/i18n/src/commonMain/resources/MR/es/strings.xml +++ b/i18n/src/commonMain/resources/MR/es/strings.xml @@ -214,7 +214,7 @@ ¿Quieres borrar los capítulos descargados? Pausado Seguimiento - Ya existe una categoría con este nombre + ¡Ya existe una categoría con este nombre! Categorías eliminadas ¿Añadir manga a la biblioteca\? Imagen guardada @@ -758,15 +758,15 @@ ¿Es la primera vez que instalas %s? Te recomendamos leer la guía de introducción. Comenzar Tienes que elegir una carpeta - Te damos la bienvenida + ¡Bienvenid@s! ¿No es la primera vez que instalas %s? Saltar Siguiente Lo primero de todo es dejar las cosas a tu gusto. Siempre puedes volver a cambiarlas más tarde en los ajustes. Todavía no has proporcionado ninguna carpeta - Selecciona una carpeta donde %1$s guardará los capítulos descargados, las copias de seguridad y otras cosas. + Seleccione una carpeta donde %1$s almacenará las descargas de capítulos, copias de seguridad, etc. \n -\nTe recomendamos que sea solo para %1$s. +\nSe recomienda una carpeta dedicada. \n \nCarpeta seleccionada: %2$s Permiso para instalar aplicaciones @@ -782,7 +782,7 @@ Descripción completa del problema: Se espera que el siguiente número salga en aproximadamente %1$s, la aplicación busca actualizaciones cada %2$s. Frecuencia de actualización personalizada: - El repositorio ya existe + ¡Este repositorio ya existe! Actualizaciones inteligentes La dirección URL del repositorio no parece ser correcta Añade más repositorios a Mihon; la dirección URL tiene que terminar en «index.min.json». @@ -798,4 +798,5 @@ Pronto Dejar de marcar todas la extensiones desconocidas como de confianza Abrir repositorio + Desactivar alejar \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/fil/plurals.xml b/i18n/src/commonMain/resources/MR/fil/plurals.xml index 04755d125..4d10091a1 100644 --- a/i18n/src/commonMain/resources/MR/fil/plurals.xml +++ b/i18n/src/commonMain/resources/MR/fil/plurals.xml @@ -68,4 +68,8 @@ %d na repo %d na mga repo - + + Bukas + Sa loob ng %1$d araw + + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/fil/strings.xml b/i18n/src/commonMain/resources/MR/fil/strings.xml index b26aa0a81..8d1d80efa 100644 --- a/i18n/src/commonMain/resources/MR/fil/strings.xml +++ b/i18n/src/commonMain/resources/MR/fil/strings.xml @@ -184,8 +184,8 @@ \nSa pamamagitan ng pagtitiwala sa extension na ito, tinatanggap mo ang mga panganib na ito. Di-pinagkakatiwalaang extension I-uninstall - Tiwala - Hindi pinagkakatiwalaan + Itiwala + Di pinagkakatiwalaan Naka-install Ini-install Dina-download @@ -798,4 +798,5 @@ Malapit na Bawiin ang mga pinagkakatiwalaang hindi kilalang extension Open source na repo - + Patayin ang pag-zoom out + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/hr/plurals.xml b/i18n/src/commonMain/resources/MR/hr/plurals.xml index a404dab79..1bddc175f 100644 --- a/i18n/src/commonMain/resources/MR/hr/plurals.xml +++ b/i18n/src/commonMain/resources/MR/hr/plurals.xml @@ -85,4 +85,9 @@ %d repozitorija %d repozitorija + + Sutra + Za %1$d dana + Za %1$d dana + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/hr/strings.xml b/i18n/src/commonMain/resources/MR/hr/strings.xml index 657095031..b9ec5c237 100644 --- a/i18n/src/commonMain/resources/MR/hr/strings.xml +++ b/i18n/src/commonMain/resources/MR/hr/strings.xml @@ -511,7 +511,7 @@ Invertirano Danas Potpuno crna tamna tema - Strawberry Daiquiri + Daiquiri jagoda Izgled Vodič za pokretanje Ovjeri za potvrditi promjenu @@ -798,4 +798,5 @@ Predviđa se da će nova poglavlja biti objavljena za oko %1$s, provjera svakih %2$s. Uskoro Dostupno: %1$s / Ukupno: %2$s + Deaktiviraj smanjivanje zumiranja \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/hu/plurals.xml b/i18n/src/commonMain/resources/MR/hu/plurals.xml index 469179e28..ee1f7b9a8 100644 --- a/i18n/src/commonMain/resources/MR/hu/plurals.xml +++ b/i18n/src/commonMain/resources/MR/hu/plurals.xml @@ -25,12 +25,12 @@ %1$d napja - %d tracker - %d tracker + %d nyilvántartó + %d nyilvántartó - %d-nak/nek - %d-nak/nek + %d elemnek + %d elemeknek %d fejezet kihagyása, hiányzik a forrás, vagy ki lett szűrve @@ -64,4 +64,12 @@ 1 nap %d nap + + Holnap + %1$d nap múlva + + + %d repo + %d repok + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/hu/strings.xml b/i18n/src/commonMain/resources/MR/hu/strings.xml index 1224c75ad..8366439ff 100644 --- a/i18n/src/commonMain/resources/MR/hu/strings.xml +++ b/i18n/src/commonMain/resources/MR/hu/strings.xml @@ -4,7 +4,7 @@ Kategóriák Könyvtár bejegyzések Fejezetek - Követés + Nyilvántartás Előzmények Beállítások Előzmények @@ -40,7 +40,7 @@ Összes Frissítések korlátozása Töltés közben - Csak a jelenleg is folyamatban lévőket frissítse + \"Befejezett\" állapotú elemek kihagyása Alapértelmezett kategória Mindig kérdezzen rá Teljes képernyő @@ -76,8 +76,8 @@ Olvasatlannak jelölés Előző olvasottnak jelölése Letöltés - A fejezet könyvjelzőzése - A fejezet eltávolítása a könyvjelzők közül + Fejezet könyvjelzőzése + Fejezet eltávolítása a könyvjelzők közül Törlés Könyvtár frissítése Szerkesztés @@ -266,16 +266,14 @@ Felhasználónév Sütik törölve A változtatások érvénybe lépéséhez újra kell indítania az applikációt - Érvénytelen backup fájl + Érvénytelen biztonsági mentési fájl: Él Könyvtár frissítése Nem található fejezet Licenszelt - Ezt a kiterjesztést nem megbízható tanúsítvánnyal írták alá, és nem lett aktiválva. + Egy rosszindulatú bővítmény hozzáférhet a tárolt bejelentkezési adatokhoz, vagy tetszőleges kódot futtathat. \n -\nEgy rosszindulatú kiterjesztés elolvashatja a Mihonban tárolt bejelentkezési adatokat, vagy tetszőleges kódot futtathat. -\n -\nAz ebben a tanúsítványban való megbízással, elfogadja ezeket a kockázatokat. +\nHa megbízik ebben a bővítményben, elfogadja ezeket a kockázatokat. Bizalom Tiltott: %s Kedvelt: %s @@ -324,16 +322,16 @@ Kinézet Letöltés elkezdése most Helyi forrás - Követve + Nyilvántartva Hitelesítsd magad a változtatás elfogadásához Alapértelmezett Szolgáltatások - Útmutató a követéshez + Nyilvántartási útmutató Új fejezetek letöltése Automatikus letöltés Hátulról az ötödik fejezet Hátulról a negyedik befejezett fejezet - Utolsó előtti előtti befejezett fejezet + Harmadikként olvasott fejezet Utolsó előtti befejezett fejezet Kikapcsolva Tiltott kategóriák @@ -407,7 +405,7 @@ Figyelmeztetés A Shizuku nem fut Régi - Egyirányú szinkronizálás, hogy a fejezetben való előrehaladás frissítve legyen a követési szolgáltatásokban. Állíts be követést egyéni bejegyzésekre, a követési gomb lenyomásával. + Egyirányú szinkronizálás, hogy a fejezetben való előrehaladás frissítve legyen a követési szolgáltatásokban. Állíts be követést egyéni elemekre, a követés gomb lenyomásával. 3 naponta Csak Wi-Fi-n keresztül Ajánlott az automatikus biztonsági mentése. Ezen kívül máshol is legyenek másolatok. @@ -428,10 +426,10 @@ Globális keresés… Feltöltési dátum szerint Ez eltávolítja a fejezet olvasási dátumát. Biztos benne\? - Csak a befejezett fejezeteket() + Olvasatlan fejezet(ek)et tartalmazó elemek kihagyása Törölni akarja a %s kategóriát \? InternalError: Nézze meg a hibaüzenetet további információért - Csak nem-lemért hálózatokon + Csak korlátlan hálózatokon Mit tartalmazzon a biztonsági mentés\? Biztonsági mentés/helyreállítás nem biztos,hogy működik ha a MIUI Optimalizáció ki van kapcsolva. Helyreállítás folyamatban van @@ -457,16 +455,14 @@ Automatikusan nagyítsa a széles képeket Helyreállítás befejeződött %02d perc, %02d másodperc - Adatok visszaállítása a biztonsági mentésből. -\n -\nEzek után telepíteni kell a hiányzó bővítményeket és be kell jelentkezni a tracking szolgáltatásokba, hogy újra használhassa őket. + Előfordulhat, hogy telepítenie kell a hiányzó bővítményeket, majd később be kell jelentkeznie a nyilvántartási szolgáltatásokba a használatukhoz. Könyvtár fedők frissítése Hibaüzenetetek törlése Sorozat beállításainak visszaállítása Néhány gyártónak extra korlátozása van arra, hogy kikapcsolja a háttér folyamatokat. Ezen a web oldalon több információt találsz, hogy hogyan oldható meg. Olvasási előzmények megállítása Fedlap - Tartva + Szüneteltetve Kategóriák törölve Válasszon képet a fedlapnak Nem található új frissítés @@ -479,7 +475,7 @@ A forrás nem található Utolsó frissítés megtekintése Olvasatlanok száma - Bejegyzés mutatása + Elem mutatása Kizárólag fedlapok mutatása Hiányzó források: DNS a HTTPS mellett (DoH) @@ -502,14 +498,14 @@ Fej. %1$s - %2$s Kezdési útmutató Adatbázis törlése - Még nem lett elindítva + El nem kezdett elemek kihagyása Új fejezet Biztonsági mentés már folyamatban van Nincs telepítve Figyelmeztetés: a tömeges letöltések a források lelassulásához és/vagy a Mihon leállásához vezethetnek. Koppintson további információért. Koppintson további információért Nem sikerült a fedlap frissítése - Először adja a mangát a könyvtárhoz + Először adja az elemet a könyvtárhoz Átugorva, mivel tartalmaz olvasatlan fejezeteket Átugorva, mivel nincs olvasott fejezet Új hivatalos verzió érhető el. Koppintson további információért, hogy hogyan importáljon nem hivatalos F-Droid verziókról. @@ -530,7 +526,7 @@ Befejezett Nincs leírás Letöltés (%1$d/%2$d) - Tracking hozzáadása + Felvétel nyilvántartáshoz Elkezdett Válaszon beletartozó adatokat Fejezet beállításai frissültek @@ -544,11 +540,11 @@ Olvasó mód Befejezve: Jelenlegi: - Hát ez kínos... + Hát, ez kínos A nagy frissítések kárt okoznak a forrásoknak, és lassabb frissítésekhez, valamint megnövekedett akkumulátorhasználathoz vezethetnek. Koppintson további információért. %1$d frissítés sikertelen - Növeli a teljesítményt a nagy képek elválasztásával. - Nem bejelentkezett trackerek: + Javítja az olvasó teljesítményét + Nem bejelentkezett nyilvántartó: Inkognitó mód Inkognitó mód kikapcsolása Szűri a könyvtár összes tartalmát @@ -567,9 +563,9 @@ A forrás nem támogatott Nincs egyezés Ilyen nevű kategória már létezik! - Mangában lévő összes fejezet visszaállítása + Összes fejezet visszaállítása ebben az elemben Hiba a kép mentése közben - Tartva lista + Szüneteltettek listája Egyéni fedlap Következő: Előző: @@ -577,13 +573,13 @@ Nincs előző fejezet %1$s oldalt nem lehetett betölteni Oldalak betöltése… - Követ + Nyilvántart Nem sikerült a kiterjesztési lista megnyitása Kikapcsolva - Szolgáltatások, melyek fejlett funkciókat kínálnak. A bejegyzés automatikusan könyvtárhoz lesz adva és frissítve lesz. + Szolgáltatások, amelyek fejlett funkciókat kínálnak. Az elemek automatikusan könyvtárhoz lesznek adva és frissítve lesznek. Biztonsági mentés készítése Felhasználható az aktuális könyvtár visszaállítására - Biztonsági mentés visszaállítása + Biztonsági mentésből visszaállítás Könyvtár visszaállítása biztonsági mentésről Biztonsági mentések gyakorisága Biztonsági mentés helyreállítása @@ -632,9 +628,9 @@ Összes Olvasott Átlagos értékelés - Véletlenszerű manga megnyitása + Véletlenszerű elem megnyitása Könyvtár utoljára frissítve: %s - Követők + Nyilvántartók Az olvasatlanok számának megjelenítése a Frissítések ikonon Olvasás folytatása gomb Kategóriák, globális frissítés, fejezet csúsztatás @@ -654,10 +650,10 @@ Másolás a vágólapra Kategória frissítése Vágólapra másolva - Van már ilyen nevű ... a könyvtáradban. + Van már egy ilyen nevű elem a könyvtáradban. \n -\nBiztos folytatni akarod\? - Ez eltávolítja az eddig kiválasztott kezdeti dátumot a/az %s +\nBiztos folytatni akarod? + Ez eltávolítja az eddig kiválasztott kezdeti dátumot a(z) %s szolgáltatásból F-Droid csomagok nem hivatalosan támogatottak. \nKattincs további információért. Befejezett olvasmányok @@ -665,10 +661,10 @@ Elérhető de a forrás nincs telepítve: %s Tárhely engedély nincs megadva Használt - %1$s error: %2$s + %1$s hiba: %2$s Biztos vagy benne\? Kategória üres - Ez eltávolítja az eddig kiválasztott befejezési dátumot a/az %s + Ez eltávolítja az eddig kiválasztott befejezési dátumot a(z) %s szolgáltatásból Olvasási idő Nem található bejegyzés ebben a kategóriában A/az \"%s\"-t elfogod távolítani a könyvtáradból @@ -686,15 +682,15 @@ Balra csúsztatási cselekmény Duplikált fejezetek átugrása Időköz állítása - Egyedi lekérési időköz + Egyedi frissítési időköz Következő várt frissítés Dupla koppintás a nagyításhoz Már könyvtári bejegyzések elrejtése - Követéshez bejelentkezés + Bejelentkezés nyilvántartóhoz Letöltött törlése Magas képek szétvágása %d soronként - Kivül az elvárt kijönési időszakon + Következő kiadás idejének megjóslása Széles oldalak forgatása az illeszkedéshez Jobbra csúsztatási cselekmény Forgatott széles oldalak megfordítása @@ -781,4 +777,26 @@ Létrehoz Teljes hiba: Kezdetnek állítsunk be néhány dolgot. Ezeket később bármikor módosíthatja a beállításokban. + Felület + Licencelt – Nincs megjeleníthető fejezet + Törlés a(z) %s szolgáltatásból + Ezzel leállítja a helyi nyilvántartást. + N/A + %d perc + %d mp. + %d nap + %d óra + Nyilvántartási pont + Nord + Érzékeny beállítások megadása (Pl. szolgáltatások bejelentkezési tokenei) + Letöltési index érvénytelenítése + Új fejezetek megjelenése %1$s múlva várható, ellenőrzési időköz: %2$s. + Leállítja a(z) %s nyilvántartását? + Nyilvántartott elemek + Kicsinyítés letiltása + Elérhető: %1$s / Összesen: %2$s + Letöltési index érvénytelenítve + Mind becslése + Hamarosan + Egyedi frissítési időköz: \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/in/plurals.xml b/i18n/src/commonMain/resources/MR/in/plurals.xml index ff5428138..cb31ac13d 100644 --- a/i18n/src/commonMain/resources/MR/in/plurals.xml +++ b/i18n/src/commonMain/resources/MR/in/plurals.xml @@ -43,7 +43,7 @@ berikutnya %d chapter - Bab %1$s yang hilang + %1$s bab hilang %d hari @@ -51,4 +51,7 @@ %d repo + + Dalam %1$d hari + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/in/strings.xml b/i18n/src/commonMain/resources/MR/in/strings.xml index 1bc7301b2..6b7bf30fb 100644 --- a/i18n/src/commonMain/resources/MR/in/strings.xml +++ b/i18n/src/commonMain/resources/MR/in/strings.xml @@ -798,4 +798,5 @@ Segera Cabut izin ekstensi tidak dikenal yang tepercaya Repo sumber terbuka + Menonaktifkan zoom out \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ja/strings.xml b/i18n/src/commonMain/resources/MR/ja/strings.xml index 126f6bdb0..9ddf503d2 100644 --- a/i18n/src/commonMain/resources/MR/ja/strings.xml +++ b/i18n/src/commonMain/resources/MR/ja/strings.xml @@ -798,4 +798,5 @@ リポジトリURL Nord スマート・アップデート + ズームアウトを無効にする \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ne/strings.xml b/i18n/src/commonMain/resources/MR/ne/strings.xml index f7b46424b..aa98d1a15 100644 --- a/i18n/src/commonMain/resources/MR/ne/strings.xml +++ b/i18n/src/commonMain/resources/MR/ne/strings.xml @@ -802,4 +802,5 @@ सिर्जना गर्नुहोस् पूर्ण त्रुटि: संवेदनशील सेटिङहरू समावेश गर्नुहोस् (जस्तै, ट्र्याकर लगइन टोकनहरू) + जूम आउट असक्षम गर्नुहोस् \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/nl/plurals.xml b/i18n/src/commonMain/resources/MR/nl/plurals.xml index 426f1662a..a6aedc6dd 100644 --- a/i18n/src/commonMain/resources/MR/nl/plurals.xml +++ b/i18n/src/commonMain/resources/MR/nl/plurals.xml @@ -64,4 +64,8 @@ %1$s hoofdstuk mist %1$s hoofdstukken missen + + Morgen + Over %1$d dagen + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/nl/strings.xml b/i18n/src/commonMain/resources/MR/nl/strings.xml index 2a19fa1aa..24f2e69c7 100644 --- a/i18n/src/commonMain/resources/MR/nl/strings.xml +++ b/i18n/src/commonMain/resources/MR/nl/strings.xml @@ -744,4 +744,13 @@ App-instellingen Relatieve tijdstempels \"%1$s\" in plaats van \"%2$s\" + Geselecteerd + Niet geselecteerd + Meer opties + Navigeer omhoog + Scanlator + Toepassen + Naar standaard terugkeren + Onboarding gids + Welkom! \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/pl/plurals.xml b/i18n/src/commonMain/resources/MR/pl/plurals.xml index 02c3f4116..da0820219 100644 --- a/i18n/src/commonMain/resources/MR/pl/plurals.xml +++ b/i18n/src/commonMain/resources/MR/pl/plurals.xml @@ -102,4 +102,10 @@ repos repos + + Jutro + Za %1$d dni + Za %1$d dni + Za %1$d dni + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/pl/strings.xml b/i18n/src/commonMain/resources/MR/pl/strings.xml index a2569acc1..b86212168 100644 --- a/i18n/src/commonMain/resources/MR/pl/strings.xml +++ b/i18n/src/commonMain/resources/MR/pl/strings.xml @@ -802,4 +802,5 @@ Licencjonowany - Brak rozdziałów Pominięto, ponieważ nie spodziewano się dzisiaj żadnej publikacji Wyklucz skanlatorów + Wyłącz oddalenie \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ru/plurals.xml b/i18n/src/commonMain/resources/MR/ru/plurals.xml index 7d85c6f3f..a4f625ebe 100644 --- a/i18n/src/commonMain/resources/MR/ru/plurals.xml +++ b/i18n/src/commonMain/resources/MR/ru/plurals.xml @@ -102,4 +102,10 @@ %d репозиториев %d репозиториев + + Завтра + В течении %1$d дней + В течении %1$d дней + В течении %1$d дней + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ru/strings.xml b/i18n/src/commonMain/resources/MR/ru/strings.xml index 9ed0c48ba..47a203ae6 100644 --- a/i18n/src/commonMain/resources/MR/ru/strings.xml +++ b/i18n/src/commonMain/resources/MR/ru/strings.xml @@ -798,4 +798,5 @@ Север Убрать доверие к ненадёжным расширениям Открыть репозиторий источника + Отключить уменьшение масштаба \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/sr/plurals.xml b/i18n/src/commonMain/resources/MR/sr/plurals.xml index 6a538ebc2..a997e61a4 100644 --- a/i18n/src/commonMain/resources/MR/sr/plurals.xml +++ b/i18n/src/commonMain/resources/MR/sr/plurals.xml @@ -41,9 +41,9 @@ Завршено у %1$s са %2$s грешака - %d трекер - %d трекера - %d трекера + %d пратилац + %d пратиоца + %d пратилаца %1$s поглавље @@ -85,4 +85,9 @@ %d репозиторија %d репозиторија + + За %1$d дан + За %1$d дана + За %1$d дана + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/sr/strings.xml b/i18n/src/commonMain/resources/MR/sr/strings.xml index b74ad34aa..6eea42150 100644 --- a/i18n/src/commonMain/resources/MR/sr/strings.xml +++ b/i18n/src/commonMain/resources/MR/sr/strings.xml @@ -49,7 +49,7 @@ Уклони Настави Отвори у претраживачу - Режим дисплеја + Режим приказа Екран Збијена мрежа Листа @@ -62,7 +62,7 @@ Ресетуј Врати назад Отвори записник - Врати се + Врати Учитавање… Апликација није доступна Опште @@ -155,7 +155,7 @@ Четири пре прочитаног поглавља Пет пре прочитаног поглавља Преузми нова поглавља - Сервиси + Сервиси праћења Направи резервну копију Може се користити за враћање на тренутно стање колекције Врати се на резервну копију @@ -235,8 +235,8 @@ Последње поглавље Погледај поглавља Откажи све - Икључено - Укључено + Светла + Тамна По систему Управљај обавештењима Безбедност и приватност @@ -280,7 +280,7 @@ Погл. %1$s - %2$s Ажурирање колекције Учитавање страница није успело: %1$s - Утитавање страница… + Учитавање страница… Нема претходног поглавља Нема следећег поглавља Претходно: @@ -321,7 +321,7 @@ Последње коришћено Филтрира све наслове у колекцији Провери ажурирања - Sajt + Вебсајт Враћање је отказано Враћање на резервну копију није успело Враћање је већ у току @@ -395,7 +395,7 @@ Јин и јанг Потпуно црна тема NSFW - извори за одрасле (18+) - Показати у листи за изворе и додатке + Покажи у листи за изворе и додатке Начин читања Инсталер Аутоматско преузимање @@ -421,7 +421,7 @@ Картице Непознат статус Поређај по - Побољшани сервиси + Побољшани сервиси за праћење Неисправна датотека резервне копије Недостају извори: Резервна копија не садржи ниједан наслов. @@ -549,7 +549,7 @@ Историја је избрисана Да ли сте сигурни\? Цела историја ће бити изгубљена. Водич за миграцију извора - Упозорење: велики број преузимања може довести до успоравања извора и/или блокирања Mihon-ја. Додирни да сазнаш више. + Упозорење: велики број преузимања може довести до успоравања извора и/или блокирања Mihon-а. Додирни да сазнаш више. Велика ажурирања штете изворима и могу довести до споријег ажурирања и повећања потрошње батерије. Кликни да сазнаш више. Ажурирај све Ажурирања апликације @@ -571,7 +571,7 @@ Мрежа насловница Померај широке слике Аутоматски зумирај слике у пејзажном облику - Sačuvaj kao CBZ arhivu + Сачувај као CBZ архиву Обрнути портрет Издавање завршено Прескочено јер је наслов завршен @@ -588,7 +588,7 @@ Режим читања, дисплеј, навигација Аутоматско преузимање, преузимање унапред Једносмерна синхронизација напретка, побољшана синхронизација - Извори, екстензије, глобална претрага + Извори, додаци, глобална претрага Ручне и аутоматске резервне копије Преузми аутоматски током читања Копирано @@ -596,7 +596,7 @@ Присили апликацију да поново провери преузета поглавља Није инсталирано %s је наишао на неочекивану грешку. Предлажемо да поделите запис о прекиду програма на нашем каналу за подршку на Discord-у. - Widget није доступан када је омогућено закључавање апликације + Виџет није доступан када је омогућено закључавање апликације Прокажи број непрочитаних на икони ажурирања Листа жеља Доступно, али извор није преузет: %s @@ -633,11 +633,11 @@ Просечна оцена Прочитано Коришћено - %d дана - %d сати + %d д + %dч N/A - %d минута - %d секунди + %dмин + %dс Нема описа Дугме за наставак читања InternalError: Провери записнике о прекиду програма за даље информације @@ -655,7 +655,7 @@ Није могуће ресетовати подешавања читача Управо ћете уклонити \"%s\" из своје колекције Погледај твоје недавно ажуриране наслове у колекцији - Неважећи индекс преузимања + Поништи индекс преузимања Број непрочитаних Последња провера ажурирања Обриши категорију @@ -710,7 +710,7 @@ Има резултата Прескочено јер данас није очекивано издање Предвиди очекивано време изласка - Постави интервалу + Постави интервал Интервали Такође уклони из %s Запис праћења @@ -735,7 +735,7 @@ Паметно ажурирање Забљесни приликом листања Смањује артефакте на е-инк екранима - Репозиторије екстензија + Репозиторије додатака Место складиштења Користи се за аутоматске резервне копије, преузимаље поглавља и локални извор. Опозови поверење непознатим екстензијама @@ -761,7 +761,7 @@ Дозвола за инсталацију апликација Пресокочи Следеће - За инсталирање екстензија. + За инсталирање додатака. За примање обавештења о ажурурању библиотеке и др. Одобри Добро дошли! @@ -780,9 +780,27 @@ Норд Ажурураш са старије верзије и не знаш шта да изабереш? Потражи информације у водичу складиштења. Водич складиштења - За инсталацију екстензија потребне су дозволе. Додирни овде за одобрење + За инсталацију додатака потребне су дозволе. Додирни овде за одобрење Грешка: Ова репозиторија већ постоји Прилагођена учесталост ажурирања: Отвори репозиторију + Није одабрано + Одабрано + Више опција + Навигација према горе + Сортирање категорија + Да ли желиш да сортираш категорије по абецеди? + Премести серију на крај + Подешавања апликације + Подешавања извора + Последња аутоматска резервна копија:%s + Никада + Ажурирање библиотеке... (%s) + Онемогући умањивање + Створи + Коришћење складишта + Ни једна датотека није одабрана + Примени + Врати на подразумевано \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/zh-rCN/plurals.xml b/i18n/src/commonMain/resources/MR/zh-rCN/plurals.xml index 93ef73539..64caf1d6b 100644 --- a/i18n/src/commonMain/resources/MR/zh-rCN/plurals.xml +++ b/i18n/src/commonMain/resources/MR/zh-rCN/plurals.xml @@ -51,4 +51,7 @@ %d 个仓库 + + %1$d 天后 + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml b/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml index c450be769..ee40e55fc 100644 --- a/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml +++ b/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml @@ -798,4 +798,5 @@ 即将更新 撤销已信任的未知插件 打开图源仓库 + 禁用缩小 \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/zh-rTW/plurals.xml b/i18n/src/commonMain/resources/MR/zh-rTW/plurals.xml index f3d9a6f0b..ee3847fac 100644 --- a/i18n/src/commonMain/resources/MR/zh-rTW/plurals.xml +++ b/i18n/src/commonMain/resources/MR/zh-rTW/plurals.xml @@ -51,4 +51,7 @@ %d 處儲存庫 + + %1$d 天後 + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml b/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml index 25937d48c..872706038 100644 --- a/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml +++ b/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml @@ -710,7 +710,7 @@ 下次預期更新 刊期 預計每個 - 預估出刊日 + 自訂預估出刊日 設定為每個 由於未臨出刊日,因此略過 有結果 @@ -798,4 +798,5 @@ 撤銷對不明擴充套件的信任 即將出刊 開啟來源儲存庫 + 停用縮小 \ No newline at end of file From 0d6f426dbd8874c7861b6cc245f9e6ff43dbbe2c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 17:52:41 +0600 Subject: [PATCH 100/212] Update dependency io.nlopez.compose.rules:detekt to v0.3.12 (#500) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 756a03dc7..d55c51452 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,7 @@ sqldelight = "2.0.0" sqlite = "2.4.0" voyager = "1.0.0" detekt = "1.23.5" -detektCompose = "0.3.11" +detektCompose = "0.3.12" [libraries] desugar = "com.android.tools:desugar_jdk_libs:2.0.4" From f66f52c244b786ae09f8e4ae683575907068d15f Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Thu, 14 Mar 2024 03:27:03 +0600 Subject: [PATCH 101/212] Bump default user agent --- .../kotlin/eu/kanade/tachiyomi/network/NetworkPreferences.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkPreferences.kt b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkPreferences.kt index 4c839b90d..c32864aec 100644 --- a/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkPreferences.kt +++ b/core/common/src/main/kotlin/eu/kanade/tachiyomi/network/NetworkPreferences.kt @@ -19,7 +19,7 @@ class NetworkPreferences( fun defaultUserAgent(): Preference { return preferenceStore.getString( "default_user_agent", - "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/121.0", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0", ) } } From f08713587685ddb27cb8ce7184e2dd21ae7968ae Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Thu, 14 Mar 2024 03:28:48 +0600 Subject: [PATCH 102/212] Fix crash in track date selection dialog Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> --- .../eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt | 1 + domain/src/main/java/tachiyomi/domain/track/model/Track.kt | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt index e0ad25182..1ba697f24 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt @@ -427,6 +427,7 @@ private data class TrackDateSelectorScreen( private val start: Boolean, ) : Screen() { + @Transient private val selectableDates = object : SelectableDates { override fun isSelectableDate(utcTimeMillis: Long): Boolean { val dateToCheck = Instant.ofEpochMilli(utcTimeMillis) diff --git a/domain/src/main/java/tachiyomi/domain/track/model/Track.kt b/domain/src/main/java/tachiyomi/domain/track/model/Track.kt index 1a656fcad..a92390494 100644 --- a/domain/src/main/java/tachiyomi/domain/track/model/Track.kt +++ b/domain/src/main/java/tachiyomi/domain/track/model/Track.kt @@ -1,5 +1,7 @@ package tachiyomi.domain.track.model +import java.io.Serializable + data class Track( val id: Long, val mangaId: Long, @@ -14,4 +16,4 @@ data class Track( val remoteUrl: String, val startDate: Long, val finishDate: Long, -) +) : Serializable From a08e03f5cbf3f4e6be1de35f97ef8ebb26a1210e Mon Sep 17 00:00:00 2001 From: Jobobby04 Date: Fri, 15 Mar 2024 19:51:56 -0400 Subject: [PATCH 103/212] Fix multiple issues regarding sources loading too late --- .../main/java/eu/kanade/core/util/SourceUtil.kt | 13 +++++++++++++ .../tachiyomi/data/download/DownloadCache.kt | 17 ++++++----------- .../tachiyomi/extension/ExtensionManager.kt | 7 ++++--- .../tachiyomi/source/AndroidSourceManager.kt | 6 ++++++ .../details/SourcePreferencesScreen.kt | 7 +++++++ .../migration/search/SourceSearchScreen.kt | 7 +++++++ .../browse/source/browse/BrowseSourceScreen.kt | 7 +++++++ .../source/globalsearch/GlobalSearchScreen.kt | 6 ++++++ .../eu/kanade/tachiyomi/ui/manga/MangaScreen.kt | 6 ++++++ .../tachiyomi/ui/reader/ReaderActivity.kt | 5 +++++ .../tachiyomi/ui/reader/ReaderViewModel.kt | 2 ++ .../domain/source/service/SourceManager.kt | 3 +++ 12 files changed, 72 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/eu/kanade/core/util/SourceUtil.kt diff --git a/app/src/main/java/eu/kanade/core/util/SourceUtil.kt b/app/src/main/java/eu/kanade/core/util/SourceUtil.kt new file mode 100644 index 000000000..c976e1e6a --- /dev/null +++ b/app/src/main/java/eu/kanade/core/util/SourceUtil.kt @@ -0,0 +1,13 @@ +package eu.kanade.core.util + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.remember +import tachiyomi.domain.source.service.SourceManager +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +@Composable +fun ifSourcesLoaded(): Boolean { + return remember { Injekt.get().isInitialized }.collectAsState().value +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt index fb1c8e176..0fdbabe03 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadCache.kt @@ -18,6 +18,7 @@ import kotlinx.coroutines.ensureActive import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart @@ -311,18 +312,12 @@ class DownloadCache( } // Try to wait until extensions and sources have loaded - var sources = getSources() - if (sources.isEmpty()) { - withTimeoutOrNull(30.seconds) { - while (!extensionManager.isInitialized) { - delay(2.seconds) - } + var sources = emptyList() + withTimeoutOrNull(30.seconds) { + extensionManager.isInitialized.first { it } + sourceManager.isInitialized.first { it } - while (extensionManager.availableExtensionsFlow.value.isNotEmpty() && sources.isEmpty()) { - delay(2.seconds) - sources = getSources() - } - } + sources = getSources() } val sourceMap = sources.associate { provider.getSourceDirName(it).lowercase() to it.id } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt index 4a5eff1ac..15f386d38 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt @@ -16,6 +16,7 @@ import eu.kanade.tachiyomi.util.system.toast import kotlinx.coroutines.async import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.emptyFlow import logcat.LogPriority @@ -41,8 +42,8 @@ class ExtensionManager( private val trustExtension: TrustExtension = Injekt.get(), ) { - var isInitialized = false - private set + private val _isInitialized = MutableStateFlow(false) + val isInitialized: StateFlow = _isInitialized.asStateFlow() /** * API where all the available extensions can be found. @@ -108,7 +109,7 @@ class ExtensionManager( .filterIsInstance() .map { it.extension } - isInitialized = true + _isInitialized.value = true } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt b/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt index a7618bd8c..0fa40ba9d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/AndroidSourceManager.kt @@ -9,6 +9,8 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch @@ -28,6 +30,9 @@ class AndroidSourceManager( private val sourceRepository: StubSourceRepository, ) : SourceManager { + private val _isInitialized = MutableStateFlow(false) + override val isInitialized: StateFlow = _isInitialized.asStateFlow() + private val downloadManager: DownloadManager by injectLazy() private val scope = CoroutineScope(Job() + Dispatchers.IO) @@ -60,6 +65,7 @@ class AndroidSourceManager( } } sourcesMapFlow.value = mutableMap + _isInitialized.value = true } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesScreen.kt index 3eac8accf..c144caa2a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesScreen.kt @@ -31,6 +31,7 @@ import androidx.preference.forEach import androidx.preference.getOnBindEditTextListener import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import eu.kanade.core.util.ifSourcesLoaded import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.R @@ -40,6 +41,7 @@ import eu.kanade.tachiyomi.source.sourcePreferences import eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText.Companion.setIncognito import tachiyomi.domain.source.service.SourceManager import tachiyomi.presentation.core.components.material.Scaffold +import tachiyomi.presentation.core.screens.LoadingScreen import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -47,6 +49,11 @@ class SourcePreferencesScreen(val sourceId: Long) : Screen() { @Composable override fun Content() { + if (!ifSourcesLoaded()) { + LoadingScreen() + return + } + val context = LocalContext.current val navigator = LocalNavigator.currentOrThrow diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt index acd8278a0..3a2adcc21 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt @@ -18,6 +18,7 @@ import androidx.paging.compose.collectAsLazyPagingItems import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import eu.kanade.core.util.ifSourcesLoaded import eu.kanade.presentation.browse.BrowseSourceContent import eu.kanade.presentation.components.SearchToolbar import eu.kanade.presentation.util.Screen @@ -34,6 +35,7 @@ import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.i18n.stringResource +import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.source.local.LocalSource data class SourceSearchScreen( @@ -44,6 +46,11 @@ data class SourceSearchScreen( @Composable override fun Content() { + if (!ifSourcesLoaded()) { + LoadingScreen() + return + } + val uriHandler = LocalUriHandler.current val navigator = LocalNavigator.currentOrThrow val scope = rememberCoroutineScope() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt index 4bbf6d7dc..61c30f604 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt @@ -35,6 +35,7 @@ import androidx.paging.compose.collectAsLazyPagingItems import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import eu.kanade.core.util.ifSourcesLoaded import eu.kanade.presentation.browse.BrowseSourceContent import eu.kanade.presentation.browse.MissingSourceScreen import eu.kanade.presentation.browse.components.BrowseSourceToolbar @@ -60,6 +61,7 @@ import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource +import tachiyomi.presentation.core.screens.LoadingScreen import tachiyomi.source.local.LocalSource data class BrowseSourceScreen( @@ -73,6 +75,11 @@ data class BrowseSourceScreen( @Composable override fun Content() { + if (!ifSourcesLoaded()) { + LoadingScreen() + return + } + val screenModel = rememberScreenModel { BrowseSourceScreenModel(sourceId, listingQuery) } val state by screenModel.state.collectAsState() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt index a09595c4d..44164f49b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.runtime.setValue import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import eu.kanade.core.util.ifSourcesLoaded import eu.kanade.presentation.browse.GlobalSearchScreen import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen @@ -23,6 +24,11 @@ class GlobalSearchScreen( @Composable override fun Content() { + if (!ifSourcesLoaded()) { + LoadingScreen() + return + } + val navigator = LocalNavigator.currentOrThrow val screenModel = rememberScreenModel { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt index f7dad1d8c..d3578aff5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt @@ -22,6 +22,7 @@ import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.Navigator import cafe.adriel.voyager.navigator.currentOrThrow +import eu.kanade.core.util.ifSourcesLoaded import eu.kanade.domain.manga.model.hasCustomCover import eu.kanade.domain.manga.model.toSManga import eu.kanade.presentation.category.components.ChangeCategoryDialog @@ -73,6 +74,11 @@ class MangaScreen( @Composable override fun Content() { + if (!ifSourcesLoaded()) { + LoadingScreen() + return + } + val navigator = LocalNavigator.currentOrThrow val context = LocalContext.current val haptic = LocalHapticFeedback.current 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 e21fa11c6..530ee87e2 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 @@ -40,6 +40,7 @@ import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.google.android.material.elevation.SurfaceColors import com.google.android.material.transition.platform.MaterialContainerTransform import dev.chrisbanes.insetter.applyInsetter +import eu.kanade.core.util.ifSourcesLoaded import eu.kanade.domain.base.BasePreferences import eu.kanade.presentation.reader.DisplayRefreshHost import eu.kanade.presentation.reader.OrientationSelectDialog @@ -344,6 +345,10 @@ class ReaderActivity : BaseActivity() { ) } + if (!ifSourcesLoaded()) { + return@setComposeContent + } + val isHttpSource = viewModel.getSource() is HttpSource val isFullscreen by readerPreferences.fullscreen().collectAsState() val flashOnPageChange by readerPreferences.flashOnPageChange().collectAsState() 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 a59f0500b..432eff749 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 @@ -47,6 +47,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach @@ -264,6 +265,7 @@ class ReaderViewModel @JvmOverloads constructor( try { val manga = getManga.await(mangaId) if (manga != null) { + sourceManager.isInitialized.first { it } mutableState.update { it.copy(manga = manga) } if (chapterId == -1L) chapterId = initialChapterId diff --git a/domain/src/main/java/tachiyomi/domain/source/service/SourceManager.kt b/domain/src/main/java/tachiyomi/domain/source/service/SourceManager.kt index 8b832f34c..da4fb3929 100644 --- a/domain/src/main/java/tachiyomi/domain/source/service/SourceManager.kt +++ b/domain/src/main/java/tachiyomi/domain/source/service/SourceManager.kt @@ -4,10 +4,13 @@ import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.online.HttpSource import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow import tachiyomi.domain.source.model.StubSource interface SourceManager { + val isInitialized: StateFlow + val catalogueSources: Flow> fun get(sourceKey: Long): Source? From bcdf17fe27dfb140e120ef2223aceb79668b8c16 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 17 Mar 2024 19:36:14 +0600 Subject: [PATCH 104/212] Disable `SerialVersionUIDInSerializableClass` detekt rule --- config/detekt/detekt.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 11c42e80d..d1a3e3dbb 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -18,5 +18,7 @@ style: ignoreCompanionObjectPropertyDeclaration: true ReturnCount: excludeGuardClauses: true + SerialVersionUIDInSerializableClass: + active: false UnusedPrivateMember: ignoreAnnotated: [ 'Preview' ] From 0a91b57f679cd5b50328dafaa42bf426c91c6ab5 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 17 Mar 2024 20:21:05 +0600 Subject: [PATCH 105/212] Use SHA for GitHub actions version --- .github/workflows/build_pull_request.yml | 10 +++++----- .github/workflows/build_push.yml | 12 ++++++------ .github/workflows/issue_moderator.yml | 14 ++------------ .github/workflows/lock.yml | 2 +- 4 files changed, 14 insertions(+), 24 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 56ac46c79..4d0d149b6 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -20,22 +20,22 @@ jobs: steps: - name: Clone repo - uses: actions/checkout@v4 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v2 + uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2 # v2.1.1 - name: Dependency Review - uses: actions/dependency-review-action@v4 + uses: actions/dependency-review-action@9129d7d40b8c12c1ed0f60400d00c92d437adcce # v4.1.3 - name: Set up JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 with: java-version: 17 distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@v3 + uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 0f400fbbf..db79bb1ce 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -17,23 +17,23 @@ jobs: steps: - name: Clone repo - uses: actions/checkout@v4 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@v2 + uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2 # v2.1.1 - name: Setup Android SDK run: | ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager "build-tools;29.0.3" - name: Set up JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 with: java-version: 17 distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@v3 + uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest @@ -48,7 +48,7 @@ jobs: - name: Sign APK if: startsWith(github.ref, 'refs/tags/') && github.repository == 'mihonapp/mihon' - uses: r0adkll/sign-android-release@v1 + uses: r0adkll/sign-android-release@/349ebdef58775b1e0d8099458af0816dc79b6407 # v1 with: releaseDirectory: app/build/outputs/apk/standard/release signingKeyBase64: ${{ secrets.SIGNING_KEY }} @@ -83,7 +83,7 @@ jobs: - name: Create Release if: startsWith(github.ref, 'refs/tags/') && github.repository == 'mihonapp/mihon' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@9d7c94cfd0a1f3ed45544c887983e9fa900f0564 # v2.0.4 with: tag_name: ${{ env.VERSION_TAG }} name: Mihon ${{ env.VERSION_TAG }} diff --git a/.github/workflows/issue_moderator.yml b/.github/workflows/issue_moderator.yml index c0859d10e..70292069d 100644 --- a/.github/workflows/issue_moderator.yml +++ b/.github/workflows/issue_moderator.yml @@ -11,28 +11,18 @@ jobs: runs-on: ubuntu-latest steps: - name: Moderate issues - uses: tachiyomiorg/issue-moderator-action@v2.6.0 + uses: keiyoushi/issue-moderator-action@a017be83547db6e107431ce7575f53c1dfa3296a with: repo-token: ${{ secrets.GITHUB_TOKEN }} duplicate-label: Duplicate auto-close-rules: | [ - { - "type": "body", - "regex": ".*DELETE THIS SECTION IF YOU HAVE READ AND ACKNOWLEDGED IT.*", - "message": "The acknowledgment section was not removed." - }, - { - "type": "body", - "regex": ".*\\* (Tachiyomi version|Android version|Device): \\?.*", - "message": "Requested information in the template was not filled out." - }, { "type": "both", "regex": "^(?!.*myanimelist.*).*(aniyomi|anime).*$", "ignoreCase": true, - "message": "Tachiyomi does not support anime, and has no plans to support anime. In addition Tachiyomi is not affiliated with Aniyomi https://github.com/jmir1/aniyomi" + "message": "Mihon does not support anime, and has no plans to support anime. In addition Mihon is not affiliated with Aniyomi https://github.com/jmir1/aniyomi" }, { "type": "both", diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index 869c1140a..cfea9b407 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -12,7 +12,7 @@ jobs: lock: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v5 + - uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1 with: github-token: ${{ github.token }} issue-inactive-days: '2' From 1b60c5f0f474a0b60cb9ebc35d180686a2247228 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 17 Mar 2024 20:22:08 +0600 Subject: [PATCH 106/212] Check for dependency update every Friday --- .github/renovate.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 669bc1e28..4cc36b133 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -3,7 +3,7 @@ "extends": [ "config:base" ], - "schedule": ["every sunday"], + "schedule": ["every friday"], "labels": ["Dependencies"], "packageRules": [ { From f2ccfb0817f7b4c77d0ed362b11b414004397e5d Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 17 Mar 2024 20:25:27 +0600 Subject: [PATCH 107/212] Typo in `r0adkll/sign-android-release` SHA --- .github/workflows/build_push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index db79bb1ce..346b0ac13 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -48,7 +48,7 @@ jobs: - name: Sign APK if: startsWith(github.ref, 'refs/tags/') && github.repository == 'mihonapp/mihon' - uses: r0adkll/sign-android-release@/349ebdef58775b1e0d8099458af0816dc79b6407 # v1 + uses: r0adkll/sign-android-release@349ebdef58775b1e0d8099458af0816dc79b6407 # v1 with: releaseDirectory: app/build/outputs/apk/standard/release signingKeyBase64: ${{ secrets.SIGNING_KEY }} From 015d9b3bd057fe218d5bab77fa0736be5488eb4d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 17 Mar 2024 20:45:09 +0600 Subject: [PATCH 108/212] fix(deps): update dependency com.squareup.okio:okio to v3.9.0 (#529) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d55c51452..ce4065a9c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,7 +22,7 @@ okhttp-core = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp_ve okhttp-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp_version" } okhttp-brotli = { module = "com.squareup.okhttp3:okhttp-brotli", version.ref = "okhttp_version" } okhttp-dnsoverhttps = { module = "com.squareup.okhttp3:okhttp-dnsoverhttps", version.ref = "okhttp_version" } -okio = "com.squareup.okio:okio:3.8.0" +okio = "com.squareup.okio:okio:3.9.0" conscrypt-android = "org.conscrypt:conscrypt-android:2.5.2" From ebee2751109daf7be86d93736806374d6255be7c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 17 Mar 2024 20:45:26 +0600 Subject: [PATCH 109/212] fix(deps): update dependency io.kotest:kotest-assertions-core to v5.8.1 (#528) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ce4065a9c..15a4c3d78 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -89,7 +89,7 @@ sqldelight-dialects-sql = { module = "app.cash.sqldelight:sqlite-3-38-dialect", sqldelight-gradle = { module = "app.cash.sqldelight:gradle-plugin", version.ref = "sqldelight" } junit = "org.junit.jupiter:junit-jupiter:5.10.2" -kotest-assertions = "io.kotest:kotest-assertions-core:5.8.0" +kotest-assertions = "io.kotest:kotest-assertions-core:5.8.1" mockk = "io.mockk:mockk:1.13.10" voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" } From 59bedb33ff59ad5db1df2e93567a2266fb63eacc Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 18 Mar 2024 23:01:23 +0600 Subject: [PATCH 110/212] Fix regression from coil3 migration Fixes #495 Co-authored-by: jobobby04 <17078382+jobobby04@users.noreply.github.com> --- .../ui/reader/viewer/ReaderPageImageView.kt | 5 +++-- .../tachiyomi/core/common/util/system/ImageUtil.kt | 13 +++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) 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 fbb1302e8..2cd0f2840 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 @@ -349,8 +349,9 @@ open class ReaderPageImageView @JvmOverloads constructor( .diskCachePolicy(CachePolicy.DISABLED) .target( onSuccess = { result -> - setImageDrawable(result.asDrawable(context.resources)) - (result as? Animatable)?.start() + val drawable = result.asDrawable(context.resources) + setImageDrawable(drawable) + (drawable as? Animatable)?.start() isVisible = true this@ReaderPageImageView.onImageLoaded() }, diff --git a/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt b/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt index b01f4e882..03a2f2d60 100644 --- a/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt @@ -77,19 +77,20 @@ object ImageUtil { } fun isAnimatedAndSupported(stream: InputStream): Boolean { - try { + return try { val type = getImageType(stream) ?: return false - return when (type.format) { + // https://coil-kt.github.io/coil/getting_started/#supported-image-formats + when (type.format) { Format.Gif -> true - // Coil supports animated WebP on Android 9.0+ - // https://coil-kt.github.io/coil/getting_started/#supported-image-formats + // Animated WebP on Android 9+ Format.Webp -> type.isAnimated && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P + // Animated Heif on Android 11+ + Format.Heif -> type.isAnimated && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R else -> false } } catch (e: Exception) { - /* Do Nothing */ + false } - return false } private fun getImageType(stream: InputStream): tachiyomi.decoder.ImageType? { From 0ea0138a73466af3d371a48e344753844e9bc3d8 Mon Sep 17 00:00:00 2001 From: MajorTanya <39014446+MajorTanya@users.noreply.github.com> Date: Thu, 21 Mar 2024 06:37:17 +0100 Subject: [PATCH 111/212] Switch to seconds for DATE_MODIFIED of saved pages (#552) While most Android skins are seemingly able to handle the millisecond format, the documentation technically specifies seconds. This seems to be causing issues on Samsung devices using the Samsung Gallery app, which renders the millisecond timestamps as if they were second ones, causing the dates to be set at some point in the year 56189. This change should fix that issue on Samsung devices and have no real impact on the rest. --- app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt index beefba353..b7b53d835 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt @@ -79,7 +79,7 @@ class ImageSaver( MediaStore.Images.Media.RELATIVE_PATH to relativePath, MediaStore.Images.Media.DISPLAY_NAME to image.name, MediaStore.Images.Media.MIME_TYPE to type.mime, - MediaStore.Images.Media.DATE_MODIFIED to Instant.now().toEpochMilli(), + MediaStore.Images.Media.DATE_MODIFIED to Instant.now().epochSecond, ) val picture = findUriOrDefault(relativePath, filename) { From 583aa430ba9e8e7454076afc049f812bd3df21df Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 Mar 2024 11:37:36 +0600 Subject: [PATCH 112/212] fix(deps): update dependency com.android.tools.build:gradle to v8.3.1 (#543) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 26e6db019..2357dfc0b 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -1,5 +1,5 @@ [versions] -agp_version = "8.3.0" +agp_version = "8.3.1" lifecycle_version = "2.7.0" paging_version = "3.2.1" From a29870c01e3b79952b0882441e266009bf1119f2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 Mar 2024 12:40:48 +0600 Subject: [PATCH 113/212] fix(deps): update dependency org.apache.commons:commons-compress to v1.26.1 (#502) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 15a4c3d78..06bdecfd9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -32,7 +32,7 @@ jsoup = "org.jsoup:jsoup:1.17.2" disklrucache = "com.jakewharton:disklrucache:2.0.2" unifile = "com.github.tachiyomiorg:unifile:7c257e1c64" -common-compress = "org.apache.commons:commons-compress:1.26.0" +common-compress = "org.apache.commons:commons-compress:1.26.1" junrar = "com.github.junrar:junrar:7.5.5" sqlite-framework = { module = "androidx.sqlite:sqlite-framework", version.ref = "sqlite" } From 3f2c8e9ef6db540c77b818ffdf771674b3e46c8b Mon Sep 17 00:00:00 2001 From: w Date: Thu, 21 Mar 2024 00:20:29 -0700 Subject: [PATCH 114/212] Update image-decoder, color management (#523) * Update image-decoder, color management * move display profile pref * remove true color pref * Move Display Profile settings to a new section * Partially revert "remove true color pref" This partially reverts commit e1a75816950e100936699279e1618adb2fa341aa. * Tweak label --------- Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- .../eu/kanade/domain/base/BasePreferences.kt | 2 ++ .../settings/screen/SettingsAdvancedScreen.kt | 31 +++++++++++++++++++ .../settings/screen/SettingsReaderScreen.kt | 6 +--- .../data/coil/TachiyomiImageDecoder.kt | 2 +- .../tachiyomi/ui/reader/ReaderActivity.kt | 24 +++++++++----- .../ui/reader/setting/ReaderPreferences.kt | 3 -- .../ui/reader/viewer/ViewerConfig.kt | 4 --- gradle/libs.versions.toml | 4 +-- .../commonMain/resources/MR/base/strings.xml | 3 +- 9 files changed, 54 insertions(+), 25 deletions(-) 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 9fa63d2cd..1700f0a02 100644 --- a/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt +++ b/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt @@ -28,4 +28,6 @@ class BasePreferences( SHIZUKU(MR.strings.ext_installer_shizuku, false), PRIVATE(MR.strings.ext_installer_private, false), } + + fun displayProfile() = preferenceStore.getString("pref_display_profile_key", "") } 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 509f2196a..646c8c042 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 @@ -6,6 +6,8 @@ import android.content.Intent import android.provider.Settings import android.webkit.WebStorage import android.webkit.WebView +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.material3.AlertDialog import androidx.compose.material3.Text import androidx.compose.material3.TextButton @@ -123,6 +125,7 @@ object SettingsAdvancedScreen : SearchableSettings { getDataGroup(), getNetworkGroup(networkPreferences = networkPreferences), getLibraryGroup(), + getReaderGroup(basePreferences = basePreferences), getExtensionsGroup(basePreferences = basePreferences), ) } @@ -313,6 +316,34 @@ object SettingsAdvancedScreen : SearchableSettings { ) } + @Composable + private fun getReaderGroup( + basePreferences: BasePreferences, + ): Preference.PreferenceGroup { + val context = LocalContext.current + val chooseColorProfile = rememberLauncherForActivityResult( + contract = ActivityResultContracts.OpenDocument(), + ) { uri -> + uri?.let { + val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION + context.contentResolver.takePersistableUriPermission(uri, flags) + basePreferences.displayProfile().set(uri.toString()) + } + } + return Preference.PreferenceGroup( + title = stringResource(MR.strings.pref_category_reader), + preferenceItems = persistentListOf( + Preference.PreferenceItem.TextPreference( + title = stringResource(MR.strings.pref_display_profile), + subtitle = basePreferences.displayProfile().get(), + onClick = { + chooseColorProfile.launch(arrayOf("*/*")) + }, + ), + ) + ) + } + @Composable private fun getExtensionsGroup( basePreferences: BasePreferences, 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 a0a2e8f97..be685a780 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 @@ -29,6 +29,7 @@ object SettingsReaderScreen : SearchableSettings { @Composable override fun getPreferences(): List { val readerPref = remember { Injekt.get() } + return listOf( Preference.PreferenceItem.ListPreference( pref = readerPref.defaultReadingMode(), @@ -56,11 +57,6 @@ object SettingsReaderScreen : SearchableSettings { title = stringResource(MR.strings.pref_show_navigation_mode), subtitle = stringResource(MR.strings.pref_show_navigation_mode_summary), ), - Preference.PreferenceItem.SwitchPreference( - pref = readerPref.trueColor(), - title = stringResource(MR.strings.pref_true_color), - subtitle = stringResource(MR.strings.pref_true_color_summary), - ), Preference.PreferenceItem.SwitchPreference( pref = readerPref.pageTransitions(), title = stringResource(MR.strings.pref_page_transitions), diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt index 3f6c13906..8105192ff 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt @@ -24,7 +24,7 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti check(decoder != null && decoder.width > 0 && decoder.height > 0) { "Failed to initialize decoder" } - val bitmap = decoder.decode(rgb565 = options.allowRgb565) + val bitmap = decoder.decode() decoder.recycle() check(bitmap != null) { "Failed to decode image" } 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 530ee87e2..de338f551 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 @@ -39,6 +39,7 @@ import androidx.lifecycle.lifecycleScope import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.google.android.material.elevation.SurfaceColors import com.google.android.material.transition.platform.MaterialContainerTransform +import com.hippo.unifile.UniFile import dev.chrisbanes.insetter.applyInsetter import eu.kanade.core.util.ifSourcesLoaded import eu.kanade.domain.base.BasePreferences @@ -94,6 +95,7 @@ import tachiyomi.i18n.MR import tachiyomi.presentation.core.util.collectAsState import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import java.io.ByteArrayOutputStream class ReaderActivity : BaseActivity() { @@ -819,8 +821,8 @@ class ReaderActivity : BaseActivity() { } .launchIn(lifecycleScope) - readerPreferences.trueColor().changes() - .onEach(::setTrueColor) + preferences.displayProfile().changes() + .onEach { setDisplayProfile(it) } .launchIn(lifecycleScope) readerPreferences.cutoutShort().changes() @@ -859,13 +861,19 @@ class ReaderActivity : BaseActivity() { } /** - * Sets the 32-bit color mode according to [enabled]. + * Sets the display profile to [path]. */ - private fun setTrueColor(enabled: Boolean) { - if (enabled) { - SubsamplingScaleImageView.setPreferredBitmapConfig(Bitmap.Config.ARGB_8888) - } else { - SubsamplingScaleImageView.setPreferredBitmapConfig(Bitmap.Config.RGB_565) + private fun setDisplayProfile(path: String) { + val file = UniFile.fromUri(baseContext, path.toUri()) + if (file != null && file.exists()) { + val inputStream = file.openInputStream() + val outputStream = ByteArrayOutputStream() + inputStream.use { input -> + outputStream.use { output -> + input.copyTo(output) + } + } + SubsamplingScaleImageView.setDisplayProfile(outputStream.toByteArray()) } } 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 53e342e76..63162788c 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 @@ -23,9 +23,6 @@ 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) fun cutoutShort() = preferenceStore.getBoolean("cutout_short", true) 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 0d973d239..c84f8f51b 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 @@ -22,7 +22,6 @@ abstract class ViewerConfig(readerPreferences: ReaderPreferences, private val sc var doubleTapAnimDuration = 500 var volumeKeysEnabled = false var volumeKeysInverted = false - var trueColor = false var alwaysShowChapterTransition = true var navigationMode = 0 protected set @@ -62,9 +61,6 @@ abstract class ViewerConfig(readerPreferences: ReaderPreferences, private val sc readerPreferences.readWithVolumeKeysInverted() .register({ volumeKeysInverted = it }) - readerPreferences.trueColor() - .register({ trueColor = it }, { imagePropertyChangedListener?.invoke() }) - readerPreferences.alwaysShowChapterTransition() .register({ alwaysShowChapterTransition = it }) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 06bdecfd9..32823e676 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -49,8 +49,8 @@ coil-gif = { module = "io.coil-kt.coil3:coil-gif" } coil-compose = { module = "io.coil-kt.coil3:coil-compose" } coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp" } -subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:7e57335" -image-decoder = "com.github.tachiyomiorg:image-decoder:398d3c074f" +subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:aeaa170036" +image-decoder = "com.github.tachiyomiorg:image-decoder:e08e9be535" natural-comparator = "com.github.gpanther:java-nat-sort:natural-comparator-1.1" diff --git a/i18n/src/commonMain/resources/MR/base/strings.xml b/i18n/src/commonMain/resources/MR/base/strings.xml index 6c6e1681a..119e82ecd 100644 --- a/i18n/src/commonMain/resources/MR/base/strings.xml +++ b/i18n/src/commonMain/resources/MR/base/strings.xml @@ -364,8 +364,7 @@ Show page number Show reading mode Briefly show current mode when reader is opened - 32-bit color - Reduces banding, but may impact performance + Custom display profile Crop borders Custom brightness Grayscale From 7e9340aa7f1021eabb4ae01eb0f4cbdfb6cc0589 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Fri, 22 Mar 2024 18:43:36 +0600 Subject: [PATCH 115/212] Address detekt issues --- .../java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt | 1 - .../main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt | 1 - 2 files changed, 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt index 8105192ff..2f0c3df49 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt @@ -7,7 +7,6 @@ import coil3.decode.Decoder import coil3.decode.ImageSource import coil3.fetch.SourceFetchResult import coil3.request.Options -import coil3.request.allowRgb565 import okio.BufferedSource import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.decoder.ImageDecoder 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 de338f551..62bf23646 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 @@ -5,7 +5,6 @@ import android.app.Activity import android.app.assist.AssistContent import android.content.Context import android.content.Intent -import android.graphics.Bitmap import android.graphics.Color import android.graphics.ColorMatrix import android.graphics.ColorMatrixColorFilter From ef6cad58fe0eeb7bfec7e8df33ada87946fa85d3 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Fri, 22 Mar 2024 20:52:01 +0800 Subject: [PATCH 116/212] Fix recycled item's height being 0 in webtoon mode (#563) Which will prevent the new image from being decoded until it's visible. --- .../tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt index 4f41d3d51..b501725f7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt @@ -118,6 +118,7 @@ class WebtoonPageHolder( removeErrorLayout() frame.recycle() progressIndicator.setProgress(0) + progressContainer.isVisible = true } /** From 6a2bfd5e87795219ba756fc1d7ed906015142a40 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Mar 2024 18:54:38 +0600 Subject: [PATCH 117/212] chore(deps): update actions/dependency-review-action action to v4.2.3 (#559) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 4d0d149b6..0f02a3add 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -26,7 +26,7 @@ jobs: uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2 # v2.1.1 - name: Dependency Review - uses: actions/dependency-review-action@9129d7d40b8c12c1ed0f60400d00c92d437adcce # v4.1.3 + uses: actions/dependency-review-action@0fa40c3c10055986a88de3baa0d6ec17c5a894b3 # v4.2.3 - name: Set up JDK uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 From 3c5f4a317a7bb4106e91af508c693f2f68a2de12 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Mar 2024 18:54:50 +0600 Subject: [PATCH 118/212] chore(deps): update gradle/wrapper-validation-action action to v2.1.2 (#560) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 0f02a3add..3ce978689 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2 # v2.1.1 + uses: gradle/wrapper-validation-action@b231772637bb498f11fdbc86052b6e8a8dc9fc92 # v2.1.2 - name: Dependency Review uses: actions/dependency-review-action@0fa40c3c10055986a88de3baa0d6ec17c5a894b3 # v4.2.3 diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 346b0ac13..af014878b 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@699bb18358f12c5b78b37bb0111d3a0e2276e0e2 # v2.1.1 + uses: gradle/wrapper-validation-action@b231772637bb498f11fdbc86052b6e8a8dc9fc92 # v2.1.2 - name: Setup Android SDK run: | From 6682b5dd39ea46ecc57e040a558420d512893ed8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Mar 2024 18:55:02 +0600 Subject: [PATCH 119/212] fix(deps): update dependency com.google.firebase:firebase-analytics-ktx to v21.6.1 (#561) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 32823e676..fef4b5b0e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -71,7 +71,7 @@ moko-gradle = { module = "dev.icerock.moko:resources-generator", version.ref = " logcat = "com.squareup.logcat:logcat:0.1" -firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.5.1" +firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.6.1" aboutLibraries-gradle = { module = "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin", version.ref = "aboutlib_version" } aboutLibraries-compose = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "aboutlib_version" } From 34930920a50be25ca05024200bf871512962e3d0 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Fri, 22 Mar 2024 20:56:48 +0800 Subject: [PATCH 120/212] Fix webtoon last visible item position calculation (#562) Covers the case when image height > screen height. --- .../viewer/webtoon/WebtoonLayoutManager.kt | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonLayoutManager.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonLayoutManager.kt index cb9f484a4..852cd76e1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonLayoutManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonLayoutManager.kt @@ -36,20 +36,21 @@ class WebtoonLayoutManager(context: Context) : LinearLayoutManager(context) { */ fun findLastEndVisibleItemPosition(): Int { ensureLayoutState() - @ViewBoundsCheck.ViewBounds val preferredBoundsFlag = - (ViewBoundsCheck.FLAG_CVE_LT_PVE or ViewBoundsCheck.FLAG_CVE_EQ_PVE) - - val fromIndex = childCount - 1 - val toIndex = -1 - - val child = if (mOrientation == HORIZONTAL) { + val callback = if (mOrientation == HORIZONTAL) { mHorizontalBoundCheck - .findOneViewWithinBoundFlags(fromIndex, toIndex, preferredBoundsFlag, 0) } else { mVerticalBoundCheck - .findOneViewWithinBoundFlags(fromIndex, toIndex, preferredBoundsFlag, 0) + }.mCallback + val start = callback.parentStart + val end = callback.parentEnd + for (i in childCount - 1 downTo 0) { + val child = getChildAt(i)!! + val childStart = callback.getChildStart(child) + val childEnd = callback.getChildEnd(child) + if (childEnd <= end || childStart < start) { + return getPosition(child) + } } - - return if (child == null) NO_POSITION else getPosition(child) + return NO_POSITION } } From c0a888807b78891b28c6f6b9f16b719e24b03de1 Mon Sep 17 00:00:00 2001 From: Maddie Witman Date: Fri, 22 Mar 2024 09:04:43 -0400 Subject: [PATCH 121/212] Rework Duplicate Dialog and Allow Migration (#492) * (Mostly) Working Manga screen migration via duplicate dialog * Fully working migrate from Browse Search * Small tweaks for Antsy * Update app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt * Update app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt --------- Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- .../manga/DuplicateMangaDialog.kt | 141 +++++++++++++----- .../migration/search/SourceSearchScreen.kt | 2 +- .../source/browse/BrowseSourceScreen.kt | 19 ++- .../source/browse/BrowseSourceScreenModel.kt | 2 +- .../kanade/tachiyomi/ui/manga/MangaScreen.kt | 29 +++- .../tachiyomi/ui/manga/MangaScreenModel.kt | 6 + .../commonMain/resources/MR/base/strings.xml | 2 + 7 files changed, 156 insertions(+), 45 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/manga/DuplicateMangaDialog.kt b/app/src/main/java/eu/kanade/presentation/manga/DuplicateMangaDialog.kt index e3ff20ad2..0707ac2bc 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/DuplicateMangaDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/DuplicateMangaDialog.kt @@ -1,16 +1,33 @@ package eu.kanade.presentation.manga +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer -import androidx.compose.material3.AlertDialog +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.sizeIn +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Add +import androidx.compose.material.icons.outlined.Book +import androidx.compose.material.icons.outlined.SwapVert +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Text -import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import eu.kanade.presentation.components.AdaptiveSheet +import eu.kanade.presentation.components.TabbedDialogPaddings +import eu.kanade.presentation.more.settings.LocalPreferenceMinHeight +import eu.kanade.presentation.more.settings.widget.TextPreferenceWidget import tachiyomi.i18n.MR -import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource @Composable @@ -18,42 +35,92 @@ fun DuplicateMangaDialog( onDismissRequest: () -> Unit, onConfirm: () -> Unit, onOpenManga: () -> Unit, + onMigrate: () -> Unit, + modifier: Modifier = Modifier, ) { - AlertDialog( + val minHeight = LocalPreferenceMinHeight.current + + AdaptiveSheet( + modifier = modifier, onDismissRequest = onDismissRequest, - title = { - Text(text = stringResource(MR.strings.are_you_sure)) - }, - text = { - Text(text = stringResource(MR.strings.confirm_add_duplicate_manga)) - }, - confirmButton = { - FlowRow( - horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.extraSmall), + ) { + Column( + modifier = Modifier + .padding( + vertical = TabbedDialogPaddings.Vertical, + horizontal = TabbedDialogPaddings.Horizontal, + ) + .fillMaxWidth(), + ) { + Text( + modifier = Modifier.padding(TitlePadding), + text = stringResource(MR.strings.are_you_sure), + style = MaterialTheme.typography.headlineMedium, + ) + + Text( + text = stringResource(MR.strings.confirm_add_duplicate_manga), + style = MaterialTheme.typography.bodyMedium, + ) + + Spacer(Modifier.height(PaddingSize)) + + TextPreferenceWidget( + title = stringResource(MR.strings.action_show_manga), + icon = Icons.Outlined.Book, + onPreferenceClick = { + onDismissRequest() + onOpenManga() + }, + ) + + HorizontalDivider() + + TextPreferenceWidget( + title = stringResource(MR.strings.action_migrate_duplicate), + icon = Icons.Outlined.SwapVert, + onPreferenceClick = { + onDismissRequest() + onMigrate() + }, + ) + + HorizontalDivider() + + TextPreferenceWidget( + title = stringResource(MR.strings.action_add_anyway), + icon = Icons.Outlined.Add, + onPreferenceClick = { + onDismissRequest() + onConfirm() + }, + ) + + Row( + modifier = Modifier + .sizeIn(minHeight = minHeight) + .clickable { onDismissRequest.invoke() } + .padding(ButtonPadding) + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, ) { - TextButton( - onClick = { - onDismissRequest() - onOpenManga() - }, - ) { - Text(text = stringResource(MR.strings.action_show_manga)) - } - - Spacer(modifier = Modifier.weight(1f)) - - TextButton(onClick = onDismissRequest) { - Text(text = stringResource(MR.strings.action_cancel)) - } - TextButton( - onClick = { - onDismissRequest() - onConfirm() - }, - ) { - Text(text = stringResource(MR.strings.action_add)) + OutlinedButton(onClick = onDismissRequest, modifier = Modifier.fillMaxWidth()) { + Text( + modifier = Modifier + .padding(vertical = 8.dp), + text = stringResource(MR.strings.action_cancel), + color = MaterialTheme.colorScheme.primary, + style = MaterialTheme.typography.titleLarge, + fontSize = 16.sp, + ) } } - }, - ) + } + } } + +private val PaddingSize = 16.dp + +private val ButtonPadding = PaddingValues(top = 16.dp, bottom = 16.dp) +private val TitlePadding = PaddingValues(bottom = 16.dp, top = 8.dp) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt index 3a2adcc21..533dbd3af 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/SourceSearchScreen.kt @@ -83,7 +83,7 @@ data class SourceSearchScreen( ) { paddingValues -> val pagingFlow by screenModel.mangaPagerFlowFlow.collectAsState() val openMigrateDialog: (Manga) -> Unit = { - screenModel.setDialog(BrowseSourceScreenModel.Dialog.Migrate(it)) + screenModel.setDialog(BrowseSourceScreenModel.Dialog.Migrate(newManga = it, oldManga = oldManga)) } BrowseSourceContent( source = screenModel.source, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt index 61c30f604..152b2adfe 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreen.kt @@ -47,6 +47,8 @@ import eu.kanade.presentation.util.Screen import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.ui.browse.extension.details.SourcePreferencesScreen +import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateDialog +import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateDialogScreenModel import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreenModel.Listing import eu.kanade.tachiyomi.ui.category.CategoryScreen import eu.kanade.tachiyomi.ui.manga.MangaScreen @@ -252,6 +254,22 @@ data class BrowseSourceScreen( onDismissRequest = onDismissRequest, onConfirm = { screenModel.addFavorite(dialog.manga) }, onOpenManga = { navigator.push(MangaScreen(dialog.duplicate.id)) }, + onMigrate = { + screenModel.setDialog(BrowseSourceScreenModel.Dialog.Migrate(dialog.manga, dialog.duplicate)) + }, + ) + } + + is BrowseSourceScreenModel.Dialog.Migrate -> { + MigrateDialog( + oldManga = dialog.oldManga, + newManga = dialog.newManga, + screenModel = MigrateDialogScreenModel(), + onDismissRequest = onDismissRequest, + onClickTitle = { navigator.push(MangaScreen(dialog.oldManga.id)) }, + onPopScreen = { + onDismissRequest() + }, ) } is BrowseSourceScreenModel.Dialog.RemoveManga -> { @@ -274,7 +292,6 @@ data class BrowseSourceScreen( }, ) } - is BrowseSourceScreenModel.Dialog.Migrate -> {} else -> {} } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt index a7e8a26ea..dff062503 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceScreenModel.kt @@ -345,7 +345,7 @@ class BrowseSourceScreenModel( val manga: Manga, val initialSelection: ImmutableList>, ) : Dialog - data class Migrate(val newManga: Manga) : Dialog + data class Migrate(val newManga: Manga, val oldManga: Manga) : Dialog } @Immutable diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt index d3578aff5..c2466ffd4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt @@ -41,6 +41,8 @@ import eu.kanade.presentation.util.isTabletUi import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.isLocalOrStub import eu.kanade.tachiyomi.source.online.HttpSource +import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateDialog +import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateDialogScreenModel import eu.kanade.tachiyomi.ui.browse.migration.search.MigrateSearchScreen import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceScreen import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchScreen @@ -191,11 +193,28 @@ class MangaScreen( }, ) } - is MangaScreenModel.Dialog.DuplicateManga -> DuplicateMangaDialog( - onDismissRequest = onDismissRequest, - onConfirm = { screenModel.toggleFavorite(onRemoved = {}, checkDuplicate = false) }, - onOpenManga = { navigator.push(MangaScreen(dialog.duplicate.id)) }, - ) + + is MangaScreenModel.Dialog.DuplicateManga -> { + DuplicateMangaDialog( + onDismissRequest = onDismissRequest, + onConfirm = { screenModel.toggleFavorite(onRemoved = {}, checkDuplicate = false) }, + onOpenManga = { navigator.push(MangaScreen(dialog.duplicate.id)) }, + onMigrate = { + screenModel.showMigrateDialog(dialog.duplicate) + }, + ) + } + + is MangaScreenModel.Dialog.Migrate -> { + MigrateDialog( + oldManga = dialog.oldManga, + newManga = dialog.newManga, + screenModel = MigrateDialogScreenModel(), + onDismissRequest = onDismissRequest, + onClickTitle = { navigator.push(MangaScreen(dialog.oldManga.id)) }, + onPopScreen = { navigator.replace(MangaScreen(dialog.newManga.id)) }, + ) + } MangaScreenModel.Dialog.SettingsSheet -> ChapterSettingsDialog( onDismissRequest = onDismissRequest, manga = successState.manga, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt index 26ad3a7fc..14cb037df 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt @@ -1003,6 +1003,7 @@ class MangaScreenModel( ) : Dialog data class DeleteChapters(val chapters: List) : Dialog data class DuplicateManga(val manga: Manga, val duplicate: Manga) : Dialog + data class Migrate(val newManga: Manga, val oldManga: Manga) : Dialog data class SetFetchInterval(val manga: Manga) : Dialog data object SettingsSheet : Dialog data object TrackSheet : Dialog @@ -1029,6 +1030,11 @@ class MangaScreenModel( updateSuccessState { it.copy(dialog = Dialog.FullCover) } } + fun showMigrateDialog(duplicate: Manga) { + val manga = successState?.manga ?: return + updateSuccessState { it.copy(dialog = Dialog.Migrate(newManga = manga, oldManga = duplicate)) } + } + fun setExcludedScanlators(excludedScanlators: Set) { screenModelScope.launchIO { setExcludedScanlators.await(mangaId, excludedScanlators) diff --git a/i18n/src/commonMain/resources/MR/base/strings.xml b/i18n/src/commonMain/resources/MR/base/strings.xml index 119e82ecd..7fc1832ca 100644 --- a/i18n/src/commonMain/resources/MR/base/strings.xml +++ b/i18n/src/commonMain/resources/MR/base/strings.xml @@ -160,6 +160,8 @@ Refresh Start downloading now Not now + Add anyway + Migrate existing entry Loading… From 70c2443e82161378a3f653bac110767370b62c46 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Fri, 22 Mar 2024 19:24:41 +0600 Subject: [PATCH 122/212] Add reference to compose compiler in `compose.versions.toml` so renovate can catch it --- gradle/compose.versions.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 54a808a29..f2e8c84e5 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -4,6 +4,8 @@ compose-bom = "2024.02.00-alpha02" accompanist = "0.35.0-alpha" [libraries] +compiler = { module = "androidx.compose.compiler:compiler", version.ref = "compiler" } + activity = "androidx.activity:activity-compose:1.8.2" bom = { group = "dev.chrisbanes.compose", name = "compose-bom", version.ref = "compose-bom" } foundation = { module = "androidx.compose.foundation:foundation" } From b3ca097e5a0a2feb582952f88a369f5a614c593f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Mar 2024 19:35:28 +0600 Subject: [PATCH 123/212] chore(deps): update kotlin (#499) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/compose.versions.toml | 2 +- gradle/kotlinx.versions.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index f2e8c84e5..5b629ff6f 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,5 +1,5 @@ [versions] -compiler = "1.5.10" +compiler = "1.5.11" compose-bom = "2024.02.00-alpha02" accompanist = "0.35.0-alpha" diff --git a/gradle/kotlinx.versions.toml b/gradle/kotlinx.versions.toml index 0cdec4d59..4c8871cd9 100644 --- a/gradle/kotlinx.versions.toml +++ b/gradle/kotlinx.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin_version = "1.9.22" +kotlin_version = "1.9.23" serialization_version = "1.6.3" xml_serialization_version = "0.86.3" From 3838dbcf0805a9688ad6b0a03dc44f64de855e12 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Mar 2024 23:05:13 +0600 Subject: [PATCH 124/212] chore(deps): update dependency gradle to v8.7 (#567) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.jar | Bin 63375 -> 43453 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 17 +++++++++-------- gradlew.bat | 20 ++++++++++---------- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 033e24c4cdf41af1ab109bc7f253b2b887023340..e6441136f3d4ba8a0da8d277868979cfbc8ad796 100644 GIT binary patch literal 43453 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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
NnX)EH+Nua)3Y(c z&{(nX_ht=QbJ%DzAya}!&uNu!4V0xI)QE$SY__m)SAKcN0P(&JcoK*Lxr@P zY&P=}&B3*UWNlc|&$Oh{BEqwK2+N2U$4WB7Fd|aIal`FGANUa9E-O)!gV`((ZGCc$ zBJA|FFrlg~9OBp#f7aHodCe{6= zay$6vN~zj1ddMZ9gQ4p32(7wD?(dE>KA2;SOzXRmPBiBc6g`eOsy+pVcHu=;Yd8@{ zSGgXf@%sKKQz~;!J;|2fC@emm#^_rnO0esEn^QxXgJYd`#FPWOUU5b;9eMAF zZhfiZb|gk8aJIw*YLp4!*(=3l8Cp{(%p?ho22*vN9+5NLV0TTazNY$B5L6UKUrd$n zjbX%#m7&F#U?QNOBXkiiWB*_tk+H?N3`vg;1F-I+83{M2!8<^nydGr5XX}tC!10&e z7D36bLaB56WrjL&HiiMVtpff|K%|*{t*ltt^5ood{FOG0<>k&1h95qPio)2`eL${YAGIx(b4VN*~nKn6E~SIQUuRH zQ+5zP6jfnP$S0iJ@~t!Ai3o`X7biohli;E zT#yXyl{bojG@-TGZzpdVDXhbmF%F9+-^YSIv|MT1l3j zrxOFq>gd2%U}?6}8mIj?M zc077Zc9fq(-)4+gXv?Az26IO6eV`RAJz8e3)SC7~>%rlzDwySVx*q$ygTR5kW2ds- z!HBgcq0KON9*8Ff$X0wOq$`T7ml(@TF)VeoF}x1OttjuVHn3~sHrMB++}f7f9H%@f z=|kP_?#+fve@{0MlbkC9tyvQ_R?lRdRJ@$qcB(8*jyMyeME5ns6ypVI1Xm*Zr{DuS zZ!1)rQfa89c~;l~VkCiHI|PCBd`S*2RLNQM8!g9L6?n`^evQNEwfO@&JJRme+uopQX0%Jo zgd5G&#&{nX{o?TQwQvF1<^Cg3?2co;_06=~Hcb6~4XWpNFL!WU{+CK;>gH%|BLOh7@!hsa(>pNDAmpcuVO-?;Bic17R}^|6@8DahH)G z!EmhsfunLL|3b=M0MeK2vqZ|OqUqS8npxwge$w-4pFVXFq$_EKrZY?BuP@Az@(k`L z`ViQBSk`y+YwRT;&W| z2e3UfkCo^uTA4}Qmmtqs+nk#gNr2W4 zTH%hhErhB)pkXR{B!q5P3-OM+M;qu~f>}IjtF%>w{~K-0*jPVLl?Chz&zIdxp}bjx zStp&Iufr58FTQ36AHU)0+CmvaOpKF;W@sMTFpJ`j;3d)J_$tNQI^c<^1o<49Z(~K> z;EZTBaVT%14(bFw2ob@?JLQ2@(1pCdg3S%E4*dJ}dA*v}_a4_P(a`cHnBFJxNobAv zf&Zl-Yt*lhn-wjZsq<9v-IsXxAxMZ58C@e0!rzhJ+D@9^3~?~yllY^s$?&oNwyH!#~6x4gUrfxplCvK#!f z$viuszW>MFEcFL?>ux*((!L$;R?xc*myjRIjgnQX79@UPD$6Dz0jutM@7h_pq z0Zr)#O<^y_K6jfY^X%A-ip>P%3saX{!v;fxT-*0C_j4=UMH+Xth(XVkVGiiKE#f)q z%Jp=JT)uy{&}Iq2E*xr4YsJ5>w^=#-mRZ4vPXpI6q~1aFwi+lQcimO45V-JXP;>(Q zo={U`{=_JF`EQj87Wf}{Qy35s8r1*9Mxg({CvOt}?Vh9d&(}iI-quvs-rm~P;eRA@ zG5?1HO}puruc@S{YNAF3vmUc2B4!k*yi))<5BQmvd3tr}cIs#9)*AX>t`=~{f#Uz0 z0&Nk!7sSZwJe}=)-R^$0{yeS!V`Dh7w{w5rZ9ir!Z7Cd7dwZcK;BT#V0bzTt>;@Cl z#|#A!-IL6CZ@eHH!CG>OO8!%G8&8t4)Ro@}USB*k>oEUo0LsljsJ-%5Mo^MJF2I8- z#v7a5VdJ-Cd%(a+y6QwTmi+?f8Nxtm{g-+WGL>t;s#epv7ug>inqimZCVm!uT5Pf6 ziEgQt7^%xJf#!aPWbuC_3Nxfb&CFbQy!(8ANpkWLI4oSnH?Q3f?0k1t$3d+lkQs{~(>06l&v|MpcFsyAv zin6N!-;pggosR*vV=DO(#+}4ps|5$`udE%Kdmp?G7B#y%H`R|i8skKOd9Xzx8xgR$>Zo2R2Ytktq^w#ul4uicxW#{ zFjG_RNlBroV_n;a7U(KIpcp*{M~e~@>Q#Av90Jc5v%0c>egEdY4v3%|K1XvB{O_8G zkTWLC>OZKf;XguMH2-Pw{BKbFzaY;4v2seZV0>^7Q~d4O=AwaPhP3h|!hw5aqOtT@ z!SNz}$of**Bl3TK209@F=Tn1+mgZa8yh(Png%Zd6Mt}^NSjy)etQrF zme*llAW=N_8R*O~d2!apJnF%(JcN??=`$qs3Y+~xs>L9x`0^NIn!8mMRFA_tg`etw z3k{9JAjnl@ygIiJcNHTy02GMAvBVqEss&t2<2mnw!; zU`J)0>lWiqVqo|ex7!+@0i>B~BSU1A_0w#Ee+2pJx0BFiZ7RDHEvE*ptc9md(B{&+ zKE>TM)+Pd>HEmdJao7U@S>nL(qq*A)#eLOuIfAS@j`_sK0UEY6OAJJ-kOrHG zjHx`g!9j*_jRcJ%>CE9K2MVf?BUZKFHY?EpV6ai7sET-tqk=nDFh-(65rhjtlKEY% z@G&cQ<5BKatfdA1FKuB=i>CCC5(|9TMW%K~GbA4}80I5%B}(gck#Wlq@$nO3%@QP_ z8nvPkJFa|znk>V92cA!K1rKtr)skHEJD;k8P|R8RkCq1Rh^&}Evwa4BUJz2f!2=MH zo4j8Y$YL2313}H~F7@J7mh>u%556Hw0VUOz-Un@ZASCL)y8}4XXS`t1AC*^>PLwIc zUQok5PFS=*#)Z!3JZN&eZ6ZDP^-c@StY*t20JhCnbMxXf=LK#;`4KHEqMZ-Ly9KsS zI2VUJGY&PmdbM+iT)zek)#Qc#_i4uH43 z@T5SZBrhNCiK~~esjsO9!qBpaWK<`>!-`b71Y5ReXQ4AJU~T2Njri1CEp5oKw;Lnm)-Y@Z3sEY}XIgSy%xo=uek(kAAH5MsV$V3uTUsoTzxp_rF=tx zV07vlJNKtJhCu`b}*#m&5LV4TAE&%KtHViDAdv#c^x`J7bg z&N;#I2GkF@SIGht6p-V}`!F_~lCXjl1BdTLIjD2hH$J^YFN`7f{Q?OHPFEM$65^!u zNwkelo*5+$ZT|oQ%o%;rBX$+?xhvjb)SHgNHE_yP%wYkkvXHS{Bf$OiKJ5d1gI0j< zF6N}Aq=(WDo(J{e-uOecxPD>XZ@|u-tgTR<972`q8;&ZD!cep^@B5CaqFz|oU!iFj zU0;6fQX&~15E53EW&w1s9gQQ~Zk16X%6 zjG`j0yq}4deX2?Tr(03kg>C(!7a|b9qFI?jcE^Y>-VhudI@&LI6Qa}WQ>4H_!UVyF z((cm&!3gmq@;BD#5P~0;_2qgZhtJS|>WdtjY=q zLnHH~Fm!cxw|Z?Vw8*~?I$g#9j&uvgm7vPr#&iZgPP~v~BI4jOv;*OQ?jYJtzO<^y z7-#C={r7CO810!^s(MT!@@Vz_SVU)7VBi(e1%1rvS!?PTa}Uv`J!EP3s6Y!xUgM^8 z4f!fq<3Wer_#;u!5ECZ|^c1{|q_lh3m^9|nsMR1#Qm|?4Yp5~|er2?W^7~cl;_r4WSme_o68J9p03~Hc%X#VcX!xAu%1`R!dfGJCp zV*&m47>s^%Ib0~-2f$6oSgn3jg8m%UA;ArcdcRyM5;}|r;)?a^D*lel5C`V5G=c~k zy*w_&BfySOxE!(~PI$*dwG><+-%KT5p?whOUMA*k<9*gi#T{h3DAxzAPxN&Xws8o9Cp*`PA5>d9*Z-ynV# z9yY*1WR^D8|C%I@vo+d8r^pjJ$>eo|j>XiLWvTWLl(^;JHCsoPgem6PvegHb-OTf| zvTgsHSa;BkbG=(NgPO|CZu9gUCGr$8*EoH2_Z#^BnxF0yM~t`|9ws_xZ8X8iZYqh! zAh;HXJ)3P&)Q0(&F>!LN0g#bdbis-cQxyGn9Qgh`q+~49Fqd2epikEUw9caM%V6WgP)532RMRW}8gNS%V%Hx7apSz}tn@bQy!<=lbhmAH=FsMD?leawbnP5BWM0 z5{)@EEIYMu5;u)!+HQWhQ;D3_Cm_NADNeb-f56}<{41aYq8p4=93d=-=q0Yx#knGYfXVt z+kMxlus}t2T5FEyCN~!}90O_X@@PQpuy;kuGz@bWft%diBTx?d)_xWd_-(!LmVrh**oKg!1CNF&LX4{*j|) zIvjCR0I2UUuuEXh<9}oT_zT#jOrJAHNLFT~Ilh9hGJPI1<5`C-WA{tUYlyMeoy!+U zhA#=p!u1R7DNg9u4|QfED-2TuKI}>p#2P9--z;Bbf4Op*;Q9LCbO&aL2i<0O$ByoI z!9;Ght733FC>Pz>$_mw(F`zU?`m@>gE`9_p*=7o=7av`-&ifU(^)UU`Kg3Kw`h9-1 z6`e6+im=|m2v`pN(2dE%%n8YyQz;#3Q-|x`91z?gj68cMrHl}C25|6(_dIGk*8cA3 zRHB|Nwv{@sP4W+YZM)VKI>RlB`n=Oj~Rzx~M+Khz$N$45rLn6k1nvvD^&HtsMA4`s=MmuOJID@$s8Ph4E zAmSV^+s-z8cfv~Yd(40Sh4JG#F~aB>WFoX7ykaOr3JaJ&Lb49=B8Vk-SQT9%7TYhv z?-Pprt{|=Y5ZQ1?od|A<_IJU93|l4oAfBm?3-wk{O<8ea+`}u%(kub(LFo2zFtd?4 zwpN|2mBNywv+d^y_8#<$r>*5+$wRTCygFLcrwT(qc^n&@9r+}Kd_u@Ithz(6Qb4}A zWo_HdBj#V$VE#l6pD0a=NfB0l^6W^g`vm^sta>Tly?$E&{F?TTX~DsKF~poFfmN%2 z4x`Dc{u{Lkqz&y!33;X}weD}&;7p>xiI&ZUb1H9iD25a(gI|`|;G^NwJPv=1S5e)j z;U;`?n}jnY6rA{V^ zxTd{bK)Gi^odL3l989DQlN+Zs39Xe&otGeY(b5>rlIqfc7Ap4}EC?j<{M=hlH{1+d zw|c}}yx88_xQr`{98Z!d^FNH77=u(p-L{W6RvIn40f-BldeF-YD>p6#)(Qzf)lfZj z?3wAMtPPp>vMehkT`3gToPd%|D8~4`5WK{`#+}{L{jRUMt zrFz+O$C7y8$M&E4@+p+oV5c%uYzbqd2Y%SSgYy#xh4G3hQv>V*BnuKQhBa#=oZB~w{azUB+q%bRe_R^ z>fHBilnRTUfaJ201czL8^~Ix#+qOHSO)A|xWLqOxB$dT2W~)e-r9;bm=;p;RjYahB z*1hegN(VKK+ztr~h1}YP@6cfj{e#|sS`;3tJhIJK=tVJ-*h-5y9n*&cYCSdg#EHE# zSIx=r#qOaLJoVVf6v;(okg6?*L_55atl^W(gm^yjR?$GplNP>BZsBYEf_>wM0Lc;T zhf&gpzOWNxS>m+mN92N0{;4uw`P+9^*|-1~$uXpggj4- z^SFc4`uzj2OwdEVT@}Q`(^EcQ_5(ZtXTql*yGzdS&vrS_w>~~ra|Nb5abwf}Y!uq6R5f&6g2ge~2p(%c< z@O)cz%%rr4*cRJ5f`n@lvHNk@lE1a*96Kw6lJ~B-XfJW%?&-y?;E&?1AacU@`N`!O z6}V>8^%RZ7SQnZ-z$(jsX`amu*5Fj8g!3RTRwK^`2_QHe;_2y_n|6gSaGyPmI#kA0sYV<_qOZc#-2BO%hX)f$s-Z3xlI!ub z^;3ru11DA`4heAu%}HIXo&ctujzE2!6DIGE{?Zs>2}J+p&C$rc7gJC35gxhflorvsb%sGOxpuWhF)dL_&7&Z99=5M0b~Qa;Mo!j&Ti_kXW!86N%n= zSC@6Lw>UQ__F&+&Rzv?gscwAz8IP!n63>SP)^62(HK98nGjLY2*e^OwOq`3O|C92? z;TVhZ2SK%9AGW4ZavTB9?)mUbOoF`V7S=XM;#3EUpR+^oHtdV!GK^nXzCu>tpR|89 zdD{fnvCaN^^LL%amZ^}-E+214g&^56rpdc@yv0b<3}Ys?)f|fXN4oHf$six)-@<;W&&_kj z-B}M5U*1sb4)77aR=@%I?|Wkn-QJVuA96an25;~!gq(g1@O-5VGo7y&E_srxL6ZfS z*R%$gR}dyONgju*D&?geiSj7SZ@ftyA|}(*Y4KbvU!YLsi1EDQQCnb+-cM=K1io78o!v*);o<XwjaQH%)uIP&Zm?)Nfbfn;jIr z)d#!$gOe3QHp}2NBak@yYv3m(CPKkwI|{;d=gi552u?xj9ObCU^DJFQp4t4e1tPzM zvsRIGZ6VF+{6PvqsplMZWhz10YwS={?`~O0Ec$`-!klNUYtzWA^f9m7tkEzCy<_nS z=&<(awFeZvt51>@o_~>PLs05CY)$;}Oo$VDO)?l-{CS1Co=nxjqben*O1BR>#9`0^ zkwk^k-wcLCLGh|XLjdWv0_Hg54B&OzCE^3NCP}~OajK-LuRW53CkV~Su0U>zN%yQP zH8UH#W5P3-!ToO-2k&)}nFe`t+mdqCxxAHgcifup^gKpMObbox9LFK;LP3}0dP-UW z?Zo*^nrQ6*$FtZ(>kLCc2LY*|{!dUn$^RW~m9leoF|@Jy|M5p-G~j%+P0_#orRKf8 zvuu5<*XO!B?1E}-*SY~MOa$6c%2cM+xa8}_8x*aVn~57v&W(0mqN1W`5a7*VN{SUH zXz98DDyCnX2EPl-`Lesf`=AQT%YSDb`$%;(jUTrNen$NPJrlpPDP}prI>Ml!r6bCT;mjsg@X^#&<}CGf0JtR{Ecwd&)2zuhr#nqdgHj+g2n}GK9CHuwO zk>oZxy{vcOL)$8-}L^iVfJHAGfwN$prHjYV0ju}8%jWquw>}_W6j~m<}Jf!G?~r5&Rx)!9JNX!ts#SGe2HzobV5); zpj@&`cNcO&q+%*<%D7za|?m5qlmFK$=MJ_iv{aRs+BGVrs)98BlN^nMr{V_fcl_;jkzRju+c-y?gqBC_@J0dFLq-D9@VN&-`R9U;nv$Hg?>$oe4N&Ht$V_(JR3TG^! zzJsbQbi zFE6-{#9{G{+Z}ww!ycl*7rRdmU#_&|DqPfX3CR1I{Kk;bHwF6jh0opI`UV2W{*|nn zf_Y@%wW6APb&9RrbEN=PQRBEpM(N1w`81s=(xQj6 z-eO0k9=Al|>Ej|Mw&G`%q8e$2xVz1v4DXAi8G};R$y)ww638Y=9y$ZYFDM$}vzusg zUf+~BPX>(SjA|tgaFZr_e0{)+z9i6G#lgt=F_n$d=beAt0Sa0a7>z-?vcjl3e+W}+ z1&9=|vC=$co}-Zh*%3588G?v&U7%N1Qf-wNWJ)(v`iO5KHSkC5&g7CrKu8V}uQGcfcz zmBz#Lbqwqy#Z~UzHgOQ;Q-rPxrRNvl(&u6ts4~0=KkeS;zqURz%!-ERppmd%0v>iRlEf+H$yl{_8TMJzo0 z>n)`On|7=WQdsqhXI?#V{>+~}qt-cQbokEbgwV3QvSP7&hK4R{Z{aGHVS3;+h{|Hz z6$Js}_AJr383c_+6sNR|$qu6dqHXQTc6?(XWPCVZv=)D#6_;D_8P-=zOGEN5&?~8S zl5jQ?NL$c%O)*bOohdNwGIKM#jSAC?BVY={@A#c9GmX0=T(0G}xs`-%f3r=m6-cpK z!%waekyAvm9C3%>sixdZj+I(wQlbB4wv9xKI*T13DYG^T%}zZYJ|0$Oj^YtY+d$V$ zAVudSc-)FMl|54n=N{BnZTM|!>=bhaja?o7s+v1*U$!v!qQ%`T-6fBvmdPbVmro&d zk07TOp*KuxRUSTLRrBj{mjsnF8`d}rMViY8j`jo~Hp$fkv9F_g(jUo#Arp;Xw0M$~ zRIN!B22~$kx;QYmOkos@%|5k)!QypDMVe}1M9tZfkpXKGOxvKXB!=lo`p?|R1l=tA zp(1}c6T3Fwj_CPJwVsYtgeRKg?9?}%oRq0F+r+kdB=bFUdVDRPa;E~~>2$w}>O>v=?|e>#(-Lyx?nbg=ckJ#5U6;RT zNvHhXk$P}m9wSvFyU3}=7!y?Y z=fg$PbV8d7g25&-jOcs{%}wTDKm>!Vk);&rr;O1nvO0VrU&Q?TtYVU=ir`te8SLlS zKSNmV=+vF|ATGg`4$N1uS|n??f}C_4Sz!f|4Ly8#yTW-FBfvS48Tef|-46C(wEO_%pPhUC5$-~Y?!0vFZ^Gu`x=m7X99_?C-`|h zfmMM&Y@zdfitA@KPw4Mc(YHcY1)3*1xvW9V-r4n-9ZuBpFcf{yz+SR{ zo$ZSU_|fgwF~aakGr(9Be`~A|3)B=9`$M-TWKipq-NqRDRQc}ABo*s_5kV%doIX7LRLRau_gd@Rd_aLFXGSU+U?uAqh z8qusWWcvgQ&wu{|sRXmv?sl=xc<$6AR$+cl& zFNh5q1~kffG{3lDUdvEZu5c(aAG~+64FxdlfwY^*;JSS|m~CJusvi-!$XR`6@XtY2 znDHSz7}_Bx7zGq-^5{stTRy|I@N=>*y$zz>m^}^{d&~h;0kYiq8<^Wq7Dz0w31ShO^~LUfW6rfitR0(=3;Uue`Y%y@ex#eKPOW zO~V?)M#AeHB2kovn1v=n^D?2{2jhIQd9t|_Q+c|ZFaWt+r&#yrOu-!4pXAJuxM+Cx z*H&>eZ0v8Y`t}8{TV6smOj=__gFC=eah)mZt9gwz>>W$!>b3O;Rm^Ig*POZP8Rl0f zT~o=Nu1J|lO>}xX&#P58%Yl z83`HRs5#32Qm9mdCrMlV|NKNC+Z~ z9OB8xk5HJ>gBLi+m@(pvpw)1(OaVJKs*$Ou#@Knd#bk+V@y;YXT?)4eP9E5{J%KGtYinNYJUH9PU3A}66c>Xn zZ{Bn0<;8$WCOAL$^NqTjwM?5d=RHgw3!72WRo0c;+houoUA@HWLZM;^U$&sycWrFd zE7ekt9;kb0`lps{>R(}YnXlyGY}5pPd9zBpgXeJTY_jwaJGSJQC#-KJqmh-;ad&F- z-Y)E>!&`Rz!HtCz>%yOJ|v(u7P*I$jqEY3}(Z-orn4 zlI?CYKNl`6I){#2P1h)y(6?i;^z`N3bxTV%wNvQW+eu|x=kbj~s8rhCR*0H=iGkSj zk23lr9kr|p7#qKL=UjgO`@UnvzU)`&fI>1Qs7ubq{@+lK{hH* zvl6eSb9%yngRn^T<;jG1SVa)eA>T^XX=yUS@NCKpk?ovCW1D@!=@kn;l_BrG;hOTC z6K&H{<8K#dI(A+zw-MWxS+~{g$tI7|SfP$EYKxA}LlVO^sT#Oby^grkdZ^^lA}uEF zBSj$weBJG{+Bh@Yffzsw=HyChS(dtLE3i*}Zj@~!_T-Ay7z=B)+*~3|?w`Zd)Co2t zC&4DyB!o&YgSw+fJn6`sn$e)29`kUwAc+1MND7YjV%lO;H2}fNy>hD#=gT ze+-aFNpyKIoXY~Vq-}OWPBe?Rfu^{ps8>Xy%42r@RV#*QV~P83jdlFNgkPN=T|Kt7 zV*M`Rh*30&AWlb$;ae130e@}Tqi3zx2^JQHpM>j$6x`#{mu%tZlwx9Gj@Hc92IuY* zarmT|*d0E~vt6<+r?W^UW0&#U&)8B6+1+;k^2|FWBRP9?C4Rk)HAh&=AS8FS|NQaZ z2j!iZ)nbEyg4ZTp-zHwVlfLC~tXIrv(xrP8PAtR{*c;T24ycA-;auWsya-!kF~CWZ zw_uZ|%urXgUbc@x=L=_g@QJ@m#5beS@6W195Hn7>_}z@Xt{DIEA`A&V82bc^#!q8$ zFh?z_Vn|ozJ;NPd^5uu(9tspo8t%&-U9Ckay-s@DnM*R5rtu|4)~e)`z0P-sy?)kc zs_k&J@0&0!q4~%cKL)2l;N*T&0;mqX5T{Qy60%JtKTQZ-xb%KOcgqwJmb%MOOKk7N zgq})R_6**{8A|6H?fO+2`#QU)p$Ei2&nbj6TpLSIT^D$|`TcSeh+)}VMb}LmvZ{O| ze*1IdCt3+yhdYVxcM)Q_V0bIXLgr6~%JS<<&dxIgfL=Vnx4YHuU@I34JXA|+$_S3~ zy~X#gO_X!cSs^XM{yzDGNM>?v(+sF#<0;AH^YrE8smx<36bUsHbN#y57K8WEu(`qHvQ6cAZPo=J5C(lSmUCZ57Rj6cx!e^rfaI5%w}unz}4 zoX=nt)FVNV%QDJH`o!u9olLD4O5fl)xp+#RloZlaA92o3x4->?rB4`gS$;WO{R;Z3>cG3IgFX2EA?PK^M}@%1%A;?f6}s&CV$cIyEr#q5;yHdNZ9h{| z-=dX+a5elJoDo?Eq&Og!nN6A)5yYpnGEp}?=!C-V)(*~z-+?kY1Q7qs#Rsy%hu_60rdbB+QQNr?S1 z?;xtjUv|*E3}HmuNyB9aFL5H~3Ho0UsmuMZELp1a#CA1g`P{-mT?BchuLEtK}!QZ=3AWakRu~?f9V~3F;TV`5%9Pcs_$gq&CcU}r8gOO zC2&SWPsSG{&o-LIGTBqp6SLQZPvYKp$$7L4WRRZ0BR$Kf0I0SCFkqveCp@f)o8W)! z$%7D1R`&j7W9Q9CGus_)b%+B#J2G;l*FLz#s$hw{BHS~WNLODV#(!u_2Pe&tMsq={ zdm7>_WecWF#D=?eMjLj=-_z`aHMZ=3_-&E8;ibPmM}61i6J3is*=dKf%HC>=xbj4$ zS|Q-hWQ8T5mWde6h@;mS+?k=89?1FU<%qH9B(l&O>k|u_aD|DY*@~(`_pb|B#rJ&g zR0(~(68fpUPz6TdS@4JT5MOPrqDh5_H(eX1$P2SQrkvN8sTxwV>l0)Qq z0pzTuvtEAKRDkKGhhv^jk%|HQ1DdF%5oKq5BS>szk-CIke{%js?~%@$uaN3^Uz6Wf z_iyx{bZ(;9y4X&>LPV=L=d+A}7I4GkK0c1Xts{rrW1Q7apHf-))`BgC^0^F(>At1* za@e7{lq%yAkn*NH8Q1{@{lKhRg*^TfGvv!Sn*ed*x@6>M%aaqySxR|oNadYt1mpUZ z6H(rupHYf&Z z29$5g#|0MX#aR6TZ$@eGxxABRKakDYtD%5BmKp;HbG_ZbT+=81E&=XRk6m_3t9PvD zr5Cqy(v?gHcYvYvXkNH@S#Po~q(_7MOuCAB8G$a9BC##gw^5mW16cML=T=ERL7wsk zzNEayTG?mtB=x*wc@ifBCJ|irFVMOvH)AFRW8WE~U()QT=HBCe@s$dA9O!@`zAAT) zaOZ7l6vyR+Nk_OOF!ZlZmjoImKh)dxFbbR~z(cMhfeX1l7S_`;h|v3gI}n9$sSQ>+3@AFAy9=B_y$)q;Wdl|C-X|VV3w8 z2S#>|5dGA8^9%Bu&fhmVRrTX>Z7{~3V&0UpJNEl0=N32euvDGCJ>#6dUSi&PxFW*s zS`}TB>?}H(T2lxBJ!V#2taV;q%zd6fOr=SGHpoSG*4PDaiG0pdb5`jelVipkEk%FV zThLc@Hc_AL1#D&T4D=w@UezYNJ%0=f3iVRuVL5H?eeZM}4W*bomebEU@e2d`M<~uW zf#Bugwf`VezG|^Qbt6R_=U0}|=k;mIIakz99*>FrsQR{0aQRP6ko?5<7bkDN8evZ& zB@_KqQG?ErKL=1*ZM9_5?Pq%lcS4uLSzN(Mr5=t6xHLS~Ym`UgM@D&VNu8e?_=nSFtF$u@hpPSmI4Vo_t&v?>$~K4y(O~Rb*(MFy_igM7 z*~yYUyR6yQgzWnWMUgDov!!g=lInM+=lOmOk4L`O?{i&qxy&D*_qorRbDwj6?)!ef z#JLd7F6Z2I$S0iYI={rZNk*<{HtIl^mx=h>Cim*04K4+Z4IJtd*-)%6XV2(MCscPiw_a+y*?BKbTS@BZ3AUao^%Zi#PhoY9Vib4N>SE%4>=Jco0v zH_Miey{E;FkdlZSq)e<{`+S3W=*ttvD#hB8w=|2aV*D=yOV}(&p%0LbEWH$&@$X3x~CiF-?ejQ*N+-M zc8zT@3iwkdRT2t(XS`d7`tJQAjRmKAhiw{WOqpuvFp`i@Q@!KMhwKgsA}%@sw8Xo5Y=F zhRJZg)O4uqNWj?V&&vth*H#je6T}}p_<>!Dr#89q@uSjWv~JuW(>FqoJ5^ho0%K?E z9?x_Q;kmcsQ@5=}z@tdljMSt9-Z3xn$k)kEjK|qXS>EfuDmu(Z8|(W?gY6-l z@R_#M8=vxKMAoi&PwnaIYw2COJM@atcgfr=zK1bvjW?9B`-+Voe$Q+H$j!1$Tjn+* z&LY<%)L@;zhnJlB^Og6I&BOR-m?{IW;tyYC%FZ!&Z>kGjHJ6cqM-F z&19n+e1=9AH1VrVeHrIzqlC`w9=*zfmrerF?JMzO&|Mmv;!4DKc(sp+jy^Dx?(8>1 zH&yS_4yL7m&GWX~mdfgH*AB4{CKo;+egw=PrvkTaoBU+P-4u?E|&!c z)DKc;>$$B6u*Zr1SjUh2)FeuWLWHl5TH(UHWkf zLs>7px!c5n;rbe^lO@qlYLzlDVp(z?6rPZel=YB)Uv&n!2{+Mb$-vQl=xKw( zve&>xYx+jW_NJh!FV||r?;hdP*jOXYcLCp>DOtJ?2S^)DkM{{Eb zS$!L$e_o0(^}n3tA1R3-$SNvgBq;DOEo}fNc|tB%%#g4RA3{|euq)p+xd3I8^4E&m zFrD%}nvG^HUAIKe9_{tXB;tl|G<%>yk6R;8L2)KUJw4yHJXUOPM>(-+jxq4R;z8H#>rnJy*)8N+$wA$^F zN+H*3t)eFEgxLw+Nw3};4WV$qj&_D`%ADV2%r zJCPCo%{=z7;`F98(us5JnT(G@sKTZ^;2FVitXyLe-S5(hV&Ium+1pIUB(CZ#h|g)u zSLJJ<@HgrDiA-}V_6B^x1>c9B6%~847JkQ!^KLZ2skm;q*edo;UA)~?SghG8;QbHh z_6M;ouo_1rq9=x$<`Y@EA{C%6-pEV}B(1#sDoe_e1s3^Y>n#1Sw;N|}8D|s|VPd+g z-_$QhCz`vLxxrVMx3ape1xu3*wjx=yKSlM~nFgkNWb4?DDr*!?U)L_VeffF<+!j|b zZ$Wn2$TDv3C3V@BHpSgv3JUif8%hk%OsGZ=OxH@8&4`bbf$`aAMchl^qN>Eyu3JH} z9-S!x8-s4fE=lad%Pkp8hAs~u?|uRnL48O|;*DEU! zuS0{cpk%1E0nc__2%;apFsTm0bKtd&A0~S3Cj^?72-*Owk3V!ZG*PswDfS~}2<8le z5+W^`Y(&R)yVF*tU_s!XMcJS`;(Tr`J0%>p=Z&InR%D3@KEzzI+-2)HK zuoNZ&o=wUC&+*?ofPb0a(E6(<2Amd6%uSu_^-<1?hsxs~0K5^f(LsGqgEF^+0_H=uNk9S0bb!|O8d?m5gQjUKevPaO+*VfSn^2892K~%crWM8+6 z25@V?Y@J<9w%@NXh-2!}SK_(X)O4AM1-WTg>sj1{lj5@=q&dxE^9xng1_z9w9DK>| z6Iybcd0e zyi;Ew!KBRIfGPGytQ6}z}MeXCfLY0?9%RiyagSp_D1?N&c{ zyo>VbJ4Gy`@Fv+5cKgUgs~na$>BV{*em7PU3%lloy_aEovR+J7TfQKh8BJXyL6|P8un-Jnq(ghd!_HEOh$zlv2$~y3krgeH;9zC}V3f`uDtW(%mT#944DQa~^8ZI+zAUu4U(j0YcDfKR$bK#gvn_{JZ>|gZ5+)u?T$w7Q%F^;!Wk?G z(le7r!ufT*cxS}PR6hIVtXa)i`d$-_1KkyBU>qmgz-=T};uxx&sKgv48akIWQ89F{ z0XiY?WM^~;|T8zBOr zs#zuOONzH?svv*jokd5SK8wG>+yMC)LYL|vLqm^PMHcT=`}V$=nIRHe2?h)8WQa6O zPAU}d`1y(>kZiP~Gr=mtJLMu`i<2CspL|q2DqAgAD^7*$xzM`PU4^ga`ilE134XBQ z99P(LhHU@7qvl9Yzg$M`+dlS=x^(m-_3t|h>S}E0bcFMn=C|KamQ)=w2^e)35p`zY zRV8X?d;s^>Cof2SPR&nP3E+-LCkS0J$H!eh8~k0qo$}00b=7!H_I2O+Ro@3O$nPdm ztmbOO^B+IHzQ5w>@@@J4cKw5&^_w6s!s=H%&byAbUtczPQ7}wfTqxxtQNfn*u73Qw zGuWsrky_ajPx-5`R<)6xHf>C(oqGf_Fw|-U*GfS?xLML$kv;h_pZ@Kk$y0X(S+K80 z6^|z)*`5VUkawg}=z`S;VhZhxyDfrE0$(PMurAxl~<>lfZa>JZ288ULK7D` zl9|#L^JL}Y$j*j`0-K6kH#?bRmg#5L3iB4Z)%iF@SqT+Lp|{i`m%R-|ZE94Np7Pa5 zCqC^V3}B(FR340pmF*qaa}M}+h6}mqE~7Sh!9bDv9YRT|>vBNAqv09zXHMlcuhKD| zcjjA(b*XCIwJ33?CB!+;{)vX@9xns_b-VO{i0y?}{!sdXj1GM8+$#v>W7nw;+O_9B z_{4L;C6ol?(?W0<6taGEn1^uG=?Q3i29sE`RfYCaV$3DKc_;?HsL?D_fSYg}SuO5U zOB_f4^vZ_x%o`5|C@9C5+o=mFy@au{s)sKw!UgC&L35aH(sgDxRE2De%(%OT=VUdN ziVLEmdOvJ&5*tCMKRyXctCwQu_RH%;m*$YK&m;jtbdH#Ak~13T1^f89tn`A%QEHWs~jnY~E}p_Z$XC z=?YXLCkzVSK+Id`xZYTegb@W8_baLt-Fq`Tv|=)JPbFsKRm)4UW;yT+J`<)%#ue9DPOkje)YF2fsCilK9MIIK>p*`fkoD5nGfmLwt)!KOT+> zOFq*VZktDDyM3P5UOg`~XL#cbzC}eL%qMB=Q5$d89MKuN#$6|4gx_Jt0Gfn8w&q}%lq4QU%6#jT*MRT% zrLz~C8FYKHawn-EQWN1B75O&quS+Z81(zN)G>~vN8VwC+e+y(`>HcxC{MrJ;H1Z4k zZWuv$w_F0-Ub%MVcpIc){4PGL^I7M{>;hS?;eH!;gmcOE66z3;Z1Phqo(t zVP(Hg6q#0gIKgsg7L7WE!{Y#1nI(45tx2{$34dDd#!Z0NIyrm)HOn5W#7;f4pQci# zDW!FI(g4e668kI9{2+mLwB+=#9bfqgX%!B34V-$wwSN(_cm*^{y0jQtv*4}eO^sOV z*9xoNvX)c9isB}Tgx&ZRjp3kwhTVK?r9;n!x>^XYT z@Q^7zp{rkIs{2mUSE^2!Gf6$6;j~&4=-0cSJJDizZp6LTe8b45;{AKM%v99}{{FfC zz709%u0mC=1KXTo(=TqmZQ;c?$M3z(!xah>aywrj40sc2y3rKFw4jCq+Y+u=CH@_V zxz|qeTwa>+<|H%8Dz5u>ZI5MmjTFwXS-Fv!TDd*`>3{krWoNVx$<133`(ftS?ZPyY z&4@ah^3^i`vL$BZa>O|Nt?ucewzsF)0zX3qmM^|waXr=T0pfIb0*$AwU=?Ipl|1Y; z*Pk6{C-p4MY;j@IJ|DW>QHZQJcp;Z~?8(Q+Kk3^0qJ}SCk^*n4W zu9ZFwLHUx-$6xvaQ)SUQcYd6fF8&x)V`1bIuX@>{mE$b|Yd(qomn3;bPwnDUc0F=; zh*6_((%bqAYQWQ~odER?h>1mkL4kpb3s7`0m@rDKGU*oyF)$j~Ffd4fXV$?`f~rHf zB%Y)@5SXZvfwm10RY5X?TEo)PK_`L6qgBp=#>fO49$D zDq8Ozj0q6213tV5Qq=;fZ0$|KroY{Dz=l@lU^J)?Ko@ti20TRplXzphBi>XGx4bou zEWrkNjz0t5j!_ke{g5I#PUlEU$Km8g8TE|XK=MkU@PT4T><2OVamoK;wJ}3X0L$vX zgd7gNa359*nc)R-0!`2X@FOTB`+oETOPc=ubp5R)VQgY+5BTZZJ2?9QwnO=dnulIUF3gFn;BODC2)65)HeVd%t86sL7Rv^Y+nbn+&l z6BAJY(ETvwI)Ts$aiE8rht4KD*qNyE{8{x6R|%akbTBzw;2+6Echkt+W+`u^XX z_z&x%n5IFJ|DjL!e)upfGNX(kojugZ3I`oH1PvW`wFW_ske0j@lB9bX zO;2)`y+|!@X(fZ1<2n!Qx*)_^Ai@Cv-dF&(vnudG?0CsddG_&Wtae(n|K59ew)6St z#dj7_(Cfwzh$H$5M!$UDd8=4>IQsD3xV=lXUq($;(h*$0^yd+b{qq63f0r_de#!o_ zXDngc>zy`uor)4A^2M#U*DC~i+dc<)Tb1Tv&~Ev@oM)5iJ4Sn#8iRw16XXuV50BS7 zdBL5Mefch(&^{luE{*5qtCZk$oFr3RH=H!c3wGR=HJ(yKc_re_X9pD` zJ;uxPzUfVpgU>DSq?J;I@a+10l0ONXPcDkiYcihREt5~T5Gb}sT0+6Q;AWHl`S5dV>lv%-p9l#xNNy7ZCr%cyqHY%TZ8Q4 zbp&#ov1*$#grNG#1vgfFOLJCaNG@K|2!W&HSh@3@Y%T?3YI75bJp!VP*$*!< z;(ffNS_;@RJ`=c7yX04!u3JP*<8jeqLHVJu#WV&v6wA!OYJS4h<_}^QI&97-;=ojW zQ-1t)7wnxG*5I%U4)9$wlv5Fr;cIizft@&N+32O%B{R1POm$oap@&f| zh+5J{>U6ftv|vAeKGc|zC=kO(+l7_cLpV}-D#oUltScw})N>~JOZLU_0{Ka2e1evz z{^a*ZrLr+JUj;)K&u2CoCAXLC2=fVScI(m_p~0FmF>>&3DHziouln?;sxW`NB}cSX z8?IsJB)Z=aYRz!X=yJn$kyOWK%rCYf-YarNqKzmWu$ZvkP12b4qH zhS9Q>j<}(*frr?z<%9hl*i^#@*O2q(Z^CN)c2c z>1B~D;@YpG?G!Yk+*yn4vM4sO-_!&m6+`k|3zd;8DJnxsBYtI;W3We+FN@|tQ5EW= z!VU>jtim0Mw#iaT8t_<+qKIEB-WwE04lBd%Letbml9N!?SLrEG$nmn7&W(W`VB@5S zaY=sEw2}i@F_1P4OtEw?xj4@D6>_e=m=797#hg}f*l^`AB|Y0# z9=)o|%TZFCY$SzgSjS|8AI-%J4x}J)!IMxY3_KYze`_I=c1nmrk@E8c9?MVRu)7+Ue79|)rBX7tVB7U|w4*h(;Gi3D9le49B38`wuv zp7{4X^p+K4*$@gU(Tq3K1a#3SmYhvI42)GzG4f|u zwQFT1n_=n|jpi=70-yE9LA+d*T8u z`=VmmXJ_f6WmZveZPct$Cgu^~gFiyL>Lnpj*6ee>*0pz=t$IJ}+rE zsf@>jlcG%Wx;Cp5x)YSVvB1$yyY1l&o zvwX=D7k)Dn;ciX?Z)Pn8$flC8#m`nB&(8?RSdBvr?>T9?E$U3uIX7T?$v4dWCa46 z+&`ot8ZTEgp7G+c52oHJ8nw5}a^dwb_l%MOh(ebVj9>_koQP^$2B~eUfSbw9RY$_< z&DDWf2LW;b0ZDOaZ&2^i^g+5uTd;GwO(-bbo|P^;CNL-%?9mRmxEw~5&z=X^Rvbo^WJW=n_%*7974RY}JhFv46> zd}`2|qkd;89l}R;i~9T)V-Q%K)O=yfVKNM4Gbacc7AOd>#^&W&)Xx!Uy5!BHnp9kh z`a(7MO6+Ren#>R^D0K)1sE{Bv>}s6Rb9MT14u!(NpZOe-?4V=>qZ>}uS)!y~;jEUK z&!U7Fj&{WdgU#L0%bM}SYXRtM5z!6M+kgaMKt%3FkjWYh=#QUpt$XX1!*XkpSq-pl zhMe{muh#knk{9_V3%qdDcWDv}v)m4t9 zQhv{;} zc{}#V^N3H>9mFM8`i`0p+fN@GqX+kl|M94$BK3J-X`Hyj8r!#x6Vt(PXjn?N)qedP z=o1T^#?1^a{;bZ&x`U{f?}TMo8ToN zkHj5v|}r}wDEi7I@)Gj+S1aE-GdnLN+$hw!=DzglMaj#{qjXi_dwpr|HL(gcCXwGLEmi|{4&4#OZ4ChceA zKVd4K!D>_N=_X;{poT~4Q+!Le+ZV>=H7v1*l%w`|`Dx8{)McN@NDlQyln&N3@bFpV z_1w~O4EH3fF@IzJ9kDk@7@QctFq8FbkbaH7K$iX=bV~o#gfh?2JD6lZf(XP>~DACF)fGFt)X%-h1yY~MJU{nA5 ze2zxWMs{YdX3q5XU*9hOH0!_S24DOBA5usB+Ws$6{|AMe*joJ?RxfV}*7AKN9V*~J zK+OMcE@bTD>TG1*yc?*qGqjBN8mgg@h1cJLDv)0!WRPIkC` zZrWXrceVw;fB%3`6kq=a!pq|hFIsQ%ZSlo~)D z|64!aCnw-?>}AG|*iOl44KVf8@|joXi&|)1rB;EQWgm+iHfVbgllP$f!$Wf42%NO5b(j9Bw6L z;0dpUUK$5GX4QbMlTmLM_jJt!ur`_0~$b#BB7FL*%XFf<b__1o)Ao3rlobbN8-(T!1d-bR8D3S0@d zLI!*GMb5s~Q<&sjd}lBb8Nr0>PqE6_!3!2d(KAWFxa{hm`@u|a(%#i(#f8{BP2wbs zt+N_slWF4IF_O|{w`c~)Xvh&R{Au~CFmW#0+}MBd2~X}t9lz6*E7uAD`@EBDe$>7W zzPUkJx<`f$0VA$=>R57^(K^h86>09?>_@M(R4q($!Ck6GG@pnu-x*exAx1jOv|>KH zjNfG5pwm`E-=ydcb+3BJwuU;V&OS=6yM^4Jq{%AVqnTTLwV`AorIDD}T&jWr8pB&j28fVtk_y*JRP^t@l*($UZ z6(B^-PBNZ+z!p?+e8@$&jCv^EWLb$WO=}Scr$6SM*&~B95El~;W_0(Bvoha|uQ1T< zO$%_oLAwf1bW*rKWmlD+@CP&$ObiDy=nh1b2ejz%LO9937N{LDe7gle4i!{}I$;&Y zkexJ9Ybr+lrCmKWg&}p=`2&Gf10orS?4$VrzWidT=*6{KzOGMo?KI0>GL0{iFWc;C z+LPq%VH5g}6V@-tg2m{C!-$fapJ9y}c$U}aUmS{9#0CM*8pC|sfer!)nG7Ji>mfRh z+~6CxNb>6eWKMHBz-w2{mLLwdA7dA-qfTu^A2yG1+9s5k zcF=le_UPYG&q!t5Zd_*E_P3Cf5T6821bO`daa`;DODm8Ih8k89=RN;-asHIigj`n=ux>*f!OC5#;X5i;Q z+V!GUy0|&Y_*8k_QRUA8$lHP;GJ3UUD08P|ALknng|YY13)}!!HW@0z$q+kCH%xet zlWf@BXQ=b=4}QO5eNnN~CzWBbHGUivG=`&eWK}beuV*;?zt=P#pM*eTuy3 zP}c#}AXJ0OIaqXji78l;YrP4sQe#^pOqwZUiiN6^0RCd#D271XCbEKpk`HI0IsN^s zES7YtU#7=8gTn#lkrc~6)R9u&SX6*Jk4GFX7){E)WE?pT8a-%6P+zS6o&A#ml{$WX zABFz#i7`DDlo{34)oo?bOa4Z_lNH>n;f0nbt$JfAl~;4QY@}NH!X|A$KgMmEsd^&Y zt;pi=>AID7ROQfr;MsMtClr5b0)xo|fwhc=qk33wQ|}$@?{}qXcmECh>#kUQ-If0$ zseb{Wf4VFGLNc*Rax#P8ko*=`MwaR-DQ8L8V8r=2N{Gaips2_^cS|oC$+yScRo*uF zUO|5=?Q?{p$inDpx*t#Xyo6=s?bbN}y>NNVxj9NZCdtwRI70jxvm3!5R7yiWjREEd zDUjrsZhS|P&|Ng5r+f^kA6BNN#|Se}_GF>P6sy^e8kBrgMv3#vk%m}9PCwUWJg-AD zFnZ=}lbi*mN-AOm zCs)r=*YQAA!`e#1N>aHF=bb*z*hXH#Wl$z^o}x##ZrUc=kh%OHWhp=7;?8%Xj||@V?1c ziWoaC$^&04;A|T)!Zd9sUzE&$ODyJaBpvqsw19Uiuq{i#VK1!htkdRWBnb z`{rat=nHArT%^R>u#CjjCkw-7%g53|&7z-;X+ewb?OLWiV|#nuc8mp*LuGSi3IP<<*Wyo9GKV7l0Noa4Jr0g3p_$ z*R9{qn=?IXC#WU>48-k5V2Oc_>P;4_)J@bo1|pf=%Rcbgk=5m)CJZ`caHBTm3%!Z9 z_?7LHr_BXbKKr=JD!%?KhwdYSdu8XxPoA{n8^%_lh5cjRHuCY9Zlpz8g+$f@bw@0V z+6DRMT9c|>1^3D|$Vzc(C?M~iZurGH2pXPT%F!JSaAMdO%!5o0uc&iqHx?ImcX6fI zCApkzc~OOnfzAd_+-DcMp&AOQxE_EsMqKM{%dRMI5`5CT&%mQO?-@F6tE*xL?aEGZ z8^wH@wRl`Izx4sDmU>}Ym{ybUm@F83qqZPD6nFm?t?(7>h*?`fw)L3t*l%*iw0Qu#?$5eq!Qc zpQvqgSxrd83NsdO@lL6#{%lsYXWen~d3p4fGBb7&5xqNYJ)yn84!e1PmPo7ChVd%4 zHUsV0Mh?VpzZD=A6%)Qrd~i7 z96*RPbid;BN{Wh?adeD_p8YU``kOrGkNox3D9~!K?w>#kFz!4lzOWR}puS(DmfjJD z`x0z|qB33*^0mZdM&6$|+T>fq>M%yoy(BEjuh9L0>{P&XJ3enGpoQRx`v6$txXt#c z0#N?b5%srj(4xmPvJxrlF3H%OMB!jvfy z;wx8RzU~lb?h_}@V=bh6p8PSb-dG|-T#A?`c&H2`_!u+uenIZe`6f~A7r)`9m8atC zt(b|6Eg#!Q*DfRU=Ix`#B_dK)nnJ_+>Q<1d7W)eynaVn`FNuN~%B;uO2}vXr5^zi2 z!ifIF5@Zlo0^h~8+ixFBGqtweFc`C~JkSq}&*a3C}L?b5Mh-bW=e)({F_g4O3 zb@SFTK3VD9QuFgFnK4Ve_pXc3{S$=+Z;;4+;*{H}Rc;845rP?DLK6G5Y-xdUKkA6E3Dz&5f{F^FjJQ(NSpZ8q-_!L3LL@H* zxbDF{gd^U3uD;)a)sJwAVi}7@%pRM&?5IaUH%+m{E)DlA_$IA1=&jr{KrhD5q&lTC zAa3c)A(K!{#nOvenH6XrR-y>*4M#DpTTOGQEO5Jr6kni9pDW`rvY*fs|ItV;CVITh z=`rxcH2nEJpkQ^(;1c^hfb8vGN;{{oR=qNyKtR1;J>CByul*+=`NydWnSWJR#I2lN zTvgnR|MBx*XFsfdA&;tr^dYaqRZp*2NwkAZE6kV@1f{76e56eUmGrZ>MDId)oqSWw z7d&r3qfazg+W2?bT}F)4jD6sWaw`_fXZGY&wnGm$FRPFL$HzVTH^MYBHWGCOk-89y zA+n+Q6EVSSCpgC~%uHfvyg@ufE^#u?JH?<73A}jj5iILz4Qqk5$+^U(SX(-qv5agK znUkfpke(KDn~dU0>gdKqjTkVk`0`9^0n_wzXO7R!0Thd@S;U`y)VVP&mOd-2 z(hT(|$=>4FY;CBY9#_lB$;|Wd$aOMT5O_3}DYXEHn&Jrc3`2JiB`b6X@EUOD zVl0S{ijm65@n^19T3l%>*;F(?3r3s?zY{thc4%AD30CeL_4{8x6&cN}zN3fE+x<9; zt2j1RRVy5j22-8U8a6$pyT+<`f+x2l$fd_{qEp_bfxfzu>ORJsXaJn4>U6oNJ#|~p z`*ZC&NPXl&=vq2{Ne79AkQncuxvbOG+28*2wU$R=GOmns3W@HE%^r)Fu%Utj=r9t` zd;SVOnA(=MXgnOzI2@3SGKHz8HN~Vpx&!Ea+Df~`*n@8O=0!b4m?7cE^K*~@fqv9q zF*uk#1@6Re_<^9eElgJD!nTA@K9C732tV~;B`hzZ321Ph=^BH?zXddiu{Du5*IPg} zqDM=QxjT!Rp|#Bkp$(mL)aar)f(dOAXUiw81pX0DC|Y4;>Vz>>DMshoips^8Frdv} zlTD=cKa48M>dR<>(YlLPOW%rokJZNF2gp8fwc8b2sN+i6&-pHr?$rj|uFgktK@jg~ zIFS(%=r|QJ=$kvm_~@n=ai1lA{7Z}i+zj&yzY+!t$iGUy|9jH#&oTNJ;JW-3n>DF+ z3aCOzqn|$X-Olu_p7brzn`uk1F*N4@=b=m;S_C?#hy{&NE#3HkATrg?enaVGT^$qIjvgc61y!T$9<1B@?_ibtDZ{G zeXInVr5?OD_nS_O|CK3|RzzMmu+8!#Zb8Ik;rkIAR%6?$pN@d<0dKD2c@k2quB%s( zQL^<_EM6ow8F6^wJN1QcPOm|ehA+dP(!>IX=Euz5qqIq}Y3;ibQtJnkDmZ8c8=Cf3 zu`mJ!Q6wI7EblC5RvP*@)j?}W=WxwCvF3*5Up_`3*a~z$`wHwCy)2risye=1mSp%p zu+tD6NAK3o@)4VBsM!@);qgsjgB$kkCZhaimHg&+k69~drbvRTacWKH;YCK(!rC?8 zP#cK5JPHSw;V;{Yji=55X~S+)%(8fuz}O>*F3)hR;STU`z6T1aM#Wd+FP(M5*@T1P z^06O;I20Sk!bxW<-O;E081KRdHZrtsGJflFRRFS zdi5w9OVDGSL3 zNrC7GVsGN=b;YH9jp8Z2$^!K@h=r-xV(aEH@#JicPy;A0k1>g1g^XeR`YV2HfmqXY zYbRwaxHvf}OlCAwHoVI&QBLr5R|THf?nAevV-=~V8;gCsX>jndvNOcFA+DI+zbh~# zZ7`qNk&w+_+Yp!}j;OYxIfx_{f0-ONc?mHCiCUak=>j>~>YR4#w# zuKz~UhT!L~GfW^CPqG8Lg)&Rc6y^{%3H7iLa%^l}cw_8UuG;8nn9)kbPGXS}p3!L_ zd#9~5CrH8xtUd?{d2y^PJg+z(xIfRU;`}^=OlehGN2=?}9yH$4Rag}*+AWotyxfCJ zHx=r7ZH>j2kV?%7WTtp+-HMa0)_*DBBmC{sd$)np&GEJ__kEd`xB5a2A z*J+yx>4o#ZxwA{;NjhU*1KT~=ZK~GAA;KZHDyBNTaWQ1+;tOFFthnD)DrCn`DjBZ% zk$N5B4^$`n^jNSOr=t(zi8TN4fpaccsb`zOPD~iY=UEK$0Y70bG{idLx@IL)7^(pL z{??Bnu=lDeguDrd%qW1)H)H`9otsOL-f4bSu};o9OXybo6J!Lek`a4ff>*O)BDT_g z<6@SrI|C9klY(>_PfA^qai7A_)VNE4c^ZjFcE$Isp>`e5fLc)rg@8Q_d^Uk24$2bn z9#}6kZ2ZxS9sI(RqT7?El2@B+($>eBQrNi_k#CDJ8D9}8$mmm z4oSKO^F$i+NG)-HE$O6s1--6EzJa?C{x=QgK&c=)b(Q9OVoAXYEEH20G|q$}Hue%~ zO3B^bF=t7t48sN zWh_zA`w~|){-!^g?6Mqf6ieV zFx~aPUOJGR=4{KsW7I?<=J2|lY`NTU=lt=%JE9H1vBpkcn=uq(q~=?iBt_-r(PLBM zP-0dxljJO>4Wq-;stY)CLB4q`-r*T$!K2o}?E-w_i>3_aEbA^MB7P5piwt1dI-6o!qWCy0 ztYy!x9arGTS?kabkkyv*yxvsPQ7Vx)twkS6z2T@kZ|kb8yjm+^$|sEBmvACeqbz)RmxkkDQX-A*K!YFziuhwb|ym>C$}U|J)4y z$(z#)GH%uV6{ec%Zy~AhK|+GtG8u@c884Nq%w`O^wv2#A(&xH@c5M`Vjk*SR_tJnq z0trB#aY)!EKW_}{#L3lph5ow=@|D5LzJYUFD6 z7XnUeo_V0DVSIKMFD_T0AqAO|#VFDc7c?c-Q%#u00F%!_TW1@JVnsfvm@_9HKWflBOUD~)RL``-!P;(bCON_4eVdduMO>?IrQ__*zE@7(OX zUtfH@AX*53&xJW*Pu9zcqxGiM>xol0I~QL5B%Toog3Jlenc^WbVgeBvV8C8AX^Vj& z^I}H})B=VboO%q1;aU5ACMh{yK4J;xlMc`jCnZR^!~LDs_MP&8;dd@4LDWw~*>#OT zeZHwdQWS!tt5MJQI~cw|Ka^b4c|qyd_ly(+Ql2m&AAw^ zQeSXDOOH!!mAgzAp0z)DD>6Xo``b6QwzUV@w%h}Yo>)a|xRi$jGuHQhJVA%>)PUvK zBQ!l0hq<3VZ*RnrDODP)>&iS^wf64C;MGqDvx>|p;35%6(u+IHoNbK z;Gb;TneFo*`zUKS6kwF*&b!U8e5m4YAo03a_e^!5BP42+r)LFhEy?_7U1IR<; z^0v|DhCYMSj<-;MtY%R@Fg;9Kky^pz_t2nJfKWfh5Eu@_l{^ph%1z{jkg5jQrkvD< z#vdK!nku*RrH~TdN~`wDs;d>XY1PH?O<4^U4lmA|wUW{Crrv#r%N>7k#{Gc44Fr|t z@UZP}Y-TrAmnEZ39A*@6;ccsR>)$A)S>$-Cj!=x$rz7IvjHIPM(TB+JFf{ehuIvY$ zsDAwREg*%|=>Hw$`us~RP&3{QJg%}RjJKS^mC_!U;E5u>`X`jW$}P`Mf}?7G7FX#{ zE(9u1SO;3q@ZhDL9O({-RD+SqqPX)`0l5IQu4q)49TUTkxR(czeT}4`WV~pV*KY&i zAl3~X%D2cPVD^B43*~&f%+Op)wl<&|D{;=SZwImydWL6@_RJjxP2g)s=dH)u9Npki zs~z9A+3fj0l?yu4N0^4aC5x)Osnm0qrhz@?nwG_`h(71P znbIewljU%T*cC=~NJy|)#hT+lx#^5MuDDnkaMb*Efw9eThXo|*WOQzJ*#3dmRWm@! zfuSc@#kY{Um^gBc^_Xdxnl!n&y&}R4yAbK&RMc+P^Ti;YIUh|C+K1|=Z^{nZ}}rxH*v{xR!i%qO~o zTr`WDE@k$M9o0r4YUFFeQO7xCu_Zgy)==;fCJ94M_rLAv&~NhfvcLWCoaGg2ao~3e zBG?Ms9B+efMkp}7BhmISGWmJsKI@a8b}4lLI48oWKY|8?zuuNc$lt5Npr+p7a#sWu zh!@2nnLBVJK!$S~>r2-pN||^w|fY`CT{TFnJy`B|e5;=+_v4l8O-fkN&UQbA4NKTyntd zqK{xEKh}U{NHoQUf!M=2(&w+eef77VtYr;xs%^cPfKLObyOV_9q<(%76-J%vR>w9!us-0c-~Y?_EVS%v!* z15s2s3eTs$Osz$JayyH|5nPAIPEX=U;r&p;K14G<1)bvn@?bM5kC{am|C5%hyxv}a z(DeSKI5ZfZ1*%dl8frIX2?);R^^~LuDOpNpk-2R8U1w92HmG1m&|j&J{EK=|p$;f9 z7Rs5|jr4r8k5El&qcuM+YRlKny%t+1CgqEWO>3;BSRZi(LA3U%Jm{@{y+A+w(gzA< z7dBq6a1sEWa4cD0W7=Ld9z0H7RI^Z7vl(bfA;72j?SWCo`#5mVC$l1Q2--%V)-uN* z9ha*s-AdfbDZ8R8*fpwjzx=WvOtmSzGFjC#X)hD%Caeo^OWjS(3h|d9_*U)l%{Ab8 zfv$yoP{OuUl@$(-sEVNt{*=qi5P=lpxWVuz2?I7Dc%BRc+NGNw+323^ z5BXGfS71oP^%apUo(Y#xkxE)y?>BFzEBZ}UBbr~R4$%b7h3iZu3S(|A;&HqBR{nK& z$;GApNnz=kNO^FL&nYcfpB7Qg;hGJPsCW44CbkG1@l9pn0`~oKy5S777uH)l{irK!ru|X+;4&0D;VE*Ii|<3P zUx#xUqvZT5kVQxsF#~MwKnv7;1pR^0;PW@$@T7I?s`_rD1EGUdSA5Q(C<>5SzE!vw z;{L&kKFM-MO>hy#-8z`sdVx})^(Dc-dw;k-h*9O2_YZw}|9^y-|8RQ`BWJUJL(Cer zP5Z@fNc>pTXABbTRY-B5*MphpZv6#i802giwV&SkFCR zGMETyUm(KJbh+&$8X*RB#+{surjr;8^REEt`2&Dubw3$mx>|~B5IKZJ`s_6fw zKAZx9&PwBqW1Oz0r0A4GtnZd7XTKViX2%kPfv+^X3|_}RrQ2e3l=KG_VyY`H?I5&CS+lAX5HbA%TD9u6&s#v!G> zzW9n4J%d5ye7x0y`*{KZvqyXUfMEE^ZIffzI=Hh|3J}^yx7eL=s+TPH(Q2GT-sJ~3 zI463C{(ag7-hS1ETtU;_&+49ABt5!A7CwLwe z=SoA8mYZIQeU;9txI=zcQVbuO%q@E)JI+6Q!3lMc=Gbj(ASg-{V27u>z2e8n;Nc*pf}AqKz1D>p9G#QA+7mqqrEjGfw+85Uyh!=tTFTv3|O z+)-kFe_8FF_EkTw!YzwK^Hi^_dV5x-Ob*UWmD-})qKj9@aE8g240nUh=g|j28^?v7 zHRTBo{0KGaWBbyX2+lx$wgXW{3aUab6Bhm1G1{jTC7ota*JM6t+qy)c5<@ zpc&(jVdTJf(q3xB=JotgF$X>cxh7k*(T`-V~AR+`%e?YOeALQ2Qud( zz35YizXt(aW3qndR}fTw1p()Ol4t!D1pitGNL95{SX4ywzh0SF;=!wf=?Q?_h6!f* zh7<+GFi)q|XBsvXZ^qVCY$LUa{5?!CgwY?EG;*)0ceFe&=A;!~o`ae}Z+6me#^sv- z1F6=WNd6>M(~ z+092z>?Clrcp)lYNQl9jN-JF6n&Y0mp7|I0dpPx+4*RRK+VQI~>en0Dc;Zfl+x z_e_b7s`t1_A`RP3$H}y7F9_na%D7EM+**G_Z0l_nwE+&d_kc35n$Fxkd4r=ltRZhh zr9zER8>j(EdV&Jgh(+i}ltESBK62m0nGH6tCBr90!4)-`HeBmz54p~QP#dsu%nb~W z7sS|(Iydi>C@6ZM(Us!jyIiszMkd)^u<1D+R@~O>HqZIW&kearPWmT>63%_t2B{_G zX{&a(gOYJx!Hq=!T$RZ&<8LDnxsmx9+TBL0gTk$|vz9O5GkK_Yx+55^R=2g!K}NJ3 zW?C;XQCHZl7H`K5^BF!Q5X2^Mj93&0l_O3Ea3!Ave|ixx+~bS@Iv18v2ctpSt4zO{ zp#7pj!AtDmti$T`e9{s^jf(ku&E|83JIJO5Qo9weT6g?@vX!{7)cNwymo1+u(YQ94 zopuz-L@|5=h8A!(g-MXgLJC0MA|CgQF8qlonnu#j z;uCeq9ny9QSD|p)9sp3ebgY3rk#y0DA(SHdh$DUm^?GI<>%e1?&}w(b zdip1;P2Z=1wM+$q=TgLP$}svd!vk+BZ@h<^4R=GS2+sri7Z*2f`9 z5_?i)xj?m#pSVchk-SR!2&uNhzEi+#5t1Z$o0PoLGz*pT64%+|Wa+rd5Z}60(j?X= z{NLjtgRb|W?CUADqOS@(*MA-l|E342NxRaxLTDqsOyfWWe%N(jjBh}G zm7WPel6jXijaTiNita+z(5GCO0NM=Melxud57PP^d_U## zbA;9iVi<@wr0DGB8=T9Ab#2K_#zi=$igyK48@;V|W`fg~7;+!q8)aCOo{HA@vpSy-4`^!ze6-~8|QE||hC{ICKllG9fbg_Y7v z$jn{00!ob3!@~-Z%!rSZ0JO#@>|3k10mLK0JRKP-Cc8UYFu>z93=Ab-r^oL2 zl`-&VBh#=-?{l1TatC;VweM^=M7-DUE>m+xO7Xi6vTEsReyLs8KJ+2GZ&rxw$d4IT zPXy6pu^4#e;;ZTsgmG+ZPx>piodegkx2n0}SM77+Y*j^~ICvp#2wj^BuqRY*&cjmL zcKp78aZt>e{3YBb4!J_2|K~A`lN=u&5j!byw`1itV(+Q_?RvV7&Z5XS1HF)L2v6ji z&kOEPmv+k_lSXb{$)of~(BkO^py&7oOzpjdG>vI1kcm_oPFHy38%D4&A4h_CSo#lX z2#oqMCTEP7UvUR3mwkPxbl8AMW(e{ARi@HCYLPSHE^L<1I}OgZD{I#YH#GKnpRmW3 z2jkz~Sa(D)f?V?$gNi?6)Y;Sm{&?~2p=0&BUl_(@hYeX8YjaRO=IqO7neK0RsSNdYjD zaw$g2sG(>JR=8Iz1SK4`*kqd_3-?;_BIcaaMd^}<@MYbYisWZm2C2|Np_l|8r9yM|JkUngSo@?wci(7&O9a z%|V(4C1c9pps0xxzPbXH=}QTxc2rr7fXk$9`a6TbWKPCz&p=VsB8^W96W=BsB|7bc zf(QR8&Ktj*iz)wK&mW`#V%4XTM&jWNnDF56O+2bo<3|NyUhQ%#OZE8$Uv2a@J>D%t zMVMiHh?es!Ex19q&6eC&L=XDU_BA&uR^^w>fpz2_`U87q_?N2y;!Z!bjoeKrzfC)} z?m^PM=(z{%n9K`p|7Bz$LuC7!>tFOuN74MFELm}OD9?%jpT>38J;=1Y-VWtZAscaI z_8jUZ#GwWz{JqvGEUmL?G#l5E=*m>`cY?m*XOc*yOCNtpuIGD+Z|kn4Xww=BLrNYS zGO=wQh}Gtr|7DGXLF%|`G>J~l{k^*{;S-Zhq|&HO7rC_r;o`gTB7)uMZ|WWIn@e0( zX$MccUMv3ABg^$%_lNrgU{EVi8O^UyGHPNRt%R!1#MQJn41aD|_93NsBQhP80yP<9 zG4(&0u7AtJJXLPcqzjv`S~5;Q|5TVGccN=Uzm}K{v)?f7W!230C<``9(64}D2raRU zAW5bp%}VEo{4Rko`bD%Ehf=0voW?-4Mk#d3_pXTF!-TyIt6U+({6OXWVAa;s-`Ta5 zTqx&8msH3+DLrVmQOTBOAj=uoxKYT3DS1^zBXM?1W+7gI!aQNPYfUl{3;PzS9*F7g zWJN8x?KjBDx^V&6iCY8o_gslO16=kh(|Gp)kz8qlQ`dzxQv;)V&t+B}wwdi~uBs4? zu~G|}y!`3;8#vIMUdyC7YEx6bb^1o}G!Jky4cN?BV9ejBfN<&!4M)L&lRKiuMS#3} z_B}Nkv+zzxhy{dYCW$oGC&J(Ty&7%=5B$sD0bkuPmj7g>|962`(Q{ZZMDv%YMuT^KweiRDvYTEop3IgFv#)(w>1 zSzH>J`q!LK)c(AK>&Ib)A{g`Fdykxqd`Yq@yB}E{gnQV$K!}RsgMGWqC3DKE(=!{}ekB3+(1?g}xF>^icEJbc z5bdxAPkW90atZT+&*7qoLqL#p=>t-(-lsnl2XMpZcYeW|o|a322&)yO_8p(&Sw{|b zn(tY$xn5yS$DD)UYS%sP?c|z>1dp!QUD)l;aW#`%qMtQJjE!s2z`+bTSZmLK7SvCR z=@I4|U^sCwZLQSfd*ACw9B@`1c1|&i^W_OD(570SDLK`MD0wTiR8|$7+%{cF&){$G zU~|$^Ed?TIxyw{1$e|D$050n8AjJvvOWhLtLHbSB|HIfhMpqVf>AF&}ZQHhOJ14Bz zww+XL+qP}nww+W`F>b!by|=&a(cM4JIDhsTXY8@|ntQG}-}jm0&Bcj|LV(#sc=BNS zRjh;k9l>EdAFdd)=H!U`~$WP*}~^3HZ_?H>gKw>NBa;tA8M1{>St|)yDF_=~{KEPAGkg3VB`QCHol!AQ0|?e^W?81f{@()Wy!vQ$bY; z0ctx)l7VK83d6;dp!s{Nu=SwXZ8lHQHC*J2g@P0a={B8qHdv(+O3wV=4-t4HK1+smO#=S; z3cSI#Nh+N@AqM#6wPqjDmQM|x95JG|l1#sAU|>I6NdF*G@bD?1t|ytHlkKD+z9}#j zbU+x_cR-j9yX4s{_y>@zk*ElG1yS({BInGJcIT>l4N-DUs6fufF#GlF2lVUNOAhJT zGZThq54GhwCG(h4?yWR&Ax8hU<*U)?g+HY5-@{#ls5CVV(Wc>Bavs|l<}U|hZn z_%m+5i_gaakS*Pk7!v&w3&?R5Xb|AkCdytTY;r+Z7f#Id=q+W8cn)*9tEet=OG+Y} z58U&!%t9gYMx2N=8F?gZhIjtkH!`E*XrVJ?$2rRxLhV1z82QX~PZi8^N5z6~f-MUE zLKxnNoPc-SGl7{|Oh?ZM$jq67sSa)Wr&3)0YxlJt(vKf!-^L)a|HaPv*IYXb;QmWx zsqM>qY;tpK3RH-omtta+Xf2Qeu^$VKRq7`e$N-UCe1_2|1F{L3&}M0XbJ@^xRe&>P zRdKTgD6601x#fkDWkoYzRkxbn#*>${dX+UQ;FbGnTE-+kBJ9KPn)501#_L4O_k`P3 zm+$jI{|EC?8BXJY{P~^f-{**E53k%kVO$%p+=H5DiIdwMmUo>2euq0UzU90FWL!>; z{5@sd0ecqo5j!6AH@g6Mf3keTP$PFztq}@)^ZjK;H6Go$#SV2|2bAFI0%?aXgVH$t zb4Kl`$Xh8qLrMbZUS<2*7^F0^?lrOE=$DHW+O zvLdczsu0^TlA6RhDy3=@s!k^1D~Awulk!Iyo#}W$xq8{yTAK!CLl={H0@YGhg-g~+ z(u>pss4k#%8{J%~%8=H5!T`rqK6w^es-cNVE}=*lP^`i&K4R=peg1tdmT~UAbDKc& zg%Y*1E{hBf<)xO>HDWV7BaMWX6FW4ou1T2m^6{Jb!Su1UaCCYY8RR8hAV$7ho|FyEyP~ zEgK`@%a$-C2`p zV*~G>GOAs*3KN;~IY_UR$ISJxB(N~K>=2C2V6>xTmuX4klRXdrJd&UPAw7&|KEwF8Zcy2j-*({gSNR1^p02Oj88GN9a_Hq;Skdp}kO0;FLbje%2ZvPiltDZgv^ z#pb4&m^!79;O8F+Wr9X71laPY!CdNXG?J6C9KvdAE2xWW1>U~3;0v≫L+crb^Bz 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 /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -201,11 +202,11 @@ 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 -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/gradlew.bat b/gradlew.bat index 93e3f59f1..25da30dbd 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail From e75488f5d98fb600408d065d1c5060520a8921a4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 23 Mar 2024 02:00:24 +0600 Subject: [PATCH 125/212] fix(deps): update aboutlib.version to v11 (major) (#473) * fix(deps): update aboutlib.version to v11 * Fix build --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- .../settings/screen/about/OpenSourceLicensesScreen.kt | 11 ++++++----- gradle/libs.versions.toml | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/OpenSourceLicensesScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/OpenSourceLicensesScreen.kt index e39de8ed6..3c2309b32 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/OpenSourceLicensesScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/OpenSourceLicensesScreen.kt @@ -32,12 +32,13 @@ class OpenSourceLicensesScreen : Screen() { .fillMaxSize(), contentPadding = contentPadding, onLibraryClick = { - val libraryLicenseScreen = OpenSourceLibraryLicenseScreen( - name = it.library.name, - website = it.library.website, - license = it.library.licenses.firstOrNull()?.htmlReadyLicenseContent.orEmpty(), + navigator.push( + OpenSourceLibraryLicenseScreen( + name = it.name, + website = it.website, + license = it.licenses.firstOrNull()?.htmlReadyLicenseContent.orEmpty(), + ) ) - navigator.push(libraryLicenseScreen) }, ) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fef4b5b0e..a82efa20f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -aboutlib_version = "10.10.0" +aboutlib_version = "11.1.0" leakcanary = "2.13" moko = "0.23.0" okhttp_version = "5.0.0-alpha.12" From 4b4e46851083c29ca412c114b1b96136fcc21442 Mon Sep 17 00:00:00 2001 From: Maddie Witman Date: Fri, 22 Mar 2024 18:58:35 -0400 Subject: [PATCH 126/212] Grab extension repo detail from `repo.json` and include in DB (#506) * WIP Extension Repo DB Support * Wired in to extension screen, browse settings screen * Detekt changes * Ui tweaks and open in browser * Migrate ExtensionRepos on Update * Migration Cleanup * Slight cleanup / error handling * Update ExtensionRepo from Repo.json during extension search. Added Manual refresh in extension repos page. * Split repo fetching into separate API module, major refactor work * Removed development strings * Moved migration to #3 * Fixed rebase * Detekt changes * Added Replace Repository Dialog * Cleanup, removed platform specific code, PR comments * Removed extra function, reverted small change * Detekt cleanup * Apply suggestions from code review Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> * Fixed error introduced in cleanup * Tweak for multiline when * Moved getCount() to flow * changed getCount to non-suspend, used property delegation * Apply suggestions from code review Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> * Fixed formatting with updated comment string * Big wave of PR comments, renaming/other tweaks * onOpenWebsite changes * onOpenWebsite changes * trying to make single line * Renamed ExtensionRepoApi.kt to ExtensionRepoService.kt --------- Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- .../java/eu/kanade/domain/DomainModule.kt | 17 +++- .../interactor/CreateExtensionRepo.kt | 25 ----- .../interactor/DeleteExtensionRepo.kt | 11 --- .../extension/interactor/GetExtensionRepos.kt | 11 --- .../settings/screen/SettingsBrowseScreen.kt | 9 +- .../screen/browse/ExtensionReposScreen.kt | 18 +++- .../browse/ExtensionReposScreenModel.kt | 62 ++++++++++--- .../components/ExtensionReposContent.kt | 24 ++++- .../components/ExtensionReposDialogs.kt | 38 +++++++- .../browse/components/ExtensionReposScreen.kt | 16 ++++ .../java/eu/kanade/tachiyomi/Migrations.kt | 33 ++++++- .../tachiyomi/extension/api/ExtensionApi.kt | 20 +++- .../kanade/tachiyomi/ui/main/MainActivity.kt | 2 + .../repository/ExtensionRepoRepositoryImpl.kt | 93 +++++++++++++++++++ .../tachiyomi/data/extension_repos.sq | 57 ++++++++++++ .../sqldelight/tachiyomi/migrations/3.sqm | 8 ++ domain/build.gradle.kts | 1 + .../exception/SaveExtensionRepoException.kt | 10 ++ .../interactor/CreateExtensionRepo.kt | 81 ++++++++++++++++ .../interactor/DeleteExtensionRepo.kt | 11 +++ .../interactor/GetExtensionRepo.kt | 13 +++ .../interactor/GetExtensionRepoCount.kt | 9 ++ .../interactor/ReplaceExtensionRepo.kt | 12 +++ .../interactor/UpdateExtensionRepo.kt | 33 +++++++ .../extensionrepo/model/ExtensionRepo.kt | 9 ++ .../repository/ExtensionRepoRepository.kt | 47 ++++++++++ .../service/ExtensionRepoService.kt | 57 ++++++++++++ .../commonMain/resources/MR/base/strings.xml | 3 + 28 files changed, 649 insertions(+), 81 deletions(-) delete mode 100644 app/src/main/java/eu/kanade/domain/extension/interactor/CreateExtensionRepo.kt delete mode 100644 app/src/main/java/eu/kanade/domain/extension/interactor/DeleteExtensionRepo.kt delete mode 100644 app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionRepos.kt create mode 100644 data/src/main/java/mihon/data/repository/ExtensionRepoRepositoryImpl.kt create mode 100644 data/src/main/sqldelight/tachiyomi/data/extension_repos.sq create mode 100644 data/src/main/sqldelight/tachiyomi/migrations/3.sqm create mode 100644 domain/src/main/java/mihon/domain/extensionrepo/exception/SaveExtensionRepoException.kt create mode 100644 domain/src/main/java/mihon/domain/extensionrepo/interactor/CreateExtensionRepo.kt create mode 100644 domain/src/main/java/mihon/domain/extensionrepo/interactor/DeleteExtensionRepo.kt create mode 100644 domain/src/main/java/mihon/domain/extensionrepo/interactor/GetExtensionRepo.kt create mode 100644 domain/src/main/java/mihon/domain/extensionrepo/interactor/GetExtensionRepoCount.kt create mode 100644 domain/src/main/java/mihon/domain/extensionrepo/interactor/ReplaceExtensionRepo.kt create mode 100644 domain/src/main/java/mihon/domain/extensionrepo/interactor/UpdateExtensionRepo.kt create mode 100644 domain/src/main/java/mihon/domain/extensionrepo/model/ExtensionRepo.kt create mode 100644 domain/src/main/java/mihon/domain/extensionrepo/repository/ExtensionRepoRepository.kt create mode 100644 domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoService.kt diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt index 4f0c86eab..1d2a214ac 100644 --- a/app/src/main/java/eu/kanade/domain/DomainModule.kt +++ b/app/src/main/java/eu/kanade/domain/DomainModule.kt @@ -4,10 +4,7 @@ import eu.kanade.domain.chapter.interactor.GetAvailableScanlators import eu.kanade.domain.chapter.interactor.SetReadStatus import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource import eu.kanade.domain.download.interactor.DeleteDownload -import eu.kanade.domain.extension.interactor.CreateExtensionRepo -import eu.kanade.domain.extension.interactor.DeleteExtensionRepo import eu.kanade.domain.extension.interactor.GetExtensionLanguages -import eu.kanade.domain.extension.interactor.GetExtensionRepos import eu.kanade.domain.extension.interactor.GetExtensionSources import eu.kanade.domain.extension.interactor.GetExtensionsByType import eu.kanade.domain.extension.interactor.TrustExtension @@ -26,6 +23,14 @@ import eu.kanade.domain.track.interactor.AddTracks import eu.kanade.domain.track.interactor.RefreshTracks import eu.kanade.domain.track.interactor.SyncChapterProgressWithTrack import eu.kanade.domain.track.interactor.TrackChapter +import mihon.data.repository.ExtensionRepoRepositoryImpl +import mihon.domain.extensionrepo.interactor.CreateExtensionRepo +import mihon.domain.extensionrepo.interactor.DeleteExtensionRepo +import mihon.domain.extensionrepo.interactor.GetExtensionRepo +import mihon.domain.extensionrepo.interactor.GetExtensionRepoCount +import mihon.domain.extensionrepo.interactor.ReplaceExtensionRepo +import mihon.domain.extensionrepo.interactor.UpdateExtensionRepo +import mihon.domain.extensionrepo.repository.ExtensionRepoRepository import tachiyomi.data.category.CategoryRepositoryImpl import tachiyomi.data.chapter.ChapterRepositoryImpl import tachiyomi.data.history.HistoryRepositoryImpl @@ -173,8 +178,12 @@ class DomainModule : InjektModule { addFactory { ToggleSourcePin(get()) } addFactory { TrustExtension(get()) } + addSingletonFactory { ExtensionRepoRepositoryImpl(get()) } + addFactory { GetExtensionRepo(get()) } + addFactory { GetExtensionRepoCount(get()) } addFactory { CreateExtensionRepo(get()) } addFactory { DeleteExtensionRepo(get()) } - addFactory { GetExtensionRepos(get()) } + addFactory { ReplaceExtensionRepo(get()) } + addFactory { UpdateExtensionRepo(get(), get()) } } } diff --git a/app/src/main/java/eu/kanade/domain/extension/interactor/CreateExtensionRepo.kt b/app/src/main/java/eu/kanade/domain/extension/interactor/CreateExtensionRepo.kt deleted file mode 100644 index a8083ec00..000000000 --- a/app/src/main/java/eu/kanade/domain/extension/interactor/CreateExtensionRepo.kt +++ /dev/null @@ -1,25 +0,0 @@ -package eu.kanade.domain.extension.interactor - -import eu.kanade.domain.source.service.SourcePreferences -import tachiyomi.core.common.preference.plusAssign - -class CreateExtensionRepo(private val preferences: SourcePreferences) { - - fun await(name: String): Result { - // Do not allow invalid formats - if (!name.matches(repoRegex)) { - return Result.InvalidUrl - } - - preferences.extensionRepos() += name.removeSuffix("/index.min.json") - - return Result.Success - } - - sealed interface Result { - data object InvalidUrl : Result - data object Success : Result - } -} - -private val repoRegex = """^https://.*/index\.min\.json$""".toRegex() diff --git a/app/src/main/java/eu/kanade/domain/extension/interactor/DeleteExtensionRepo.kt b/app/src/main/java/eu/kanade/domain/extension/interactor/DeleteExtensionRepo.kt deleted file mode 100644 index 8e50ebeca..000000000 --- a/app/src/main/java/eu/kanade/domain/extension/interactor/DeleteExtensionRepo.kt +++ /dev/null @@ -1,11 +0,0 @@ -package eu.kanade.domain.extension.interactor - -import eu.kanade.domain.source.service.SourcePreferences -import tachiyomi.core.common.preference.minusAssign - -class DeleteExtensionRepo(private val preferences: SourcePreferences) { - - fun await(repo: String) { - preferences.extensionRepos() -= repo - } -} diff --git a/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionRepos.kt b/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionRepos.kt deleted file mode 100644 index 0d3b0e988..000000000 --- a/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionRepos.kt +++ /dev/null @@ -1,11 +0,0 @@ -package eu.kanade.domain.extension.interactor - -import eu.kanade.domain.source.service.SourcePreferences -import kotlinx.coroutines.flow.Flow - -class GetExtensionRepos(private val preferences: SourcePreferences) { - - fun subscribe(): Flow> { - return preferences.extensionRepos().changes() - } -} diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt index c8bfd10ca..b47dbe502 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt @@ -2,6 +2,7 @@ package eu.kanade.presentation.more.settings.screen import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext @@ -13,11 +14,11 @@ import eu.kanade.presentation.more.settings.Preference import eu.kanade.presentation.more.settings.screen.browse.ExtensionReposScreen import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.authenticate import kotlinx.collections.immutable.persistentListOf +import mihon.domain.extensionrepo.interactor.GetExtensionRepoCount import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.pluralStringResource import tachiyomi.presentation.core.i18n.stringResource -import tachiyomi.presentation.core.util.collectAsState import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -33,7 +34,9 @@ object SettingsBrowseScreen : SearchableSettings { val navigator = LocalNavigator.currentOrThrow val sourcePreferences = remember { Injekt.get() } - val reposCount by sourcePreferences.extensionRepos().collectAsState() + val getExtensionRepoCount = remember { Injekt.get() } + + val reposCount by getExtensionRepoCount.subscribe().collectAsState(0) return listOf( Preference.PreferenceGroup( @@ -45,7 +48,7 @@ object SettingsBrowseScreen : SearchableSettings { ), Preference.PreferenceItem.TextPreference( title = stringResource(MR.strings.label_extension_repos), - subtitle = pluralStringResource(MR.plurals.num_repos, reposCount.size, reposCount.size), + subtitle = pluralStringResource(MR.plurals.num_repos, reposCount, reposCount), onClick = { navigator.push(ExtensionReposScreen()) }, diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionReposScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionReposScreen.kt index 4801829d5..03c9acd7c 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionReposScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionReposScreen.kt @@ -8,11 +8,14 @@ import androidx.compose.ui.platform.LocalContext import cafe.adriel.voyager.core.model.rememberScreenModel import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionRepoConflictDialog import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionRepoCreateDialog import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionRepoDeleteDialog import eu.kanade.presentation.more.settings.screen.browse.components.ExtensionReposScreen import eu.kanade.presentation.util.Screen +import eu.kanade.tachiyomi.util.system.openInBrowser import eu.kanade.tachiyomi.util.system.toast +import kotlinx.collections.immutable.toImmutableSet import kotlinx.coroutines.flow.collectLatest import tachiyomi.presentation.core.screens.LoadingScreen @@ -42,17 +45,19 @@ class ExtensionReposScreen( ExtensionReposScreen( state = successState, onClickCreate = { screenModel.showDialog(RepoDialog.Create) }, + onOpenWebsite = { context.openInBrowser(it.website) }, onClickDelete = { screenModel.showDialog(RepoDialog.Delete(it)) }, + onClickRefresh = { screenModel.refreshRepos() }, navigateUp = navigator::pop, ) when (val dialog = successState.dialog) { null -> {} - RepoDialog.Create -> { + is RepoDialog.Create -> { ExtensionRepoCreateDialog( onDismissRequest = screenModel::dismissDialog, onCreate = { screenModel.createRepo(it) }, - repos = successState.repos, + repoUrls = successState.repos.map { it.baseUrl }.toImmutableSet(), ) } is RepoDialog.Delete -> { @@ -62,6 +67,15 @@ class ExtensionReposScreen( repo = dialog.repo, ) } + + is RepoDialog.Conflict -> { + ExtensionRepoConflictDialog( + onDismissRequest = screenModel::dismissDialog, + onMigrate = { screenModel.replaceRepo(dialog.newRepo) }, + oldRepo = dialog.oldRepo, + newRepo = dialog.newRepo, + ) + } } LaunchedEffect(Unit) { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionReposScreenModel.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionReposScreenModel.kt index d694618fb..4131bc6fd 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionReposScreenModel.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/ExtensionReposScreenModel.kt @@ -4,24 +4,29 @@ import androidx.compose.runtime.Immutable import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.screenModelScope import dev.icerock.moko.resources.StringResource -import eu.kanade.domain.extension.interactor.CreateExtensionRepo -import eu.kanade.domain.extension.interactor.DeleteExtensionRepo -import eu.kanade.domain.extension.interactor.GetExtensionRepos import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.toImmutableSet import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update +import mihon.domain.extensionrepo.interactor.CreateExtensionRepo +import mihon.domain.extensionrepo.interactor.DeleteExtensionRepo +import mihon.domain.extensionrepo.interactor.GetExtensionRepo +import mihon.domain.extensionrepo.interactor.ReplaceExtensionRepo +import mihon.domain.extensionrepo.interactor.UpdateExtensionRepo +import mihon.domain.extensionrepo.model.ExtensionRepo import tachiyomi.core.common.util.lang.launchIO import tachiyomi.i18n.MR import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get class ExtensionReposScreenModel( - private val getExtensionRepos: GetExtensionRepos = Injekt.get(), + private val getExtensionRepo: GetExtensionRepo = Injekt.get(), private val createExtensionRepo: CreateExtensionRepo = Injekt.get(), private val deleteExtensionRepo: DeleteExtensionRepo = Injekt.get(), + private val replaceExtensionRepo: ReplaceExtensionRepo = Injekt.get(), + private val updateExtensionRepo: UpdateExtensionRepo = Injekt.get(), ) : StateScreenModel(RepoScreenState.Loading) { private val _events: Channel = Channel(Int.MAX_VALUE) @@ -29,7 +34,7 @@ class ExtensionReposScreenModel( init { screenModelScope.launchIO { - getExtensionRepos.subscribe() + getExtensionRepo.subscribeAll() .collectLatest { repos -> mutableState.update { RepoScreenState.Success( @@ -43,25 +48,51 @@ class ExtensionReposScreenModel( /** * Creates and adds a new repo to the database. * - * @param name The name of the repo to create. + * @param baseUrl The baseUrl of the repo to create. */ - fun createRepo(name: String) { + fun createRepo(baseUrl: String) { screenModelScope.launchIO { - when (createExtensionRepo.await(name)) { - is CreateExtensionRepo.Result.InvalidUrl -> _events.send(RepoEvent.InvalidUrl) + when (val result = createExtensionRepo.await(baseUrl)) { + CreateExtensionRepo.Result.InvalidUrl -> _events.send(RepoEvent.InvalidUrl) + CreateExtensionRepo.Result.RepoAlreadyExists -> _events.send(RepoEvent.RepoAlreadyExists) + is CreateExtensionRepo.Result.DuplicateFingerprint -> { + showDialog(RepoDialog.Conflict(result.oldRepo, result.newRepo)) + } else -> {} } } } /** - * Deletes the given repo from the database. + * Inserts a repo to the database, replace a matching repo with the same signing key fingerprint if found. * - * @param repo The repo to delete. + * @param newRepo The repo to insert */ - fun deleteRepo(repo: String) { + fun replaceRepo(newRepo: ExtensionRepo) { screenModelScope.launchIO { - deleteExtensionRepo.await(repo) + replaceExtensionRepo.await(newRepo) + } + } + + /** + * Refreshes information for each repository. + */ + fun refreshRepos() { + val status = state.value + + if (status is RepoScreenState.Success) { + screenModelScope.launchIO { + updateExtensionRepo.awaitAll() + } + } + } + + /** + * Deletes the given repo from the database + */ + fun deleteRepo(baseUrl: String) { + screenModelScope.launchIO { + deleteExtensionRepo.await(baseUrl) } } @@ -87,11 +118,13 @@ class ExtensionReposScreenModel( sealed class RepoEvent { sealed class LocalizedMessage(val stringRes: StringResource) : RepoEvent() data object InvalidUrl : LocalizedMessage(MR.strings.invalid_repo_name) + data object RepoAlreadyExists : LocalizedMessage(MR.strings.error_repo_exists) } sealed class RepoDialog { data object Create : RepoDialog() data class Delete(val repo: String) : RepoDialog() + data class Conflict(val oldRepo: ExtensionRepo, val newRepo: ExtensionRepo) : RepoDialog() } sealed class RepoScreenState { @@ -101,7 +134,8 @@ sealed class RepoScreenState { @Immutable data class Success( - val repos: ImmutableSet, + val repos: ImmutableSet, + val oldRepos: ImmutableSet? = null, val dialog: RepoDialog? = null, ) : RepoScreenState() { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposContent.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposContent.kt index 7d837b32d..83be8846d 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposContent.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposContent.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.Label +import androidx.compose.material.icons.automirrored.outlined.OpenInNew import androidx.compose.material.icons.outlined.ContentCopy import androidx.compose.material.icons.outlined.Delete import androidx.compose.material3.ElevatedCard @@ -22,15 +23,17 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import eu.kanade.tachiyomi.util.system.copyToClipboard import kotlinx.collections.immutable.ImmutableSet +import mihon.domain.extensionrepo.model.ExtensionRepo import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource @Composable fun ExtensionReposContent( - repos: ImmutableSet, + repos: ImmutableSet, lazyListState: LazyListState, paddingValues: PaddingValues, + onOpenWebsite: (ExtensionRepo) -> Unit, onClickDelete: (String) -> Unit, modifier: Modifier = Modifier, ) { @@ -44,8 +47,9 @@ fun ExtensionReposContent( item { ExtensionRepoListItem( modifier = Modifier.animateItemPlacement(), - repo = it, - onDelete = { onClickDelete(it) }, + repo = it.name, + onOpenWebsite = { onOpenWebsite(it) }, + onDelete = { onClickDelete(it.baseUrl) }, ) } } @@ -55,6 +59,7 @@ fun ExtensionReposContent( @Composable private fun ExtensionRepoListItem( repo: String, + onOpenWebsite: () -> Unit, onDelete: () -> Unit, modifier: Modifier = Modifier, ) { @@ -74,13 +79,24 @@ private fun ExtensionRepoListItem( verticalAlignment = Alignment.CenterVertically, ) { Icon(imageVector = Icons.AutoMirrored.Outlined.Label, contentDescription = null) - Text(text = repo, modifier = Modifier.padding(start = MaterialTheme.padding.medium)) + Text( + text = repo, + modifier = Modifier.padding(start = MaterialTheme.padding.medium), + style = MaterialTheme.typography.titleMedium, + ) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End, ) { + IconButton(onClick = onOpenWebsite) { + Icon( + imageVector = Icons.AutoMirrored.Outlined.OpenInNew, + contentDescription = stringResource(MR.strings.action_open_in_browser), + ) + } + IconButton( onClick = { val url = "$repo/index.min.json" diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposDialogs.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposDialogs.kt index b4ef8b575..5022f44f0 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposDialogs.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposDialogs.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import kotlinx.collections.immutable.ImmutableSet import kotlinx.coroutines.delay +import mihon.domain.extensionrepo.model.ExtensionRepo import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource import kotlin.time.Duration.Companion.seconds @@ -24,12 +25,12 @@ import kotlin.time.Duration.Companion.seconds fun ExtensionRepoCreateDialog( onDismissRequest: () -> Unit, onCreate: (String) -> Unit, - repos: ImmutableSet, + repoUrls: ImmutableSet, ) { var name by remember { mutableStateOf("") } val focusRequester = remember { FocusRequester() } - val nameAlreadyExists = remember(name) { repos.contains(name) } + val nameAlreadyExists = remember(name) { repoUrls.contains(name) } AlertDialog( onDismissRequest = onDismissRequest, @@ -115,3 +116,36 @@ fun ExtensionRepoDeleteDialog( }, ) } + +@Composable +fun ExtensionRepoConflictDialog( + oldRepo: ExtensionRepo, + newRepo: ExtensionRepo, + onDismissRequest: () -> Unit, + onMigrate: () -> Unit, +) { + AlertDialog( + onDismissRequest = onDismissRequest, + confirmButton = { + TextButton( + onClick = { + onMigrate() + onDismissRequest() + }, + ) { + Text(text = stringResource(MR.strings.action_replace_repo)) + } + }, + dismissButton = { + TextButton(onClick = onDismissRequest) { + Text(text = stringResource(MR.strings.action_cancel)) + } + }, + title = { + Text(text = stringResource(MR.strings.action_replace_repo_title)) + }, + text = { + Text(text = stringResource(MR.strings.action_replace_repo_message, newRepo.name, oldRepo.name)) + }, + ) +} diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposScreen.kt index 1bd680d06..b07ba4101 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposScreen.kt @@ -5,12 +5,17 @@ package eu.kanade.presentation.more.settings.screen.browse.components import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Refresh +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import eu.kanade.presentation.category.components.CategoryFloatingActionButton import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.more.settings.screen.browse.RepoScreenState +import mihon.domain.extensionrepo.model.ExtensionRepo import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.components.material.padding @@ -23,7 +28,9 @@ import tachiyomi.presentation.core.util.plus fun ExtensionReposScreen( state: RepoScreenState.Success, onClickCreate: () -> Unit, + onOpenWebsite: (ExtensionRepo) -> Unit, onClickDelete: (String) -> Unit, + onClickRefresh: () -> Unit, navigateUp: () -> Unit, ) { val lazyListState = rememberLazyListState() @@ -33,6 +40,14 @@ fun ExtensionReposScreen( navigateUp = navigateUp, title = stringResource(MR.strings.label_extension_repos), scrollBehavior = scrollBehavior, + actions = { + IconButton(onClick = onClickRefresh) { + Icon( + imageVector = Icons.Outlined.Refresh, + contentDescription = stringResource(resource = MR.strings.action_webview_refresh), + ) + } + }, ) }, floatingActionButton = { @@ -55,6 +70,7 @@ fun ExtensionReposScreen( lazyListState = lazyListState, paddingValues = paddingValues + topSmallPaddingValues + PaddingValues(horizontal = MaterialTheme.padding.medium), + onOpenWebsite = onOpenWebsite, onClickDelete = onClickDelete, ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt index 154326514..7b617eb9b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt @@ -1,10 +1,18 @@ package eu.kanade.tachiyomi import android.content.Context +import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob import eu.kanade.tachiyomi.data.library.LibraryUpdateJob +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import logcat.LogPriority +import mihon.domain.extensionrepo.exception.SaveExtensionRepoException +import mihon.domain.extensionrepo.repository.ExtensionRepoRepository import tachiyomi.core.common.preference.Preference import tachiyomi.core.common.preference.PreferenceStore +import tachiyomi.core.common.util.lang.launchIO +import tachiyomi.core.common.util.system.logcat object Migrations { @@ -13,10 +21,12 @@ object Migrations { * * @return true if a migration is performed, false otherwise. */ - @Suppress("SameReturnValue") + @Suppress("SameReturnValue", "MagicNumber") fun upgrade( context: Context, preferenceStore: PreferenceStore, + sourcePreferences: SourcePreferences, + extensionRepoRepository: ExtensionRepoRepository, ): Boolean { val lastVersionCode = preferenceStore.getInt(Preference.appStateKey("last_version_code"), 0) val oldVersion = lastVersionCode.get() @@ -31,6 +41,27 @@ object Migrations { if (oldVersion == 0) { return false } + + val coroutineScope = CoroutineScope(Dispatchers.IO) + + if (oldVersion < 6) { + coroutineScope.launchIO { + for ((index, source) in sourcePreferences.extensionRepos().get().withIndex()) { + try { + extensionRepoRepository.upsertRepository( + source, + "Repo #${index + 1}", + null, + source, + "NOFINGERPRINT-${index + 1}", + ) + } catch (e: SaveExtensionRepoException) { + logcat(LogPriority.ERROR, e) { "Error Migrating Extension Repo with baseUrl: $source" } + } + } + sourcePreferences.extensionRepos().delete() + } + } } return false diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt index 0d79fd335..71e6251db 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionApi.kt @@ -1,7 +1,6 @@ package eu.kanade.tachiyomi.extension.api import android.content.Context -import eu.kanade.domain.source.service.SourcePreferences import eu.kanade.tachiyomi.extension.ExtensionManager import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.extension.model.LoadResult @@ -10,9 +9,14 @@ import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.awaitSuccess import eu.kanade.tachiyomi.network.parseAs +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import logcat.LogPriority +import mihon.domain.extensionrepo.interactor.GetExtensionRepo +import mihon.domain.extensionrepo.interactor.UpdateExtensionRepo +import mihon.domain.extensionrepo.model.ExtensionRepo import tachiyomi.core.common.preference.Preference import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.core.common.util.lang.withIOContext @@ -25,7 +29,8 @@ internal class ExtensionApi { private val networkService: NetworkHelper by injectLazy() private val preferenceStore: PreferenceStore by injectLazy() - private val sourcePreferences: SourcePreferences by injectLazy() + private val getExtensionRepo: GetExtensionRepo by injectLazy() + private val updateExtensionRepo: UpdateExtensionRepo by injectLazy() private val extensionManager: ExtensionManager by injectLazy() private val json: Json by injectLazy() @@ -35,11 +40,15 @@ internal class ExtensionApi { suspend fun findExtensions(): List { return withIOContext { - sourcePreferences.extensionRepos().get().flatMap { getExtensions(it) } + getExtensionRepo.getAll() + .map { async { getExtensions(it) } } + .awaitAll() + .flatten() } } - private suspend fun getExtensions(repoBaseUrl: String): List { + private suspend fun getExtensions(extRepo: ExtensionRepo): List { + val repoBaseUrl = extRepo.baseUrl return try { val response = networkService.client .newCall(GET("$repoBaseUrl/index.min.json")) @@ -67,6 +76,9 @@ internal class ExtensionApi { return null } + // Update extension repo details + updateExtensionRepo.awaitAll() + val extensions = if (fromAvailableExtensionList) { extensionManager.availableExtensionsFlow.value } else { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 87ab0b4c8..ae05ade4b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -134,6 +134,8 @@ class MainActivity : BaseActivity() { Migrations.upgrade( context = applicationContext, preferenceStore = Injekt.get(), + sourcePreferences = Injekt.get(), + extensionRepoRepository = Injekt.get(), ) } else { false diff --git a/data/src/main/java/mihon/data/repository/ExtensionRepoRepositoryImpl.kt b/data/src/main/java/mihon/data/repository/ExtensionRepoRepositoryImpl.kt new file mode 100644 index 000000000..65fcd149c --- /dev/null +++ b/data/src/main/java/mihon/data/repository/ExtensionRepoRepositoryImpl.kt @@ -0,0 +1,93 @@ +package mihon.data.repository + +import android.database.sqlite.SQLiteException +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import mihon.domain.extensionrepo.exception.SaveExtensionRepoException +import mihon.domain.extensionrepo.model.ExtensionRepo +import mihon.domain.extensionrepo.repository.ExtensionRepoRepository +import tachiyomi.data.DatabaseHandler + +class ExtensionRepoRepositoryImpl( + private val handler: DatabaseHandler, +) : ExtensionRepoRepository { + override fun subscribeAll(): Flow> { + return handler.subscribeToList { extension_reposQueries.findAll(::mapExtensionRepo) } + } + + override suspend fun getAll(): List { + return handler.awaitList { extension_reposQueries.findAll(::mapExtensionRepo) } + } + + override suspend fun getRepository(baseUrl: String): ExtensionRepo? { + return handler.awaitOneOrNull { extension_reposQueries.findOne(baseUrl, ::mapExtensionRepo) } + } + + override suspend fun getRepositoryBySigningKeyFingerprint(fingerprint: String): ExtensionRepo? { + return handler.awaitOneOrNull { + extension_reposQueries.findOneBySigningKeyFingerprint(fingerprint, ::mapExtensionRepo) + } + } + + override fun getCount(): Flow { + return handler.subscribeToOne { extension_reposQueries.count() }.map { it.toInt() } + } + + override suspend fun insertRepository( + baseUrl: String, + name: String, + shortName: String?, + website: String, + signingKeyFingerprint: String, + ) { + try { + handler.await { extension_reposQueries.insert(baseUrl, name, shortName, website, signingKeyFingerprint) } + } catch (ex: SQLiteException) { + throw SaveExtensionRepoException(ex) + } + } + + override suspend fun upsertRepository( + baseUrl: String, + name: String, + shortName: String?, + website: String, + signingKeyFingerprint: String, + ) { + try { + handler.await { extension_reposQueries.upsert(baseUrl, name, shortName, website, signingKeyFingerprint) } + } catch (ex: SQLiteException) { + throw SaveExtensionRepoException(ex) + } + } + + override suspend fun replaceRepository(newRepo: ExtensionRepo) { + handler.await { + extension_reposQueries.replace( + newRepo.baseUrl, + newRepo.name, + newRepo.shortName, + newRepo.website, + newRepo.signingKeyFingerprint, + ) + } + } + + override suspend fun deleteRepository(baseUrl: String) { + return handler.await { extension_reposQueries.delete(baseUrl) } + } + + private fun mapExtensionRepo( + baseUrl: String, + name: String, + shortName: String?, + website: String, + signingKeyFingerprint: String, + ): ExtensionRepo = ExtensionRepo( + baseUrl = baseUrl, + name = name, + shortName = shortName, + website = website, + signingKeyFingerprint = signingKeyFingerprint, + ) +} diff --git a/data/src/main/sqldelight/tachiyomi/data/extension_repos.sq b/data/src/main/sqldelight/tachiyomi/data/extension_repos.sq new file mode 100644 index 000000000..6db69132a --- /dev/null +++ b/data/src/main/sqldelight/tachiyomi/data/extension_repos.sq @@ -0,0 +1,57 @@ +CREATE TABLE extension_repos ( + base_url TEXT NOT NULL PRIMARY KEY, + name TEXT NOT NULL, + short_name TEXT, + website TEXT NOT NULL, + signing_key_fingerprint TEXT UNIQUE NOT NULL +); + +findOne: +SELECT * +FROM extension_repos +WHERE base_url = :base_url; + +findOneBySigningKeyFingerprint: +SELECT * +FROM extension_repos +WHERE signing_key_fingerprint = :fingerprint; + +findAll: +SELECT * +FROM extension_repos; + +count: +SELECT COUNT(*) +FROM extension_repos; + +insert: +INSERT INTO extension_repos(base_url, name, short_name, website, signing_key_fingerprint) +VALUES (:base_url, :name, :short_name, :website, :fingerprint); + +upsert: +INSERT INTO extension_repos(base_url, name, short_name, website, signing_key_fingerprint) +VALUES (:base_url, :name, :short_name, :website, :fingerprint) +ON CONFLICT(base_url) +DO UPDATE +SET + name = :name, + short_name = :short_name, + website =: website, + signing_key_fingerprint = :fingerprint +WHERE base_url = base_url; + +replace: +INSERT INTO extension_repos(base_url, name, short_name, website, signing_key_fingerprint) +VALUES (:base_url, :name, :short_name, :website, :fingerprint) +ON CONFLICT(signing_key_fingerprint) +DO UPDATE +SET + base_url = :base_url, + name = :name, + short_name = :short_name, + website =: website +WHERE signing_key_fingerprint = signing_key_fingerprint; + +delete: +DELETE FROM extension_repos +WHERE base_url = :base_url; diff --git a/data/src/main/sqldelight/tachiyomi/migrations/3.sqm b/data/src/main/sqldelight/tachiyomi/migrations/3.sqm new file mode 100644 index 000000000..ecf3b16a8 --- /dev/null +++ b/data/src/main/sqldelight/tachiyomi/migrations/3.sqm @@ -0,0 +1,8 @@ +-- Create ExtensionRepo table -- +CREATE TABLE extension_repos ( + base_url TEXT NOT NULL PRIMARY KEY, + name TEXT NOT NULL, + short_name TEXT, + website TEXT NOT NULL, + signing_key_fingerprint TEXT UNIQUE NOT NULL +); diff --git a/domain/build.gradle.kts b/domain/build.gradle.kts index dc8c33315..63f2b9df9 100644 --- a/domain/build.gradle.kts +++ b/domain/build.gradle.kts @@ -33,6 +33,7 @@ tasks { withType { kotlinOptions.freeCompilerArgs += listOf( "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", + "-Xcontext-receivers", ) } } diff --git a/domain/src/main/java/mihon/domain/extensionrepo/exception/SaveExtensionRepoException.kt b/domain/src/main/java/mihon/domain/extensionrepo/exception/SaveExtensionRepoException.kt new file mode 100644 index 000000000..4c6990be0 --- /dev/null +++ b/domain/src/main/java/mihon/domain/extensionrepo/exception/SaveExtensionRepoException.kt @@ -0,0 +1,10 @@ +package mihon.domain.extensionrepo.exception + +import java.io.IOException + +/** + * Exception to abstract over SQLiteException and SQLiteConstraintException for multiplatform. + * + * @param throwable the source throwable to include for tracing. + */ +class SaveExtensionRepoException(throwable: Throwable) : IOException("Error Saving Repository to Database", throwable) diff --git a/domain/src/main/java/mihon/domain/extensionrepo/interactor/CreateExtensionRepo.kt b/domain/src/main/java/mihon/domain/extensionrepo/interactor/CreateExtensionRepo.kt new file mode 100644 index 000000000..6285d4915 --- /dev/null +++ b/domain/src/main/java/mihon/domain/extensionrepo/interactor/CreateExtensionRepo.kt @@ -0,0 +1,81 @@ +package mihon.domain.extensionrepo.interactor + +import eu.kanade.tachiyomi.network.NetworkHelper +import logcat.LogPriority +import mihon.domain.extensionrepo.exception.SaveExtensionRepoException +import mihon.domain.extensionrepo.model.ExtensionRepo +import mihon.domain.extensionrepo.repository.ExtensionRepoRepository +import mihon.domain.extensionrepo.service.ExtensionRepoService +import okhttp3.OkHttpClient +import tachiyomi.core.common.util.system.logcat +import uy.kohesive.injekt.injectLazy + +class CreateExtensionRepo( + private val extensionRepoRepository: ExtensionRepoRepository, +) { + private val repoRegex = """^https://.*/index\.min\.json$""".toRegex() + + private val networkService: NetworkHelper by injectLazy() + + private val client: OkHttpClient + get() = networkService.client + + private val extensionRepoService = ExtensionRepoService(client) + + suspend fun await(repoUrl: String): Result { + if (!repoUrl.matches(repoRegex)) { + return Result.InvalidUrl + } + + val baseUrl = repoUrl.removeSuffix("/index.min.json") + return extensionRepoService.fetchRepoDetails(baseUrl)?.let { insert(it) } ?: Result.InvalidUrl + } + + private suspend fun insert(repo: ExtensionRepo): Result { + return try { + extensionRepoRepository.insertRepository( + repo.baseUrl, + repo.name, + repo.shortName, + repo.website, + repo.signingKeyFingerprint, + ) + Result.Success + } catch (e: SaveExtensionRepoException) { + logcat(LogPriority.WARN, e) { "SQL Conflict attempting to add new repository ${repo.baseUrl}" } + return handleInsertionError(repo) + } + } + + /** + * Error Handler for insert when there are trying to create new repositories + * + * SaveExtensionRepoException doesn't provide constraint info in exceptions. + * First check if the conflict was on primary key. if so return RepoAlreadyExists + * Then check if the conflict was on fingerprint. if so Return DuplicateFingerprint + * If neither are found, there was some other Error, and return Result.Error + * + * @param repo Extension Repo holder for passing to DB/Error Dialog + */ + @Suppress("ReturnCount") + private suspend fun handleInsertionError(repo: ExtensionRepo): Result { + val repoExists = extensionRepoRepository.getRepository(repo.baseUrl) + if (repoExists != null) { + return Result.RepoAlreadyExists + } + val matchingFingerprintRepo = + extensionRepoRepository.getRepositoryBySigningKeyFingerprint(repo.signingKeyFingerprint) + if (matchingFingerprintRepo != null) { + return Result.DuplicateFingerprint(matchingFingerprintRepo, repo) + } + return Result.Error + } + + sealed interface Result { + data class DuplicateFingerprint(val oldRepo: ExtensionRepo, val newRepo: ExtensionRepo) : Result + data object InvalidUrl : Result + data object RepoAlreadyExists : Result + data object Success : Result + data object Error : Result + } +} diff --git a/domain/src/main/java/mihon/domain/extensionrepo/interactor/DeleteExtensionRepo.kt b/domain/src/main/java/mihon/domain/extensionrepo/interactor/DeleteExtensionRepo.kt new file mode 100644 index 000000000..3be5c1ad9 --- /dev/null +++ b/domain/src/main/java/mihon/domain/extensionrepo/interactor/DeleteExtensionRepo.kt @@ -0,0 +1,11 @@ +package mihon.domain.extensionrepo.interactor + +import mihon.domain.extensionrepo.repository.ExtensionRepoRepository + +class DeleteExtensionRepo( + private val extensionRepoRepository: ExtensionRepoRepository, +) { + suspend fun await(baseUrl: String) { + extensionRepoRepository.deleteRepository(baseUrl) + } +} diff --git a/domain/src/main/java/mihon/domain/extensionrepo/interactor/GetExtensionRepo.kt b/domain/src/main/java/mihon/domain/extensionrepo/interactor/GetExtensionRepo.kt new file mode 100644 index 000000000..e85bd6c01 --- /dev/null +++ b/domain/src/main/java/mihon/domain/extensionrepo/interactor/GetExtensionRepo.kt @@ -0,0 +1,13 @@ +package mihon.domain.extensionrepo.interactor + +import kotlinx.coroutines.flow.Flow +import mihon.domain.extensionrepo.model.ExtensionRepo +import mihon.domain.extensionrepo.repository.ExtensionRepoRepository + +class GetExtensionRepo( + private val extensionRepoRepository: ExtensionRepoRepository, +) { + fun subscribeAll(): Flow> = extensionRepoRepository.subscribeAll() + + suspend fun getAll(): List = extensionRepoRepository.getAll() +} diff --git a/domain/src/main/java/mihon/domain/extensionrepo/interactor/GetExtensionRepoCount.kt b/domain/src/main/java/mihon/domain/extensionrepo/interactor/GetExtensionRepoCount.kt new file mode 100644 index 000000000..6ca59f10b --- /dev/null +++ b/domain/src/main/java/mihon/domain/extensionrepo/interactor/GetExtensionRepoCount.kt @@ -0,0 +1,9 @@ +package mihon.domain.extensionrepo.interactor + +import mihon.domain.extensionrepo.repository.ExtensionRepoRepository + +class GetExtensionRepoCount( + private val extensionRepoRepository: ExtensionRepoRepository, +) { + fun subscribe() = extensionRepoRepository.getCount() +} diff --git a/domain/src/main/java/mihon/domain/extensionrepo/interactor/ReplaceExtensionRepo.kt b/domain/src/main/java/mihon/domain/extensionrepo/interactor/ReplaceExtensionRepo.kt new file mode 100644 index 000000000..6543b8924 --- /dev/null +++ b/domain/src/main/java/mihon/domain/extensionrepo/interactor/ReplaceExtensionRepo.kt @@ -0,0 +1,12 @@ +package mihon.domain.extensionrepo.interactor + +import mihon.domain.extensionrepo.model.ExtensionRepo +import mihon.domain.extensionrepo.repository.ExtensionRepoRepository + +class ReplaceExtensionRepo( + private val extensionRepoRepository: ExtensionRepoRepository, +) { + suspend fun await(repo: ExtensionRepo) { + extensionRepoRepository.replaceRepository(repo) + } +} diff --git a/domain/src/main/java/mihon/domain/extensionrepo/interactor/UpdateExtensionRepo.kt b/domain/src/main/java/mihon/domain/extensionrepo/interactor/UpdateExtensionRepo.kt new file mode 100644 index 000000000..90e49307e --- /dev/null +++ b/domain/src/main/java/mihon/domain/extensionrepo/interactor/UpdateExtensionRepo.kt @@ -0,0 +1,33 @@ +package mihon.domain.extensionrepo.interactor + +import eu.kanade.tachiyomi.network.NetworkHelper +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope +import mihon.domain.extensionrepo.model.ExtensionRepo +import mihon.domain.extensionrepo.repository.ExtensionRepoRepository +import mihon.domain.extensionrepo.service.ExtensionRepoService + +class UpdateExtensionRepo( + private val extensionRepoRepository: ExtensionRepoRepository, + networkService: NetworkHelper, +) { + + private val extensionRepoService = ExtensionRepoService(networkService.client) + + suspend fun awaitAll() = coroutineScope { + extensionRepoRepository.getAll() + .map { async { await(it) } } + .awaitAll() + } + + suspend fun await(repo: ExtensionRepo) { + val newRepo = extensionRepoService.fetchRepoDetails(repo.baseUrl) ?: return + if ( + repo.signingKeyFingerprint.startsWith("NOFINGERPRINT") || + repo.signingKeyFingerprint == newRepo.signingKeyFingerprint + ) { + extensionRepoRepository.upsertRepository(newRepo) + } + } +} diff --git a/domain/src/main/java/mihon/domain/extensionrepo/model/ExtensionRepo.kt b/domain/src/main/java/mihon/domain/extensionrepo/model/ExtensionRepo.kt new file mode 100644 index 000000000..ec9ccca87 --- /dev/null +++ b/domain/src/main/java/mihon/domain/extensionrepo/model/ExtensionRepo.kt @@ -0,0 +1,9 @@ +package mihon.domain.extensionrepo.model + +data class ExtensionRepo( + val baseUrl: String, + val name: String, + val shortName: String?, + val website: String, + val signingKeyFingerprint: String, +) diff --git a/domain/src/main/java/mihon/domain/extensionrepo/repository/ExtensionRepoRepository.kt b/domain/src/main/java/mihon/domain/extensionrepo/repository/ExtensionRepoRepository.kt new file mode 100644 index 000000000..8551254be --- /dev/null +++ b/domain/src/main/java/mihon/domain/extensionrepo/repository/ExtensionRepoRepository.kt @@ -0,0 +1,47 @@ +package mihon.domain.extensionrepo.repository + +import kotlinx.coroutines.flow.Flow +import mihon.domain.extensionrepo.model.ExtensionRepo + +interface ExtensionRepoRepository { + + fun subscribeAll(): Flow> + + suspend fun getAll(): List + + suspend fun getRepository(baseUrl: String): ExtensionRepo? + + suspend fun getRepositoryBySigningKeyFingerprint(fingerprint: String): ExtensionRepo? + + fun getCount(): Flow + + suspend fun insertRepository( + baseUrl: String, + name: String, + shortName: String?, + website: String, + signingKeyFingerprint: String, + ) + + suspend fun upsertRepository( + baseUrl: String, + name: String, + shortName: String?, + website: String, + signingKeyFingerprint: String, + ) + + suspend fun upsertRepository(repo: ExtensionRepo) { + upsertRepository( + baseUrl = repo.baseUrl, + name = repo.name, + shortName = repo.shortName, + website = repo.website, + signingKeyFingerprint = repo.signingKeyFingerprint, + ) + } + + suspend fun replaceRepository(newRepo: ExtensionRepo) + + suspend fun deleteRepository(baseUrl: String) +} diff --git a/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoService.kt b/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoService.kt new file mode 100644 index 000000000..ca061304e --- /dev/null +++ b/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoService.kt @@ -0,0 +1,57 @@ +package mihon.domain.extensionrepo.service + +import androidx.core.net.toUri +import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.HttpException +import eu.kanade.tachiyomi.network.awaitSuccess +import eu.kanade.tachiyomi.network.parseAs +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive +import mihon.domain.extensionrepo.model.ExtensionRepo +import okhttp3.OkHttpClient +import tachiyomi.core.common.util.lang.withIOContext +import uy.kohesive.injekt.injectLazy + +class ExtensionRepoService( + private val client: OkHttpClient, +) { + + private val json: Json by injectLazy() + + suspend fun fetchRepoDetails( + repo: String, + ): ExtensionRepo? { + return withIOContext { + val url = "$repo/repo.json".toUri() + + try { + val response = with(json) { + client.newCall(GET(url.toString())) + .awaitSuccess() + .parseAs() + } + response["meta"] + ?.jsonObject + ?.let { jsonToExtensionRepo(baseUrl = repo, it) } + } catch (_: HttpException) { + null + } + } + } + + private fun jsonToExtensionRepo(baseUrl: String, obj: JsonObject): ExtensionRepo? { + return try { + ExtensionRepo( + baseUrl = baseUrl, + name = obj["name"]!!.jsonPrimitive.content, + shortName = obj["shortName"]?.jsonPrimitive?.content, + website = obj["website"]!!.jsonPrimitive.content, + signingKeyFingerprint = obj["signingKeyFingerprint"]!!.jsonPrimitive.content, + ) + } catch (_: NullPointerException) { + null + } + } +} diff --git a/i18n/src/commonMain/resources/MR/base/strings.xml b/i18n/src/commonMain/resources/MR/base/strings.xml index 7fc1832ca..23ce8a441 100644 --- a/i18n/src/commonMain/resources/MR/base/strings.xml +++ b/i18n/src/commonMain/resources/MR/base/strings.xml @@ -347,6 +347,9 @@ Invalid repo URL Do you wish to delete the repo \"%s\"? Open source repo + Replace + Signing Key Fingerprint Already Exists + Repository %1$s has the same Signing Key Fingerprint as %2$s.\nIf this is expected, %2$s will be replaced, otherwise contact your repo maintainer. Fullscreen From ba9cfd867c028551c0b0740922c5130b14455c9f Mon Sep 17 00:00:00 2001 From: Maddie Witman Date: Fri, 22 Mar 2024 21:10:18 -0400 Subject: [PATCH 127/212] Migrated from Accompanist Webview to KevinZou WebView (#569) * Migrated from Accompanist Webview to KevinZou WebView to preempt deprecation * Removed old webview from version library --- app/build.gradle.kts | 3 ++- .../presentation/webview/WebViewScreenContent.kt | 10 +++++----- gradle/compose.versions.toml | 1 - gradle/libs.versions.toml | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8b63752a0..59750e53b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -161,7 +161,6 @@ dependencies { debugImplementation(compose.ui.tooling) implementation(compose.ui.tooling.preview) implementation(compose.ui.util) - implementation(compose.accompanist.webview) implementation(compose.accompanist.systemuicontroller) implementation(androidx.paging.runtime) @@ -237,6 +236,8 @@ dependencies { implementation(libs.bundles.voyager) implementation(libs.compose.materialmotion) implementation(libs.swipe) + implementation(libs.compose.webview) + // Logging implementation(libs.logcat) diff --git a/app/src/main/java/eu/kanade/presentation/webview/WebViewScreenContent.kt b/app/src/main/java/eu/kanade/presentation/webview/WebViewScreenContent.kt index f1779da58..508790fff 100644 --- a/app/src/main/java/eu/kanade/presentation/webview/WebViewScreenContent.kt +++ b/app/src/main/java/eu/kanade/presentation/webview/WebViewScreenContent.kt @@ -28,11 +28,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.unit.dp -import com.google.accompanist.web.AccompanistWebViewClient -import com.google.accompanist.web.LoadingState -import com.google.accompanist.web.WebView -import com.google.accompanist.web.rememberWebViewNavigator -import com.google.accompanist.web.rememberWebViewState +import com.kevinnzou.web.AccompanistWebViewClient +import com.kevinnzou.web.LoadingState +import com.kevinnzou.web.WebView +import com.kevinnzou.web.rememberWebViewNavigator +import com.kevinnzou.web.rememberWebViewState import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.components.WarningBanner diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 5b629ff6f..b37960e0a 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -23,5 +23,4 @@ material-core = { module = "androidx.compose.material:material" } glance = "androidx.glance:glance-appwidget:1.0.0" -accompanist-webview = { module = "com.google.accompanist:accompanist-webview", version.ref = "accompanist" } accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a82efa20f..affef4ac6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -63,6 +63,7 @@ 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-materialmotion = "io.github.fornewid:material-motion-compose-core:1.2.0" +compose-webview = "io.github.kevinnzou:compose-webview:0.33.4" swipe = "me.saket.swipe:swipe:1.3.0" From 9672ea8b1b06f464800e310c96e060ead182f7ca Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 23 Mar 2024 17:27:57 +0600 Subject: [PATCH 128/212] Fix extension repo migration not triggering --- app/build.gradle.kts | 2 +- app/src/main/java/eu/kanade/tachiyomi/Migrations.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 59750e53b..fad306768 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,7 +22,7 @@ android { defaultConfig { applicationId = "app.mihon" - versionCode = 6 + versionCode = 7 versionName = "0.16.4" buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") diff --git a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt index 7b617eb9b..58ea5d29d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt @@ -44,7 +44,7 @@ object Migrations { val coroutineScope = CoroutineScope(Dispatchers.IO) - if (oldVersion < 6) { + if (oldVersion < 7) { coroutineScope.launchIO { for ((index, source) in sourcePreferences.extensionRepos().get().withIndex()) { try { From 8c437ceecf3c5d8d944a70439d3549e21d751736 Mon Sep 17 00:00:00 2001 From: MajorTanya <39014446+MajorTanya@users.noreply.github.com> Date: Sat, 23 Mar 2024 16:03:44 +0100 Subject: [PATCH 129/212] Refactor the ExtensionRepoService to use DTOs (#573) * Refactor the ExtensionRepoService to use DTOs Slightly refactored the `ExtensionRepoService` so it uses a DTO with `parseAs` to avoid parsing the JSON response by hand. The default Json instance Injekt provides here has `ignoreUnknownKeys` enabled, so the `ExtensionRepoMetaDto` only specifies the meta key of the response content. The extension function `toExtensionRepo` allows for mapping the new DTO to the `domain` `ExtensionRepo` data class. * Implement feedback - Removed SerialName of the ExtensionRepoMetaDto property and renamed it `meta`, same as the incoming attribute. - Added a more general catch clause that also logs the occurring Exception Detekt likes to complain about TooGenericExceptionCaught, hence the Suppress annotation on the function. --- .../extensionrepo/service/ExtensionRepoDto.kt | 27 ++++++++++++++++ .../service/ExtensionRepoService.kt | 32 +++++-------------- 2 files changed, 35 insertions(+), 24 deletions(-) create mode 100644 domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoDto.kt diff --git a/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoDto.kt b/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoDto.kt new file mode 100644 index 000000000..6a0a492de --- /dev/null +++ b/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoDto.kt @@ -0,0 +1,27 @@ +package mihon.domain.extensionrepo.service + +import kotlinx.serialization.Serializable +import mihon.domain.extensionrepo.model.ExtensionRepo + +@Serializable +data class ExtensionRepoMetaDto( + val meta: ExtensionRepoDto, +) + +@Serializable +data class ExtensionRepoDto( + val name: String, + val shortName: String?, + val website: String, + val signingKeyFingerprint: String, +) + +fun ExtensionRepoMetaDto.toExtensionRepo(baseUrl: String): ExtensionRepo { + return ExtensionRepo( + baseUrl = baseUrl, + name = meta.name, + shortName = meta.shortName, + website = meta.website, + signingKeyFingerprint = meta.signingKeyFingerprint, + ) +} diff --git a/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoService.kt b/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoService.kt index ca061304e..a8522ac40 100644 --- a/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoService.kt +++ b/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoService.kt @@ -2,16 +2,14 @@ package mihon.domain.extensionrepo.service import androidx.core.net.toUri import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.HttpException import eu.kanade.tachiyomi.network.awaitSuccess import eu.kanade.tachiyomi.network.parseAs import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.jsonObject -import kotlinx.serialization.json.jsonPrimitive +import logcat.LogPriority import mihon.domain.extensionrepo.model.ExtensionRepo import okhttp3.OkHttpClient import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.system.logcat import uy.kohesive.injekt.injectLazy class ExtensionRepoService( @@ -20,6 +18,7 @@ class ExtensionRepoService( private val json: Json by injectLazy() + @Suppress("TooGenericExceptionCaught") suspend fun fetchRepoDetails( repo: String, ): ExtensionRepo? { @@ -27,31 +26,16 @@ class ExtensionRepoService( val url = "$repo/repo.json".toUri() try { - val response = with(json) { + with(json) { client.newCall(GET(url.toString())) .awaitSuccess() - .parseAs() + .parseAs() + .toExtensionRepo(baseUrl = repo) } - response["meta"] - ?.jsonObject - ?.let { jsonToExtensionRepo(baseUrl = repo, it) } - } catch (_: HttpException) { + } catch (e: Exception) { + logcat(LogPriority.ERROR, e) { "Failed to fetch repo details" } null } } } - - private fun jsonToExtensionRepo(baseUrl: String, obj: JsonObject): ExtensionRepo? { - return try { - ExtensionRepo( - baseUrl = baseUrl, - name = obj["name"]!!.jsonPrimitive.content, - shortName = obj["shortName"]?.jsonPrimitive?.content, - website = obj["website"]!!.jsonPrimitive.content, - signingKeyFingerprint = obj["signingKeyFingerprint"]!!.jsonPrimitive.content, - ) - } catch (_: NullPointerException) { - null - } - } } From da20d00481f112802aade5d63fc1eca15c5496ba Mon Sep 17 00:00:00 2001 From: MajorTanya <39014446+MajorTanya@users.noreply.github.com> Date: Sat, 23 Mar 2024 16:03:55 +0100 Subject: [PATCH 130/212] Fix repo name used for URL instead of baseUrl (#572) * Fix repo name used for URL instead of baseUrl This applies to both the item being shown in the screen as well as the "copy to clipboard" button. Before, copying a repo url would return "The Repo Name/index.json.min". This PR fixes that. * Correct Misunderstanding Passing the whole ExtensionRepo data class through now, using the name for display purposes and the baseUrl for copying the URL. --- .../screen/browse/components/ExtensionReposContent.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposContent.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposContent.kt index 83be8846d..20a924d11 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposContent.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposContent.kt @@ -47,7 +47,7 @@ fun ExtensionReposContent( item { ExtensionRepoListItem( modifier = Modifier.animateItemPlacement(), - repo = it.name, + repo = it, onOpenWebsite = { onOpenWebsite(it) }, onDelete = { onClickDelete(it.baseUrl) }, ) @@ -58,7 +58,7 @@ fun ExtensionReposContent( @Composable private fun ExtensionRepoListItem( - repo: String, + repo: ExtensionRepo, onOpenWebsite: () -> Unit, onDelete: () -> Unit, modifier: Modifier = Modifier, @@ -80,7 +80,7 @@ private fun ExtensionRepoListItem( ) { Icon(imageVector = Icons.AutoMirrored.Outlined.Label, contentDescription = null) Text( - text = repo, + text = repo.name, modifier = Modifier.padding(start = MaterialTheme.padding.medium), style = MaterialTheme.typography.titleMedium, ) @@ -99,7 +99,7 @@ private fun ExtensionRepoListItem( IconButton( onClick = { - val url = "$repo/index.min.json" + val url = "${repo.baseUrl}/index.min.json" context.copyToClipboard(url, url) }, ) { From 05071b420572a8fa93a55ab02c743c7da4fd3b3a Mon Sep 17 00:00:00 2001 From: MajorTanya <39014446+MajorTanya@users.noreply.github.com> Date: Sat, 23 Mar 2024 23:21:19 +0100 Subject: [PATCH 131/212] Fix extension repo crash with TypeReference issue (#574) Fix by @AntsyLich. Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- app/src/main/java/eu/kanade/domain/DomainModule.kt | 2 +- .../domain/extensionrepo/interactor/CreateExtensionRepo.kt | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt index 1d2a214ac..2bea7586b 100644 --- a/app/src/main/java/eu/kanade/domain/DomainModule.kt +++ b/app/src/main/java/eu/kanade/domain/DomainModule.kt @@ -181,7 +181,7 @@ class DomainModule : InjektModule { addSingletonFactory { ExtensionRepoRepositoryImpl(get()) } addFactory { GetExtensionRepo(get()) } addFactory { GetExtensionRepoCount(get()) } - addFactory { CreateExtensionRepo(get()) } + addFactory { CreateExtensionRepo(get(), get()) } addFactory { DeleteExtensionRepo(get()) } addFactory { ReplaceExtensionRepo(get()) } addFactory { UpdateExtensionRepo(get(), get()) } diff --git a/domain/src/main/java/mihon/domain/extensionrepo/interactor/CreateExtensionRepo.kt b/domain/src/main/java/mihon/domain/extensionrepo/interactor/CreateExtensionRepo.kt index 6285d4915..2e6de446d 100644 --- a/domain/src/main/java/mihon/domain/extensionrepo/interactor/CreateExtensionRepo.kt +++ b/domain/src/main/java/mihon/domain/extensionrepo/interactor/CreateExtensionRepo.kt @@ -8,17 +8,15 @@ import mihon.domain.extensionrepo.repository.ExtensionRepoRepository import mihon.domain.extensionrepo.service.ExtensionRepoService import okhttp3.OkHttpClient import tachiyomi.core.common.util.system.logcat -import uy.kohesive.injekt.injectLazy class CreateExtensionRepo( private val extensionRepoRepository: ExtensionRepoRepository, + private val networkHelper: NetworkHelper, ) { private val repoRegex = """^https://.*/index\.min\.json$""".toRegex() - private val networkService: NetworkHelper by injectLazy() - private val client: OkHttpClient - get() = networkService.client + get() = networkHelper.client private val extensionRepoService = ExtensionRepoService(client) From e020ae5ed558e80742ef0ad8bfa0f69af0959d5a Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 24 Mar 2024 05:16:31 +0600 Subject: [PATCH 132/212] Fix more TypeReference issues and cleanup --- .../java/eu/kanade/domain/DomainModule.kt | 2 ++ .../java/eu/kanade/tachiyomi/Migrations.kt | 2 +- .../repository/ExtensionRepoRepositoryImpl.kt | 12 +++++------ .../interactor/CreateExtensionRepo.kt | 20 ++++++------------- .../interactor/DeleteExtensionRepo.kt | 4 ++-- .../interactor/GetExtensionRepo.kt | 6 +++--- .../interactor/GetExtensionRepoCount.kt | 4 ++-- .../interactor/ReplaceExtensionRepo.kt | 4 ++-- .../interactor/UpdateExtensionRepo.kt | 13 +++++------- .../repository/ExtensionRepoRepository.kt | 16 +++++++-------- .../service/ExtensionRepoService.kt | 4 +--- 11 files changed, 38 insertions(+), 49 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt index 2bea7586b..cace3e974 100644 --- a/app/src/main/java/eu/kanade/domain/DomainModule.kt +++ b/app/src/main/java/eu/kanade/domain/DomainModule.kt @@ -31,6 +31,7 @@ import mihon.domain.extensionrepo.interactor.GetExtensionRepoCount import mihon.domain.extensionrepo.interactor.ReplaceExtensionRepo import mihon.domain.extensionrepo.interactor.UpdateExtensionRepo import mihon.domain.extensionrepo.repository.ExtensionRepoRepository +import mihon.domain.extensionrepo.service.ExtensionRepoService import tachiyomi.data.category.CategoryRepositoryImpl import tachiyomi.data.chapter.ChapterRepositoryImpl import tachiyomi.data.history.HistoryRepositoryImpl @@ -179,6 +180,7 @@ class DomainModule : InjektModule { addFactory { TrustExtension(get()) } addSingletonFactory { ExtensionRepoRepositoryImpl(get()) } + addFactory { ExtensionRepoService(get(), get()) } addFactory { GetExtensionRepo(get()) } addFactory { GetExtensionRepoCount(get()) } addFactory { CreateExtensionRepo(get(), get()) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt index 58ea5d29d..7bb0042e2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt @@ -48,7 +48,7 @@ object Migrations { coroutineScope.launchIO { for ((index, source) in sourcePreferences.extensionRepos().get().withIndex()) { try { - extensionRepoRepository.upsertRepository( + extensionRepoRepository.upsertRepo( source, "Repo #${index + 1}", null, diff --git a/data/src/main/java/mihon/data/repository/ExtensionRepoRepositoryImpl.kt b/data/src/main/java/mihon/data/repository/ExtensionRepoRepositoryImpl.kt index 65fcd149c..24910d968 100644 --- a/data/src/main/java/mihon/data/repository/ExtensionRepoRepositoryImpl.kt +++ b/data/src/main/java/mihon/data/repository/ExtensionRepoRepositoryImpl.kt @@ -19,11 +19,11 @@ class ExtensionRepoRepositoryImpl( return handler.awaitList { extension_reposQueries.findAll(::mapExtensionRepo) } } - override suspend fun getRepository(baseUrl: String): ExtensionRepo? { + override suspend fun getRepo(baseUrl: String): ExtensionRepo? { return handler.awaitOneOrNull { extension_reposQueries.findOne(baseUrl, ::mapExtensionRepo) } } - override suspend fun getRepositoryBySigningKeyFingerprint(fingerprint: String): ExtensionRepo? { + override suspend fun getRepoBySigningKeyFingerprint(fingerprint: String): ExtensionRepo? { return handler.awaitOneOrNull { extension_reposQueries.findOneBySigningKeyFingerprint(fingerprint, ::mapExtensionRepo) } @@ -33,7 +33,7 @@ class ExtensionRepoRepositoryImpl( return handler.subscribeToOne { extension_reposQueries.count() }.map { it.toInt() } } - override suspend fun insertRepository( + override suspend fun insertRepo( baseUrl: String, name: String, shortName: String?, @@ -47,7 +47,7 @@ class ExtensionRepoRepositoryImpl( } } - override suspend fun upsertRepository( + override suspend fun upsertRepo( baseUrl: String, name: String, shortName: String?, @@ -61,7 +61,7 @@ class ExtensionRepoRepositoryImpl( } } - override suspend fun replaceRepository(newRepo: ExtensionRepo) { + override suspend fun replaceRepo(newRepo: ExtensionRepo) { handler.await { extension_reposQueries.replace( newRepo.baseUrl, @@ -73,7 +73,7 @@ class ExtensionRepoRepositoryImpl( } } - override suspend fun deleteRepository(baseUrl: String) { + override suspend fun deleteRepo(baseUrl: String) { return handler.await { extension_reposQueries.delete(baseUrl) } } diff --git a/domain/src/main/java/mihon/domain/extensionrepo/interactor/CreateExtensionRepo.kt b/domain/src/main/java/mihon/domain/extensionrepo/interactor/CreateExtensionRepo.kt index 2e6de446d..ec9ca6e72 100644 --- a/domain/src/main/java/mihon/domain/extensionrepo/interactor/CreateExtensionRepo.kt +++ b/domain/src/main/java/mihon/domain/extensionrepo/interactor/CreateExtensionRepo.kt @@ -1,37 +1,30 @@ package mihon.domain.extensionrepo.interactor -import eu.kanade.tachiyomi.network.NetworkHelper import logcat.LogPriority import mihon.domain.extensionrepo.exception.SaveExtensionRepoException import mihon.domain.extensionrepo.model.ExtensionRepo import mihon.domain.extensionrepo.repository.ExtensionRepoRepository import mihon.domain.extensionrepo.service.ExtensionRepoService -import okhttp3.OkHttpClient import tachiyomi.core.common.util.system.logcat class CreateExtensionRepo( - private val extensionRepoRepository: ExtensionRepoRepository, - private val networkHelper: NetworkHelper, + private val repository: ExtensionRepoRepository, + private val service: ExtensionRepoService, ) { private val repoRegex = """^https://.*/index\.min\.json$""".toRegex() - private val client: OkHttpClient - get() = networkHelper.client - - private val extensionRepoService = ExtensionRepoService(client) - suspend fun await(repoUrl: String): Result { if (!repoUrl.matches(repoRegex)) { return Result.InvalidUrl } val baseUrl = repoUrl.removeSuffix("/index.min.json") - return extensionRepoService.fetchRepoDetails(baseUrl)?.let { insert(it) } ?: Result.InvalidUrl + return service.fetchRepoDetails(baseUrl)?.let { insert(it) } ?: Result.InvalidUrl } private suspend fun insert(repo: ExtensionRepo): Result { return try { - extensionRepoRepository.insertRepository( + repository.insertRepo( repo.baseUrl, repo.name, repo.shortName, @@ -57,12 +50,11 @@ class CreateExtensionRepo( */ @Suppress("ReturnCount") private suspend fun handleInsertionError(repo: ExtensionRepo): Result { - val repoExists = extensionRepoRepository.getRepository(repo.baseUrl) + val repoExists = repository.getRepo(repo.baseUrl) if (repoExists != null) { return Result.RepoAlreadyExists } - val matchingFingerprintRepo = - extensionRepoRepository.getRepositoryBySigningKeyFingerprint(repo.signingKeyFingerprint) + val matchingFingerprintRepo = repository.getRepoBySigningKeyFingerprint(repo.signingKeyFingerprint) if (matchingFingerprintRepo != null) { return Result.DuplicateFingerprint(matchingFingerprintRepo, repo) } diff --git a/domain/src/main/java/mihon/domain/extensionrepo/interactor/DeleteExtensionRepo.kt b/domain/src/main/java/mihon/domain/extensionrepo/interactor/DeleteExtensionRepo.kt index 3be5c1ad9..4b4b678e1 100644 --- a/domain/src/main/java/mihon/domain/extensionrepo/interactor/DeleteExtensionRepo.kt +++ b/domain/src/main/java/mihon/domain/extensionrepo/interactor/DeleteExtensionRepo.kt @@ -3,9 +3,9 @@ package mihon.domain.extensionrepo.interactor import mihon.domain.extensionrepo.repository.ExtensionRepoRepository class DeleteExtensionRepo( - private val extensionRepoRepository: ExtensionRepoRepository, + private val repository: ExtensionRepoRepository, ) { suspend fun await(baseUrl: String) { - extensionRepoRepository.deleteRepository(baseUrl) + repository.deleteRepo(baseUrl) } } diff --git a/domain/src/main/java/mihon/domain/extensionrepo/interactor/GetExtensionRepo.kt b/domain/src/main/java/mihon/domain/extensionrepo/interactor/GetExtensionRepo.kt index e85bd6c01..25b919607 100644 --- a/domain/src/main/java/mihon/domain/extensionrepo/interactor/GetExtensionRepo.kt +++ b/domain/src/main/java/mihon/domain/extensionrepo/interactor/GetExtensionRepo.kt @@ -5,9 +5,9 @@ import mihon.domain.extensionrepo.model.ExtensionRepo import mihon.domain.extensionrepo.repository.ExtensionRepoRepository class GetExtensionRepo( - private val extensionRepoRepository: ExtensionRepoRepository, + private val repository: ExtensionRepoRepository, ) { - fun subscribeAll(): Flow> = extensionRepoRepository.subscribeAll() + fun subscribeAll(): Flow> = repository.subscribeAll() - suspend fun getAll(): List = extensionRepoRepository.getAll() + suspend fun getAll(): List = repository.getAll() } diff --git a/domain/src/main/java/mihon/domain/extensionrepo/interactor/GetExtensionRepoCount.kt b/domain/src/main/java/mihon/domain/extensionrepo/interactor/GetExtensionRepoCount.kt index 6ca59f10b..a7c4e7c6d 100644 --- a/domain/src/main/java/mihon/domain/extensionrepo/interactor/GetExtensionRepoCount.kt +++ b/domain/src/main/java/mihon/domain/extensionrepo/interactor/GetExtensionRepoCount.kt @@ -3,7 +3,7 @@ package mihon.domain.extensionrepo.interactor import mihon.domain.extensionrepo.repository.ExtensionRepoRepository class GetExtensionRepoCount( - private val extensionRepoRepository: ExtensionRepoRepository, + private val repository: ExtensionRepoRepository, ) { - fun subscribe() = extensionRepoRepository.getCount() + fun subscribe() = repository.getCount() } diff --git a/domain/src/main/java/mihon/domain/extensionrepo/interactor/ReplaceExtensionRepo.kt b/domain/src/main/java/mihon/domain/extensionrepo/interactor/ReplaceExtensionRepo.kt index 6543b8924..112ea701c 100644 --- a/domain/src/main/java/mihon/domain/extensionrepo/interactor/ReplaceExtensionRepo.kt +++ b/domain/src/main/java/mihon/domain/extensionrepo/interactor/ReplaceExtensionRepo.kt @@ -4,9 +4,9 @@ import mihon.domain.extensionrepo.model.ExtensionRepo import mihon.domain.extensionrepo.repository.ExtensionRepoRepository class ReplaceExtensionRepo( - private val extensionRepoRepository: ExtensionRepoRepository, + private val repository: ExtensionRepoRepository, ) { suspend fun await(repo: ExtensionRepo) { - extensionRepoRepository.replaceRepository(repo) + repository.replaceRepo(repo) } } diff --git a/domain/src/main/java/mihon/domain/extensionrepo/interactor/UpdateExtensionRepo.kt b/domain/src/main/java/mihon/domain/extensionrepo/interactor/UpdateExtensionRepo.kt index 90e49307e..a393e69d5 100644 --- a/domain/src/main/java/mihon/domain/extensionrepo/interactor/UpdateExtensionRepo.kt +++ b/domain/src/main/java/mihon/domain/extensionrepo/interactor/UpdateExtensionRepo.kt @@ -1,6 +1,5 @@ package mihon.domain.extensionrepo.interactor -import eu.kanade.tachiyomi.network.NetworkHelper import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope @@ -9,25 +8,23 @@ import mihon.domain.extensionrepo.repository.ExtensionRepoRepository import mihon.domain.extensionrepo.service.ExtensionRepoService class UpdateExtensionRepo( - private val extensionRepoRepository: ExtensionRepoRepository, - networkService: NetworkHelper, + private val repository: ExtensionRepoRepository, + private val service: ExtensionRepoService, ) { - private val extensionRepoService = ExtensionRepoService(networkService.client) - suspend fun awaitAll() = coroutineScope { - extensionRepoRepository.getAll() + repository.getAll() .map { async { await(it) } } .awaitAll() } suspend fun await(repo: ExtensionRepo) { - val newRepo = extensionRepoService.fetchRepoDetails(repo.baseUrl) ?: return + val newRepo = service.fetchRepoDetails(repo.baseUrl) ?: return if ( repo.signingKeyFingerprint.startsWith("NOFINGERPRINT") || repo.signingKeyFingerprint == newRepo.signingKeyFingerprint ) { - extensionRepoRepository.upsertRepository(newRepo) + repository.upsertRepo(newRepo) } } } diff --git a/domain/src/main/java/mihon/domain/extensionrepo/repository/ExtensionRepoRepository.kt b/domain/src/main/java/mihon/domain/extensionrepo/repository/ExtensionRepoRepository.kt index 8551254be..47be56dcf 100644 --- a/domain/src/main/java/mihon/domain/extensionrepo/repository/ExtensionRepoRepository.kt +++ b/domain/src/main/java/mihon/domain/extensionrepo/repository/ExtensionRepoRepository.kt @@ -9,13 +9,13 @@ interface ExtensionRepoRepository { suspend fun getAll(): List - suspend fun getRepository(baseUrl: String): ExtensionRepo? + suspend fun getRepo(baseUrl: String): ExtensionRepo? - suspend fun getRepositoryBySigningKeyFingerprint(fingerprint: String): ExtensionRepo? + suspend fun getRepoBySigningKeyFingerprint(fingerprint: String): ExtensionRepo? fun getCount(): Flow - suspend fun insertRepository( + suspend fun insertRepo( baseUrl: String, name: String, shortName: String?, @@ -23,7 +23,7 @@ interface ExtensionRepoRepository { signingKeyFingerprint: String, ) - suspend fun upsertRepository( + suspend fun upsertRepo( baseUrl: String, name: String, shortName: String?, @@ -31,8 +31,8 @@ interface ExtensionRepoRepository { signingKeyFingerprint: String, ) - suspend fun upsertRepository(repo: ExtensionRepo) { - upsertRepository( + suspend fun upsertRepo(repo: ExtensionRepo) { + upsertRepo( baseUrl = repo.baseUrl, name = repo.name, shortName = repo.shortName, @@ -41,7 +41,7 @@ interface ExtensionRepoRepository { ) } - suspend fun replaceRepository(newRepo: ExtensionRepo) + suspend fun replaceRepo(newRepo: ExtensionRepo) - suspend fun deleteRepository(baseUrl: String) + suspend fun deleteRepo(baseUrl: String) } diff --git a/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoService.kt b/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoService.kt index a8522ac40..09a52d908 100644 --- a/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoService.kt +++ b/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoService.kt @@ -10,14 +10,12 @@ import mihon.domain.extensionrepo.model.ExtensionRepo import okhttp3.OkHttpClient import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.system.logcat -import uy.kohesive.injekt.injectLazy class ExtensionRepoService( private val client: OkHttpClient, + private val json: Json, ) { - private val json: Json by injectLazy() - @Suppress("TooGenericExceptionCaught") suspend fun fetchRepoDetails( repo: String, From 6965e59a643c67a2bf81b3c69ec70268e5da5797 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 24 Mar 2024 05:54:27 +0600 Subject: [PATCH 133/212] Fix mishap in e020ae5ed558e80742ef0ad8bfa0f69af0959d5a --- .../domain/extensionrepo/service/ExtensionRepoService.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoService.kt b/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoService.kt index 09a52d908..8262961a7 100644 --- a/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoService.kt +++ b/domain/src/main/java/mihon/domain/extensionrepo/service/ExtensionRepoService.kt @@ -2,19 +2,20 @@ package mihon.domain.extensionrepo.service import androidx.core.net.toUri import eu.kanade.tachiyomi.network.GET +import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.awaitSuccess import eu.kanade.tachiyomi.network.parseAs import kotlinx.serialization.json.Json import logcat.LogPriority import mihon.domain.extensionrepo.model.ExtensionRepo -import okhttp3.OkHttpClient import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.system.logcat class ExtensionRepoService( - private val client: OkHttpClient, + networkHelper: NetworkHelper, private val json: Json, ) { + val client = networkHelper.client @Suppress("TooGenericExceptionCaught") suspend fun fetchRepoDetails( From 666d6aa117756f0a9a57b31f91b7acb0ee5d7409 Mon Sep 17 00:00:00 2001 From: Andreas Date: Mon, 25 Mar 2024 18:26:19 +0100 Subject: [PATCH 134/212] Rewrite Migrations (#577) * Rewrite Migrations * Fix Detekt errors * Do migrations synchronous * Filter and sort migrations * Review changes * Review changes 2 * Fix Detekt errors --- .../java/eu/kanade/tachiyomi/Migrations.kt | 69 ------------- .../kanade/tachiyomi/ui/main/MainActivity.kt | 35 ++++--- .../java/mihon/core/migration/Migration.kt | 19 ++++ .../mihon/core/migration/MigrationContext.kt | 10 ++ .../java/mihon/core/migration/Migrator.kt | 53 ++++++++++ .../core/migration/migrations/Migrations.kt | 10 ++ .../migrations/SetupBackupCreateMigration.kt | 16 ++++ .../migrations/SetupLibraryUpdateMigration.kt | 16 ++++ .../TrustExtensionRepositoryMigration.kt | 35 +++++++ .../java/mihon/core/migration/MigratorTest.kt | 96 +++++++++++++++++++ 10 files changed, 275 insertions(+), 84 deletions(-) delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/Migrations.kt create mode 100644 app/src/main/java/mihon/core/migration/Migration.kt create mode 100644 app/src/main/java/mihon/core/migration/MigrationContext.kt create mode 100644 app/src/main/java/mihon/core/migration/Migrator.kt create mode 100644 app/src/main/java/mihon/core/migration/migrations/Migrations.kt create mode 100644 app/src/main/java/mihon/core/migration/migrations/SetupBackupCreateMigration.kt create mode 100644 app/src/main/java/mihon/core/migration/migrations/SetupLibraryUpdateMigration.kt create mode 100644 app/src/main/java/mihon/core/migration/migrations/TrustExtensionRepositoryMigration.kt create mode 100644 app/src/test/java/mihon/core/migration/MigratorTest.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt b/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt deleted file mode 100644 index 7bb0042e2..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/Migrations.kt +++ /dev/null @@ -1,69 +0,0 @@ -package eu.kanade.tachiyomi - -import android.content.Context -import eu.kanade.domain.source.service.SourcePreferences -import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob -import eu.kanade.tachiyomi.data.library.LibraryUpdateJob -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import logcat.LogPriority -import mihon.domain.extensionrepo.exception.SaveExtensionRepoException -import mihon.domain.extensionrepo.repository.ExtensionRepoRepository -import tachiyomi.core.common.preference.Preference -import tachiyomi.core.common.preference.PreferenceStore -import tachiyomi.core.common.util.lang.launchIO -import tachiyomi.core.common.util.system.logcat - -object Migrations { - - /** - * Performs a migration when the application is updated. - * - * @return true if a migration is performed, false otherwise. - */ - @Suppress("SameReturnValue", "MagicNumber") - fun upgrade( - context: Context, - preferenceStore: PreferenceStore, - sourcePreferences: SourcePreferences, - extensionRepoRepository: ExtensionRepoRepository, - ): Boolean { - val lastVersionCode = preferenceStore.getInt(Preference.appStateKey("last_version_code"), 0) - val oldVersion = lastVersionCode.get() - if (oldVersion < BuildConfig.VERSION_CODE) { - lastVersionCode.set(BuildConfig.VERSION_CODE) - - // Always set up background tasks to ensure they're running - LibraryUpdateJob.setupTask(context) - BackupCreateJob.setupTask(context) - - // Fresh install - if (oldVersion == 0) { - return false - } - - val coroutineScope = CoroutineScope(Dispatchers.IO) - - if (oldVersion < 7) { - coroutineScope.launchIO { - for ((index, source) in sourcePreferences.extensionRepos().get().withIndex()) { - try { - extensionRepoRepository.upsertRepo( - source, - "Repo #${index + 1}", - null, - source, - "NOFINGERPRINT-${index + 1}", - ) - } catch (e: SaveExtensionRepoException) { - logcat(LogPriority.ERROR, e) { "Error Migrating Extension Repo with baseUrl: $source" } - } - } - sourcePreferences.extensionRepos().delete() - } - } - } - - return false - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index ae05ade4b..d49a7fc68 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -50,8 +50,6 @@ import cafe.adriel.voyager.navigator.NavigatorDisposeBehavior import cafe.adriel.voyager.navigator.currentOrThrow import com.google.accompanist.systemuicontroller.rememberSystemUiController import eu.kanade.domain.base.BasePreferences -import eu.kanade.domain.source.service.SourcePreferences -import eu.kanade.domain.ui.UiPreferences import eu.kanade.presentation.components.AppStateBanners import eu.kanade.presentation.components.DownloadedOnlyBannerBackgroundColor import eu.kanade.presentation.components.IncognitoModeBannerBackgroundColor @@ -61,7 +59,6 @@ import eu.kanade.presentation.more.settings.screen.data.RestoreBackupScreen import eu.kanade.presentation.util.AssistContentScreen import eu.kanade.presentation.util.DefaultNavigatorScreenTransition import eu.kanade.tachiyomi.BuildConfig -import eu.kanade.tachiyomi.Migrations import eu.kanade.tachiyomi.data.cache.ChapterCache import eu.kanade.tachiyomi.data.download.DownloadCache import eu.kanade.tachiyomi.data.notification.NotificationReceiver @@ -89,7 +86,11 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import logcat.LogPriority +import mihon.core.migration.Migrator +import mihon.core.migration.migrations.migrations import tachiyomi.core.common.Constants +import tachiyomi.core.common.preference.Preference +import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.library.service.LibraryPreferences @@ -105,9 +106,7 @@ import androidx.compose.ui.graphics.Color.Companion as ComposeColor class MainActivity : BaseActivity() { - private val sourcePreferences: SourcePreferences by injectLazy() private val libraryPreferences: LibraryPreferences by injectLazy() - private val uiPreferences: UiPreferences by injectLazy() private val preferences: BasePreferences by injectLazy() private val downloadCache: DownloadCache by injectLazy() @@ -130,16 +129,7 @@ class MainActivity : BaseActivity() { super.onCreate(savedInstanceState) - val didMigration = if (isLaunch) { - Migrations.upgrade( - context = applicationContext, - preferenceStore = Injekt.get(), - sourcePreferences = Injekt.get(), - extensionRepoRepository = Injekt.get(), - ) - } else { - false - } + val didMigration = migrate() // Do not let the launcher create a new activity http://stackoverflow.com/questions/16283079 if (!isTaskRoot) { @@ -350,6 +340,21 @@ class MainActivity : BaseActivity() { } } + private fun migrate(): Boolean { + val preferenceStore = Injekt.get() + val preference = preferenceStore.getInt(Preference.appStateKey("last_version_code"), 0) + logcat { "Migration from ${preference.get()} to ${BuildConfig.VERSION_CODE}" } + return Migrator.migrate( + old = preference.get(), + new = BuildConfig.VERSION_CODE, + migrations = migrations, + onMigrationComplete = { + logcat { "Updating last version to ${BuildConfig.VERSION_CODE}" } + preference.set(BuildConfig.VERSION_CODE) + }, + ) + } + /** * Sets custom splash screen exit animation on devices prior to Android 12. * diff --git a/app/src/main/java/mihon/core/migration/Migration.kt b/app/src/main/java/mihon/core/migration/Migration.kt new file mode 100644 index 000000000..2fa04d1c9 --- /dev/null +++ b/app/src/main/java/mihon/core/migration/Migration.kt @@ -0,0 +1,19 @@ +package mihon.core.migration + +interface Migration { + val version: Float + + suspend operator fun invoke(migrationContext: MigrationContext): Boolean + + companion object { + const val ALWAYS = -1f + + fun of(version: Float, action: suspend (MigrationContext) -> Boolean): Migration = object : Migration { + override val version: Float = version + + override suspend operator fun invoke(migrationContext: MigrationContext): Boolean { + return action(migrationContext) + } + } + } +} diff --git a/app/src/main/java/mihon/core/migration/MigrationContext.kt b/app/src/main/java/mihon/core/migration/MigrationContext.kt new file mode 100644 index 000000000..68ddf6464 --- /dev/null +++ b/app/src/main/java/mihon/core/migration/MigrationContext.kt @@ -0,0 +1,10 @@ +package mihon.core.migration + +import uy.kohesive.injekt.Injekt + +class MigrationContext { + + inline fun get(): T? { + return Injekt.getInstanceOrNull(T::class.java) + } +} diff --git a/app/src/main/java/mihon/core/migration/Migrator.kt b/app/src/main/java/mihon/core/migration/Migrator.kt new file mode 100644 index 000000000..86288e2a0 --- /dev/null +++ b/app/src/main/java/mihon/core/migration/Migrator.kt @@ -0,0 +1,53 @@ +package mihon.core.migration + +import kotlinx.coroutines.runBlocking +import tachiyomi.core.common.util.system.logcat + +object Migrator { + + @SuppressWarnings("ReturnCount") + fun migrate( + old: Int, + new: Int, + migrations: List, + dryrun: Boolean = false, + onMigrationComplete: () -> Unit + ): Boolean { + val migrationContext = MigrationContext() + + if (old == 0) { + return migrationContext.migrate( + migrations = migrations.filter { it.isAlways() }, + dryrun = dryrun + ) + .also { onMigrationComplete() } + } + + if (old >= new) { + return false + } + + return migrationContext.migrate( + migrations = migrations.filter { it.isAlways() || it.version.toInt() in (old + 1)..new }, + dryrun = dryrun + ) + .also { onMigrationComplete() } + } + + private fun Migration.isAlways() = version == Migration.ALWAYS + + @SuppressWarnings("MaxLineLength") + private fun MigrationContext.migrate(migrations: List, dryrun: Boolean): Boolean { + return migrations.sortedBy { it.version } + .map { migration -> + if (!dryrun) { + logcat { "Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }" } + runBlocking { migration(this@migrate) } + } else { + logcat { "(Dry-run) Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }" } + true + } + } + .reduce { acc, b -> acc || b } + } +} diff --git a/app/src/main/java/mihon/core/migration/migrations/Migrations.kt b/app/src/main/java/mihon/core/migration/migrations/Migrations.kt new file mode 100644 index 000000000..821434e2e --- /dev/null +++ b/app/src/main/java/mihon/core/migration/migrations/Migrations.kt @@ -0,0 +1,10 @@ +package mihon.core.migration.migrations + +import mihon.core.migration.Migration + +val migrations: List + get() = listOf( + SetupBackupCreateMigration(), + SetupLibraryUpdateMigration(), + TrustExtensionRepositoryMigration(), + ) diff --git a/app/src/main/java/mihon/core/migration/migrations/SetupBackupCreateMigration.kt b/app/src/main/java/mihon/core/migration/migrations/SetupBackupCreateMigration.kt new file mode 100644 index 000000000..44c0b557c --- /dev/null +++ b/app/src/main/java/mihon/core/migration/migrations/SetupBackupCreateMigration.kt @@ -0,0 +1,16 @@ +package mihon.core.migration.migrations + +import eu.kanade.tachiyomi.App +import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob +import mihon.core.migration.Migration +import mihon.core.migration.MigrationContext + +class SetupBackupCreateMigration : Migration { + override val version: Float = Migration.ALWAYS + + override suspend fun invoke(migrationContext: MigrationContext): Boolean { + val context = migrationContext.get() ?: return false + BackupCreateJob.setupTask(context) + return true + } +} diff --git a/app/src/main/java/mihon/core/migration/migrations/SetupLibraryUpdateMigration.kt b/app/src/main/java/mihon/core/migration/migrations/SetupLibraryUpdateMigration.kt new file mode 100644 index 000000000..65482cd89 --- /dev/null +++ b/app/src/main/java/mihon/core/migration/migrations/SetupLibraryUpdateMigration.kt @@ -0,0 +1,16 @@ +package mihon.core.migration.migrations + +import eu.kanade.tachiyomi.App +import eu.kanade.tachiyomi.data.library.LibraryUpdateJob +import mihon.core.migration.Migration +import mihon.core.migration.MigrationContext + +class SetupLibraryUpdateMigration : Migration { + override val version: Float = Migration.ALWAYS + + override suspend fun invoke(migrationContext: MigrationContext): Boolean { + val context = migrationContext.get() ?: return false + LibraryUpdateJob.setupTask(context) + return true + } +} diff --git a/app/src/main/java/mihon/core/migration/migrations/TrustExtensionRepositoryMigration.kt b/app/src/main/java/mihon/core/migration/migrations/TrustExtensionRepositoryMigration.kt new file mode 100644 index 000000000..cacba046f --- /dev/null +++ b/app/src/main/java/mihon/core/migration/migrations/TrustExtensionRepositoryMigration.kt @@ -0,0 +1,35 @@ +package mihon.core.migration.migrations + +import eu.kanade.domain.source.service.SourcePreferences +import logcat.LogPriority +import mihon.core.migration.Migration +import mihon.core.migration.MigrationContext +import mihon.domain.extensionrepo.exception.SaveExtensionRepoException +import mihon.domain.extensionrepo.repository.ExtensionRepoRepository +import tachiyomi.core.common.util.lang.withIOContext +import tachiyomi.core.common.util.system.logcat + +class TrustExtensionRepositoryMigration : Migration { + override val version: Float = 7f + + override suspend fun invoke(migrationContext: MigrationContext): Boolean = withIOContext { + val sourcePreferences = migrationContext.get() ?: return@withIOContext false + val extensionRepositoryRepository = + migrationContext.get() ?: return@withIOContext false + for ((index, source) in sourcePreferences.extensionRepos().get().withIndex()) { + try { + extensionRepositoryRepository.upsertRepo( + source, + "Repo #${index + 1}", + null, + source, + "NOFINGERPRINT-${index + 1}", + ) + } catch (e: SaveExtensionRepoException) { + logcat(LogPriority.ERROR, e) { "Error Migrating Extension Repo with baseUrl: $source" } + } + } + sourcePreferences.extensionRepos().delete() + return@withIOContext true + } +} diff --git a/app/src/test/java/mihon/core/migration/MigratorTest.kt b/app/src/test/java/mihon/core/migration/MigratorTest.kt new file mode 100644 index 000000000..89fe4db8c --- /dev/null +++ b/app/src/test/java/mihon/core/migration/MigratorTest.kt @@ -0,0 +1,96 @@ +package mihon.core.migration + +import io.mockk.Called +import io.mockk.spyk +import io.mockk.verify +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class MigratorTest { + + @Test + fun initialVersion() { + val onMigrationComplete: () -> Unit = {} + val onMigrationCompleteSpy = spyk(onMigrationComplete) + val didMigration = Migrator.migrate( + old = 0, + new = 1, + migrations = listOf(Migration.of(Migration.ALWAYS) { true }, Migration.of(2f) { false }), + onMigrationComplete = onMigrationCompleteSpy + ) + verify { onMigrationCompleteSpy() } + Assertions.assertTrue(didMigration) + } + + @Test + fun sameVersion() { + val onMigrationComplete: () -> Unit = {} + val onMigrationCompleteSpy = spyk(onMigrationComplete) + val didMigration = Migrator.migrate( + old = 1, + new = 1, + migrations = listOf(Migration.of(Migration.ALWAYS) { true }, Migration.of(2f) { true }), + onMigrationComplete = onMigrationCompleteSpy + ) + verify { onMigrationCompleteSpy wasNot Called } + Assertions.assertFalse(didMigration) + } + + @Test + fun smallMigration() { + val onMigrationComplete: () -> Unit = {} + val onMigrationCompleteSpy = spyk(onMigrationComplete) + val didMigration = Migrator.migrate( + old = 1, + new = 2, + migrations = listOf(Migration.of(Migration.ALWAYS) { true }, Migration.of(2f) { true }), + onMigrationComplete = onMigrationCompleteSpy + ) + verify { onMigrationCompleteSpy() } + Assertions.assertTrue(didMigration) + } + + @Test + fun largeMigration() { + val onMigrationComplete: () -> Unit = {} + val onMigrationCompleteSpy = spyk(onMigrationComplete) + val input = listOf( + Migration.of(Migration.ALWAYS) { true }, + Migration.of(2f) { true }, + Migration.of(3f) { true }, + Migration.of(4f) { true }, + Migration.of(5f) { true }, + Migration.of(6f) { true }, + Migration.of(7f) { true }, + Migration.of(8f) { true }, + Migration.of(9f) { true }, + Migration.of(10f) { true }, + ) + val didMigration = Migrator.migrate( + old = 1, + new = 10, + migrations = input, + onMigrationComplete = onMigrationCompleteSpy + ) + verify { onMigrationCompleteSpy() } + Assertions.assertTrue(didMigration) + } + + @Test + fun withinRangeMigration() { + val onMigrationComplete: () -> Unit = {} + val onMigrationCompleteSpy = spyk(onMigrationComplete) + val didMigration = Migrator.migrate( + old = 1, + new = 2, + migrations = listOf( + Migration.of(Migration.ALWAYS) { true }, + Migration.of(2f) { true }, + Migration.of(3f) { false } + ), + onMigrationComplete = onMigrationCompleteSpy + ) + verify { onMigrationCompleteSpy() } + Assertions.assertTrue(didMigration) + } +} From 0265c16eb239518d52b7e9fb4200b5b003418d5d Mon Sep 17 00:00:00 2001 From: Andreas Date: Thu, 28 Mar 2024 19:36:33 +0100 Subject: [PATCH 135/212] Migrator improvements (#588) --- app/build.gradle.kts | 2 + app/src/main/java/eu/kanade/tachiyomi/App.kt | 21 +++ .../kanade/tachiyomi/ui/main/MainActivity.kt | 22 +-- .../java/mihon/core/migration/Migration.kt | 3 + .../migration/MigrationCompletedListener.kt | 3 + .../mihon/core/migration/MigrationContext.kt | 2 +- .../core/migration/MigrationJobFactory.kt | 30 +++ .../mihon/core/migration/MigrationStrategy.kt | 55 ++++++ .../migration/MigrationStrategyFactory.kt | 23 +++ .../java/mihon/core/migration/Migrator.kt | 62 +++--- .../java/mihon/core/migration/MigratorTest.kt | 177 ++++++++++++------ 11 files changed, 284 insertions(+), 116 deletions(-) create mode 100644 app/src/main/java/mihon/core/migration/MigrationCompletedListener.kt create mode 100644 app/src/main/java/mihon/core/migration/MigrationJobFactory.kt create mode 100644 app/src/main/java/mihon/core/migration/MigrationStrategy.kt create mode 100644 app/src/main/java/mihon/core/migration/MigrationStrategyFactory.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index fad306768..9edadd5fc 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -254,6 +254,8 @@ dependencies { // For detecting memory leaks; see https://square.github.io/leakcanary/ // debugImplementation(libs.leakcanary.android) implementation(libs.leakcanary.plumber) + + testImplementation(kotlinx.coroutines.test) } androidComponents { diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index 6c29060c1..3ffc83ebd 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -50,8 +50,12 @@ import kotlinx.coroutines.flow.onEach import logcat.AndroidLogcatLogger import logcat.LogPriority import logcat.LogcatLogger +import mihon.core.migration.Migrator +import mihon.core.migration.migrations.migrations import org.conscrypt.Conscrypt import tachiyomi.core.common.i18n.stringResource +import tachiyomi.core.common.preference.Preference +import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.core.common.util.system.logcat import tachiyomi.i18n.MR import tachiyomi.presentation.widget.WidgetManager @@ -131,6 +135,23 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor if (!LogcatLogger.isInstalled && networkPreferences.verboseLogging().get()) { LogcatLogger.install(AndroidLogcatLogger(LogPriority.VERBOSE)) } + + initializeMigrator() + } + + private fun initializeMigrator() { + val preferenceStore = Injekt.get() + val preference = preferenceStore.getInt(Preference.appStateKey("last_version_code"), 0) + logcat { "Migration from ${preference.get()} to ${BuildConfig.VERSION_CODE}" } + Migrator.initialize( + old = preference.get(), + new = BuildConfig.VERSION_CODE, + migrations = migrations, + onMigrationComplete = { + logcat { "Updating last version to ${BuildConfig.VERSION_CODE}" } + preference.set(BuildConfig.VERSION_CODE) + }, + ) } override fun newImageLoader(context: Context): ImageLoader { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index d49a7fc68..39c9dd5c7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -87,10 +87,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import logcat.LogPriority import mihon.core.migration.Migrator -import mihon.core.migration.migrations.migrations import tachiyomi.core.common.Constants -import tachiyomi.core.common.preference.Preference -import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.library.service.LibraryPreferences @@ -99,8 +96,6 @@ import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.collectAsState -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy import androidx.compose.ui.graphics.Color.Companion as ComposeColor @@ -129,7 +124,7 @@ class MainActivity : BaseActivity() { super.onCreate(savedInstanceState) - val didMigration = migrate() + val didMigration = Migrator.awaitAndRelease() // Do not let the launcher create a new activity http://stackoverflow.com/questions/16283079 if (!isTaskRoot) { @@ -340,21 +335,6 @@ class MainActivity : BaseActivity() { } } - private fun migrate(): Boolean { - val preferenceStore = Injekt.get() - val preference = preferenceStore.getInt(Preference.appStateKey("last_version_code"), 0) - logcat { "Migration from ${preference.get()} to ${BuildConfig.VERSION_CODE}" } - return Migrator.migrate( - old = preference.get(), - new = BuildConfig.VERSION_CODE, - migrations = migrations, - onMigrationComplete = { - logcat { "Updating last version to ${BuildConfig.VERSION_CODE}" } - preference.set(BuildConfig.VERSION_CODE) - }, - ) - } - /** * Sets custom splash screen exit animation on devices prior to Android 12. * diff --git a/app/src/main/java/mihon/core/migration/Migration.kt b/app/src/main/java/mihon/core/migration/Migration.kt index 2fa04d1c9..2c7283bb2 100644 --- a/app/src/main/java/mihon/core/migration/Migration.kt +++ b/app/src/main/java/mihon/core/migration/Migration.kt @@ -5,6 +5,9 @@ interface Migration { suspend operator fun invoke(migrationContext: MigrationContext): Boolean + val isAlways: Boolean + get() = version == ALWAYS + companion object { const val ALWAYS = -1f diff --git a/app/src/main/java/mihon/core/migration/MigrationCompletedListener.kt b/app/src/main/java/mihon/core/migration/MigrationCompletedListener.kt new file mode 100644 index 000000000..52f3324a2 --- /dev/null +++ b/app/src/main/java/mihon/core/migration/MigrationCompletedListener.kt @@ -0,0 +1,3 @@ +package mihon.core.migration + +typealias MigrationCompletedListener = () -> Unit diff --git a/app/src/main/java/mihon/core/migration/MigrationContext.kt b/app/src/main/java/mihon/core/migration/MigrationContext.kt index 68ddf6464..3d0473f27 100644 --- a/app/src/main/java/mihon/core/migration/MigrationContext.kt +++ b/app/src/main/java/mihon/core/migration/MigrationContext.kt @@ -2,7 +2,7 @@ package mihon.core.migration import uy.kohesive.injekt.Injekt -class MigrationContext { +class MigrationContext(val dryrun: Boolean) { inline fun get(): T? { return Injekt.getInstanceOrNull(T::class.java) diff --git a/app/src/main/java/mihon/core/migration/MigrationJobFactory.kt b/app/src/main/java/mihon/core/migration/MigrationJobFactory.kt new file mode 100644 index 000000000..846ebc277 --- /dev/null +++ b/app/src/main/java/mihon/core/migration/MigrationJobFactory.kt @@ -0,0 +1,30 @@ +package mihon.core.migration + +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.async +import tachiyomi.core.common.util.system.logcat + +class MigrationJobFactory( + private val migrationContext: MigrationContext, + private val scope: CoroutineScope +) { + + @SuppressWarnings("MaxLineLength") + fun create(migrations: List): Deferred = with(scope) { + return migrations.sortedBy { it.version } + .fold(CompletableDeferred(true)) { acc: Deferred, migration: Migration -> + if (!migrationContext.dryrun) { + logcat { "Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }" } + async { + val prev = acc.await() + migration(migrationContext) || prev + } + } else { + logcat { "(Dry-run) Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }" } + CompletableDeferred(true) + } + } + } +} diff --git a/app/src/main/java/mihon/core/migration/MigrationStrategy.kt b/app/src/main/java/mihon/core/migration/MigrationStrategy.kt new file mode 100644 index 000000000..9fd5f4f91 --- /dev/null +++ b/app/src/main/java/mihon/core/migration/MigrationStrategy.kt @@ -0,0 +1,55 @@ +package mihon.core.migration + +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.launch + +interface MigrationStrategy { + operator fun invoke(migrations: List): Deferred +} + +class DefaultMigrationStrategy( + private val migrationJobFactory: MigrationJobFactory, + private val migrationCompletedListener: MigrationCompletedListener, + private val scope: CoroutineScope +) : MigrationStrategy { + + override operator fun invoke(migrations: List): Deferred = with(scope) { + if (migrations.isEmpty()) { + return@with CompletableDeferred(false) + } + + val chain = migrationJobFactory.create(migrations) + + launch { + if (chain.await()) migrationCompletedListener() + }.start() + + chain + } +} + +class InitialMigrationStrategy(private val strategy: DefaultMigrationStrategy) : MigrationStrategy { + + override operator fun invoke(migrations: List): Deferred { + return strategy(migrations.filter { it.isAlways }) + } +} + +class NoopMigrationStrategy(val state: Boolean) : MigrationStrategy { + + override fun invoke(migrations: List): Deferred { + return CompletableDeferred(state) + } +} + +class VersionRangeMigrationStrategy( + private val versions: IntRange, + private val strategy: DefaultMigrationStrategy +) : MigrationStrategy { + + override operator fun invoke(migrations: List): Deferred { + return strategy(migrations.filter { it.isAlways || it.version.toInt() in versions }) + } +} diff --git a/app/src/main/java/mihon/core/migration/MigrationStrategyFactory.kt b/app/src/main/java/mihon/core/migration/MigrationStrategyFactory.kt new file mode 100644 index 000000000..7e06fecb3 --- /dev/null +++ b/app/src/main/java/mihon/core/migration/MigrationStrategyFactory.kt @@ -0,0 +1,23 @@ +package mihon.core.migration + +class MigrationStrategyFactory( + private val factory: MigrationJobFactory, + private val migrationCompletedListener: MigrationCompletedListener, +) { + + fun create(old: Int, new: Int): MigrationStrategy { + val versions = (old + 1)..new + val strategy = when { + old == 0 -> InitialMigrationStrategy( + strategy = DefaultMigrationStrategy(factory, migrationCompletedListener, Migrator.scope), + ) + + old >= new -> NoopMigrationStrategy(false) + else -> VersionRangeMigrationStrategy( + versions = versions, + strategy = DefaultMigrationStrategy(factory, migrationCompletedListener, Migrator.scope), + ) + } + return strategy + } +} diff --git a/app/src/main/java/mihon/core/migration/Migrator.kt b/app/src/main/java/mihon/core/migration/Migrator.kt index 86288e2a0..11f22a8c9 100644 --- a/app/src/main/java/mihon/core/migration/Migrator.kt +++ b/app/src/main/java/mihon/core/migration/Migrator.kt @@ -1,53 +1,41 @@ package mihon.core.migration +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import kotlinx.coroutines.runBlocking -import tachiyomi.core.common.util.system.logcat object Migrator { - @SuppressWarnings("ReturnCount") - fun migrate( + private var result: Deferred? = null + val scope = CoroutineScope(Dispatchers.Main + Job()) + + fun initialize( old: Int, new: Int, migrations: List, dryrun: Boolean = false, onMigrationComplete: () -> Unit - ): Boolean { - val migrationContext = MigrationContext() - - if (old == 0) { - return migrationContext.migrate( - migrations = migrations.filter { it.isAlways() }, - dryrun = dryrun - ) - .also { onMigrationComplete() } - } - - if (old >= new) { - return false - } - - return migrationContext.migrate( - migrations = migrations.filter { it.isAlways() || it.version.toInt() in (old + 1)..new }, - dryrun = dryrun - ) - .also { onMigrationComplete() } + ) { + val migrationContext = MigrationContext(dryrun) + val migrationJobFactory = MigrationJobFactory(migrationContext, scope) + val migrationStrategyFactory = MigrationStrategyFactory(migrationJobFactory, onMigrationComplete) + val strategy = migrationStrategyFactory.create(old, new) + result = strategy(migrations) } - private fun Migration.isAlways() = version == Migration.ALWAYS + suspend fun await(): Boolean { + val result = result ?: CompletableDeferred(false) + return result.await() + } - @SuppressWarnings("MaxLineLength") - private fun MigrationContext.migrate(migrations: List, dryrun: Boolean): Boolean { - return migrations.sortedBy { it.version } - .map { migration -> - if (!dryrun) { - logcat { "Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }" } - runBlocking { migration(this@migrate) } - } else { - logcat { "(Dry-run) Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }" } - true - } - } - .reduce { acc, b -> acc || b } + fun release() { + result = null + } + + fun awaitAndRelease(): Boolean = runBlocking { + await().also { release() } } } diff --git a/app/src/test/java/mihon/core/migration/MigratorTest.kt b/app/src/test/java/mihon/core/migration/MigratorTest.kt index 89fe4db8c..e0feff3fd 100644 --- a/app/src/test/java/mihon/core/migration/MigratorTest.kt +++ b/app/src/test/java/mihon/core/migration/MigratorTest.kt @@ -1,59 +1,97 @@ package mihon.core.migration import io.mockk.Called +import io.mockk.slot import io.mockk.spyk import io.mockk.verify -import org.junit.jupiter.api.Assertions +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.newSingleThreadContext +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.setMain +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertInstanceOf +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test class MigratorTest { - @Test - fun initialVersion() { - val onMigrationComplete: () -> Unit = {} - val onMigrationCompleteSpy = spyk(onMigrationComplete) - val didMigration = Migrator.migrate( - old = 0, - new = 1, - migrations = listOf(Migration.of(Migration.ALWAYS) { true }, Migration.of(2f) { false }), - onMigrationComplete = onMigrationCompleteSpy - ) - verify { onMigrationCompleteSpy() } - Assertions.assertTrue(didMigration) + lateinit var migrationCompletedListener: MigrationCompletedListener + lateinit var migrationContext: MigrationContext + lateinit var migrationJobFactory: MigrationJobFactory + lateinit var migrationStrategyFactory: MigrationStrategyFactory + + @BeforeEach + fun initilize() { + migrationContext = MigrationContext(false) + migrationJobFactory = spyk(MigrationJobFactory(migrationContext, CoroutineScope(Dispatchers.Main + Job()))) + migrationCompletedListener = spyk<() -> Unit>({}) + migrationStrategyFactory = spyk(MigrationStrategyFactory(migrationJobFactory, migrationCompletedListener)) } @Test - fun sameVersion() { - val onMigrationComplete: () -> Unit = {} - val onMigrationCompleteSpy = spyk(onMigrationComplete) - val didMigration = Migrator.migrate( - old = 1, - new = 1, - migrations = listOf(Migration.of(Migration.ALWAYS) { true }, Migration.of(2f) { true }), - onMigrationComplete = onMigrationCompleteSpy - ) - verify { onMigrationCompleteSpy wasNot Called } - Assertions.assertFalse(didMigration) + fun initialVersion() = runBlocking { + val strategy = migrationStrategyFactory.create(0, 1) + assertInstanceOf(InitialMigrationStrategy::class.java, strategy) + + val migrations = slot>() + val execute = strategy(listOf(Migration.of(Migration.ALWAYS) { true }, Migration.of(2f) { false })) + + execute.await() + + verify { migrationJobFactory.create(capture(migrations)) } + assertEquals(1, migrations.captured.size) + verify { migrationCompletedListener() } } @Test - fun smallMigration() { - val onMigrationComplete: () -> Unit = {} - val onMigrationCompleteSpy = spyk(onMigrationComplete) - val didMigration = Migrator.migrate( - old = 1, - new = 2, - migrations = listOf(Migration.of(Migration.ALWAYS) { true }, Migration.of(2f) { true }), - onMigrationComplete = onMigrationCompleteSpy - ) - verify { onMigrationCompleteSpy() } - Assertions.assertTrue(didMigration) + fun sameVersion() = runBlocking { + val strategy = migrationStrategyFactory.create(1, 1) + assertInstanceOf(NoopMigrationStrategy::class.java, strategy) + + val execute = strategy(listOf(Migration.of(Migration.ALWAYS) { true }, Migration.of(2f) { false })) + + val result = execute.await() + assertFalse(result) + + verify { migrationJobFactory.create(any()) wasNot Called } } @Test - fun largeMigration() { - val onMigrationComplete: () -> Unit = {} - val onMigrationCompleteSpy = spyk(onMigrationComplete) + fun noMigrations() = runBlocking { + val strategy = migrationStrategyFactory.create(1, 2) + assertInstanceOf(VersionRangeMigrationStrategy::class.java, strategy) + + val execute = strategy(emptyList()) + + val result = execute.await() + assertFalse(result) + + verify { migrationJobFactory.create(any()) wasNot Called } + } + + @Test + fun smallMigration() = runBlocking { + val strategy = migrationStrategyFactory.create(1, 2) + assertInstanceOf(VersionRangeMigrationStrategy::class.java, strategy) + + val migrations = slot>() + val execute = strategy(listOf(Migration.of(Migration.ALWAYS) { true }, Migration.of(2f) { true })) + + execute.await() + + verify { migrationJobFactory.create(capture(migrations)) } + assertEquals(2, migrations.captured.size) + verify { migrationCompletedListener() } + } + + @Test + fun largeMigration() = runBlocking { val input = listOf( Migration.of(Migration.ALWAYS) { true }, Migration.of(2f) { true }, @@ -66,31 +104,56 @@ class MigratorTest { Migration.of(9f) { true }, Migration.of(10f) { true }, ) - val didMigration = Migrator.migrate( - old = 1, - new = 10, - migrations = input, - onMigrationComplete = onMigrationCompleteSpy - ) - verify { onMigrationCompleteSpy() } - Assertions.assertTrue(didMigration) + + val strategy = migrationStrategyFactory.create(1, 10) + assertInstanceOf(VersionRangeMigrationStrategy::class.java, strategy) + + val migrations = slot>() + val execute = strategy(input) + + execute.await() + + verify { migrationJobFactory.create(capture(migrations)) } + assertEquals(10, migrations.captured.size) + verify { migrationCompletedListener() } } @Test - fun withinRangeMigration() { - val onMigrationComplete: () -> Unit = {} - val onMigrationCompleteSpy = spyk(onMigrationComplete) - val didMigration = Migrator.migrate( - old = 1, - new = 2, - migrations = listOf( + fun withinRangeMigration() = runBlocking { + val strategy = migrationStrategyFactory.create(1, 2) + assertInstanceOf(VersionRangeMigrationStrategy::class.java, strategy) + + val migrations = slot>() + val execute = strategy( + listOf( Migration.of(Migration.ALWAYS) { true }, Migration.of(2f) { true }, Migration.of(3f) { false } - ), - onMigrationComplete = onMigrationCompleteSpy + ) ) - verify { onMigrationCompleteSpy() } - Assertions.assertTrue(didMigration) + + execute.await() + + verify { migrationJobFactory.create(capture(migrations)) } + assertEquals(2, migrations.captured.size) + verify { migrationCompletedListener() } + } + + companion object { + + val mainThreadSurrogate = newSingleThreadContext("UI thread") + + @BeforeAll + @JvmStatic + fun setUp() { + Dispatchers.setMain(mainThreadSurrogate) + } + + @AfterAll + @JvmStatic + fun tearDown() { + Dispatchers.resetMain() // reset the main dispatcher to the original Main dispatcher + mainThreadSurrogate.close() + } } } From 72222ad86d6fb328d20eead86c6357833d08c061 Mon Sep 17 00:00:00 2001 From: Maddie Witman Date: Thu, 28 Mar 2024 15:02:33 -0400 Subject: [PATCH 136/212] New Feature: Introduce Upcoming page to Mihon (#420) * Work in progress upcoming feature * Checkpointing WIP upcoming feature * Functional Upcoming Screen * Rename UpdateCalendar to UpdateUpcoming * Converted Strings to resources * Cleanup * Fixed detekt issues * Removed Link icon per @AntsyLich's suggestion. * Detekt * Fixed Calendar display on wide form factor devices * Added Key to upcoming lazycolumn * Updated tablet mode UI to support two column view * Updated header creation logic * Updated header creation logic... again * Moved stray string to resources * Fixed PR Comments and query refactor * Tweaks to query, refactored to flow, comments on calendar * Switched to Date Formatter * Cleaned up date formatter * More Refactor work * Updated Calendar to support localized week formats * Fixed year format * Refactored Header animation * Moved upcoming FAQ * Completed YearMonth Migration * Replaced currentYearMonth with delegate * Even more cleanup * cleaned up alignment modifiers * Click Handler and other refactors * Removed Wrapped Content Height/Size/extra clips * Huge Refactor for CalendarDay * Another cleanup attempt * Migrated to new mihon.feature.* module pattern * changed access modifier * A Bunch of changes from the next round of reviews * Cleanups * Cleanup 2 --------- Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- app/build.gradle.kts | 1 + .../java/eu/kanade/domain/DomainModule.kt | 2 + .../presentation/updates/UpdatesScreen.kt | 9 + .../kanade/tachiyomi/ui/updates/UpdatesTab.kt | 2 + .../tachiyomi/util/lang/DateExtensions.kt | 6 +- .../core/designsystem/utils/WindowSize.kt | 23 ++ .../mihon/feature/upcoming/UpcomingScreen.kt | 27 +++ .../feature/upcoming/UpcomingScreenContent.kt | 198 ++++++++++++++++++ .../feature/upcoming/UpcomingScreenModel.kt | 87 ++++++++ .../mihon/feature/upcoming/UpcomingUIModel.kt | 9 + .../upcoming/components/UpcomingItem.kt | 54 +++++ .../upcoming/components/calendar/Calendar.kt | 112 ++++++++++ .../components/calendar/CalendarDay.kt | 92 ++++++++ .../components/calendar/CalendarHeader.kt | 100 +++++++++ .../components/calendar/CalendarIndicator.kt | 32 +++ .../kotlin/tachiyomi/core/common/Constants.kt | 1 + .../data/manga/MangaRepositoryImpl.kt | 6 + .../main/sqldelight/tachiyomi/data/mangas.sq | 8 + .../upcoming/interactor/GetUpcomingManga.kt | 20 ++ .../manga/repository/MangaRepository.kt | 2 + gradle/libs.versions.toml | 1 + .../commonMain/resources/MR/base/strings.xml | 7 + 22 files changed, 797 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/mihon/core/designsystem/utils/WindowSize.kt create mode 100644 app/src/main/java/mihon/feature/upcoming/UpcomingScreen.kt create mode 100644 app/src/main/java/mihon/feature/upcoming/UpcomingScreenContent.kt create mode 100644 app/src/main/java/mihon/feature/upcoming/UpcomingScreenModel.kt create mode 100644 app/src/main/java/mihon/feature/upcoming/UpcomingUIModel.kt create mode 100644 app/src/main/java/mihon/feature/upcoming/components/UpcomingItem.kt create mode 100644 app/src/main/java/mihon/feature/upcoming/components/calendar/Calendar.kt create mode 100644 app/src/main/java/mihon/feature/upcoming/components/calendar/CalendarDay.kt create mode 100644 app/src/main/java/mihon/feature/upcoming/components/calendar/CalendarHeader.kt create mode 100644 app/src/main/java/mihon/feature/upcoming/components/calendar/CalendarIndicator.kt create mode 100644 domain/src/main/java/mihon/domain/upcoming/interactor/GetUpcomingManga.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9edadd5fc..3101b6a0e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -237,6 +237,7 @@ dependencies { implementation(libs.compose.materialmotion) implementation(libs.swipe) implementation(libs.compose.webview) + implementation(libs.compose.grid) // Logging diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt index cace3e974..48c183a92 100644 --- a/app/src/main/java/eu/kanade/domain/DomainModule.kt +++ b/app/src/main/java/eu/kanade/domain/DomainModule.kt @@ -32,6 +32,7 @@ import mihon.domain.extensionrepo.interactor.ReplaceExtensionRepo import mihon.domain.extensionrepo.interactor.UpdateExtensionRepo import mihon.domain.extensionrepo.repository.ExtensionRepoRepository import mihon.domain.extensionrepo.service.ExtensionRepoService +import mihon.domain.upcoming.interactor.GetUpcomingManga import tachiyomi.data.category.CategoryRepositoryImpl import tachiyomi.data.chapter.ChapterRepositoryImpl import tachiyomi.data.history.HistoryRepositoryImpl @@ -117,6 +118,7 @@ class DomainModule : InjektModule { addFactory { GetMangaByUrlAndSourceId(get()) } addFactory { GetManga(get()) } addFactory { GetNextChapters(get(), get(), get()) } + addFactory { GetUpcomingManga(get()) } addFactory { ResetViewerFlags(get()) } addFactory { SetMangaChapterFlags(get()) } addFactory { FetchInterval(get()) } diff --git a/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt b/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt index ac97de83f..fa2bd53fe 100644 --- a/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt @@ -4,6 +4,7 @@ import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.CalendarMonth import androidx.compose.material.icons.outlined.FlipToBack import androidx.compose.material.icons.outlined.Refresh import androidx.compose.material.icons.outlined.SelectAll @@ -47,6 +48,7 @@ fun UpdateScreen( onClickCover: (UpdatesItem) -> Unit, onSelectAll: (Boolean) -> Unit, onInvertSelection: () -> Unit, + onCalendarClicked: () -> Unit, onUpdateLibrary: () -> Boolean, onDownloadChapter: (List, ChapterDownloadAction) -> Unit, onMultiBookmarkClicked: (List, bookmark: Boolean) -> Unit, @@ -60,6 +62,7 @@ fun UpdateScreen( Scaffold( topBar = { scrollBehavior -> UpdatesAppBar( + onCalendarClicked = { onCalendarClicked() }, onUpdateLibrary = { onUpdateLibrary() }, actionModeCounter = state.selected.size, onSelectAll = { onSelectAll(true) }, @@ -126,6 +129,7 @@ fun UpdateScreen( @Composable private fun UpdatesAppBar( + onCalendarClicked: () -> Unit, onUpdateLibrary: () -> Unit, // For action mode actionModeCounter: Int, @@ -141,6 +145,11 @@ private fun UpdatesAppBar( actions = { AppBarActions( persistentListOf( + AppBar.Action( + title = stringResource(MR.strings.action_view_upcoming), + icon = Icons.Outlined.CalendarMonth, + onClick = onCalendarClicked, + ), AppBar.Action( title = stringResource(MR.strings.action_update_library), icon = Icons.Outlined.Refresh, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesTab.kt index a9dc4281a..8064d123a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesTab.kt @@ -26,6 +26,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaScreen import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.ui.updates.UpdatesScreenModel.Event import kotlinx.coroutines.flow.collectLatest +import mihon.feature.upcoming.UpcomingScreen import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource @@ -72,6 +73,7 @@ object UpdatesTab : Tab { val intent = ReaderActivity.newIntent(context, it.update.mangaId, it.update.chapterId) context.startActivity(intent) }, + onCalendarClicked = { navigator.push(UpcomingScreen()) }, ) val onDismissDialog = { screenModel.setDialog(null) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt index 8326fe2f8..8827ae4a6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt @@ -39,6 +39,10 @@ fun Long.toLocalDate(): LocalDate { return LocalDate.ofInstant(Instant.ofEpochMilli(this), ZoneId.systemDefault()) } +fun Instant.toLocalDate(zoneId: ZoneId = ZoneId.systemDefault()): LocalDate { + return LocalDate.ofInstant(this, zoneId) +} + fun LocalDate.toRelativeString( context: Context, relative: Boolean = true, @@ -56,14 +60,12 @@ fun LocalDate.toRelativeString( difference.toInt().absoluteValue, difference.toInt().absoluteValue, ) - difference < 1 -> context.stringResource(MR.strings.relative_time_today) difference < 7 -> context.pluralStringResource( MR.plurals.relative_time, difference.toInt(), difference.toInt(), ) - else -> dateFormat.format(this) } } diff --git a/app/src/main/java/mihon/core/designsystem/utils/WindowSize.kt b/app/src/main/java/mihon/core/designsystem/utils/WindowSize.kt new file mode 100644 index 000000000..ca80625ca --- /dev/null +++ b/app/src/main/java/mihon/core/designsystem/utils/WindowSize.kt @@ -0,0 +1,23 @@ +package mihon.core.designsystem.utils + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.unit.dp + +@Composable +@ReadOnlyComposable +fun isMediumWidthWindow(): Boolean { + val configuration = LocalConfiguration.current + return configuration.screenWidthDp > MediumWidthWindowSize.value +} + +@Composable +@ReadOnlyComposable +fun isExpandedWidthWindow(): Boolean { + val configuration = LocalConfiguration.current + return configuration.screenWidthDp > ExpandedWidthWindowSize.value +} + +val MediumWidthWindowSize = 600.dp +val ExpandedWidthWindowSize = 840.dp diff --git a/app/src/main/java/mihon/feature/upcoming/UpcomingScreen.kt b/app/src/main/java/mihon/feature/upcoming/UpcomingScreen.kt new file mode 100644 index 000000000..981c3d540 --- /dev/null +++ b/app/src/main/java/mihon/feature/upcoming/UpcomingScreen.kt @@ -0,0 +1,27 @@ +package mihon.feature.upcoming + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +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.ui.manga.MangaScreen + +class UpcomingScreen : Screen() { + + @Composable + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + + val screenModel = rememberScreenModel { UpcomingScreenModel() } + val state by screenModel.state.collectAsState() + + UpcomingScreenContent( + state = state, + setSelectedYearMonth = screenModel::setSelectedYearMonth, + onClickUpcoming = { navigator.push(MangaScreen(it.id)) }, + ) + } +} diff --git a/app/src/main/java/mihon/feature/upcoming/UpcomingScreenContent.kt b/app/src/main/java/mihon/feature/upcoming/UpcomingScreenContent.kt new file mode 100644 index 000000000..0baa1c8ee --- /dev/null +++ b/app/src/main/java/mihon/feature/upcoming/UpcomingScreenContent.kt @@ -0,0 +1,198 @@ +package mihon.feature.upcoming + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.outlined.HelpOutline +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalUriHandler +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import eu.kanade.presentation.components.AppBar +import eu.kanade.presentation.components.relativeDateText +import eu.kanade.presentation.util.isTabletUi +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.ImmutableMap +import kotlinx.coroutines.launch +import mihon.feature.upcoming.components.UpcomingItem +import mihon.feature.upcoming.components.calendar.Calendar +import tachiyomi.core.common.Constants +import tachiyomi.domain.manga.model.Manga +import tachiyomi.i18n.MR +import tachiyomi.presentation.core.components.FastScrollLazyColumn +import tachiyomi.presentation.core.components.ListGroupHeader +import tachiyomi.presentation.core.components.TwoPanelBox +import tachiyomi.presentation.core.components.material.Scaffold +import tachiyomi.presentation.core.i18n.stringResource +import java.time.LocalDate +import java.time.YearMonth + +@Composable +fun UpcomingScreenContent( + state: UpcomingScreenModel.State, + setSelectedYearMonth: (YearMonth) -> Unit, + onClickUpcoming: (manga: Manga) -> Unit, + modifier: Modifier = Modifier, +) { + val scope = rememberCoroutineScope() + val listState = rememberLazyListState() + val onClickDay: (LocalDate, Int) -> Unit = { date, offset -> + state.headerIndexes[date]?.let { + scope.launch { + listState.animateScrollToItem(it + offset) + } + } + } + Scaffold( + topBar = { UpcomingToolbar() }, + modifier = modifier, + ) { paddingValues -> + if (isTabletUi()) { + UpcomingScreenLargeImpl( + listState = listState, + items = state.items, + events = state.events, + paddingValues = paddingValues, + selectedYearMonth = state.selectedYearMonth, + setSelectedYearMonth = setSelectedYearMonth, + onClickDay = { onClickDay(it, 0) }, + onClickUpcoming = onClickUpcoming, + ) + } else { + UpcomingScreenSmallImpl( + listState = listState, + items = state.items, + events = state.events, + paddingValues = paddingValues, + selectedYearMonth = state.selectedYearMonth, + setSelectedYearMonth = setSelectedYearMonth, + onClickDay = { onClickDay(it, 1) }, + onClickUpcoming = onClickUpcoming, + ) + } + } +} + +@Composable +private fun UpcomingToolbar() { + val navigator = LocalNavigator.currentOrThrow + val uriHandler = LocalUriHandler.current + + AppBar( + title = stringResource(MR.strings.label_upcoming), + navigateUp = navigator::pop, + actions = { + IconButton(onClick = { uriHandler.openUri(Constants.URL_HELP_UPCOMING) }) { + Icon( + imageVector = Icons.AutoMirrored.Outlined.HelpOutline, + contentDescription = stringResource(MR.strings.upcoming_guide), + ) + } + }, + ) +} + +@Composable +private fun UpcomingScreenSmallImpl( + listState: LazyListState, + items: ImmutableList, + events: ImmutableMap, + paddingValues: PaddingValues, + selectedYearMonth: YearMonth, + setSelectedYearMonth: (YearMonth) -> Unit, + onClickDay: (LocalDate) -> Unit, + onClickUpcoming: (manga: Manga) -> Unit, +) { + FastScrollLazyColumn( + contentPadding = paddingValues, + state = listState, + ) { + item(key = "upcoming-calendar") { + Calendar( + selectedYearMonth = selectedYearMonth, + events = events, + setSelectedYearMonth = setSelectedYearMonth, + onClickDay = onClickDay, + ) + } + items( + items = items, + key = { "upcoming-${it.hashCode()}" }, + contentType = { + when (it) { + is UpcomingUIModel.Header -> "header" + is UpcomingUIModel.Item -> "item" + } + }, + ) { item -> + when (item) { + is UpcomingUIModel.Item -> { + UpcomingItem( + upcoming = item.manga, + onClick = { onClickUpcoming(item.manga) }, + ) + } + is UpcomingUIModel.Header -> { + ListGroupHeader(text = relativeDateText(item.date)) + } + } + } + } +} + +@Composable +private fun UpcomingScreenLargeImpl( + listState: LazyListState, + items: ImmutableList, + events: ImmutableMap, + paddingValues: PaddingValues, + selectedYearMonth: YearMonth, + setSelectedYearMonth: (YearMonth) -> Unit, + onClickDay: (LocalDate) -> Unit, + onClickUpcoming: (manga: Manga) -> Unit, +) { + TwoPanelBox( + modifier = Modifier.padding(paddingValues), + startContent = { + Calendar( + selectedYearMonth = selectedYearMonth, + events = events, + setSelectedYearMonth = setSelectedYearMonth, + onClickDay = onClickDay, + ) + }, + endContent = { + FastScrollLazyColumn(state = listState) { + items( + items = items, + key = { "upcoming-${it.hashCode()}" }, + contentType = { + when (it) { + is UpcomingUIModel.Header -> "header" + is UpcomingUIModel.Item -> "item" + } + }, + ) { item -> + when (item) { + is UpcomingUIModel.Item -> { + UpcomingItem( + upcoming = item.manga, + onClick = { onClickUpcoming(item.manga) }, + ) + } + is UpcomingUIModel.Header -> { + ListGroupHeader(text = relativeDateText(item.date)) + } + } + } + } + }, + ) +} diff --git a/app/src/main/java/mihon/feature/upcoming/UpcomingScreenModel.kt b/app/src/main/java/mihon/feature/upcoming/UpcomingScreenModel.kt new file mode 100644 index 000000000..b404a7f96 --- /dev/null +++ b/app/src/main/java/mihon/feature/upcoming/UpcomingScreenModel.kt @@ -0,0 +1,87 @@ +package mihon.feature.upcoming + +import androidx.compose.ui.util.fastMap +import androidx.compose.ui.util.fastMapIndexedNotNull +import cafe.adriel.voyager.core.model.StateScreenModel +import cafe.adriel.voyager.core.model.screenModelScope +import eu.kanade.core.util.insertSeparators +import eu.kanade.tachiyomi.util.lang.toLocalDate +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.ImmutableMap +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.persistentMapOf +import kotlinx.collections.immutable.toImmutableList +import kotlinx.collections.immutable.toImmutableMap +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import mihon.domain.upcoming.interactor.GetUpcomingManga +import tachiyomi.domain.manga.model.Manga +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import java.time.LocalDate +import java.time.YearMonth + +class UpcomingScreenModel( + private val getUpcomingManga: GetUpcomingManga = Injekt.get(), +) : StateScreenModel(State()) { + + init { + screenModelScope.launch { + getUpcomingManga.subscribe().collectLatest { + mutableState.update { state -> + val upcomingItems = it.toUpcomingUIModels() + state.copy( + items = upcomingItems, + events = it.toEvents(), + headerIndexes = upcomingItems.getHeaderIndexes(), + ) + } + } + } + } + + private fun List.toUpcomingUIModels(): ImmutableList { + return fastMap { UpcomingUIModel.Item(it) } + .insertSeparators { before, after -> + val beforeDate = before?.manga?.expectedNextUpdate?.toLocalDate() + val afterDate = after?.manga?.expectedNextUpdate?.toLocalDate() + + if (beforeDate != afterDate && afterDate != null) { + UpcomingUIModel.Header(afterDate) + } else { + null + } + } + .toImmutableList() + } + + private fun List.toEvents(): ImmutableMap { + return groupBy { it.expectedNextUpdate?.toLocalDate() ?: LocalDate.MAX } + .mapValues { it.value.size } + .toImmutableMap() + } + + private fun List.getHeaderIndexes(): ImmutableMap { + return fastMapIndexedNotNull { index, upcomingUIModel -> + if (upcomingUIModel is UpcomingUIModel.Header) { + upcomingUIModel.date to index + } else { + null + } + } + .toMap() + .toImmutableMap() + } + + fun setSelectedYearMonth(yearMonth: YearMonth) { + mutableState.update { it.copy(selectedYearMonth = yearMonth) } + } + + data class State( + val selectedYearMonth: YearMonth = YearMonth.now(), + val items: ImmutableList = persistentListOf(), + val events: ImmutableMap = persistentMapOf(), + val headerIndexes: ImmutableMap = persistentMapOf(), + ) +} diff --git a/app/src/main/java/mihon/feature/upcoming/UpcomingUIModel.kt b/app/src/main/java/mihon/feature/upcoming/UpcomingUIModel.kt new file mode 100644 index 000000000..c394f45f6 --- /dev/null +++ b/app/src/main/java/mihon/feature/upcoming/UpcomingUIModel.kt @@ -0,0 +1,9 @@ +package mihon.feature.upcoming + +import tachiyomi.domain.manga.model.Manga +import java.time.LocalDate + +sealed interface UpcomingUIModel { + data class Header(val date: LocalDate) : UpcomingUIModel + data class Item(val manga: Manga) : UpcomingUIModel +} diff --git a/app/src/main/java/mihon/feature/upcoming/components/UpcomingItem.kt b/app/src/main/java/mihon/feature/upcoming/components/UpcomingItem.kt new file mode 100644 index 000000000..bf82c73cd --- /dev/null +++ b/app/src/main/java/mihon/feature/upcoming/components/UpcomingItem.kt @@ -0,0 +1,54 @@ +package mihon.feature.upcoming.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.height +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.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import eu.kanade.presentation.manga.components.MangaCover +import tachiyomi.domain.manga.model.Manga +import tachiyomi.domain.manga.model.asMangaCover +import tachiyomi.presentation.core.components.material.padding + +private val UpcomingItemHeight = 96.dp + +@Composable +fun UpcomingItem( + upcoming: Manga, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier + .clickable(onClick = onClick) + .height(UpcomingItemHeight) + .padding( + horizontal = MaterialTheme.padding.medium, + vertical = MaterialTheme.padding.small, + ), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.large), + ) { + MangaCover.Book( + modifier = Modifier.fillMaxHeight(), + data = upcoming.asMangaCover(), + ) + Text( + modifier = Modifier.weight(1f), + text = upcoming.title, + fontWeight = FontWeight.SemiBold, + maxLines = 2, + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.bodyMedium, + ) + } +} diff --git a/app/src/main/java/mihon/feature/upcoming/components/calendar/Calendar.kt b/app/src/main/java/mihon/feature/upcoming/components/calendar/Calendar.kt new file mode 100644 index 000000000..da511ddad --- /dev/null +++ b/app/src/main/java/mihon/feature/upcoming/components/calendar/Calendar.kt @@ -0,0 +1,112 @@ +package mihon.feature.upcoming.components.calendar + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.widthIn +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.util.fastForEach +import io.woong.compose.grid.SimpleGridCells +import io.woong.compose.grid.VerticalGrid +import kotlinx.collections.immutable.ImmutableMap +import kotlinx.collections.immutable.toImmutableList +import mihon.core.designsystem.utils.isExpandedWidthWindow +import mihon.core.designsystem.utils.isMediumWidthWindow +import tachiyomi.presentation.core.components.material.padding +import java.time.DayOfWeek +import java.time.LocalDate +import java.time.YearMonth +import java.time.format.TextStyle +import java.time.temporal.WeekFields +import java.util.Locale + +private val FontSize = 16.sp +private const val DaysOfWeek = 7 + +@Composable +fun Calendar( + selectedYearMonth: YearMonth, + events: ImmutableMap, + setSelectedYearMonth: (YearMonth) -> Unit, + onClickDay: (day: LocalDate) -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(4.dp), + ) { + CalenderHeader( + yearMonth = selectedYearMonth, + onPreviousClick = { setSelectedYearMonth(selectedYearMonth.minusMonths(1L)) }, + onNextClick = { setSelectedYearMonth(selectedYearMonth.plusMonths(1L)) }, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = MaterialTheme.padding.small) + .padding(start = MaterialTheme.padding.medium) + ) + CalendarGrid( + selectedYearMonth = selectedYearMonth, + events = events, + onClickDay = onClickDay, + ) + } +} + +@Composable +private fun CalendarGrid( + selectedYearMonth: YearMonth, + events: ImmutableMap, + onClickDay: (day: LocalDate) -> Unit, +) { + val localeFirstDayOfWeek = WeekFields.of(Locale.getDefault()).firstDayOfWeek.value + val weekDays = remember { + (0 until DaysOfWeek) + .map { DayOfWeek.of((localeFirstDayOfWeek - 1 + it) % DaysOfWeek + 1) } + .toImmutableList() + } + + val emptyFieldCount = weekDays.indexOf(selectedYearMonth.atDay(1).dayOfWeek) + val daysInMonth = selectedYearMonth.lengthOfMonth() + + VerticalGrid( + columns = SimpleGridCells.Fixed(DaysOfWeek), + modifier = if (isMediumWidthWindow() && !isExpandedWidthWindow()) { + Modifier.widthIn(max = 360.dp) + } else { + Modifier + } + ) { + weekDays.fastForEach { item -> + Text( + text = item.getDisplayName( + TextStyle.NARROW, + Locale.getDefault(), + ), + textAlign = TextAlign.Center, + fontWeight = FontWeight.SemiBold, + fontSize = FontSize, + ) + } + repeat(emptyFieldCount) { Box { } } + repeat(daysInMonth) { dayIndex -> + val localDate = selectedYearMonth.atDay(dayIndex + 1) + CalendarDay( + date = localDate, + onDayClick = { onClickDay(localDate) }, + events = events[localDate] ?: 0, + ) + } + } +} diff --git a/app/src/main/java/mihon/feature/upcoming/components/calendar/CalendarDay.kt b/app/src/main/java/mihon/feature/upcoming/components/calendar/CalendarDay.kt new file mode 100644 index 000000000..46ed355ab --- /dev/null +++ b/app/src/main/java/mihon/feature/upcoming/components/calendar/CalendarDay.kt @@ -0,0 +1,92 @@ +package mihon.feature.upcoming.components.calendar + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.layout +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import java.time.LocalDate + +private const val MaxEvents = 3 + +@Composable +fun CalendarDay( + date: LocalDate, + events: Int, + onDayClick: () -> Unit, + modifier: Modifier = Modifier, +) { + val today = remember { LocalDate.now() } + + Box( + modifier = modifier + .then( + if (today == date) { + Modifier.border( + border = BorderStroke( + width = 1.dp, + color = MaterialTheme.colorScheme.onBackground + ), + shape = CircleShape, + ) + } else { + Modifier + }, + ) + .clip(shape = CircleShape) + .clickable(onClick = onDayClick) + .circleLayout(), + contentAlignment = Alignment.Center, + ) { + Text( + text = date.dayOfMonth.toString(), + textAlign = TextAlign.Center, + fontSize = 16.sp, + color = if (date.isBefore(today)) { + MaterialTheme.colorScheme.onBackground.copy(alpha = 0.38f) + } else { + MaterialTheme.colorScheme.onBackground + }, + fontWeight = FontWeight.SemiBold, + ) + Row(Modifier.offset(y = 12.dp)) { + val size = events.coerceAtMost(MaxEvents) + for (index in 0 until size) { + CalendarIndicator( + index = index, + size = 56.dp, + color = MaterialTheme.colorScheme.primary, + ) + } + } + } +} + +private fun Modifier.circleLayout() = layout { measurable, constraints -> + val placeable = measurable.measure(constraints) + + val currentHeight = placeable.height + val currentWidth = placeable.width + val newDiameter = maxOf(currentHeight, currentWidth) + + layout(newDiameter, newDiameter) { + placeable.placeRelative( + x = (newDiameter - currentWidth) / 2, + y = (newDiameter - currentHeight) / 2, + ) + } +} diff --git a/app/src/main/java/mihon/feature/upcoming/components/calendar/CalendarHeader.kt b/app/src/main/java/mihon/feature/upcoming/components/calendar/CalendarHeader.kt new file mode 100644 index 000000000..55498ebb9 --- /dev/null +++ b/app/src/main/java/mihon/feature/upcoming/components/calendar/CalendarHeader.kt @@ -0,0 +1,100 @@ +package mihon.feature.upcoming.components.calendar + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.AnimatedContentTransitionScope +import androidx.compose.animation.ContentTransform +import androidx.compose.animation.SizeTransform +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.animation.togetherWith +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.KeyboardArrowLeft +import androidx.compose.material.icons.filled.KeyboardArrowRight +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import tachiyomi.i18n.MR +import tachiyomi.presentation.core.i18n.stringResource +import java.time.YearMonth +import java.time.format.DateTimeFormatter +import java.util.Locale + +@Composable +fun CalenderHeader( + yearMonth: YearMonth, + onPreviousClick: () -> Unit, + onNextClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier, + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + ) { + AnimatedContent( + targetState = yearMonth, + transitionSpec = { getAnimation() }, + label = "Change Month", + ) { monthYear -> + Text( + text = getTitleText(monthYear), + style = MaterialTheme.typography.titleLarge, + ) + } + Row { + IconButton(onClick = onPreviousClick) { + Icon(Icons.Default.KeyboardArrowLeft, stringResource(MR.strings.upcoming_calendar_prev)) + } + IconButton(onClick = onNextClick) { + Icon(Icons.Default.KeyboardArrowRight, stringResource(MR.strings.upcoming_calendar_next)) + } + } + } +} + +private const val MonthYearChangeAnimationDuration = 200 + +private fun AnimatedContentTransitionScope.getAnimation(): ContentTransform { + val movingForward = targetState > initialState + + val enterTransition = slideInVertically( + animationSpec = tween(durationMillis = MonthYearChangeAnimationDuration), + ) { height -> if (movingForward) height else -height } + fadeIn( + animationSpec = tween(durationMillis = MonthYearChangeAnimationDuration), + ) + val exitTransition = slideOutVertically( + animationSpec = tween(durationMillis = MonthYearChangeAnimationDuration), + ) { height -> if (movingForward) -height else height } + fadeOut( + animationSpec = tween(durationMillis = MonthYearChangeAnimationDuration), + ) + return (enterTransition togetherWith exitTransition) + .using(SizeTransform(clip = false)) +} + +@Composable +@ReadOnlyComposable +private fun getTitleText(monthYear: YearMonth): String { + val formatter = DateTimeFormatter.ofPattern("MMMM yyyy", Locale.getDefault()) + return formatter.format(monthYear) +} + +@Preview +@Composable +private fun CalenderHeaderPreview() { + CalenderHeader( + yearMonth = YearMonth.now(), + onNextClick = {}, + onPreviousClick = {}, + ) +} diff --git a/app/src/main/java/mihon/feature/upcoming/components/calendar/CalendarIndicator.kt b/app/src/main/java/mihon/feature/upcoming/components/calendar/CalendarIndicator.kt new file mode 100644 index 000000000..9aaca69de --- /dev/null +++ b/app/src/main/java/mihon/feature/upcoming/components/calendar/CalendarIndicator.kt @@ -0,0 +1,32 @@ +package mihon.feature.upcoming.components.calendar + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +private const val IndicatorScale = 12 +private const val IndicatorAlphaMultiplier = 0.3f + +@Composable +fun CalendarIndicator( + index: Int, + size: Dp, + color: Color, + modifier: Modifier = Modifier, +) { + Box( + modifier = modifier + .padding(horizontal = 1.dp) + .clip(shape = CircleShape) + .background(color = color.copy(alpha = (index + 1) * IndicatorAlphaMultiplier)) + .size(size = size.div(IndicatorScale)), + ) +} diff --git a/core/common/src/main/kotlin/tachiyomi/core/common/Constants.kt b/core/common/src/main/kotlin/tachiyomi/core/common/Constants.kt index 649418997..38caeb3dc 100644 --- a/core/common/src/main/kotlin/tachiyomi/core/common/Constants.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/Constants.kt @@ -2,6 +2,7 @@ package tachiyomi.core.common object Constants { const val URL_HELP = "https://mihon.app/docs/guides/troubleshooting/" + const val URL_HELP_UPCOMING = "https://mihon.app/docs/faq/updates/upcoming" const val MANGA_EXTRA = "manga" diff --git a/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt index d7ac06abf..a1fce650f 100644 --- a/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt @@ -65,6 +65,12 @@ class MangaRepositoryImpl( } } + override suspend fun getUpcomingManga(statuses: Set): Flow> { + return handler.subscribeToList { + mangasQueries.getUpcomingManga(statuses, MangaMapper::mapManga) + } + } + override suspend fun resetViewerFlags(): Boolean { return try { handler.await { mangasQueries.resetViewerFlags() } diff --git a/data/src/main/sqldelight/tachiyomi/data/mangas.sq b/data/src/main/sqldelight/tachiyomi/data/mangas.sq index 6bc655a76..1baea800f 100644 --- a/data/src/main/sqldelight/tachiyomi/data/mangas.sq +++ b/data/src/main/sqldelight/tachiyomi/data/mangas.sq @@ -112,6 +112,14 @@ WHERE favorite = 1 AND LOWER(title) = :title AND _id != :id; +getUpcomingManga: +SELECT * +FROM mangas +WHERE next_update > 0 +AND favorite = 1 +AND status IN :statuses +ORDER BY next_update ASC; + resetViewerFlags: UPDATE mangas SET viewer = 0; diff --git a/domain/src/main/java/mihon/domain/upcoming/interactor/GetUpcomingManga.kt b/domain/src/main/java/mihon/domain/upcoming/interactor/GetUpcomingManga.kt new file mode 100644 index 000000000..dd618b955 --- /dev/null +++ b/domain/src/main/java/mihon/domain/upcoming/interactor/GetUpcomingManga.kt @@ -0,0 +1,20 @@ +package mihon.domain.upcoming.interactor + +import eu.kanade.tachiyomi.source.model.SManga +import kotlinx.coroutines.flow.Flow +import tachiyomi.domain.manga.model.Manga +import tachiyomi.domain.manga.repository.MangaRepository + +class GetUpcomingManga( + private val mangaRepository: MangaRepository, +) { + + private val includedStatuses = setOf( + SManga.ONGOING.toLong(), + SManga.PUBLISHING_FINISHED.toLong(), + ) + + suspend fun subscribe(): Flow> { + return mangaRepository.getUpcomingManga(includedStatuses) + } +} diff --git a/domain/src/main/java/tachiyomi/domain/manga/repository/MangaRepository.kt b/domain/src/main/java/tachiyomi/domain/manga/repository/MangaRepository.kt index c460038cd..8c74851f3 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/repository/MangaRepository.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/repository/MangaRepository.kt @@ -25,6 +25,8 @@ interface MangaRepository { suspend fun getDuplicateLibraryManga(id: Long, title: String): List + suspend fun getUpcomingManga(statuses: Set): Flow> + suspend fun resetViewerFlags(): Boolean suspend fun setMangaCategories(mangaId: Long, categoryIds: List) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index affef4ac6..0bf8d3e11 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -64,6 +64,7 @@ directionalviewpager = "com.github.tachiyomiorg:DirectionalViewPager:1.0.0" insetter = "dev.chrisbanes.insetter:insetter:0.6.1" compose-materialmotion = "io.github.fornewid:material-motion-compose-core:1.2.0" compose-webview = "io.github.kevinnzou:compose-webview:0.33.4" +compose-grid = "io.woong.compose.grid:grid:1.2.2" swipe = "me.saket.swipe:swipe:1.3.0" diff --git a/i18n/src/commonMain/resources/MR/base/strings.xml b/i18n/src/commonMain/resources/MR/base/strings.xml index 23ce8a441..bd30e2900 100644 --- a/i18n/src/commonMain/resources/MR/base/strings.xml +++ b/i18n/src/commonMain/resources/MR/base/strings.xml @@ -26,6 +26,7 @@ Download queue Library Updates + Upcoming History Sources Backup and restore @@ -789,6 +790,12 @@ Library last updated: %s Just now Never + View Upcoming Updates + + + Upcoming Guide + Next Month + Previous Month Ch. %1$s - %2$s From f1660beafc8303ba8d7ebfd160029e869a077f69 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 29 Mar 2024 13:25:21 +0600 Subject: [PATCH 137/212] fix(deps): update aboutlib.version to v11.1.1 (#592) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0bf8d3e11..1c511597d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -aboutlib_version = "11.1.0" +aboutlib_version = "11.1.1" leakcanary = "2.13" moko = "0.23.0" okhttp_version = "5.0.0-alpha.12" From 8c8d65d3c70942a8b58c2fdcc575687a040d06eb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 29 Mar 2024 13:25:28 +0600 Subject: [PATCH 138/212] fix(deps): update dependency io.github.kevinnzou:compose-webview to v0.33.6 (#594) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1c511597d..ff666a03a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -63,7 +63,7 @@ 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-materialmotion = "io.github.fornewid:material-motion-compose-core:1.2.0" -compose-webview = "io.github.kevinnzou:compose-webview:0.33.4" +compose-webview = "io.github.kevinnzou:compose-webview:0.33.6" compose-grid = "io.woong.compose.grid:grid:1.2.2" swipe = "me.saket.swipe:swipe:1.3.0" From 86fbd20665613cacb8d3c733aed9731792a07392 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 29 Mar 2024 13:25:35 +0600 Subject: [PATCH 139/212] fix(deps): update detekt to v1.23.6 (#595) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ff666a03a..bfc25a4f1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ shizuku_version = "12.2.0" sqldelight = "2.0.0" sqlite = "2.4.0" voyager = "1.0.0" -detekt = "1.23.5" +detekt = "1.23.6" detektCompose = "0.3.12" [libraries] From 7c6fd026a3ce476d866514defd203ff11c4d4e97 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 29 Mar 2024 13:52:47 +0600 Subject: [PATCH 140/212] chore(deps): update actions/dependency-review-action action to v4.2.5 (#591) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 3ce978689..72310a6e4 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -26,7 +26,7 @@ jobs: uses: gradle/wrapper-validation-action@b231772637bb498f11fdbc86052b6e8a8dc9fc92 # v2.1.2 - name: Dependency Review - uses: actions/dependency-review-action@0fa40c3c10055986a88de3baa0d6ec17c5a894b3 # v4.2.3 + uses: actions/dependency-review-action@5bbc3ba658137598168acb2ab73b21c432dd411b # v4.2.5 - name: Set up JDK uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 From 6b3423a12b620dd2aae635ac4e859d00a4f62ceb Mon Sep 17 00:00:00 2001 From: Andreas Date: Sun, 31 Mar 2024 00:03:28 +0100 Subject: [PATCH 141/212] Fix Migrator not doing work (#604) --- app/src/main/java/mihon/core/migration/MigrationJobFactory.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/mihon/core/migration/MigrationJobFactory.kt b/app/src/main/java/mihon/core/migration/MigrationJobFactory.kt index 846ebc277..801411013 100644 --- a/app/src/main/java/mihon/core/migration/MigrationJobFactory.kt +++ b/app/src/main/java/mihon/core/migration/MigrationJobFactory.kt @@ -2,6 +2,7 @@ package mihon.core.migration import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Deferred import kotlinx.coroutines.async import tachiyomi.core.common.util.system.logcat @@ -17,7 +18,7 @@ class MigrationJobFactory( .fold(CompletableDeferred(true)) { acc: Deferred, migration: Migration -> if (!migrationContext.dryrun) { logcat { "Running migration: { name = ${migration::class.simpleName}, version = ${migration.version} }" } - async { + async(start = CoroutineStart.UNDISPATCHED) { val prev = acc.await() migration(migrationContext) || prev } From 555d2f834fb64df9a56fdf4f54d528c15fefa4cb Mon Sep 17 00:00:00 2001 From: MajorTanya <39014446+MajorTanya@users.noreply.github.com> Date: Sun, 31 Mar 2024 00:35:12 +0100 Subject: [PATCH 142/212] Add ProGuard rule to keep mihon namespace classes (#605) Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- app/proguard-rules.pro | 1 + 1 file changed, 1 insertion(+) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index c44a8da74..c6471bea7 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -2,6 +2,7 @@ -keep,allowoptimization class eu.kanade.** -keep,allowoptimization class tachiyomi.** +-keep,allowoptimization class mihon.** # Keep common dependencies used in extensions -keep,allowoptimization class androidx.preference.** { public protected *; } From c9fddf9e388cff5e4071a89719825dee466deaf4 Mon Sep 17 00:00:00 2001 From: Maddie Witman Date: Sun, 31 Mar 2024 03:49:06 -0400 Subject: [PATCH 143/212] Moves upcoming requirement from existence to current day or later. (#606) * Moves upcoming requirement from existence to current day or later. * Suppress millis conversion warning --- .../main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt | 6 +++++- data/src/main/sqldelight/tachiyomi/data/mangas.sq | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt index a1fce650f..06b7bde58 100644 --- a/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt @@ -10,6 +10,8 @@ import tachiyomi.domain.library.model.LibraryManga import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.MangaUpdate import tachiyomi.domain.manga.repository.MangaRepository +import java.time.LocalDate +import java.time.ZoneId class MangaRepositoryImpl( private val handler: DatabaseHandler, @@ -65,9 +67,11 @@ class MangaRepositoryImpl( } } + @Suppress("MagicNumber") override suspend fun getUpcomingManga(statuses: Set): Flow> { + val epochMillis = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toEpochSecond() * 1000 return handler.subscribeToList { - mangasQueries.getUpcomingManga(statuses, MangaMapper::mapManga) + mangasQueries.getUpcomingManga(epochMillis, statuses, MangaMapper::mapManga) } } diff --git a/data/src/main/sqldelight/tachiyomi/data/mangas.sq b/data/src/main/sqldelight/tachiyomi/data/mangas.sq index 1baea800f..3e21e5bc0 100644 --- a/data/src/main/sqldelight/tachiyomi/data/mangas.sq +++ b/data/src/main/sqldelight/tachiyomi/data/mangas.sq @@ -115,7 +115,7 @@ AND _id != :id; getUpcomingManga: SELECT * FROM mangas -WHERE next_update > 0 +WHERE next_update >= :startOfDay AND favorite = 1 AND status IN :statuses ORDER BY next_update ASC; From 6abaa47f5beacdc36a40cec98e3d7f02ac77f320 Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Mon, 1 Apr 2024 03:51:46 +0200 Subject: [PATCH 144/212] Translations update from Hosted Weblate (#508) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Translated using Weblate (Greek) Currently translated at 99.8% (793 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/el/ * Translated using Weblate (Turkish) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/tr/ * Translated using Weblate (German) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/de/ * Translated using Weblate (Persian) Currently translated at 84.7% (673 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fa/ * Translated using Weblate (German) Currently translated at 100.0% (794 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/ * Translated using Weblate (Greek) Currently translated at 100.0% (794 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/el/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (794 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt_BR/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/pt_BR/ * Translated using Weblate (Galician) Currently translated at 95.9% (762 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/gl/ * Translated using Weblate (Japanese) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ja/ * Translated using Weblate (Javanese) Currently translated at 38.8% (7 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/jv/ * Translated using Weblate (Galician) Currently translated at 96.5% (767 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/gl/ * Translated using Weblate (Galician) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/gl/ * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ * Translated using Weblate (Spanish) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/ * Translated using Weblate (Croatian) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/ * Translated using Weblate (Russian) Currently translated at 100.0% (795 of 795 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/ * Translated using Weblate (Spanish) Currently translated at 100.0% (795 of 795 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (795 of 795 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (795 of 795 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/ * Translated using Weblate (Russian) Currently translated at 99.7% (796 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/ * Translated using Weblate (Spanish) Currently translated at 100.0% (798 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/ * Translated using Weblate (Filipino) Currently translated at 100.0% (798 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/ * Translated using Weblate (German) Currently translated at 100.0% (798 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (798 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (798 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/ * Translated using Weblate (Japanese) Currently translated at 99.4% (794 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (798 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/ * Translated using Weblate (Czech) Currently translated at 99.8% (797 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/cs/ * Translated using Weblate (Italian) Currently translated at 100.0% (798 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/it/ * Translated using Weblate (Nepali) Currently translated at 100.0% (798 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/ * Translated using Weblate (Czech) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/cs/ * Translated using Weblate (Italian) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/it/ * Translated using Weblate (Spanish) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/ * Translated using Weblate (Japanese) Currently translated at 99.7% (801 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/ * Translated using Weblate (Russian) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/ * Translated using Weblate (Filipino) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/ * Translated using Weblate (Japanese) Currently translated at 99.7% (801 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/ * Translated using Weblate (German) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/ * Translated using Weblate (Japanese) Currently translated at 99.7% (801 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/ --------- Co-authored-by: Syrodil Eventalious Co-authored-by: NukeSource Co-authored-by: Lyfja <45209212+lyfja@users.noreply.github.com> Co-authored-by: Arash Co-authored-by: Pitpe11 Co-authored-by: Mr. Fakezay Co-authored-by: kevans Co-authored-by: TheKingTermux Co-authored-by: gallegonovato Co-authored-by: Milo Ivir Co-authored-by: Lzmxya Co-authored-by: Dexroneum Co-authored-by: B4LiN7 Co-authored-by: Infy's Tagalog Translations Co-authored-by: gekka <1778962971@qq.com> Co-authored-by: akir45 Co-authored-by: Matyáš Caras Co-authored-by: Federico Pierantoni Co-authored-by: FateXBlood --- .../commonMain/resources/MR/am/strings.xml | 2 - .../commonMain/resources/MR/ar/strings.xml | 2 - .../commonMain/resources/MR/be/strings.xml | 2 - .../commonMain/resources/MR/bg/strings.xml | 2 - .../commonMain/resources/MR/bn/strings.xml | 2 - .../commonMain/resources/MR/ca/strings.xml | 2 - .../commonMain/resources/MR/ceb/strings.xml | 2 - .../commonMain/resources/MR/cs/plurals.xml | 5 + .../commonMain/resources/MR/cs/strings.xml | 17 ++- .../commonMain/resources/MR/cv/strings.xml | 2 - .../commonMain/resources/MR/de/plurals.xml | 4 + .../commonMain/resources/MR/de/strings.xml | 18 ++- .../commonMain/resources/MR/el/strings.xml | 5 +- .../commonMain/resources/MR/eo/strings.xml | 1 - .../commonMain/resources/MR/es/strings.xml | 14 +- .../commonMain/resources/MR/eu/strings.xml | 2 - .../commonMain/resources/MR/fa/strings.xml | 6 +- .../commonMain/resources/MR/fi/strings.xml | 2 - .../commonMain/resources/MR/fil/strings.xml | 14 +- .../commonMain/resources/MR/fr/strings.xml | 4 +- .../commonMain/resources/MR/gl/plurals.xml | 12 ++ .../commonMain/resources/MR/gl/strings.xml | 132 +++++++++++++----- .../commonMain/resources/MR/he/strings.xml | 2 - .../commonMain/resources/MR/hi/strings.xml | 2 - .../commonMain/resources/MR/hr/strings.xml | 5 +- .../commonMain/resources/MR/hu/strings.xml | 70 ++++++---- .../commonMain/resources/MR/in/strings.xml | 2 - .../commonMain/resources/MR/it/plurals.xml | 10 ++ .../commonMain/resources/MR/it/strings.xml | 12 +- .../commonMain/resources/MR/ja/plurals.xml | 3 + .../commonMain/resources/MR/ja/strings.xml | 16 ++- .../commonMain/resources/MR/jv/plurals.xml | 12 ++ .../commonMain/resources/MR/jv/strings.xml | 2 - .../resources/MR/ka-rGE/strings.xml | 2 - .../commonMain/resources/MR/kk/strings.xml | 2 - .../commonMain/resources/MR/kn/strings.xml | 2 - .../commonMain/resources/MR/ko/strings.xml | 2 - .../commonMain/resources/MR/lt/strings.xml | 2 - .../commonMain/resources/MR/lv/strings.xml | 2 - .../commonMain/resources/MR/mr/strings.xml | 2 - .../commonMain/resources/MR/ms/strings.xml | 2 - .../resources/MR/nb-rNO/strings.xml | 2 - .../commonMain/resources/MR/ne/strings.xml | 13 +- .../commonMain/resources/MR/nl/strings.xml | 2 - .../commonMain/resources/MR/nn/strings.xml | 1 - .../commonMain/resources/MR/pl/strings.xml | 2 - .../resources/MR/pt-rBR/plurals.xml | 5 + .../resources/MR/pt-rBR/strings.xml | 5 +- .../commonMain/resources/MR/pt/strings.xml | 2 - .../commonMain/resources/MR/ro/strings.xml | 2 - .../commonMain/resources/MR/ru/strings.xml | 14 +- .../commonMain/resources/MR/sa/strings.xml | 2 - .../commonMain/resources/MR/sah/strings.xml | 2 - .../commonMain/resources/MR/sc/strings.xml | 2 - .../commonMain/resources/MR/sk/strings.xml | 2 - .../commonMain/resources/MR/sq/strings.xml | 2 - .../commonMain/resources/MR/sr/strings.xml | 2 - .../commonMain/resources/MR/sv/strings.xml | 2 - .../commonMain/resources/MR/th/strings.xml | 2 - .../commonMain/resources/MR/tr/plurals.xml | 4 + .../commonMain/resources/MR/tr/strings.xml | 2 - .../commonMain/resources/MR/uk/strings.xml | 2 - .../commonMain/resources/MR/uz/strings.xml | 2 - .../commonMain/resources/MR/vi/strings.xml | 2 - .../resources/MR/zh-rCN/strings.xml | 14 +- .../resources/MR/zh-rTW/strings.xml | 24 +++- 66 files changed, 323 insertions(+), 195 deletions(-) diff --git a/i18n/src/commonMain/resources/MR/am/strings.xml b/i18n/src/commonMain/resources/MR/am/strings.xml index d8c6fd9ae..25c40d236 100644 --- a/i18n/src/commonMain/resources/MR/am/strings.xml +++ b/i18n/src/commonMain/resources/MR/am/strings.xml @@ -209,8 +209,6 @@ ብጁ ቀለም ማጣሪያ ብጁ ብሩህነት የሰብል ድንበሮች - ማሰሪያን ይቀንሳል ፣ ግን በአፈፃፀም ላይ ተጽዕኖ ያሳድራል - 32-ቢት ቀለም አንባቢ ሲከፈት የአሁኑን ሁነታ በአጭሩ ያሳዩ የንባብ ሁነታን አሳይ የገጽ ቁጥርን አሳይ diff --git a/i18n/src/commonMain/resources/MR/ar/strings.xml b/i18n/src/commonMain/resources/MR/ar/strings.xml index a4fb5a345..0ef96b7dd 100644 --- a/i18n/src/commonMain/resources/MR/ar/strings.xml +++ b/i18n/src/commonMain/resources/MR/ar/strings.xml @@ -272,7 +272,6 @@ فشل تحميل الصّفحات: %1$s أظهر الإجراءات عند الضغط المطوَّل فتح في WebView - ألوان 32bit تخطي الفصول المقروءة التراكب مضاعفة @@ -364,7 +363,6 @@ الإزاحة الجانبية شريط طويل بين أجزائه فجوات رمادي - يزيد من سلاسة تدرج الالوان بالصور، ولكن قد يؤثر على الأداء تعذر فتح إعدادات الجهاز نقل البيانات diff --git a/i18n/src/commonMain/resources/MR/be/strings.xml b/i18n/src/commonMain/resources/MR/be/strings.xml index 0984eef10..2e5c6b378 100644 --- a/i18n/src/commonMain/resources/MR/be/strings.xml +++ b/i18n/src/commonMain/resources/MR/be/strings.xml @@ -239,8 +239,6 @@ Выключаны Уключаны Абрэзаць поля - Памяншае акантоўку, але ўплывае на прадукцыйнасць - 32-разрадны колер Ненадоўга адлюстраваць бягучы рэжым пры адкрыцці чыталкі Паказаць рэжым чытання Паказаць нумар старонкі diff --git a/i18n/src/commonMain/resources/MR/bg/strings.xml b/i18n/src/commonMain/resources/MR/bg/strings.xml index 32d976159..5de52305e 100644 --- a/i18n/src/commonMain/resources/MR/bg/strings.xml +++ b/i18n/src/commonMain/resources/MR/bg/strings.xml @@ -274,7 +274,6 @@ Не бяха заредени успешно: %1$s страници Покажи при задържане върху екрана Отвори в WebView - 32-битови цветове Пропускай прочетените глави Режим на цветовия филтър Наложен @@ -369,7 +368,6 @@ Обнови кориците Еднопосочна синхронизация на прочетените глави. Установи проследяване за отделни манги от индивидуалния бутон за проследяване. Сиво - Намалява бандинга, но се отразява на производителността Мигрирай Източникът не е намерен Няма намерени страници diff --git a/i18n/src/commonMain/resources/MR/bn/strings.xml b/i18n/src/commonMain/resources/MR/bn/strings.xml index 467c134a3..cb07113e9 100644 --- a/i18n/src/commonMain/resources/MR/bn/strings.xml +++ b/i18n/src/commonMain/resources/MR/bn/strings.xml @@ -274,7 +274,6 @@ পৃষ্ঠাগুলো লোড করতে ব্যর্থ হয়েছে: %1$s দীর্ঘ ট্যাপে দেখান WebView তে খুলুন - ৩২-বিট রং পঠিত অধ্যায়গুলো এড়িয়ে যান রঙ ফিল্টার মিশ্রন মোড আস্তরণ @@ -361,7 +360,6 @@ পঠন ধরন দেখান ধূসর ফিলটারড অধ্যায় গুলো এড়িয়ে যান - ব্যানডিং কমায়, কিন্তু কর্মক্ষমতায় প্রভাভ ফেলে বিষয়বস্তু কাটআউটে দেখুন প্রাপ্তবয়স্কদের (18+) কন্টেন্ট থাকতে পারে ১৮+ diff --git a/i18n/src/commonMain/resources/MR/ca/strings.xml b/i18n/src/commonMain/resources/MR/ca/strings.xml index 7a6a3f33f..b0e4c2835 100644 --- a/i18n/src/commonMain/resources/MR/ca/strings.xml +++ b/i18n/src/commonMain/resources/MR/ca/strings.xml @@ -272,7 +272,6 @@ General Mostra les accions amb un toc llarg Obre en una WebView - Color de 32 bits Omet capítols marcats com a llegits Mode de mescla del filtre de color Sobreposa @@ -365,7 +364,6 @@ Mode de lectura Per a aquesta sèrie No s’ha pogut obrir la configuració del dispositiu - Redueix les bandes de colors, però pot afectar el rendiment La sincronització és unidireccional per a actualitzar el progrés dels capítols als serveis de seguiment externs. Configureu el seguiment d’elements individuals al seu botó de seguiment. Refresca les portades de la biblioteca Per data de pujada diff --git a/i18n/src/commonMain/resources/MR/ceb/strings.xml b/i18n/src/commonMain/resources/MR/ceb/strings.xml index 10e40aecd..85229825a 100644 --- a/i18n/src/commonMain/resources/MR/ceb/strings.xml +++ b/i18n/src/commonMain/resources/MR/ceb/strings.xml @@ -207,7 +207,6 @@ Ipakita ang numero sa panid Ipakita ang mode sa pagbasa Sa daklit ipakita ang kasamtangan nga mode kung giablihan ang magbabasa - 32-bit nga kolor Mga utlanan sa pananom Sa Grayscale @@ -238,7 +237,6 @@ Prev Ipakita ang mga tap zone nga overlay Sa daklit ipakita kung giablihan ang magbabasa - Pagpakunhod sa banding, apan mahimong makaapekto sa performance Balika ang mga tap zone Karaan na Dili iapil: %s diff --git a/i18n/src/commonMain/resources/MR/cs/plurals.xml b/i18n/src/commonMain/resources/MR/cs/plurals.xml index b21f3e786..2716c07c0 100644 --- a/i18n/src/commonMain/resources/MR/cs/plurals.xml +++ b/i18n/src/commonMain/resources/MR/cs/plurals.xml @@ -85,4 +85,9 @@ %d repozitáře %d repozitářů + + Zítra + Za %1$d dny + Za %1$d dnů + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/cs/strings.xml b/i18n/src/commonMain/resources/MR/cs/strings.xml index 553d893ff..5b7a05480 100644 --- a/i18n/src/commonMain/resources/MR/cs/strings.xml +++ b/i18n/src/commonMain/resources/MR/cs/strings.xml @@ -8,7 +8,7 @@ Knihovna Historie Záloha a obnovení - Migrovat + Přesunout Rozšíření Informace o rozšíření Nastavení @@ -127,7 +127,7 @@ Žádná další kapitola Žádná předchozí kapitola Načítání stránek… - Migrovat + Přesunout Kopírovat Došlo k chybě při stahování kapitol. Zkuste to znovu v sekci stahování Nalezeny nové kapitoly @@ -273,7 +273,6 @@ Nápověda Otevřít ve WebView Zobrazit akce dlouhým stisknutím - 32bitová barva Přeskakovat přečtené kapitoly Režim mísení filtru barev Překrytí @@ -282,7 +281,7 @@ Zesvětlit Ztmavit Nenalezeny žádné výsledky - Vyberte zdroj ze kterého chcete migrovat + Vyberte zdroj, ze kterého chcete přesouvat Zpět Vpřed Obnovit @@ -426,7 +425,7 @@ Přesunout dolů Zrušit vše Pohodlná mřížka - Migrovat + Přesunout Zakázat vše Povolit vše Nastavení vyhledávání @@ -551,7 +550,6 @@ Chyba v získání seznamu rozšíření Zásady ochrany osobních údajů %1$d položek v databázi co nejsou v knihovně - Snižuje pruhování barev, ale může mít vliv na výkon Přeskočit položky s nepřečtenými kapitolami Nic k vyčištění Uložit jako CBZ archiv @@ -798,4 +796,11 @@ Open source repozitář Brzy Aktualizujete ze starší verze a nevíte, co vybrat? Další informace naleznete v průvodci úložištěm. + Přesto přidat + Přesunout existující záznam + Zakázat oddálení + Nahradit + Otisk podepisovacího klíče již existuje + Repozitář %1$s má stejný otisk podepisovacího klíče jako %2$s +\nPokud to tak je správně, bude%2$s nahrazen, v opačném případě kontaktujte správce repozitáře. \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/cv/strings.xml b/i18n/src/commonMain/resources/MR/cv/strings.xml index 617a009ed..db00e5cff 100644 --- a/i18n/src/commonMain/resources/MR/cv/strings.xml +++ b/i18n/src/commonMain/resources/MR/cv/strings.xml @@ -231,8 +231,6 @@ Усӑҫ тӗс али Усӑҫ йӑлтӑрлӑхӗ Касӑк чиккисем - Йӑрӑм-йӑрӑм чакать, анчах тухӑҫлӑх ҫине витӗм кӳрет - 32 битлӑ тӗс Ел шутне кӑтартни Икӗ хут пуссан эним хӑвӑртлӑхӗ Елсем хушшипе енимлӗ куҫни diff --git a/i18n/src/commonMain/resources/MR/de/plurals.xml b/i18n/src/commonMain/resources/MR/de/plurals.xml index 3be82034b..785b237c5 100644 --- a/i18n/src/commonMain/resources/MR/de/plurals.xml +++ b/i18n/src/commonMain/resources/MR/de/plurals.xml @@ -68,4 +68,8 @@ %d Repository %d Repositorys + + Morgen + In %1$d Tagen + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/de/strings.xml b/i18n/src/commonMain/resources/MR/de/strings.xml index e038287cd..62808c86b 100644 --- a/i18n/src/commonMain/resources/MR/de/strings.xml +++ b/i18n/src/commonMain/resources/MR/de/strings.xml @@ -55,7 +55,7 @@ Rückgängig machen Protokoll öffnen Wiederherstellen - Wird geladen … + Wird geladen… App nicht verfügbar Allgemein Leser @@ -271,7 +271,6 @@ Lade Seiten… Seiten konnten nicht geladen werden: %1$s In WebView öffnen - 32-Bit-Farben Gelesene Kapitel überspringen Aktionen bei langem Antippen anzeigen Overlay @@ -364,7 +363,6 @@ Grau Lesemodus Für diese Serie - Reduziert Streifenbildung, könnte aber die Leistung beeinflussen Geräteeinstellungen konnten nicht geöffnet werden Einweg-Synchronisation zum Aktualisieren der Kapitelfortschritte in den externen Trackingdiensten. Richte Tracking für einzelne Einträge über deren jeweiligen Trackingbutton ein. Bibliothekscover aktualisieren @@ -759,7 +757,7 @@ Loslegen Es muss ein Ordner ausgewählt sein Willkommen! - %s reinstallieren? + %s reinstalliert? Überspringen Weiter Lass uns zuerst ein paar Dinge einrichten. Du kannst diese später in den Einstellungen jederzeit ändern. @@ -799,4 +797,16 @@ Bald Zertifikatsbestätigungen zurücknehmen Rauszoomen deaktivieren + Benutzerdefiniertes Anzeigeprofil + Trotzdem hinzufügen + Existierenden Eintrag migrieren + Ersetzen + Signaturschlüssel-Fingerprint existiert bereits + Repository %1$s hat den gleichen Signaturschlüssel-Fingerprint wie %2$s. +\nFalls das erwünscht ist, wird %2$s nun ersetzt. Falls nicht, kontaktiere den Repository-Maintainer. + Nächster Monat + Vorheriger Monat + Bevorstehend + Bevorstehende Aktualisierungen ansehen + Leitfaden für Bevorstehendes \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/el/strings.xml b/i18n/src/commonMain/resources/MR/el/strings.xml index 226a7d3d5..00fb56e96 100644 --- a/i18n/src/commonMain/resources/MR/el/strings.xml +++ b/i18n/src/commonMain/resources/MR/el/strings.xml @@ -272,7 +272,6 @@ Κοινό Εμφάνιση ενεργειών με παρατεταμένο πάτημα Άνοιγμα στο WebView - Χρώμα 32-bit Προσπέραση διαβασμένων κεφαλαίων Επικάλυψη Πολλαπλασιασμός @@ -362,7 +361,6 @@ Νεότερο Φιλτράρει όλες τις καταχωρήσεις στη βιβλιοθήκη σας Γκρι - Μειώνει το banding, αλλά μπορεί να επηρεάσει την απόδοση Λειτουργία ανάγνωσης Για αυτήν τη σειρά Δεν ήταν δυνατό το άνοιγμα των ρυθμίσεων της συσκευής @@ -721,7 +719,7 @@ Σύνδεση παρακολούθησης Δεν ήταν δυνατή η δημιουργία αντιγράφου ασφαλείας Αδειοδοτημένο - Δεν υπάρχουν κεφάλαια προς εμφάνιση - Δεν μπορούσε να φτάσει το %s + Δεν ήταν δυνατή η πρόσβαση στο %s HTTP %d, ελέγξτε την ιστοσελίδα στο WebView Δεν υπάρχει σύνδεση στο διαδίκτυο Ξεκλείδωμα %s @@ -798,4 +796,5 @@ Σύντομα Ανάκληση αξιόπιστων άγνωστων επεκτάσεων Αποθετήριο ανοικτού κώδικα + Απενεργοποίηση σμίκρυνσης εικόνας \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/eo/strings.xml b/i18n/src/commonMain/resources/MR/eo/strings.xml index 991249e0e..bc8fdbbc4 100644 --- a/i18n/src/commonMain/resources/MR/eo/strings.xml +++ b/i18n/src/commonMain/resources/MR/eo/strings.xml @@ -161,7 +161,6 @@ Plustavolo Adaptita lumeco Stuci borderojn - 32-bitaj koloroj Legada reĝimo Montri numero de paĝo Plenekrano diff --git a/i18n/src/commonMain/resources/MR/es/strings.xml b/i18n/src/commonMain/resources/MR/es/strings.xml index 0aedfb7b4..a382ea762 100644 --- a/i18n/src/commonMain/resources/MR/es/strings.xml +++ b/i18n/src/commonMain/resources/MR/es/strings.xml @@ -274,7 +274,6 @@ Menú de pulsación prolongada Abrir en WebView Saltar capítulos leídos - Color de 32 bits Mezcla de colores Superponer Multiplicar @@ -365,7 +364,6 @@ Modo de lectura Para esta serie Gris - Reduce el efecto anillado en los degradados y mejora la calidad de los grises, pero puede afectar al rendimiento No se pudieron abrir los ajustes del dispositivo Actualizar las portadas de la biblioteca La sincronización de estos servicios solo funciona en un solo sentido. Cada elemento en tu biblioteca tiene un botón de seguimiento y tendrás que configurarlo a mano, uno a uno. @@ -799,4 +797,16 @@ Dejar de marcar todas la extensiones desconocidas como de confianza Abrir repositorio Desactivar alejar + Perfil de visualización personalizado + Agregar de todos modos + Migrar la entrada existente + Reemplazar + La huella digital de la clave de la firma ya existe + El repositorio %1$s tiene la misma huella digital de clave de la firma que %2$s. +\nSi esto es lo esperado, %2$s será reemplazado, de lo contrario contacte con quien mantiene su repositorio. + Próximo + Ver las próximas actualizaciones + Próxima guía + Próximo mes + Mes anterior \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/eu/strings.xml b/i18n/src/commonMain/resources/MR/eu/strings.xml index 92e60e1bf..9674a1604 100644 --- a/i18n/src/commonMain/resources/MR/eu/strings.xml +++ b/i18n/src/commonMain/resources/MR/eu/strings.xml @@ -102,7 +102,6 @@ NSFW (18+) edukia izan dezake Instalatzailea Pantaila osoa - 32 bit-eko kolorea Gris-eskala Alderantzikatua Kolore-iragazkia modu nahastuan @@ -439,7 +438,6 @@ Gorantz Beherantz Inoiz ere ez - Kolore-marraketak murrizten ditu, baina errendimenduan eragina dauka Moztu ertzak Alderantzikatu bolumen-teklak Sakatzea alderantzikatu diff --git a/i18n/src/commonMain/resources/MR/fa/strings.xml b/i18n/src/commonMain/resources/MR/fa/strings.xml index ba003bb23..6137d857d 100644 --- a/i18n/src/commonMain/resources/MR/fa/strings.xml +++ b/i18n/src/commonMain/resources/MR/fa/strings.xml @@ -144,7 +144,6 @@ هم اندازه حالت هوشمند Multiply Overlay - نوار نوار بودن صفحه را کاهش می‌دهد، ولی ممکن است در عملکرد تاثیر بگذارد Double tap animation speed نصب نشانک قسمت @@ -204,16 +203,13 @@ استفاده از فیلتر رنگ سفارشی استفاده از سیستم روشنایی سفارشی بریدن حاشیه‌ها - رنگ ۳۲ بیتی هنگام باز شدن کتابخوان ، حالت فعلی را به طور خلاصه نشان داده شود نمایش حالت خواندن نمایش شماره صفحه انمیشن‌های تغییر صفحه تمام صفحه این افزونه دیگر در دسترس نیست. ممکن است به درستی کار نکند و باعث ایجاد مشکلاتی در برنامه شود. حذف نصب آن توصیه می شود. - این افزونه با یک گواهی نامطمئن امضا شده است و فعال نشده است. -\n -\nیک افزونه مخرب می تواند هر گونه طلاعات ورود به سیستم ذخیره شده تاچیومی را بخواند یا کدهای دلخواه(احتمالا خطرناک) را اجرا کند. + یک افزونه مخرب می تواند هر گونه طلاعات ورود به سیستم زخیره شده را بخواند یا اختیاری یک کد را اجرا کند. \n \nبا اعتماد به این گواهی شما این خطرات را می‌پذیرید. افزونه نامطمئن diff --git a/i18n/src/commonMain/resources/MR/fi/strings.xml b/i18n/src/commonMain/resources/MR/fi/strings.xml index 24e863234..3cec33782 100644 --- a/i18n/src/commonMain/resources/MR/fi/strings.xml +++ b/i18n/src/commonMain/resources/MR/fi/strings.xml @@ -148,7 +148,6 @@ Animoi sivusiirtymät Tuplanäpäytyksen animaation nopeus Näytä sivunumero - 32-bittinen väri Rajaa reunat Mukautettu kirkkaus Mukautettu värisuodatin @@ -363,7 +362,6 @@ Suodattaa kaikki sarjat kirjastossasi Harmaa Tälle sarjalle - Vähentää juovia, mutta vaikuttaa suorituskykyyn Lukutila Laiteasetuksia ei voitu avata Yksisuuntainen synkronointi lukujen seurantapalveluiden päivittämiseksi. Määritä yksittäisten mangamerkintöjen seuranta seurantapainikkeesta. diff --git a/i18n/src/commonMain/resources/MR/fil/strings.xml b/i18n/src/commonMain/resources/MR/fil/strings.xml index 8d1d80efa..8489e87d4 100644 --- a/i18n/src/commonMain/resources/MR/fil/strings.xml +++ b/i18n/src/commonMain/resources/MR/fil/strings.xml @@ -169,8 +169,6 @@ Pinili kong filter ng kulay Pinili kong liwanag Gupitin ang gilid - 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 Ipakita ang bilang ng pahina @@ -799,4 +797,16 @@ Bawiin ang mga pinagkakatiwalaang hindi kilalang extension Open source na repo Patayin ang pag-zoom out + Palitan + Idagdag pa rin + Ilipat ang kasalukuyang entry + Umiiral na ang Signing Key Fingerprint + Ang repository na %1$s ay may magkaparehong Signing Key Fingerprint sa %2$s. +\nKung ito ay inaasahan, %2$s ang papalitan, kung hindi naman ay makipag-ugnayan sa tagapamahala ng iyong repo. + Ipasadya ang profile sa display + Paparating + Gabay sa Paparating + Susunod na Buwan + Nakaraang Buwan + Tingnan ang mga Paparating na Update \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/fr/strings.xml b/i18n/src/commonMain/resources/MR/fr/strings.xml index 31a9a27f0..abf7d13d2 100644 --- a/i18n/src/commonMain/resources/MR/fr/strings.xml +++ b/i18n/src/commonMain/resources/MR/fr/strings.xml @@ -272,7 +272,6 @@ Copier Afficher les actions en appuyant longuement Ouvrir dans WebView - Couleurs à 32 bits Passer les chapitres marqués comme lus Produit Écran @@ -363,7 +362,6 @@ Filtrer toutes les entrées dans la bibliothèque Gris Pour cette série - Réduit les bandes de couleur, mais peut impacter les performances Mode de lecture Impossible d\'ouvrir les paramètres de l\'appareil Actualiser les couvertures de la bibliothèque @@ -798,4 +796,4 @@ Impossible de créer un fichier de sauvegarde Dernière sauvegarde automatique : %s Paramètres sources - + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/gl/plurals.xml b/i18n/src/commonMain/resources/MR/gl/plurals.xml index 0c75de0e0..d6407f42c 100644 --- a/i18n/src/commonMain/resources/MR/gl/plurals.xml +++ b/i18n/src/commonMain/resources/MR/gl/plurals.xml @@ -60,4 +60,16 @@ Falta %1$s capítulo Faltan %1$s capítulos + + %d repositorio + %d repositorios + + + Mañá + Dentro de %1$d días + + + Un día + %d días + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/gl/strings.xml b/i18n/src/commonMain/resources/MR/gl/strings.xml index ee5a4585a..f5986462e 100644 --- a/i18n/src/commonMain/resources/MR/gl/strings.xml +++ b/i18n/src/commonMain/resources/MR/gl/strings.xml @@ -7,7 +7,7 @@ Historial Actualizacións Biblioteca - Cola de baixadas + Cola de descargas Último capítulo lido Deshabilitado Permitir eliminar os capítulos marcados como favoritos @@ -68,21 +68,17 @@ Filtro de cor personalizado Brillo presonalizado Recortar bordes - Reduce o anelado (\"banding\"), pero pode afectar ao rendemento - Cor de 32 bits Amosar brevemente o modo actual cando se abre o lector Amosar modo de lectura Amosar número de páxina - Velocidade da animación do doble toque + Velocidade da animación de doble toque Animar transición de páxinas Mostrar contido na área recortada Pantalla completa As fontes desta extensión poden ter contido NSFW (+18) +18 Esta extensión xa non está dispoñible. Pode que non funcione ben e cause problemas na aplicación. Recoméndase desinstalala. - Esta extensión foi firmada cun certificado non confiable e non se activou. -\n -\nUnha extensión maliciosa podería ler calquera credencial de inicio de sesión gardado en Mihon ou executar código arbitrario. + Unha extensión maliciosa podería ler calquera credencial de inicio de sesión gardado en Mihon ou executar código arbitrario. \n \nConfiando neste certificado aceptas estes riscos. Extensión non confiable @@ -91,7 +87,7 @@ De confianza Instaladas Instalando - Baixando + Descargando Pendentes Instalar Obsoletas @@ -140,7 +136,7 @@ Acerca de Avanzado Seguimento - Baixadas + Descargas Lector Biblioteca Xeral @@ -167,7 +163,7 @@ Fixar Deshabilitar Amosar laretas de categorías - Capítulos baixados + Capítulos descargados Cuadrícula ampla Lista Cuadrícula compacta @@ -197,7 +193,7 @@ Eliminar Quitar favorito Engadir favorito - Baixar + Descargar Marcar anteriores como lidos Marcar como non lido Marcar como lido @@ -246,7 +242,7 @@ Tema da aplicación Sincronización do progreso de lectura; unidireccional ou mellorada Fontes, extensións, procura global - Baixar os rexistros de erros, optimizacións de batería + Envorcar os rexistros de erros, optimizacións de batería Maremoto Hoxe Con capítulos sin ler @@ -270,7 +266,7 @@ Restaurar a copia de seguridade Frecuencia das copias de seguridade Fontes faltantes: - Invalidar o índice de baixadas + Invalidar o índice de descargas Garda os rexistros de erros nun ficheiro para compartilo cos desenvolvedores Fai que funcionen mellor as actualizacións e as copias de seguridade que se fagan en segundo plano Procurar actualizacións @@ -280,7 +276,7 @@ Axuda a corrixir os erros. Non se enviará ningunha información sensíbel Desactivar o modo incógnito Iniciar sesión - Capítulos baixados + Capítulos descargados Da biblioteca Eliminar categoría Dispoñible, mais a fonte non se instalou: %s @@ -288,8 +284,8 @@ Amosar o número de capítulos por ler na icona de actualizacións Excluír: %s Saltar os capítulos duplicados - Baixar automaticamente durante a lectura - Só funciona con elementos da biblioteca e se o capítulo actual e o seguinte xa están baixados + Descargar automaticamente durante a lectura + Só funciona con elementos da biblioteca e se o capítulo actual e o seguinte xa están descargados. Restauración completada %02d min y %02d seg De que queres facer unha copia de seguridade\? @@ -320,12 +316,12 @@ Se a separación das páxinas anchas non coincide coa dirección de lectura Sensibilidade para ocultar o menú ao desprazarse Quinto capítulo lido antes do último - Baixar capítulos novos + Descargar capítulos novos Nome de usuario Erro ao gardar a portada Localización non válida: %s Antepenúltimo capítulo lido - Os elementos das categorías excluídas non se baixarán aínda se están en categorías incluídas. + Os elementos das categorías excluídas non se descargarán, aínda se están en categorías incluídas. Baixa Creando a copia de seguridade Categorías, actualización global, accións ao arrastrar @@ -344,7 +340,7 @@ Estatísticas Comezados Local - Baixados + Descargados Idioma da aplicación ErroInterno: Revisa o rexistro de erros para máis información Información da aplicación @@ -372,9 +368,7 @@ Horizontal Esquerda Ampliar a imaxe en horizontal - Restauraranse os datos da copia de seguridade. -\n -\nTerás que instalar as extensións que falten e iniciar sesión nos servizos de seguemento para usalos. + Pode ser que debas instalar as extensións que falten e iniciar sesión nos servizos de seguemento para usalos. Datos Restableceuse a configuración do lector Pechar a sesión en %1$s\? @@ -384,11 +378,11 @@ Establecer por defecto Ningún Penúltimo capítulo lido - Baixada automática + Descarga automática Crear unha copia de seguridade Non se puido facer a copia de seguridade Actualizar as portadas da biblioteca - O arquivo da copia de seguridade non é válido + 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) Cada 3 días @@ -426,7 +420,7 @@ Yin e Yang Modo negro puro Incluír: %s - Non se puido baixar a listaxe de extensións + Non se puido obter as extensións dispoñibles Borrar a base de datos Anterior Só con Wi-Fi @@ -465,7 +459,7 @@ Actividade en segundo plano Interface de tableta Rexistro detallado - Só os baixados + Só os descargados Modo incógnito Pausar o historial de lectura Correo electrónico @@ -489,7 +483,7 @@ Categorías excluídas Alta Anterior ao antepenúltimo capítulo lido - Baixar por adiantado + Descargar por adiantado Gardar como arquivo CBZ Pode usarse para restaurar a biblioteca actual Restaurar a biblioteca dende unha copia de seguridade @@ -502,7 +496,7 @@ Cookies borradas Borrar a caché dos capítulos Borrouse a caché. Elimináronse %1$d ficheiros - Forzar á aplicación a comprobar de novo os capítulos baixados + Forzar á aplicación a comprobar de novo os capítulos descargados Estás seguro\? Perderanse os capítulos lidos e o progreso dos elementos que non estean na biblioteca Restablecer os axustes do lector en cada serie Desactivar a optimización de batería @@ -518,7 +512,7 @@ En pausa Título da fonte Por número de capítulo - Baixar + Descargar Data de finalización Queres eliminar a data\? Data de comezo @@ -535,7 +529,7 @@ Non se puido evitar Cloudflare O widget non está dispoñible cando está activo o bloqueo da aplicación Xa se está a actualizar - Baixando (%1$d/%2$d) + Descargando (%1$d/%2$d) Non lidos Isto eliminará a data de comezo seleccionada de %s Está dispoñible unha nova versión oficial. Preme para saber como migrar das versións non oficiais de F-Droid. @@ -587,7 +581,7 @@ %s pechouse por un problema inesperado. Aconsellámoste que compartas os rexistros de erros na canle de soporte no Discord (en inglés). Reinicia a aplicación Non se atopou ninguna fonte instalada - Advertencia: As baixadas grandes poden facer que as fontes se volvan máis lentas e/ou bloqueen Mihon. Preme para saber máis. + Advertencia: As descargas pesadas poden facer que as fontes se volvan máis lentas e/ou bloqueen Mihon. Preme para saber máis. Abrir no GitHub Xestor de descargas Erro @@ -618,7 +612,7 @@ Páxina: %1$d Produciuse un erro ao gardar a imaxe Actualizacións erradas: %1$d - Baixar + Descargar Actualizacións da aplicación Preme para saber máis Erro ao actualizar a portada @@ -653,7 +647,7 @@ Tempo de lectura Na actualización global Totais - Baixando… + Descargando… Aínda non tes categorías. *necesario Páxina anterior @@ -704,4 +698,76 @@ Arrastre de capítulo Acción de arrastrar á dereita Información de depuración + Seleccionado + Non seleccionado + Establecer intervalo + Ordenar categorías + Non se puido acceder a %s + Sen conexión a Internet + Primeiro hai que configurar algunhas cousas. Sempre podes modificalas nos axustes. + Seguinte + Debes elexir un cartafol + Guía de almacenamento + Actualizaches dende unha versión máis antiga e non sabes que elixir? Bótalle unha ollada á guía de almacenamento. + Nórdico + Marcas de tempo relativas + Actualizacións intelixentes + Engadir un repositorio + Engade máis repositorios a Mihon. Debe ser unha dirección URL que remate en \"index.min.json\". + Borrar repositorio + A dirección URL do repositorio non é válida + Queres eliminar o repositorio \"%s\"? + Iniciar sesión no servizo de seguemento + Ubicación do almacenamento + Frecuencia de actualización personalizada + Puntuación do rastrexador + Máis opcións + Borrar os descargados + Equipo de tradución + Datos e almacenamento + Desbloquear %s + Próxima actualización prevista + Queres ordenar as categorías por orde alfabético? + Aplicar + Aceptar + Mover ao último posto + Restablecer os axustes por defecto + Guía de benvida + Dámosche a benvida! + Comecemos + Omitir + Elixe o cartafol onde %1$s almacenará os capítulos descargados, as copias de seguridade e máis. +\n +\nRecoméndase usar un cartafol dedicado. +\n +\nElixir cartafol: %2$s + Elixe un cartafol + Permiso para instalar aplicacións + Para instalar as extensións das fontes. + Pemiso para amosar notificacións + Recibe notificacións cando se actualice a biblioteca e máis cousas. + Uso de batería en segundo plano + Evita as interrupcións nas actualizacións da biblioteca, nas descargas e na restauración de copias de seguridade que tarden un anaco. + Permitir + Primeira vez usando %s? Recomendámosche ler a guía de introdución. + Volvendo instalar %s? + «%1$s» en lugar de «%2$s» + Predicir cando sae o seguinte capítulo + Preme aquí para conceder os permisos para instalar extensións. + Revogar as extensións de confianza de orixe descoñecido + Repositorios de extensións + Aínda non engadiches ningún repositorio. + Dirección URL do repositorio + Este repositorio xa existe! + Abrir o repositorio + Tremelucir ao cambiar de páxina + Reducir o efecto de retención de imaxe nas pantallas de tinta electrónica + Aínda non seleccionaches ningunha localización de almacenamento + Desactivar redución de zoom + HTTP %d, comproba a páxina web no WebView + Utilízase para as copias de seguridade automáticas, as descargas de capítulos e a fonte local. + Axustes da aplicación + Axustes da fonte + Crear + Erro completo: \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/he/strings.xml b/i18n/src/commonMain/resources/MR/he/strings.xml index d4c29d6fa..43020db5d 100644 --- a/i18n/src/commonMain/resources/MR/he/strings.xml +++ b/i18n/src/commonMain/resources/MR/he/strings.xml @@ -262,7 +262,6 @@ מסנן צבע מותאם אישית בהירות מותאמת אישית חתוך גבולות - צבע 32-סיביות הצג מספר עמוד מהירות הנפשה בהקשה כפולה הנפשת מעברי דפים @@ -534,7 +533,6 @@ דולג בגלל שיש פרקים שלא נקראו דולג בגלל שאין פרקים שנקראו סגור - מפחית פסים, אך עשוי להשפיע על הביצועים שגיאות אפס את הגדרות מצב הקריאה של כל סדרה בנפרד Shizuku לא פועל diff --git a/i18n/src/commonMain/resources/MR/hi/strings.xml b/i18n/src/commonMain/resources/MR/hi/strings.xml index cf7c1e2bc..f167674c8 100644 --- a/i18n/src/commonMain/resources/MR/hi/strings.xml +++ b/i18n/src/commonMain/resources/MR/hi/strings.xml @@ -274,7 +274,6 @@ पृष्ठों को लोड करने में विफल है: %1$s लंबे टैप पर क्रियाएँ दिखाएँ WebView में खोलें - 32 बिट रंग पढ़े हुए अध्यायों को छोड़ें रंग फिल्टर मिश्रण मोड ओवरले @@ -366,7 +365,6 @@ पढ़न मोड इस श्रृंखला के लिए ग्रे - बैंडिंग को कम करता है, लेकिन प्रदर्शन को प्रभावित कर सकता है डिवाइस सेटिंग नहीं खोल सका ट्रैकिंग सेवाओं में अध्याय की प्रगति को अद्यतन करने के लिए एकतरफा सिंक। अपने ट्रैकिंग बटन से व्यक्तिगत प्रविष्टियों के लिए ट्रैकिंग सेट अप करें। रिफ्रेश पुस्तकालय मंगा कवर diff --git a/i18n/src/commonMain/resources/MR/hr/strings.xml b/i18n/src/commonMain/resources/MR/hr/strings.xml index b9ec5c237..dd70cde6f 100644 --- a/i18n/src/commonMain/resources/MR/hr/strings.xml +++ b/i18n/src/commonMain/resources/MR/hr/strings.xml @@ -207,10 +207,8 @@ Prilagođeni filtar boja Prilagođena svjetlina Odreži rubove - Smanjuje oštre prijelaze između nijansi, ali može utjecati na performansu - 32-bitna boja Prikaži broj stranice - Brzina animacije dvostrukog dodira + Brzina animacije dvostrukim dodirom Animiraj prijelaze stranica Prikaži sadržaj u izrezanom području Boja pozadine @@ -799,4 +797,5 @@ Uskoro Dostupno: %1$s / Ukupno: %2$s Deaktiviraj smanjivanje zumiranja + Prilagođeni profil prikaza \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/hu/strings.xml b/i18n/src/commonMain/resources/MR/hu/strings.xml index 8366439ff..5f2a28828 100644 --- a/i18n/src/commonMain/resources/MR/hu/strings.xml +++ b/i18n/src/commonMain/resources/MR/hu/strings.xml @@ -2,7 +2,7 @@ Név Kategóriák - Könyvtár bejegyzések + Könyvtári elemek Fejezetek Nyilvántartás Előzmények @@ -53,12 +53,12 @@ Verzió Összeomlás-jelentések küldése Befejezett - Dobott + Eldobva Tervezett Értékelés Cím Állapot - %1$d oldal + %1$d. oldal Nem található a következő fejezet A hálózat nem érhető el Letöltési sor @@ -133,13 +133,13 @@ Sötét Világos Könyvtár - Frissít + Frissítés Előre Vissza Legrégebbi Legújabb Legújabb fejezet - Letölzozz fejezetek + Letöltött fejezetek Áttelepítés Kezdés Fejezetek megtekintése @@ -180,7 +180,7 @@ Ugrás legalulra Ugrás legfelülre Keresési beállítások - Dátum hozzáadva + Hozzáadás dátuma Nem megbízható bővítmény Elavult Függőben lévő frissítések @@ -190,7 +190,6 @@ Megjelenés Ez nem akadályozza meg a nem hivatalos vagy az esetlegesen helytelenül megjelölt bővítményeket a 18+-os tartalmak megjelenítésétől az alkalmazáson belül. Megjelenítés a forrás- és kiterjesztéseklistában - 32 bites színmélység Rövid ideig mutassa a jelenlegi módot az olvasó megnyitásakor Olvasó mód mutatása Tartalmazhat felnőtt (18+) tartalmat @@ -246,8 +245,8 @@ Áttelepítés %1$s. fejezet A Mihon működéséhez WebView szükséges - %1$s. fejezetek - A(z) %1$s. fejezet és %2$d egyéb fejezetek + %1$s fejezet + %1$s. fejezet és további %2$d %1$s:%2$s, %3$d. oldal Újraolvasás Biztosan törölni akarja a kijelölt fejezeteket\? @@ -278,8 +277,8 @@ Tiltott: %s Kedvelt: %s Semmi - Bejegyzések a tiltott kategóriákban nem lesznek letöltve, még akkor se, ha a kedvelt kategóriában is vannak. - Mangák a tiltott kategóriákban nem lesznek frissítve, még akkor se, ha a kedvelt kategóriákban is vannak. + Elemek a tiltott kategóriákban nem lesznek letöltve, még akkor se, ha a kedvelt kategóriában is vannak. + Elemek a tiltott kategóriákban nem lesznek frissítve, még akkor se, ha a kedvelt kategóriákban is vannak. Biztonságos képernyő Zárás tétlen állapotban Biometrikus zár @@ -329,10 +328,10 @@ Nyilvántartási útmutató Új fejezetek letöltése Automatikus letöltés - Hátulról az ötödik fejezet - Hátulról a negyedik befejezett fejezet - Harmadikként olvasott fejezet - Utolsó előtti befejezett fejezet + Ötödik utoljára olvasott fejezet + Negyedik utoljára olvasott fejezet + Harmadik utoljára olvasott fejezet + Második utoljára olvasott fejezet Kikapcsolva Tiltott kategóriák Könyvjelzőzött fejezetek törlésének engedélyezése @@ -375,7 +374,7 @@ Hosszú szalag lyukakkal Automatikus Szürke - Mappák létrehozása bejegyzések címe szerint + Mappák létrehozása elemek címe szerint Mentse a lapokat külön mappákba Cselekmények mutatása hosszú koppintáskor Műveletek @@ -393,11 +392,10 @@ Szürkeárnyalatos Ki Be - Csökkenti a sávosodást, de befolyásolhatja a teljesítményt Dupla koppintás animációjának sebessége Telepítő Bővítmény telepítése… - Összes bejegyzés + Összes elem Az összes frissítése Nyelv Telepítsd és indítsd el a Shizukut, hogy kiegészítő telepítőként használd. @@ -408,7 +406,7 @@ Egyirányú szinkronizálás, hogy a fejezetben való előrehaladás frissítve legyen a követési szolgáltatásokban. Állíts be követést egyéni elemekre, a követés gomb lenyomásával. 3 naponta Csak Wi-Fi-n keresztül - Ajánlott az automatikus biztonsági mentése. Ezen kívül máshol is legyenek másolatok. + A biztonsági mentésekről más helyeken is érdemes másolatokat tartani. A biztonsági mentések érzékeny adatokat tartalmazhatnak, beleértve a tárolt jelszavakat is; legyen óvatos a megosztáskor. Nincs könyvtári elem, amit menteni lehetne Helyi forrás Legújabb @@ -420,7 +418,7 @@ Beállítás alapértelmezettként Tablet mód Lista elejére - Globális keresés \"%1$s\"-ra/re + Globális keresés erre: \"%1$s\" Nincs több találat Nincs találat Globális keresés… @@ -513,17 +511,17 @@ Megnyitás GitHub-on Ez az Android verzió már nem támogatott Alkalmazás frissítések - Nem sikerült megnyitni az utolsó olvasott fejezetet - v%1$s-ra/re frissült + Nem sikerült megnyitni az utoljára olvasott fejezetet + Frissítve erre: v%1$s Széles képek pásztázása Mentés CBZ archívumkén Levendula WebView adatok törlése Feljegyzi a hibát egy fájlba, hogy meg lehessen osztani a fejlesztővel - A biztonsági mentés nem tartalmaza a bejegyzéseket. + A biztonsági mentés nem tartalmazza az elemeket. Megszakított helyreállítás Letöltés - Befejezett + Publikálás befejezve Nincs leírás Letöltés (%1$d/%2$d) Felvétel nyilvántartáshoz @@ -666,7 +664,7 @@ Kategória üres Ez eltávolítja az eddig kiválasztott befejezési dátumot a(z) %s szolgáltatásból Olvasási idő - Nem található bejegyzés ebben a kategóriában + Nem található elem ebben a kategóriában A/az \"%s\"-t elfogod távolítani a könyvtáradból Éppen most Letöltések ellenőrzése @@ -685,11 +683,11 @@ Egyedi frissítési időköz Következő várt frissítés Dupla koppintás a nagyításhoz - Már könyvtári bejegyzések elrejtése + Már könyvtári elemek elrejtése Bejelentkezés nyilvántartóhoz Letöltött törlése Magas képek szétvágása - %d soronként + %d elem soronként Következő kiadás idejének megjóslása Széles oldalak forgatása az illeszkedéshez Jobbra csúsztatási cselekmény @@ -713,7 +711,7 @@ Könyvtár szinkronizálása elkészült Utolsó automatikus biztonsági mentés: %s Kiválasztott - Nincs szkennelő-fordító találat + Nincsenek szkennelő-fordítók Nem kiválasztott Sorozat alulra küldése Szkennelő-fordító @@ -721,7 +719,7 @@ Nincs internet kapcsolat Tárolóhely felhasználás Könyvtár frissítése ... (%s) - Bejegyzések + Elemek Felnavigálás Adat és tárolás Szeretnéd a kategóriákat betűrendbe rendezni? @@ -790,7 +788,7 @@ Nord Érzékeny beállítások megadása (Pl. szolgáltatások bejelentkezési tokenei) Letöltési index érvénytelenítése - Új fejezetek megjelenése %1$s múlva várható, ellenőrzési időköz: %2$s. + Új fejezetek megjelenése %1$s múlva várható, frissítési időköz: %2$s. Leállítja a(z) %s nyilvántartását? Nyilvántartott elemek Kicsinyítés letiltása @@ -799,4 +797,16 @@ Mind becslése Hamarosan Egyedi frissítési időköz: + Hozzáadás mindenképp + Meglévő elem áttelepítése + Egyedi megjelenítési profil + Lecserélés + Aláíró kulcs ujjlenyomat már létezik + A(z) %1$s tárolónak ugyanaz az aláíró kulcs ujjlenyomat, mint a(z) %2$s tárolónak. +\nHa ez várható volt, akkor a(z) %2$s lecserélődik, ellenkező esetben lépjen kapcsolatba a tároló karbantartójával. + Közelgő + Következő hónap + Közelgő frissítések megtekintése + Közelgő útmutató + Előző hónap \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/in/strings.xml b/i18n/src/commonMain/resources/MR/in/strings.xml index 6b7bf30fb..ab9e07461 100644 --- a/i18n/src/commonMain/resources/MR/in/strings.xml +++ b/i18n/src/commonMain/resources/MR/in/strings.xml @@ -269,7 +269,6 @@ Memuat halaman… Gagal memuat halaman: %1$s Buka dalam WebView - Warna 32-bit Lewati bab yang sudah dibaca Tampilkan tindakan saat tekan lama Mode saringan warna campur @@ -360,7 +359,6 @@ Pencadangan masih dalam proses %02d menit, %02d detik Mode membaca - Mengurangi banding, namun berdampak pada performa Abu-abu Untuk seri ini Tidak dapat membuka pengaturan perangkat diff --git a/i18n/src/commonMain/resources/MR/it/plurals.xml b/i18n/src/commonMain/resources/MR/it/plurals.xml index 29e0d9b29..971d6dc75 100644 --- a/i18n/src/commonMain/resources/MR/it/plurals.xml +++ b/i18n/src/commonMain/resources/MR/it/plurals.xml @@ -80,4 +80,14 @@ %d giorni %d giorni + + Domani + Tra %1$d giorni + Tra %1$d giorni + + + %d repository + %d repository + %d repository + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/it/strings.xml b/i18n/src/commonMain/resources/MR/it/strings.xml index b68658c88..ad504b170 100644 --- a/i18n/src/commonMain/resources/MR/it/strings.xml +++ b/i18n/src/commonMain/resources/MR/it/strings.xml @@ -198,7 +198,7 @@ App non disponibile Ogni settimana Categoria predefinita - Tracking + Tracciamento Tracciamento Chiedi sempre Ritaglia bordi @@ -271,7 +271,6 @@ Caricamento pagine… Caricamento pagine fallito: %1$s Apri in WebView - Colori a 32 bit Salta i capitoli segnati come letti Schermo Mostra azioni con un tocco prolungato @@ -366,7 +365,6 @@ Aggiorna le copertine della libreria %02d min, %02d sec Sincronizzazione a senso unico per aggiornare l\'avanzamento dei capitoli sui servizi di tracking. Imposta il tracking per le singole voci dai loro pulsanti di tracking. - Riduce il banding, ma potrebbe influire sulle prestazioni Grigio Nessuna pagina trovata Per data di aggiunta @@ -800,4 +798,12 @@ Vuoi eleminare la repository \"%s\"? Errore completo: Si prevede che i nuovi capitoli saranno rilasciati tra circa %1$s, con controlli ogni %2$s. + Aggiungere comunque + Migrare la voce esistente + La Firma dell\'Impronta Digitale della Chiave Esiste Già + Profilo di visualizzazione personalizzato + La repository %1$s ha la stessa Firma d\'Impronta Digitale della Chiave %2$s. +\nSe è quello che si desidera, %2$s verrà sostituita, altrimenti contatta il manutentore della repository. + Sostituisci + Disattiva zoom indietro \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ja/plurals.xml b/i18n/src/commonMain/resources/MR/ja/plurals.xml index be36ec45c..b94cb9d90 100644 --- a/i18n/src/commonMain/resources/MR/ja/plurals.xml +++ b/i18n/src/commonMain/resources/MR/ja/plurals.xml @@ -51,4 +51,7 @@ %dリポジトリ + + %1$d 日後 + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/ja/strings.xml b/i18n/src/commonMain/resources/MR/ja/strings.xml index 9ddf503d2..800ceb8fd 100644 --- a/i18n/src/commonMain/resources/MR/ja/strings.xml +++ b/i18n/src/commonMain/resources/MR/ja/strings.xml @@ -270,7 +270,6 @@ ページをロード中… ページのロードに失敗: %1$s 長押しでアクションを表示 - 32ビットカラー 読み終わった章をスキップ カラーフィルターブレンドモード オーバーレイ @@ -340,7 +339,6 @@ 焼き込み/比較(暗) 覆い焼き/比較(明) 乗算 - バンディングを軽減しますが、パフォーマンスを影響するかもしれません 画面の切り抜きエリアにも内容を表示 更新あり ライブラリを更新時、新しい表紙と情報を確認します @@ -682,7 +680,7 @@ クリップボードにコピーしました 利用可能ですが未インストールのソース: %s 重複の章をスキップ - ライブラリにはもう同名の項目が存在しています。 + ライブラリには既に同名の項目が存在しています。 \n \nそれでも続行しますか? %1$s エラー: %2$s @@ -799,4 +797,16 @@ Nord スマート・アップデート ズームアウトを無効にする + 追加 + 移行 + 置き換える + カスタムディスプレイプロファイル + 前月 + 翌月 + 近々公開 + 今後のアップデート + 署名キーのフィンガープリントはすでに存在します + 今後のご案内 + リポジトリ %1$s は %2$s と同じ署名キーフィンガープリントを持っています。 +\nこれが予想される場合は %2$s が置き換えされますが、そうでない場合はリポジトリのメンテナーに連絡してください。 \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/jv/plurals.xml b/i18n/src/commonMain/resources/MR/jv/plurals.xml index e06b9c6b6..5f3256f9f 100644 --- a/i18n/src/commonMain/resources/MR/jv/plurals.xml +++ b/i18n/src/commonMain/resources/MR/jv/plurals.xml @@ -9,4 +9,16 @@ %d kategori + + %1$d dino engkas + + + Lanjut %d chapter sing gorong diwoco + + + Mari kurang %1$s karo %2$s error + + + Kurang %1$s + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/jv/strings.xml b/i18n/src/commonMain/resources/MR/jv/strings.xml index f4e240408..1592a2048 100644 --- a/i18n/src/commonMain/resources/MR/jv/strings.xml +++ b/i18n/src/commonMain/resources/MR/jv/strings.xml @@ -27,7 +27,6 @@ Ireng Klawu Putih - Werna 32-bit Layar wutuh Kabeh Pas ngisi daya @@ -272,7 +271,6 @@ Ketok pinggiran Ganti panggonan pamisah kaca kaping pindho Yen panggonan pamisah kaca dobel ora cocog karo arah maca - Nyuda banding, nanging bisa mengaruhi kinerja Mati Padhang khusus Animasi transisi kaca diff --git a/i18n/src/commonMain/resources/MR/ka-rGE/strings.xml b/i18n/src/commonMain/resources/MR/ka-rGE/strings.xml index 5cc64e3cc..fc98436a2 100644 --- a/i18n/src/commonMain/resources/MR/ka-rGE/strings.xml +++ b/i18n/src/commonMain/resources/MR/ka-rGE/strings.xml @@ -124,7 +124,6 @@ გვერდის ნომრის ჩვენება კითხვის რეჟიმის ჩვენება მოკლედ აჩვენე ახლანდელი რეჟიმი როცა მკითხველი გახსნილია - 32-bit ფერი კუთხეების ჩამოჭრა გაამრავლე ეკრანი @@ -351,7 +350,6 @@ არასანდო დამატება დამატება ხელმისაწვდომი აღარაა. მან შეიძლება არასწორად იმუშაოს და აპლიკაციას პრობლემები შეუქმნას. გირჩევთ, წაშალოთ ის. ორჯერ დაჭერისას ანიმაციის სისწრაფე - აუმჯობესებს ხარისხს, თუმცა ამცირებს წარმადობას გამოიყენე პერსონალიზებული სიკაშკაშე გამოიყენე პერსონალიზებული ფერთა ფილტრი ფერთა ფილტრის შეხამების რეჟიმი diff --git a/i18n/src/commonMain/resources/MR/kk/strings.xml b/i18n/src/commonMain/resources/MR/kk/strings.xml index 7cc9ab12e..795deab72 100644 --- a/i18n/src/commonMain/resources/MR/kk/strings.xml +++ b/i18n/src/commonMain/resources/MR/kk/strings.xml @@ -224,8 +224,6 @@ Қос бетті бөлуді инверттеу Ықшам кесте Оқыма ашылғаннан кейін оқу түрін қысқаша көрсету - 32-битті түс - Жолақты азайтады, бірақ өнімділікке әсер етуі мүмкін Кесу жиектері Кеңейтулер тізімін алу сәтсіз өтті Мазмұнды кесу аймағында көрсету diff --git a/i18n/src/commonMain/resources/MR/kn/strings.xml b/i18n/src/commonMain/resources/MR/kn/strings.xml index 7c7b7f2c3..fb0b0d9ea 100644 --- a/i18n/src/commonMain/resources/MR/kn/strings.xml +++ b/i18n/src/commonMain/resources/MR/kn/strings.xml @@ -184,8 +184,6 @@ ಕಸ್ಟಮ್ ಬಣ್ಣಗಳ ಫಿಲ್ಟರ್ ಕಸ್ಟಮ್ ಬೆಳಕು ಗಡಿಗಳನ್ನು ಕತ್ತರಿಸಿ - ಬ್ಯಾಂಡಿಂಗ್ ಅನ್ನು ಕಡಿಮೆ ಮಾಡುತ್ತದೆ, ಆದರೆ ಕಾರ್ಯಕ್ಷಮತೆಯ ಮೇಲೆ ಪರಿಣಾಮ ಬೀರುತ್ತದೆ - 32-ಬಿಟ್ ಬಣ್ಣ ಪುಟ ಸಂಖ್ಯೆಯನ್ನು ತೋರಿಸಿ ಡಬಲ್ ಟ್ಯಾಪ್ ಅನಿಮೇಷನ್ ವೇಗ ಪುಟ ಪರಿವರ್ತನೆಗಳನ್ನು ಅನಿಮೇಟ್ ಮಾಡಿ diff --git a/i18n/src/commonMain/resources/MR/ko/strings.xml b/i18n/src/commonMain/resources/MR/ko/strings.xml index 95f966e83..83e27351b 100644 --- a/i18n/src/commonMain/resources/MR/ko/strings.xml +++ b/i18n/src/commonMain/resources/MR/ko/strings.xml @@ -272,7 +272,6 @@ 일반 길게 탭하여 액션 표시 WebView로 열기 - 32비트 컬러 읽음 표시된 회차 건너뛰기 컬러 필터 블렌딩 모드 오버레이 @@ -413,7 +412,6 @@ 뷰어가 켜질 때 현재 읽기 모드를 표시합니다 탭 영역 오버레이 표시 뷰어가 열렸을 때 간략히 표시 - 밴딩을 줄이지만 성능에 영향을 줄 수 있음 디스플레이 필터링된 회차 건너뛰기 매우 높음 diff --git a/i18n/src/commonMain/resources/MR/lt/strings.xml b/i18n/src/commonMain/resources/MR/lt/strings.xml index fdc51805a..6aa6d7130 100644 --- a/i18n/src/commonMain/resources/MR/lt/strings.xml +++ b/i18n/src/commonMain/resources/MR/lt/strings.xml @@ -170,8 +170,6 @@ Išjungta Įjungta Apkarpyti kraštus - Sumažina spalvų gradientą, bet daro įtaką našumui - 32-bitų spalva Trumpai parodyti dabartinį rėžimą, kai skaitiniai yra atidaryti Parodyti skaitymo rėžimą Parodyti puslapio numerį diff --git a/i18n/src/commonMain/resources/MR/lv/strings.xml b/i18n/src/commonMain/resources/MR/lv/strings.xml index 72534b8bd..ea1c7bdf2 100644 --- a/i18n/src/commonMain/resources/MR/lv/strings.xml +++ b/i18n/src/commonMain/resources/MR/lv/strings.xml @@ -231,8 +231,6 @@ Rādīt lappušu numuru Rādīt lasīšanas režīmu Pēc lasītāja atvēršanas īsi parādīt pašreizējo režīmu - 32-bit krāsa - Samazina grupēšanu, bet var ietekmēt veiktspēju Ieslēgts Izslēgts Pielāgots spilgtums diff --git a/i18n/src/commonMain/resources/MR/mr/strings.xml b/i18n/src/commonMain/resources/MR/mr/strings.xml index d6fa1b5ab..08056d30a 100644 --- a/i18n/src/commonMain/resources/MR/mr/strings.xml +++ b/i18n/src/commonMain/resources/MR/mr/strings.xml @@ -22,7 +22,6 @@ वाचले नाही अशी खुण करा डाउनलोड हटवा - ३२-बिट रंग काळा सामान्य मेनू @@ -110,7 +109,6 @@ पुढे मागे जा सीमानां कापा - बॅन्डिंग कमी करते, परंतु कार्यप्रदर्शनावर परिणाम करते पृष्ठ क्रमांक दर्शवा दोनदा टॅप साठी अ‍ॅनिमेशन गती पृष्ठ संक्रमण संजीवित करा diff --git a/i18n/src/commonMain/resources/MR/ms/strings.xml b/i18n/src/commonMain/resources/MR/ms/strings.xml index befa0d041..b29446d39 100644 --- a/i18n/src/commonMain/resources/MR/ms/strings.xml +++ b/i18n/src/commonMain/resources/MR/ms/strings.xml @@ -247,7 +247,6 @@ \n \nDengan mempercayai sambungan ini, anda menerima risiko tersebut. Kelajuan animasi ketik dua kali - 32-bit warna Langkau bab yang sudah dibaca Tunjuk tindakan bila menahan ketik Muka surat @@ -364,7 +363,6 @@ Kelabu Mod membaca Untuk siri ini - Kurangkan jalinan, tapi memberi kesan kepada prestasi Tetapan peranti tidak dapat dibuka Menyelaras satu hala untuk kemas kini bab kemajuan dalam sistem penjejak luaran. Sediakan penjejakan untuk entri individu daripada butang penjejakan mereka. Segar semula muka hadapan pustaka diff --git a/i18n/src/commonMain/resources/MR/nb-rNO/strings.xml b/i18n/src/commonMain/resources/MR/nb-rNO/strings.xml index 4e3e142f4..05bb16a09 100644 --- a/i18n/src/commonMain/resources/MR/nb-rNO/strings.xml +++ b/i18n/src/commonMain/resources/MR/nb-rNO/strings.xml @@ -101,7 +101,6 @@ Enhetsbegrensninger for automatiske oppdateringer Hopp over oppføringer med \"Fullført\" status Oppdater fremdrift etter lesing - 32-biters fargerom Beskjær kanter Egendefinert lysstyrke Egendefinert fargefilter @@ -419,7 +418,6 @@ Sporingstjenester ikke logget inn i: Sikkerhetskopi inneholder ingen bibliotekoppføringer. Enveissynkronisering for å oppdatere kapittelfremdrift i eksterne sporetjenester. Sett opp sporing for individuelle oppføringer med sporingsknappen. - Reduserer sjattering, men koster ytelse Sidepolstring Neste side Forrige side diff --git a/i18n/src/commonMain/resources/MR/ne/strings.xml b/i18n/src/commonMain/resources/MR/ne/strings.xml index aa98d1a15..9dc0527bc 100644 --- a/i18n/src/commonMain/resources/MR/ne/strings.xml +++ b/i18n/src/commonMain/resources/MR/ne/strings.xml @@ -200,7 +200,6 @@ पृष्ठ ट्रान्जिसन एनिमेट गर्नुहोस् एनिमेसनको गति डबल ट्याप गर्नुहोस् रिडर खोल्दा छोटकरीमा हालको मोड देखाउनुहोस् - ब्यान्डिङ घटाउँछ, तर कार्यसम्पादनलाई असर गर्न सक्छ अन कस्टम चमक पढिएको चिन्ह लगाइएका अध्यायहरू छोड्नुहोस् @@ -256,7 +255,6 @@ यदि विभाजित चौडा पृष्ठहरूको प्लेसमेन्ट पढ्ने दिशासँग मेल खाँदैन पढ्ने मोड देखाउनुहोस् पृष्ठ नम्बर देखाउनुहोस् - ३२-बिट रङ सीमाना क्रप गर्नुहोस् अफ ग्रे-स्केल @@ -794,8 +792,8 @@ नयाँ अध्यायहरू लगभग %1$s मा रिलीज हुने भविष्यवाणी गरिएको छ, हरेक %2$s को वरिपरि जाँच गर्दै। छिटै कस्टम अपडेट फ्रिक्वेन्सी: - स्क्यालेटरहरू समावेश नगर्नुहोस् - कुनै स्क्यालेटर फेला परेन + स्क्यानलेटर समावेश नगर्नुहोस् + कुनै स्क्यानलेटर फेला परेन भण्डारण प्रयोग कुनै भण्डारण स्थान सेट गरिएको छैन स्वचालित ब्याकअप, अध्याय डाउनलोड, र स्थानीय स्रोतको लागि प्रयोग गरिन्छ। @@ -803,4 +801,11 @@ पूर्ण त्रुटि: संवेदनशील सेटिङहरू समावेश गर्नुहोस् (जस्तै, ट्र्याकर लगइन टोकनहरू) जूम आउट असक्षम गर्नुहोस् + जे भए पनि थप्नुहोस् + अवस्थित इन्ट्री माइग्रेट गर्नुहोस् + कस्टम प्रदर्शन प्रोफाइल + रिप्लेस + साइन गर्ने की फिंगरप्रिन्ट पहिले नै अवस्थित छ + रिपो %1$s सँग %2$s जस्तै साइन गर्ने की फिंगरप्रिन्ट छ। +\nयदि यो अपेक्षित छ भने, %2$s प्रतिस्थापन गरिनेछ, अन्यथा आफ्नो रिपो प्रबन्धकलाई सम्पर्क गर्नुहोस्। \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/nl/strings.xml b/i18n/src/commonMain/resources/MR/nl/strings.xml index 24f2e69c7..6ffd060ca 100644 --- a/i18n/src/commonMain/resources/MR/nl/strings.xml +++ b/i18n/src/commonMain/resources/MR/nl/strings.xml @@ -274,7 +274,6 @@ Migreren Kopiëren Openen in WebView - 32-bits kleuren Gelezen hoofdstukken overslaan Overlay Vermenigvuldigen @@ -367,7 +366,6 @@ Kon de apparaatinstellingen niet openen Opvulling zijkant Grijs - Vermindert kleurstrepen, maar beïnvloedt de prestaties Omslagen van manga in bibliotheek bijwerken Eenzijdige synchronisatie om voortang van hoofdstukken bij te werken bij externe trackerdiensten. Stel tracking in bij individuele items via de \"Tracking\"-knop. Controleer bij het bijwerken van de bibliotheek op nieuwe omslag en details diff --git a/i18n/src/commonMain/resources/MR/nn/strings.xml b/i18n/src/commonMain/resources/MR/nn/strings.xml index 18ec460d2..03ddfe0c5 100644 --- a/i18n/src/commonMain/resources/MR/nn/strings.xml +++ b/i18n/src/commonMain/resources/MR/nn/strings.xml @@ -135,7 +135,6 @@ Installatør Shizuku køyrar ikkje Vis sidetal - 32-bits farge Av Eigendefinert lysstyrke diff --git a/i18n/src/commonMain/resources/MR/pl/strings.xml b/i18n/src/commonMain/resources/MR/pl/strings.xml index b86212168..1aa9e7429 100644 --- a/i18n/src/commonMain/resources/MR/pl/strings.xml +++ b/i18n/src/commonMain/resources/MR/pl/strings.xml @@ -273,7 +273,6 @@ Ładowanie stron… Nie udało się załadować stron: %1$s Otwórz używając WebView - Kolor 32-bitowy Pomiń rozdziały ozn. jako przeczytane Pokaż akcje po przytrzymaniu Tryb mieszania kolorów @@ -345,7 +344,6 @@ %02d min, %02d s Marginesy boczne Szary - Ogranicza banding, ale może mieć wpływ na wydajność Dostępne aktualizacje Ukryj ekran Odepnij diff --git a/i18n/src/commonMain/resources/MR/pt-rBR/plurals.xml b/i18n/src/commonMain/resources/MR/pt-rBR/plurals.xml index dcd4d5b67..edc3cbfba 100644 --- a/i18n/src/commonMain/resources/MR/pt-rBR/plurals.xml +++ b/i18n/src/commonMain/resources/MR/pt-rBR/plurals.xml @@ -85,4 +85,9 @@ %d repositórios %d repositórios + + Amanhã + Em %1$d dias + Em breve + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/pt-rBR/strings.xml b/i18n/src/commonMain/resources/MR/pt-rBR/strings.xml index 3b8063460..38776da71 100644 --- a/i18n/src/commonMain/resources/MR/pt-rBR/strings.xml +++ b/i18n/src/commonMain/resources/MR/pt-rBR/strings.xml @@ -272,7 +272,6 @@ Falha ao carregar as páginas: %1$s Mostrar as ações no toque longo Abrir na WebView - Cor de 32 bits Pular os capítulos marcados como lidos Divisão Ajuda @@ -364,7 +363,6 @@ Cinza Modo de leitura Para esta série - Reduz o efeito de anéis nos degradês, mas pode afetar o desempenho Não foi possível abrir as configurações do dispositivo Sincronização unidirecional para atualizar o progresso dos capítulos nos serviços monitoradores externos. Configure o monitoramento para itens individuais a partir de seus botões de monitoramento. Atualizar as capas da biblioteca @@ -744,7 +742,7 @@ Aplicar Reverter para o padrão Nenhum scanlator encontrado - Scanlator + Scanner Excluir scanlators Criar Selecionado @@ -798,4 +796,5 @@ Nord Revogar a confiabilidade de extensões desconhecidas Abrir repositório da fonte + Desativar redução de zoom \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/pt/strings.xml b/i18n/src/commonMain/resources/MR/pt/strings.xml index 0881bcd09..7203697cd 100644 --- a/i18n/src/commonMain/resources/MR/pt/strings.xml +++ b/i18n/src/commonMain/resources/MR/pt/strings.xml @@ -232,7 +232,6 @@ Não confiável Mostrar com toque longo Abrir em WebView - Cores 32-bit Ignorar capítulos marcados como lido Sem mais resultados Fonte local @@ -366,7 +365,6 @@ Cinza Modo de leitura Para esta série - Reduz bandas de cor, mas pode impactar a performance Não foi possível abrir as definições do aparelho Atualizar capas da biblioteca Sincronização unidirecional para atualizar o progresso dos capítulos nos serviços de monitoramento. Configure o monitoramento para itens individuais a partir dos botões de monitoramento deles. diff --git a/i18n/src/commonMain/resources/MR/ro/strings.xml b/i18n/src/commonMain/resources/MR/ro/strings.xml index 5bc15ead1..05f744bed 100644 --- a/i18n/src/commonMain/resources/MR/ro/strings.xml +++ b/i18n/src/commonMain/resources/MR/ro/strings.xml @@ -273,7 +273,6 @@ Descărcare întreruptă General Deschide în WebView - Culoare 32-bit Mod filtru de culoare Acoperire Înmulțește @@ -366,7 +365,6 @@ Mod de citire Pentru această serie Gri - Reduce banding-ul, dar poate avea un impact asupra performanței Imposibil de deschis setările dispozitivului Sincronizare unidirecțională pentru actualizarea progresului capitolului în cadrul serviciilor de urmărire. Configurați urmărirea pentru intrările individuale din butonul de urmărire al acestora. Reîmprospătarea coperților bibliotecii diff --git a/i18n/src/commonMain/resources/MR/ru/strings.xml b/i18n/src/commonMain/resources/MR/ru/strings.xml index 47a203ae6..ca7229123 100644 --- a/i18n/src/commonMain/resources/MR/ru/strings.xml +++ b/i18n/src/commonMain/resources/MR/ru/strings.xml @@ -273,7 +273,6 @@ Показать действия при долгом нажатии Открыть в WebView Пропускать прочитанные главы - 32-битный цвет Режим светофильтра Наложение Умножение @@ -364,7 +363,6 @@ Режим чтения Для этой серии Серый - Уменьшает полосатость, но может влиять на производительность Не удалось открыть настройки устройства Обновить обложки серий Односторонняя синхронизация для обновления прогресса в сторонних сервисах отслеживания. Настройте отслеживание при помощи кнопки «Отслеживание». @@ -799,4 +797,16 @@ Убрать доверие к ненадёжным расширениям Открыть репозиторий источника Отключить уменьшение масштаба + Добавить в любом случае + Пользовательский профиль отображения + Перенести существующую серию + Заменить + Репозиторий %1$s имеет тот же подписанный ключ отпечатка, что и %2$s. +\nЕсли это так и задумано, %2$s будет заменён, в противном случае, свяжитесь с сопровождающим вашего репозитория. + Подписанный ключ отпечатка уже существует + Предыдущий месяц + Просмотр предстоящих обновлений + Предстоящее + Предстоящее руководство + Следующий месяц \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/sa/strings.xml b/i18n/src/commonMain/resources/MR/sa/strings.xml index b381fb2b3..1e22a0f1b 100644 --- a/i18n/src/commonMain/resources/MR/sa/strings.xml +++ b/i18n/src/commonMain/resources/MR/sa/strings.xml @@ -212,8 +212,6 @@ पुटसङ्ख्यां दर्शयतु पठनदशां दर्शयतु प्रदर्शकस्य उद्घाटने वर्तमानदशां सङ्क्षेपं दर्शयतु - ३२-बिट् वर्णः - बाञ्डिङ्गसमस्यां न्यूनी करिष्यति परन्तु प्रवृत्तिं प्रभाविष्यति सीमाः कर्तयतु सशक्तं कुरु अशक्तं कुरु diff --git a/i18n/src/commonMain/resources/MR/sah/strings.xml b/i18n/src/commonMain/resources/MR/sah/strings.xml index 948c47350..ba1f87335 100644 --- a/i18n/src/commonMain/resources/MR/sah/strings.xml +++ b/i18n/src/commonMain/resources/MR/sah/strings.xml @@ -191,8 +191,6 @@ Туттааччы өҥ биилтирэ Туттааччы сырдыга Быһыллы кыраныыссата - Тиэрбэс аҕыйыыр, ол гынан баран улэҕэ дьайар - 32-бит өҥ Сирэй нүөмэрин көрдөр Иккитэ баттыырга хамсааһын түргэнэ Хамсааһыннах, сирэй арыйыыта diff --git a/i18n/src/commonMain/resources/MR/sc/strings.xml b/i18n/src/commonMain/resources/MR/sc/strings.xml index a2e1908e4..a2bfa211d 100644 --- a/i18n/src/commonMain/resources/MR/sc/strings.xml +++ b/i18n/src/commonMain/resources/MR/sc/strings.xml @@ -274,7 +274,6 @@ Iscarrigamentos in pàusa Comunu Aberi in WebView - Colore a 32 bit Brinca sos capìtulos marcados comente lèghidos Modalidade de fusione de su filtru colore Istratu superiore @@ -365,7 +364,6 @@ Filtrat totu sos elementos in sa biblioteca tua Pro custa sèrie Murru - Mìnimat sa creatzione de bandas, ma diat pòdere influentzare sas prestatziones Modalidade de leghidura Abertura de sas impostatziones de sistema fallida Sincronizatzione a una diretzione ebbia pro agiornare su progressu in sos capìtulos in servìtzios de arrastamentu esternos. Imposta s\'arrastamentu pro sos elementos dae su butone de arrastamentu issoro. diff --git a/i18n/src/commonMain/resources/MR/sk/strings.xml b/i18n/src/commonMain/resources/MR/sk/strings.xml index 35f55134f..75508fe65 100644 --- a/i18n/src/commonMain/resources/MR/sk/strings.xml +++ b/i18n/src/commonMain/resources/MR/sk/strings.xml @@ -108,7 +108,6 @@ Animácia prechodov strán Rýchlosť animácie pre dvojité ťuknutie Zobraziť číslo strany - 32-bitová farba Orezať okraje Vlastný jas Vlastný farebný filter @@ -444,7 +443,6 @@ Zatiaľ nemáte žiadne kategórie. Vekové hodnotenie Dátum - Znižuje pruhovanie farieb, ale môže ovplyvniť výkon Obe Predchádzajúci Vysoké diff --git a/i18n/src/commonMain/resources/MR/sq/strings.xml b/i18n/src/commonMain/resources/MR/sq/strings.xml index 1cd271f26..1fecf51d7 100644 --- a/i18n/src/commonMain/resources/MR/sq/strings.xml +++ b/i18n/src/commonMain/resources/MR/sq/strings.xml @@ -259,8 +259,6 @@ Prekni dy herë shpejtësia e animacionit Shfaq numrin e faqes Shfaq modalitetin e leximit - Ngjyra 32-bit - Redukton brezin, por mund të ndikojë në performancën Shkëlqimi i personalizuar Shkallëgri I përmbysur diff --git a/i18n/src/commonMain/resources/MR/sr/strings.xml b/i18n/src/commonMain/resources/MR/sr/strings.xml index 6eea42150..3d9c8d964 100644 --- a/i18n/src/commonMain/resources/MR/sr/strings.xml +++ b/i18n/src/commonMain/resources/MR/sr/strings.xml @@ -215,7 +215,6 @@ Непрочитано Да ли сте сигурни да желите да обришете следећа поглавља\? Отвори у WebView - 32-битна боја Прескочи поглавља означена као прочитана Помножи Екран @@ -487,7 +486,6 @@ Ништа није пронађено Раздели широке странице Ништа - Смањује оштре прелазе међу нијансама, али утиче на перформансе Датум Начин навигације Непознат аутор diff --git a/i18n/src/commonMain/resources/MR/sv/strings.xml b/i18n/src/commonMain/resources/MR/sv/strings.xml index c5baf79a6..695f501a4 100644 --- a/i18n/src/commonMain/resources/MR/sv/strings.xml +++ b/i18n/src/commonMain/resources/MR/sv/strings.xml @@ -271,7 +271,6 @@ Nedladdningar pausade Vanlig Öppna i WebView - 32-bitars färg Hoppa över lästa kapitel Visa åtgärder vid lång tryckning Färgfilterblandningsläge @@ -364,7 +363,6 @@ Läsläge För denna serie Grå - Minskar bandning, men kan påverka prestandan Det gick inte att öppna enhetsinställningarna Uppdatera bibliotekets omslag Envägssynkronisering för att uppdatera kapitlets framsteg i externa spårningstjänster. Ställ in spårning för enskilda poster från deras spårningsknapp. diff --git a/i18n/src/commonMain/resources/MR/th/strings.xml b/i18n/src/commonMain/resources/MR/th/strings.xml index 26c76e992..033996db9 100644 --- a/i18n/src/commonMain/resources/MR/th/strings.xml +++ b/i18n/src/commonMain/resources/MR/th/strings.xml @@ -221,7 +221,6 @@ คะแนน แสดงการกระทำเมื่อแตะค้าง เปิดใน WebView - สี 32 บิต ข้ามตอนที่ทำเครื่องหมายว่าอ่านแล้ว โหมดผสมตัวกรองสี ซ้อนทับ @@ -305,7 +304,6 @@ กลับด้านโซนการแตะ ข้ามตอนที่กรองไว้ โทนสีเทา - ลดปัญหาแถบสี แต่อาจส่งผลต่อประสิทธิภาพ แสดงโหมดปัจจุบันสั้น ๆ เมื่อตัวอ่านเปิดขั้นมา แสดงโหมดการอ่าน แสดงเนื้อหาในพื้นที่ตัดออก diff --git a/i18n/src/commonMain/resources/MR/tr/plurals.xml b/i18n/src/commonMain/resources/MR/tr/plurals.xml index 7e40a5df6..3d090ebc2 100644 --- a/i18n/src/commonMain/resources/MR/tr/plurals.xml +++ b/i18n/src/commonMain/resources/MR/tr/plurals.xml @@ -68,4 +68,8 @@ %d depo %d depo + + Yarın + %1$d gün içinde + \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/tr/strings.xml b/i18n/src/commonMain/resources/MR/tr/strings.xml index fdd88b422..7163b3dc7 100644 --- a/i18n/src/commonMain/resources/MR/tr/strings.xml +++ b/i18n/src/commonMain/resources/MR/tr/strings.xml @@ -272,7 +272,6 @@ Sayfalar yüklenemedi: %1$s Uzun dokununca eylemleri göster WebView ile Aç - 32-bit renk Okundu olarak işaretli bölümleri atla Renk filtresi karışım modu Kaplama @@ -362,7 +361,6 @@ %02d dk, %02d sn Kitaplığınızdaki tüm girdileri süzer Gri - Renk şeritlenmesini azaltır, ancak performansı etkileyebilir Okuma kipi Bu dizi için Aygıt ayarları açılamadı diff --git a/i18n/src/commonMain/resources/MR/uk/strings.xml b/i18n/src/commonMain/resources/MR/uk/strings.xml index d6e60c477..264cf3b13 100644 --- a/i18n/src/commonMain/resources/MR/uk/strings.xml +++ b/i18n/src/commonMain/resources/MR/uk/strings.xml @@ -272,7 +272,6 @@ Звичайний Показувати дії при довгому натисканні Відкрити у WebView - 32-бітний колір Пропускати розділи, відмічені як прочитані Режим накладання кольорового фільтру Оверлей @@ -351,7 +350,6 @@ Сторінка Тільки завантажені Сірий - Зменшує смугастість, але може вплинути на продуктивність Перевірити наявність нової обкладинки та подробиць при оновленні бібліотеки Автоматично оновлювати метадані Мігрувати diff --git a/i18n/src/commonMain/resources/MR/uz/strings.xml b/i18n/src/commonMain/resources/MR/uz/strings.xml index 57795db07..17f1ac02f 100644 --- a/i18n/src/commonMain/resources/MR/uz/strings.xml +++ b/i18n/src/commonMain/resources/MR/uz/strings.xml @@ -200,7 +200,6 @@ Keng sahifalarni teskarisiga ajratish Chetlarini kesish Rang filtri rejimi - Chiziqlanishni kamaytiradi, lekin ishlashga ta\'sir ko\'rsatishi mumkin Barchasi O\'qilmagan boblar soni «Yangilanishlar» ikonida Kutilmoqda @@ -262,7 +261,6 @@ O\'rnatildi Versiya Ikki marta bosilganda animatsiya tezligi - 32-bitli rang Foydalanuvchi tanlagan yoruqlik Yosh cheklovi Faqat cheksiz tarmoq orqali diff --git a/i18n/src/commonMain/resources/MR/vi/strings.xml b/i18n/src/commonMain/resources/MR/vi/strings.xml index 5721049fa..16148241d 100644 --- a/i18n/src/commonMain/resources/MR/vi/strings.xml +++ b/i18n/src/commonMain/resources/MR/vi/strings.xml @@ -274,7 +274,6 @@ Thường gặp Nhấn giữ để mở mục hành động Mở bằng WebView - Màu 32-bit Bỏ qua chap đã đọc Chế độ hòa trộn bộ lọc màu Phủ lên @@ -463,7 +462,6 @@ Không Đảo ngược khu vực nhấn Độ xám - Giảm lỗi hiện sai dải màu, nhưng có thể ảnh hưởng đến hiệu suất Nếu vị trí của phần tách trang rộng không khớp với hướng đọc Đảo ngược vị trí tách trang Chia các trang đôi diff --git a/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml b/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml index ee40e55fc..21290a04d 100644 --- a/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml +++ b/i18n/src/commonMain/resources/MR/zh-rCN/strings.xml @@ -272,7 +272,6 @@ 通用 长按显示操作菜单 在 WebView 中打开 - 32 位色彩 跳过已读章节 滤镜混合模式 帮助 @@ -365,7 +364,6 @@ 本作品 阅读模式 打开系统设置失败 - 缓解色带问题,但可能会影响性能 刷新书架封面 将阅读进度上传到第三方进度记录平台。在作品页面点击“进度记录”按钮即可设置。 按上传日期 @@ -799,4 +797,16 @@ 撤销已信任的未知插件 打开图源仓库 禁用缩小 + 依然添加 + 替换 + 自定义校色文件 + %1$s 仓库已经存在和 %2$s 仓库相同的签名密钥指纹。 +\n若继续,%2$s 将被替换,否则请联系你的仓库维护员。 + 签名密钥指纹已存在 + 迁移已存在条目 + 显示即将推出的更新 + 即将推出的指南 + 即将推出 + 下个月 + 上个月 \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml b/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml index 872706038..d2ec23ffe 100644 --- a/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml +++ b/i18n/src/commonMain/resources/MR/zh-rTW/strings.xml @@ -93,7 +93,6 @@ 全螢幕 輕觸兩下動畫速度 顯示頁碼 - 32 位元色彩 自訂亮度 色彩濾鏡 保持螢幕恆亮 @@ -147,7 +146,7 @@ 輕觸即可更新 更新下載失敗 有可用的更新! - 無下載 + 尚無下載作業 錯誤 沒有可用的 Wi-Fi 連線 尚未連接至網路 @@ -327,7 +326,7 @@ 你已登出 登出 最近使用 - 本機來源入門指南 + 本機來源指南 請更新 WebView 以獲得更佳的相容性 應用程式運作需要 WebView 反向選擇 @@ -349,7 +348,6 @@ 使用螢幕凹口區域 電子郵件地址 略過已篩選的章節 - 減緩色調分離,但可能拖累效能 開始閱讀時,快顯闡明目前的模式 閱讀模式提示 來源 @@ -427,7 +425,7 @@ 無痕模式 找不到檔案選擇器 《%1$s》:%2$s,第 %3$d 頁 - 來源遷移說明 + 來源遷移指南 下一頁 上一頁 請重新登入 MAL @@ -503,7 +501,7 @@ 若停用 MIUI 最佳化,備份與還原可能無法正確執行。 為特定來源提供增強功能。當作品被加入書櫃時,將自動登錄閱讀歷程。 增強式歷程平台 - 歷程平台說明 + 歷程平台指南 獨立各類別的排序方式 純黑深色模式 四葉 @@ -710,7 +708,7 @@ 下次預期更新 刊期 預計每個 - 自訂預估出刊日 + 預估出刊日 設定為每個 由於未臨出刊日,因此略過 有結果 @@ -799,4 +797,16 @@ 即將出刊 開啟來源儲存庫 停用縮小 + 自訂顯示設定檔 + 仍要新增 + 遷移現有作品 + 取代 + 已有相同的簽署金鑰指紋 + 「%1$s」儲存庫的簽署金鑰指紋與「%2$s」相同。 +\n若此為預期現象,則「%2$s」將被取代,否則請聯絡該儲存庫的維護者。 + 預告指南 + 下個月 + 上個月 + 預告 + 檢視新刊預告 \ No newline at end of file From 34bf5c6f87d74df2dcc6d0f23f5a73425d2fd6ef Mon Sep 17 00:00:00 2001 From: "Weblate (bot)" Date: Mon, 1 Apr 2024 04:04:30 +0200 Subject: [PATCH 145/212] Translations update from Hosted Weblate (#609) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Translated using Weblate (Greek) Currently translated at 99.8% (793 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/el/ * Translated using Weblate (Turkish) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/tr/ * Translated using Weblate (German) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/de/ * Translated using Weblate (Persian) Currently translated at 84.7% (673 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fa/ * Translated using Weblate (German) Currently translated at 100.0% (794 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/ * Translated using Weblate (Greek) Currently translated at 100.0% (794 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/el/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (794 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt_BR/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/pt_BR/ * Translated using Weblate (Galician) Currently translated at 95.9% (762 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/gl/ * Translated using Weblate (Japanese) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/ja/ * Translated using Weblate (Javanese) Currently translated at 38.8% (7 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/jv/ * Translated using Weblate (Galician) Currently translated at 96.5% (767 of 794 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/gl/ * Translated using Weblate (Galician) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/gl/ * Update translation files Updated by "Cleanup translation files" hook in Weblate. Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ * Translated using Weblate (Spanish) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/ * Translated using Weblate (Croatian) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (793 of 793 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/ * Translated using Weblate (Russian) Currently translated at 100.0% (795 of 795 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/ * Translated using Weblate (Spanish) Currently translated at 100.0% (795 of 795 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (795 of 795 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (795 of 795 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/ * Translated using Weblate (Russian) Currently translated at 99.7% (796 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/ * Translated using Weblate (Spanish) Currently translated at 100.0% (798 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/ * Translated using Weblate (Filipino) Currently translated at 100.0% (798 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/ * Translated using Weblate (German) Currently translated at 100.0% (798 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (798 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (798 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/ * Translated using Weblate (Japanese) Currently translated at 99.4% (794 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (798 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/ * Translated using Weblate (Czech) Currently translated at 99.8% (797 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/cs/ * Translated using Weblate (Italian) Currently translated at 100.0% (798 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/it/ * Translated using Weblate (Nepali) Currently translated at 100.0% (798 of 798 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/ * Translated using Weblate (Czech) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/cs/ * Translated using Weblate (Italian) Currently translated at 100.0% (18 of 18 strings) Translation: Mihon/Mihon Plurals Translate-URL: https://hosted.weblate.org/projects/mihon/mihon-plurals/it/ * Translated using Weblate (Spanish) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/ * Translated using Weblate (Japanese) Currently translated at 99.7% (801 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/ * Translated using Weblate (Russian) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/ * Translated using Weblate (Filipino) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/ * Translated using Weblate (Japanese) Currently translated at 99.7% (801 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/ * Translated using Weblate (German) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/ * Translated using Weblate (Japanese) Currently translated at 99.7% (801 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/ * Translated using Weblate (Amharic) Currently translated at 34.3% (276 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/am/ * Translated using Weblate (Arabic) Currently translated at 98.5% (791 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ar/ * Translated using Weblate (Belarusian) Currently translated at 42.0% (338 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/be/ * Translated using Weblate (Bulgarian) Currently translated at 79.8% (641 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/bg/ * Translated using Weblate (Bengali) Currently translated at 79.2% (636 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/bn/ * Translated using Weblate (Catalan) Currently translated at 98.5% (791 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ca/ * Translated using Weblate (Cebuano) Currently translated at 55.0% (442 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ceb/ * Translated using Weblate (Czech) Currently translated at 99.2% (797 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/cs/ * Translated using Weblate (Chuvash) Currently translated at 74.5% (599 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/cv/ * Translated using Weblate (Danish) Currently translated at 39.9% (321 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/da/ * Translated using Weblate (German) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/de/ * Translated using Weblate (Greek) Currently translated at 98.6% (792 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/el/ * Translated using Weblate (Esperanto) Currently translated at 64.2% (516 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/eo/ * Translated using Weblate (Spanish) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/es/ * Translated using Weblate (Basque) Currently translated at 74.4% (598 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/eu/ * Translated using Weblate (Persian) Currently translated at 83.5% (671 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fa/ * Translated using Weblate (Finnish) Currently translated at 84.0% (675 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fi/ * Translated using Weblate (Filipino) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fil/ * Translated using Weblate (French) Currently translated at 98.5% (791 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/fr/ * Translated using Weblate (Galician) Currently translated at 95.2% (765 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/gl/ * Translated using Weblate (Hebrew) Currently translated at 89.7% (721 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/he/ * Translated using Weblate (Hindi) Currently translated at 82.6% (664 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hi/ * Translated using Weblate (Croatian) Currently translated at 98.7% (793 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hr/ * Translated using Weblate (Hungarian) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/hu/ * Translated using Weblate (Indonesian) Currently translated at 98.6% (792 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/id/ * Translated using Weblate (Italian) Currently translated at 99.3% (798 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/it/ * Translated using Weblate (Japanese) Currently translated at 99.7% (801 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ja/ * Translated using Weblate (Javanese) Currently translated at 38.3% (308 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/jv/ * Translated using Weblate (Georgian) Currently translated at 52.5% (422 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ka/ * Translated using Weblate (Kazakh) Currently translated at 86.1% (692 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/kk/ * Translated using Weblate (Khmer (Central)) Currently translated at 26.7% (215 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/km/ * Translated using Weblate (Kannada) Currently translated at 62.2% (500 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/kn/ * Translated using Weblate (Korean) Currently translated at 98.5% (791 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ko/ * Translated using Weblate (Lithuanian) Currently translated at 84.9% (682 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/lt/ * Translated using Weblate (Latvian) Currently translated at 93.3% (750 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/lv/ * Translated using Weblate (Marathi) Currently translated at 26.6% (214 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/mr/ * Translated using Weblate (Malay) Currently translated at 98.5% (791 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ms/ * Translated using Weblate (Norwegian Bokmål) Currently translated at 98.5% (791 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/nb_NO/ * Translated using Weblate (Nepali) Currently translated at 99.3% (798 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ne/ * Translated using Weblate (Dutch) Currently translated at 92.9% (746 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/nl/ * Translated using Weblate (Norwegian Nynorsk) Currently translated at 33.6% (270 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/nn/ * Translated using Weblate (Polish) Currently translated at 98.6% (792 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pl/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 98.6% (792 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt_BR/ * Translated using Weblate (Portuguese) Currently translated at 88.6% (712 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/pt/ * Translated using Weblate (Romanian) Currently translated at 97.8% (786 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ro/ * Translated using Weblate (Russian) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ru/ * Translated using Weblate (Sanskrit) Currently translated at 71.3% (573 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sa/ * Translated using Weblate (Yakut) Currently translated at 51.3% (412 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sah/ * Translated using Weblate (Sardinian) Currently translated at 93.3% (750 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sc/ * Translated using Weblate (Kurdish (Southern)) Currently translated at 29.8% (240 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sdh/ * Translated using Weblate (Slovak) Currently translated at 78.7% (632 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sk/ * Translated using Weblate (Albanian) Currently translated at 86.6% (696 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sq/ * Translated using Weblate (Serbian) Currently translated at 98.6% (792 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sr/ * Translated using Weblate (Swedish) Currently translated at 98.5% (791 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/sv/ * Translated using Weblate (Telugu) Currently translated at 24.5% (197 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/te/ * Translated using Weblate (Thai) Currently translated at 98.5% (791 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/th/ * Translated using Weblate (Turkish) Currently translated at 98.5% (791 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/tr/ * Translated using Weblate (Ukrainian) Currently translated at 98.5% (791 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/uk/ * Translated using Weblate (Uzbek) Currently translated at 44.4% (357 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/uz/ * Translated using Weblate (Vietnamese) Currently translated at 96.3% (774 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/vi/ * Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hans/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (803 of 803 strings) Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/zh_Hant/ * Update translation files Updated by "Remove blank strings" hook in Weblate. Translation: Mihon/Mihon Translate-URL: https://hosted.weblate.org/projects/mihon/mihon/ --------- Co-authored-by: Syrodil Eventalious Co-authored-by: NukeSource Co-authored-by: Lyfja <45209212+lyfja@users.noreply.github.com> Co-authored-by: Arash Co-authored-by: Pitpe11 Co-authored-by: Mr. Fakezay Co-authored-by: kevans Co-authored-by: TheKingTermux Co-authored-by: gallegonovato Co-authored-by: Milo Ivir Co-authored-by: Lzmxya Co-authored-by: Dexroneum Co-authored-by: B4LiN7 Co-authored-by: Infy's Tagalog Translations Co-authored-by: gekka <1778962971@qq.com> Co-authored-by: akir45 Co-authored-by: Matyáš Caras Co-authored-by: Federico Pierantoni Co-authored-by: FateXBlood --- .../commonMain/resources/MR/am/strings.xml | 2 +- .../commonMain/resources/MR/ar/strings.xml | 2 +- .../commonMain/resources/MR/be/strings.xml | 2 +- .../commonMain/resources/MR/bg/strings.xml | 14 ++++---- .../commonMain/resources/MR/bn/strings.xml | 16 +++++----- .../commonMain/resources/MR/ca/strings.xml | 32 +++++++++---------- .../commonMain/resources/MR/ceb/strings.xml | 8 ++--- .../commonMain/resources/MR/cs/strings.xml | 30 ++++++++--------- .../commonMain/resources/MR/cv/strings.xml | 20 ++++++------ .../commonMain/resources/MR/da/strings.xml | 4 +-- .../commonMain/resources/MR/de/strings.xml | 30 ++++++++--------- .../commonMain/resources/MR/eo/strings.xml | 16 +++++----- .../commonMain/resources/MR/es/strings.xml | 26 +++++++-------- .../commonMain/resources/MR/eu/strings.xml | 22 ++++++------- .../commonMain/resources/MR/fi/strings.xml | 24 +++++++------- .../commonMain/resources/MR/fil/strings.xml | 28 ++++++++-------- .../commonMain/resources/MR/fr/strings.xml | 22 ++++++------- .../commonMain/resources/MR/gl/strings.xml | 28 ++++++++-------- .../commonMain/resources/MR/he/strings.xml | 30 ++++++++--------- .../commonMain/resources/MR/hi/strings.xml | 16 +++++----- .../commonMain/resources/MR/hr/strings.xml | 32 +++++++++---------- .../commonMain/resources/MR/hu/strings.xml | 26 +++++++-------- .../commonMain/resources/MR/in/strings.xml | 32 +++++++++---------- .../commonMain/resources/MR/it/strings.xml | 30 ++++++++--------- .../commonMain/resources/MR/ja/strings.xml | 2 +- .../commonMain/resources/MR/jv/strings.xml | 2 +- .../resources/MR/ka-rGE/strings.xml | 8 ++--- .../commonMain/resources/MR/kk/strings.xml | 28 ++++++++-------- .../commonMain/resources/MR/kn/strings.xml | 20 ++++++------ .../commonMain/resources/MR/ko/strings.xml | 30 ++++++++--------- .../commonMain/resources/MR/lt/strings.xml | 28 ++++++++-------- .../commonMain/resources/MR/lv/strings.xml | 30 ++++++++--------- .../commonMain/resources/MR/mr/strings.xml | 4 +-- .../commonMain/resources/MR/ms/strings.xml | 24 +++++++------- .../resources/MR/nb-rNO/strings.xml | 32 +++++++++---------- .../commonMain/resources/MR/ne/strings.xml | 32 +++++++++---------- .../commonMain/resources/MR/nl/strings.xml | 28 ++++++++-------- .../commonMain/resources/MR/nn/strings.xml | 4 +-- .../commonMain/resources/MR/pl/strings.xml | 22 ++++++------- .../resources/MR/pt-rBR/strings.xml | 32 +++++++++---------- .../commonMain/resources/MR/pt/strings.xml | 26 +++++++-------- .../commonMain/resources/MR/ro/strings.xml | 26 +++++++-------- .../commonMain/resources/MR/ru/strings.xml | 32 +++++++++---------- .../commonMain/resources/MR/sa/strings.xml | 4 +-- .../commonMain/resources/MR/sah/strings.xml | 20 ++++++------ .../commonMain/resources/MR/sc/strings.xml | 30 ++++++++--------- .../commonMain/resources/MR/sk/strings.xml | 24 +++++++------- .../commonMain/resources/MR/sq/strings.xml | 24 +++++++------- .../commonMain/resources/MR/sr/strings.xml | 30 ++++++++--------- .../commonMain/resources/MR/sv/strings.xml | 32 +++++++++---------- .../commonMain/resources/MR/te/strings.xml | 2 +- .../commonMain/resources/MR/th/strings.xml | 32 +++++++++---------- .../commonMain/resources/MR/tr/strings.xml | 20 ++++++------ .../commonMain/resources/MR/uk/strings.xml | 30 ++++++++--------- .../commonMain/resources/MR/uz/strings.xml | 2 +- .../commonMain/resources/MR/vi/strings.xml | 24 +++++++------- 56 files changed, 588 insertions(+), 588 deletions(-) diff --git a/i18n/src/commonMain/resources/MR/am/strings.xml b/i18n/src/commonMain/resources/MR/am/strings.xml index 25c40d236..072085e38 100644 --- a/i18n/src/commonMain/resources/MR/am/strings.xml +++ b/i18n/src/commonMain/resources/MR/am/strings.xml @@ -268,7 +268,7 @@ እነበረበት መልስ ቀድሞውኑ በሂደት ላይ ነው ምትኬ አልተሳካም ምትኬን መፍጠር - ምትኬ ለማስቀመጥ ምን ይፈልጋሉ\? + ምትኬ ለማስቀመጥ ምን ይፈልጋሉ? ምትኬ ቀድሞውኑ በሂደት ላይ ነው %02d ደቂቃ ፣ %02d ሰከንድ እነበረበት መልስ ተጠናቅቋል diff --git a/i18n/src/commonMain/resources/MR/ar/strings.xml b/i18n/src/commonMain/resources/MR/ar/strings.xml index 0ef96b7dd..3f5ca8fd2 100644 --- a/i18n/src/commonMain/resources/MR/ar/strings.xml +++ b/i18n/src/commonMain/resources/MR/ar/strings.xml @@ -679,7 +679,7 @@ إظهار عدد الادخالات غير المقروءة في ايقونة التحديثات تجاوز الفصول المكررة متوفر ولكن المصدر غير مثبت: %s - لديك إدخال في مكتبتك بنفس الاسم. + لديك إدخال في مكتبتك بنفس الاسم. \n \nهل مازلت ترغب في الاستمرار؟ نسخ الى الحافظة diff --git a/i18n/src/commonMain/resources/MR/be/strings.xml b/i18n/src/commonMain/resources/MR/be/strings.xml index 2e5c6b378..b0e470588 100644 --- a/i18n/src/commonMain/resources/MR/be/strings.xml +++ b/i18n/src/commonMain/resources/MR/be/strings.xml @@ -309,7 +309,7 @@ Тып арыентацыі па змаўчанні папярэджанне Выдаліць катэгорыю - Вы хочаце выдаліць катэгорыю \"%s\"\? + Вы хочаце выдаліць катэгорыю \"%s\"? InternalError: Праверце журнал збояў для атрымання дадатковай інфармацыі Абнавіць усё Узроставае абмежаванне diff --git a/i18n/src/commonMain/resources/MR/bg/strings.xml b/i18n/src/commonMain/resources/MR/bg/strings.xml index 5de52305e..817a229ef 100644 --- a/i18n/src/commonMain/resources/MR/bg/strings.xml +++ b/i18n/src/commonMain/resources/MR/bg/strings.xml @@ -124,7 +124,7 @@ Бисквитки изчистени Изчисти базата данни Изтрий историята на манги, които не са запазени в библиотеката - Сигурни ли сте\? Прочетените глави и напредъкът на манги, които не са в библиотеката Ви, ще бъдат изгубени + Сигурни ли сте? Прочетените глави и напредъкът на манги, които не са в библиотеката Ви, ще бъдат изгубени Базата данни изчистена Версия Изпращай данни за сривове @@ -165,7 +165,7 @@ Категориите бяха изтрити Това ще премахне датата на прочитане на главата. Сигурни ли сте? Изчисти всички дати за тази манга - Добавяне на мангата към библиотеката\? + Добавяне на мангата към библиотеката? Изображението запазено Персонализиран филтър Постави за корица @@ -292,7 +292,7 @@ Това разширение вече не е достъпно. Формат на датата Глобалния обновявания - Искате ли да излезете от %1$s\? + Искате ли да излезете от %1$s? Излез Излязохте успешно На пауза @@ -511,13 +511,13 @@ Изтеглени глави Не можа да се копира върху клипборда Корицата запазена - Сигурни ли сте, че искате да наложите тези настройки\? + Сигурни ли сте, че искате да наложите тези настройки? Също приложи към всяка манга в библиотеката ми Направи по подразбиране Дата на започване Изчисти историята Историята изтрита - Напълно ли сте сигурен\? Всичката ви история ще бъде загубена. + Напълно ли сте сигурен? Всичката ви история ще бъде загубена. Наръчник за мигриране между източници Предупреждение: големите актуализации вредят на източниците и могат да доведат до по-бавни актуализации, както и до повишено използване на батерията. Натиснете тук, за да научите повече. Не бе намерено приложение за подбор на файлове @@ -558,7 +558,7 @@ Предупреждение: големите масови изтегляния могат да доведат до забавяне на източниците и/или блокиране на Mihon. Натиснете тук, за да научите повече. Покажи манга Решетка само с корици - Искате ли да изтриете категорията \"%s\"\? + Искате ли да изтриете категорията \"%s\"? InternalError: Провери записите за крашове за повече информация Грешка при запазването на изображението Обновява се @@ -570,7 +570,7 @@ Няма описание Няма заглавия в библиотеката за резервно копие Нулирай режима на четене и ориентацията на всички поредици - Сигурни ли сте\? + Сигурни ли сте? Популярни Списък с незавършени %s се сблъска с неочаквана грешка. Препоръчваме да заснемете това съобщение, да запазите записите от крашовете и да ги споделите в нашия канал за поддръжка в Discord. diff --git a/i18n/src/commonMain/resources/MR/bn/strings.xml b/i18n/src/commonMain/resources/MR/bn/strings.xml index cb07113e9..31bb98cec 100644 --- a/i18n/src/commonMain/resources/MR/bn/strings.xml +++ b/i18n/src/commonMain/resources/MR/bn/strings.xml @@ -150,7 +150,7 @@ কুকিস পরিষ্কার হয়েছে ডাটাবেজ পরিষ্কার করুন আপনার সংগ্রহশালাতে যেসব মাংগা সংরক্ষিত নেই সেগুলোর ইতিহাস মুছে ফেলুন - আপনি কি নিশ্চিত\? পঠিত অধ্যায় এবং সংগ্রহশালাতে অনুপস্থিত মাংগার অগ্রগতি মুছে যাবে + আপনি কি নিশ্চিত? পঠিত অধ্যায় এবং সংগ্রহশালাতে অনুপস্থিত মাংগার অগ্রগতি মুছে যাবে এন্ট্রিগুলো মুছে ফেলা হয়েছে সংস্করণ ক্র‍্যাশের প্রতিবেদন পাঠান @@ -197,7 +197,7 @@ বিভাগগুলি মোছা হয়েছে এটা এই অধ্যায়ের পড়ার সময়কাল সরিয়ে ফেলবে। আপনি নিশ্চিত? এই মাংগার জন্য সব অধ্যায় পুনঃবিন্যাস করুন - সংগ্রহশালায় মাঙ্গা যোগ করবেন\? + সংগ্রহশালায় মাঙ্গা যোগ করবেন? ছবি সংরক্ষিত হয়েছে স্বনির্ধারিত ফিল্টার প্রচ্ছদ হিসাবে সংরক্ষণ করুন @@ -205,7 +205,7 @@ পৃষ্ঠা: %1$d পরবর্তী অধ্যায় খুঁজে পাওয়া যায়নি ছবিটি লোড করা যাচ্ছে না - এই চিত্রটি কভার আর্ট হিসাবে ব্যবহার করবেন\? + এই চিত্রটি কভার আর্ট হিসাবে ব্যবহার করবেন? অধ্যায়গুলি ডাউনলোড করা যায়নি। ডাউনলোড বিভাগে আপনি আবার চেষ্টা করতে পারেন নতুন অধ্যায় পাওয়া গিয়েছে প্রচ্ছদ হালনাগাদ করতে ব্যর্থ @@ -337,7 +337,7 @@ কর্মহীন হলে লক করুন হালনাগাদকৃত প্রকৃত শ্রেণির সেটিংস প্রকৃত হিসেবে সংরক্ষণ করুন - আপনি কি নিশ্চিত সেটিংসগুলো প্রকৃত হিসেবে সংরক্ষণ করবেন\? + আপনি কি নিশ্চিত সেটিংসগুলো প্রকৃত হিসেবে সংরক্ষণ করবেন? তথ্য কার্যকর করতে অ্যাপ পুনরারম্ভ করা লাগে নেটওয়ার্ক @@ -410,7 +410,7 @@ লাইব্রেরীর মাঙ্গা আপনি এখন প্রস্থান করেছেন প্রস্থান - প্রস্থান করবেন %1$s থেকে\? + প্রস্থান করবেন %1$s থেকে? আপনার লাইব্রেরীর সব মাঙ্গা ফিলটার করে পড়ার ইতিহাসে বিরতি দেয় ছদ্মবেশী মোড @@ -421,7 +421,7 @@ নতুন কি ওয়েবসাইট ইতিহাস সাফ করুন - আপনি নিশ্চিত\? সব ইতিহাস মুছে যাবে। + আপনি নিশ্চিত? সব ইতিহাস মুছে যাবে। ইতিহাস মুছে ফেলা হয়েছে যন্ত্রের সেটিংস খোলা সম্ভব হয়নি ব্যাটারি অপ্টিমাইজেশন ইতিমধ্যে অক্ষম করা আছে @@ -561,9 +561,9 @@ %1$d আপডেট(গুলি) ব্যর্থ হয়েছে সম্পূর্ণ প্রকাশিত সর্বশেষ হালনাগাদ চেক - আপনি কি \"%s\" বিভাগটি মুছে ফেলতে চান\? + আপনি কি \"%s\" বিভাগটি মুছে ফেলতে চান? সম্প্রতি আপনার হালনাগাদকৃত মাঙ্গা দেখুন - আপনি কি নিশ্চিত\? + আপনি কি নিশ্চিত? অপঠিত সংখ্যা সবকিছু মুছুন লেখা দেখান diff --git a/i18n/src/commonMain/resources/MR/ca/strings.xml b/i18n/src/commonMain/resources/MR/ca/strings.xml index b0e4c2835..bc63d1260 100644 --- a/i18n/src/commonMain/resources/MR/ca/strings.xml +++ b/i18n/src/commonMain/resources/MR/ca/strings.xml @@ -160,7 +160,7 @@ Freqüència de la còpia de seguretat automàtica S’ha creat la còpia de seguretat S’ha completat la restauració - De què voleu fer una còpia de seguretat\? + De què voleu fer una còpia de seguretat? S’està restaurant la còpia de seguretat S’està creant la còpia de seguretat Buida la memòria cau de capítols @@ -171,7 +171,7 @@ S’han esborrat les galetes Buida la base de dades Suprimeix l’historial dels elements que no siguin a la biblioteca - N’esteu segur\? Es perdrà el progrés i els capítols llegits dels elements que no siguin a la biblioteca + N’esteu segur? Es perdrà el progrés i els capítols llegits dels elements que no siguin a la biblioteca S’han suprimit les entrades Versió Envia informes d’errors @@ -195,7 +195,7 @@ Elimina de la biblioteca S’ha afegit a la biblioteca S’ha eliminat de la biblioteca - Voleu suprimir els capítols baixats\? + Voleu suprimir els capítols baixats? S’ha copiat al porta-retalls: \n%1$s Font no instal·lada: %1$s @@ -209,7 +209,7 @@ Per número de capítol Baixa Els no llegits - Esteu segur que voleu suprimir els capítols seleccionats\? + Esteu segur que voleu suprimir els capítols seleccionats? En seguiment Llegint Completats @@ -224,9 +224,9 @@ Tipus Ja hi ha una categoria amb aquest nom! S’han suprimit les categories - S’eliminarà la data de lectura d’aquest capítol. N’esteu segur\? + S’eliminarà la data de lectura d’aquest capítol. N’esteu segur? Reinicia tots els capítols d’aquest element - Voleu afegir-lo a la biblioteca\? + Voleu afegir-lo a la biblioteca? S’ha desat la imatge Filtre personalitzat Defineix com a portada @@ -234,7 +234,7 @@ Pàgina: %1$d No s’ha trobat el capítol següent No s’ha pogut carregar la imatge - Voleu fer servir aquesta imatge com a portada\? + Voleu fer servir aquesta imatge com a portada? Finalitzat: Actual: Següent: @@ -290,7 +290,7 @@ Aquesta extensió ja no està disponible. És possible que no funcioni correctament i pot causar problemes a l’aplicació. És recomanable que la desinstal·leu. Format de data Actualització global - Voleu tancar la sessió a %1$s\? + Voleu tancar la sessió a %1$s? Tanca la sessió S’ha tancat la sessió Pausat @@ -415,13 +415,13 @@ S’ha actualitzat la configuració per defecte dels capítols Estableix com a per defecte Aplica-ho també a tots els elements de la biblioteca - Esteu segur que voleu desar aquesta configuració com a configuració per defecte\? + Esteu segur que voleu desar aquesta configuració com a configuració per defecte? Configuració dels capítols %1$s: %2$s, pàgina %3$d Configuració de la cerca Capítols baixats De la biblioteca - N’esteu segur\? Es perdrà tot l’historial. + N’esteu segur? Es perdrà tot l’historial. Pausa l’historial de lectura Mode d’incògnit Suprimeix l’historial @@ -602,7 +602,7 @@ Portada personalitzada No instal·lat Llengua de l’aplicació - Voleu eliminar la categoria «%s»\? + Voleu eliminar la categoria «%s»? Elimina la categoria Lavanda Sense descripció @@ -618,7 +618,7 @@ La cadena de l’agent d’usuari no pot estar buida Tema, formats de data i hora Categories, actualitzacions globals i lliscament en capítols - N’esteu segur\? + N’esteu segur? Ubicació invàlida: %s Baixa automàticament mentre es llegeix Només funciona si el capítol actual i el següent ja estan baixats. @@ -651,7 +651,7 @@ Botó per a continuar llegint Invalida l’índex de baixades S’eliminarà la data de finalització seleccionada de %s - Voleu eliminar-ne la data\? + Voleu eliminar-ne la data? S’eliminarà la data d’inici seleccionada de %s Força que l’aplicació torni a comprovar els capítols baixats Estadístiques @@ -666,7 +666,7 @@ Disponible però la font no està instal·lada: %s Ja teniu un element a la biblioteca amb el mateix nom. \n -\nVoleu continuar igualment\? +\nVoleu continuar igualment? Temps de lectura Elements Elements completats @@ -706,7 +706,7 @@ Intervals Estima cada S’actualitzarà cada - Voleu deixar de seguir %s\? + Voleu deixar de seguir %s? Elimina també de %s Se n’eliminarà el seguiment local. S’ha omès perquè no se n’espera cap publicació avui @@ -728,7 +728,7 @@ «%1$s» en comptes de «%2$s» Ordena les categories S’està actualitzant la biblioteca… (%s) - Voleu ordenar les categories alfabèticament\? + Voleu ordenar les categories alfabèticament? Configuració de la font Configuració de l’aplicació No s’ha seleccionat cap fitxer diff --git a/i18n/src/commonMain/resources/MR/ceb/strings.xml b/i18n/src/commonMain/resources/MR/ceb/strings.xml index 85229825a..327208bcc 100644 --- a/i18n/src/commonMain/resources/MR/ceb/strings.xml +++ b/i18n/src/commonMain/resources/MR/ceb/strings.xml @@ -358,7 +358,7 @@ Nagkinahanglan nga i-restart ang app aron ma-epekto Ang pag-backup nagpadayon na %02d minuto , %02d ikaduha - Unsa ang gusto nimo i-backup\? + Unsa ang gusto nimo i-backup? Ang datos gikan sa backup file mapasig-uli. \n \nKinahanglan nimo nga i-install ang bisan unsang nawala nga mga extension ug mag-log in sa mga serbisyo sa pagsubay pagkahuman aron magamit kini. @@ -370,7 +370,7 @@ Data Hawani ang cache sa kapitulo Napakyas ang pag-backup - Gusto ba nimong papason ang kategoryang \"%s\"\? + Gusto ba nimong papason ang kategoryang \"%s\"? Pagtangtang sa kategorya Internal nga Sayop: Susiha ang mga crash log para sa dugang nga impormasyon Pag-navigate sa pan @@ -386,7 +386,7 @@ Hawani ang cache sa kapitulo sa pagsira sa app Klaro nga database %1$d non-library manga sa database - Sigurado ka\? Basaha ang mga kapitulo ug ang pag-uswag sa dili librarya nga manga mawala + Sigurado ka? Basaha ang mga kapitulo ug ang pag-uswag sa dili librarya nga manga mawala Gitipigan ang mga log sa sayup sa usa ka file aron ipaambit sa mga nag-develop I-delete ang history para sa manga nga wala ma-save sa imong library I-disable ang pag-optimize sa baterya @@ -425,7 +425,7 @@ Naka-log out ka na karon Password Login - Log out gikan sa %1$s\? + Log out gikan sa %1$s? Log out Naka-log in Login sa %1$s diff --git a/i18n/src/commonMain/resources/MR/cs/strings.xml b/i18n/src/commonMain/resources/MR/cs/strings.xml index 5b7a05480..e0bf3524a 100644 --- a/i18n/src/commonMain/resources/MR/cs/strings.xml +++ b/i18n/src/commonMain/resources/MR/cs/strings.xml @@ -96,7 +96,7 @@ Odstranit z knihovny Přidáno do knihovny Odstraněno z knihovny - Smazat stažené kapitoly\? + Smazat stažené kapitoly? Zdroj není nainstalován: %1$s Kapitola %1$s Stahování (%1$d/%2$d) @@ -105,7 +105,7 @@ Podle zdroje Stáhnout Stáhnout nepřečtené - Opravdu chcete smazat vybrané kapitoly\? + Opravdu chcete smazat vybrané kapitoly? Rozečtené Dokončeno Plánuji číst @@ -114,7 +114,7 @@ Název Typ Obnovit všechny kapitoly pro tento záznam - Přidat do knihovny\? + Přidat do knihovny? Obrázek uložen Vlastní filtr Stránka: %1$d @@ -206,7 +206,7 @@ Frekvence automatických záloh Záloha vytvořena Obnova dokončena - Co chcete zálohovat\? + Co chcete zálohovat? Obnovování zálohy Vytváření zálohy Vymazat mezipaměť kapitol @@ -217,7 +217,7 @@ Cookies vymazány Vyčistit databázi Smazat historii položek, které nejsou uloženy ve vaší knihovně - Jste si jistí\? Přečtené kapitoly a postup v položkách mimo knihovnu bude ztracen + Jste si jistí? Přečtené kapitoly a postup v položkách mimo knihovnu bude ztracen Položky byly smazány Verze Odesílat hlášení o pádu @@ -257,8 +257,8 @@ Název zdroje Číslo kapitoly Kategorie s tímto jménem již existuje! - Toto odstraní datum přečtení této kapitoly. Jste si jistý\? - Chcete tento obrázek nastavit jako obal\? + Toto odstraní datum přečtení této kapitoly. Jste si jistý? + Chcete tento obrázek nastavit jako obal? Chyba při načítání stránek: %1$s Vyberte data, která chcete zahrnout Chyba při aktualizaci obalu @@ -291,7 +291,7 @@ Odhlásit se Formát data Globální aktualizace - Odhlásit z %1$s\? + Odhlásit z %1$s? Nyní jste odhlášený Pozastaveno Více @@ -312,7 +312,7 @@ Přidat sledování Nebyly nalezeny žádné kapitoly Nastavit jako výchozí - Jsi si jistý, že chceš tato nastavení uložit jako výchozí\? + Jsi si jistý, že chceš tato nastavení uložit jako výchozí? Nastavení kapitoly Podle data nahrání Méně @@ -410,7 +410,7 @@ Kapitola %1$s Nelze stáhnout kapitoly kvůli nedostatku místa Návod k přesunu zdrojů - Jste si jistý/á\? Všechna historie bude navždy ztracena. + Jste si jistý/á? Všechna historie bude navždy ztracena. Vymazat historii Historie vymazána Kap. %1$s - %2$s @@ -604,7 +604,7 @@ Jazyk aplikace Bez popisu Levandule - Přejete si smazat kategorii \"%s\"\? + Přejete si smazat kategorii \"%s\"? Smazat kategorii InternalError: Zkontrolujte záznamy pádu pro další informace Obnovit výchozí řetězec pro user agent @@ -619,7 +619,7 @@ Stáhování dopředu Automatické stahování při čtení Funguje pouze v případě, že aktuální kapitola + následující kapitola jsou již staženy. - Jste si jistí\? + Jste si jistí? Chystáte se odstranit \"%s\" ze své knihovny Více Poslední aktualizace knihovny: %s @@ -649,7 +649,7 @@ F-Droid sestavení nejsou oficiálně podporovány. \nKlepnutím zobrazíte další informace. Tlačítko Pokračovat ve čtení - Odstranit datum\? + Odstranit datum? Tím se odstraní dříve vybrané datum ukončení z %s Tím se odstraní dříve vybrané datum zahájení z %s Zneplatnit index stahování @@ -682,7 +682,7 @@ Přeskakovat duplicitní kapitoly Ve své knihovně máte položku se stejným názvem. \n -\nStále si přejete pokračovat\? +\nStále si přejete pokračovat? %1$s chyba: %2$s *vyžadováno Skrýt položky, které jsou již v knihovně @@ -709,7 +709,7 @@ Tím se lokálně odstraní sledování. Odebrat také z %s OK - Odebrat sledování %s\? + Odebrat sledování %s? Odstranit stažené Má výsledky Synchronizace knihovny diff --git a/i18n/src/commonMain/resources/MR/cv/strings.xml b/i18n/src/commonMain/resources/MR/cv/strings.xml index db00e5cff..b19d50070 100644 --- a/i18n/src/commonMain/resources/MR/cv/strings.xml +++ b/i18n/src/commonMain/resources/MR/cv/strings.xml @@ -287,14 +287,14 @@ Пӗтернӗ: Вулав тытӑмӗ Ҫак серилӗх валли - Ҫак сӑна хуплашка пек усӑ курмалла-и\? + Ҫак сӑна хуплашка пек усӑ курмалла-и? Сӑн тийенмерӗ Малалли сыпӑк тупӑнман %1$d-мӗш ел Хуплашкана ҫӗнетнӗ Хуплашка пек ларт Ӳкерчӗке упранӑ - Ҫакӑ вара сыпӑка вулани вӑхӑта катертӗ. Чӑнах катертесшӗн-и\? + Ҫакӑ вара сыпӑка вулани вӑхӑта катертӗ. Чӑнах катертесшӗн-и? Пухмӑшсем катертнӗ Ун пек ятпа пухмӑш пур ӗнтӗ! Тӗс @@ -309,7 +309,7 @@ Пӑрахнӑ Вӗҫленӗ Вулатӑп - Есӗ суйланӑ сыпӑксене катертесшӗнех-и\? + Есӗ суйланӑ сыпӑксене катертесшӗнех-и? Вуламанине Тиесе ил Тиенӗ вӑхӑчӗпе @@ -320,11 +320,11 @@ Вӑхӑтлӑха чарнӑ Йӑнӑш Тиенет (%1$d/%2$d) - Манкка вулавăша хушмалла-и\? + Манкка вулавăша хушмалла-и? Ҫӑл куҫа лартман: %1$s Пайлашу буфере ӑтавланӑ: \n%1$s - Тиенӗ сыпӑксене катертмелле-и\? + Тиенӗ сыпӑксене катертмелле-и? Сахалрах Ытларах Вулавӑшран катерт @@ -353,7 +353,7 @@ Паллӑ мар йӑнӑш Есӗ тухрӑн Тух - “%1$s тухмалла-и\? + “%1$s тухмалла-и? Ӑнӑҫлӑ кӗни Кӗр Кӗме сӑмах @@ -373,7 +373,7 @@ Янтӑвлани тӑтӑшлӑхӗ Вулавӑшри манкӑсен хуплашкисене ҫӗнет Ҫыравсене катертнӗ - Чӑнах катертесшӗн-и\? Вулавӑшри мар манкӑн вуланӑ сыпӑкӗсем тата вулав ӳсӗмӗ ҫухалӗ + Чӑнах катертесшӗн-и? Вулавӑшри мар манкӑн вуланӑ сыпӑкӗсем тата вулав ӳсӗмӗ ҫухалӗ Санӑн вулавӑшра упранман манкӑн вуланӑ кун-ҫулне катерт Пӗлӗмсен кӗлете тасат Кеше тасатнӑ чух йӑнӑш тухрӗ @@ -387,7 +387,7 @@ Тавӑрни пурнӑҫланать ӗнтӗ Янтӑвлани йӑнӑшӗ Янтӑв тӑвӑнать - Мӗн эсӗ янтӑвласшӑн\? + Мӗн эсӗ янтӑвласшӑн? Янтӑ пурнӑҫланать ӗнтӗ Тавӑрни вӗҫленнӗ Хальхи вулавăша тавӑрма усӑ курма пулать @@ -417,7 +417,7 @@ Тата манӑн вулавăшри пур манкӑ валли усӑ кур Яланхилле сыпӑк ӗнерлев ҫӗнетнӗ Яланхилле - Ҫак ӗнерелевсене яланхилле упрасшӑнах-и\? + Ҫак ӗнерелевсене яланхилле упрасшӑнах-и? %1$s: %2$s, %3$d-мӗш ел Сыпӑк ӗнерлеве Ӗнерӳ шыра @@ -427,7 +427,7 @@ Кун-ҫул тасат Кун-ҫула катертнӗ Вӑрттӑнлӑх тытӑмӗ - Чӑнах катертесшӗн-и\? Веҫ кун-ҫулӗ ҫухалӗ. + Чӑнах катертесшӗн-и? Веҫ кун-ҫулӗ ҫухалӗ. Маллали сыпӑк Умӗнхи эл Ҫӑл куҫ куҫарассипе пулӑшу diff --git a/i18n/src/commonMain/resources/MR/da/strings.xml b/i18n/src/commonMain/resources/MR/da/strings.xml index 98a75cb2a..508593d97 100644 --- a/i18n/src/commonMain/resources/MR/da/strings.xml +++ b/i18n/src/commonMain/resources/MR/da/strings.xml @@ -173,7 +173,7 @@ Med \"Færdig\" status Der ikke er blevet startet Multi - Ønsker du at slette kategorien \"%s\"\? + Ønsker du at slette kategorien \"%s\"? Tidevandsbølge Søg efter nyt cover og detaljer, når biblioteket opdateres Opdater automatisk metadata @@ -195,7 +195,7 @@ InternalError: Tjek nedbrudslogfiler for yderligere information Du er nu logget ud Logget ind - Log af %1$s\? + Log af %1$s? Tilføj til bibliotek I Biblioteket Fjern fra biblioteket diff --git a/i18n/src/commonMain/resources/MR/de/strings.xml b/i18n/src/commonMain/resources/MR/de/strings.xml index 62808c86b..7978b5a8c 100644 --- a/i18n/src/commonMain/resources/MR/de/strings.xml +++ b/i18n/src/commonMain/resources/MR/de/strings.xml @@ -135,7 +135,7 @@ Automatische Sicherungshäufigkeit Datensicherung erstellt Wiederherstellen abgeschlossen - Was möchtest du sichern\? + Was möchtest du sichern? Datensicherung wird wiederhergestellt Datensicherung wird erstellt Kapitel-Zwischenspeicher leeren @@ -146,7 +146,7 @@ Cookies gelöscht Datenbank leeren Verlauf für Einträge löschen, die nicht in deiner Bibliothek gespeichert sind - Bist du dir sicher\? Die gelesenen Kapitel und Fortschritte von Einträgen, die nicht in deiner Bibliothek sind, werden gelöscht + Bist du dir sicher? Die gelesenen Kapitel und Fortschritte von Einträgen, die nicht in deiner Bibliothek sind, werden gelöscht Einträge gelöscht Version Fehlerberichte senden @@ -175,7 +175,7 @@ Kapitelnummer Herunterladen Ungelesen - Bist du sicher, dass du die ausgewählten Kapitel löschen willst\? + Bist du sicher, dass du die ausgewählten Kapitel löschen willst? Tracking Am Lesen Abgeschlossen @@ -187,9 +187,9 @@ Status Eine Kategorie mit diesem Namen existiert bereits! Kategorien gelöscht - Das Lesedatum dieses Kapitels wird gelöscht. Bist du sicher\? + Das Lesedatum dieses Kapitels wird gelöscht. Bist du sicher? Alle Kapitel dieses Eintrags zurücksetzen - Zur Bibliothek hinzufügen\? + Zur Bibliothek hinzufügen? Bild gespeichert Eigener Filter Als Vorschaubild festlegen @@ -197,7 +197,7 @@ Seite: %1$d Nächstes Kapitel nicht gefunden Das Bild konnte nicht geladen werden - Dieses Bild als Vorschaubild setzen\? + Dieses Bild als Vorschaubild setzen? Fehler beim Herunterladen der Kapitel. Du kannst es in der Downloadsektion erneut versuchen Neue Kapitel gefunden Ändern des Vorschaubildes fehlgeschlagen @@ -290,7 +290,7 @@ Diese Erweiterung ist nicht länger verfügbar. Sie funktioniert möglicherweise nicht mehr ordnungsgemäß und kann Probleme mit der App verursachen. Es ist empfohlen, sie zu deinstallieren. Datumsformat Globale Aktualisierung - Aus %1$s abmelden\? + Aus %1$s abmelden? Abmelden Du bist jetzt abgemeldet Pausiert @@ -415,7 +415,7 @@ Standard-Kapiteleinstellungen aktualisiert Als Standard setzen Auch für alle Einträge in meiner Bibliothek anwenden - Bist du sicher, dass du diese Einstellungen als Standard speichern willst\? + Bist du sicher, dass du diese Einstellungen als Standard speichern willst? Kapiteleinstellungen %1$s: %2$s, Seite %3$d Einstellungen durchsuchen @@ -424,7 +424,7 @@ Leseverlauf pausieren Inkognito-Modus Verlauf löschen - Bist du sicher\? Der gesamte Verlauf wird gelöscht. + Bist du sicher? Der gesamte Verlauf wird gelöscht. Verlauf gelöscht Nächste Seite Vorherige Seite @@ -604,7 +604,7 @@ App-Sprache Lavendel Keine Beschreibung - Möchtest du die Kategorie „%s“ löschen\? + Möchtest du die Kategorie „%s“ löschen? Kategorie löschen InternalError: Überprüfe die Absturzprotokolle für mehr Infos Standard-User-Agent-Text @@ -619,7 +619,7 @@ Im Voraus herunterladen Automatisch während des Lesens herunterladen Funktioniert nur, wenn das aktuelle Kapitel sowie das darauf folgende bereits heruntergeladen sind. - Bist du dir sicher\? + Bist du dir sicher? Du bist gerade dabei, „%s“ aus deiner Bibliothek zu entfernen Mehrere Sprachen Bibliothek zuletzt aktualisiert: %s @@ -650,7 +650,7 @@ \nTippe, um mehr zu erfahren. Weiterlesen-Button Index der Downloads invalidieren - Datum entfernen\? + Datum entfernen? Dies wird dein zuvor gewähltes Startdatum von %s entfernen Dies wird dein zuvor gewähltes Enddatum von %s entfernen App zwingen, heruntergeladene Kapitel erneut zu überprüfen @@ -682,7 +682,7 @@ Doppelte Kapitel überspringen In deiner Bibliothek gibt es einen Eintrag mit dem gleichen Namen. \n -\nMöchtest du trotzdem fortfahren\? +\nMöchtest du trotzdem fortfahren? %1$s-Fehler: %2$s *erforderlich Einträge verbergen, die bereits in der Bibliothek sind @@ -709,7 +709,7 @@ Dies wird das lokale Tracking entfernen. Auch aus %s entfernen OK - Tracking von %s entfernen\? + Tracking von %s entfernen? Heruntergeladenes löschen Hat Ergebnisse Bibliothek wird synchronisiert @@ -730,7 +730,7 @@ Quelleneinstellungen Bibliothek wird aktualisiert… (%s) Kategorien sortieren - Möchtest du die Kategorien alphabetisch sortieren\? + Möchtest du die Kategorien alphabetisch sortieren? Keine Datei ausgewählt Nie Reduziert Ghosting auf E-Papier-Displays diff --git a/i18n/src/commonMain/resources/MR/eo/strings.xml b/i18n/src/commonMain/resources/MR/eo/strings.xml index bc8fdbbc4..5b0d29da5 100644 --- a/i18n/src/commonMain/resources/MR/eo/strings.xml +++ b/i18n/src/commonMain/resources/MR/eo/strings.xml @@ -219,7 +219,7 @@ Novaj ĉapitroj trovitaj Kopii Migri - Ĉu vi certas\? La tuta historio forigos. + Ĉu vi certas? La tuta historio forigos. Historio viŝita Viŝi historion Ĉap. %1$s - %2$s @@ -253,7 +253,7 @@ Neniu ĉapitro trovita Agordi kiel defaŭlta Ĉapitrajn agordojn - Ĉu vi certe volas forigi elektitajn ĉapitrojn\? + Ĉu vi certe volas forigi elektitajn ĉapitrojn? Nelegitajn Elŝuti Laŭ laŝutita dato @@ -265,12 +265,12 @@ Eraro Elŝutas (%1$d/%2$d) Ĉapitro %1$s - Ĉu aldoni mangaon al biblioteko\? + Ĉu aldoni mangaon al biblioteko? Fonto ne instalita: %1$s Kopiado al tondejo malsukcesis Kopita al tondejo: \n%1$s - Ĉu forigi elŝutitajn ĉapitrojn\? + Ĉu forigi elŝutitajn ĉapitrojn? Malpli Pli Forigita de biblioteko @@ -293,7 +293,7 @@ Nekonata eraro Vi nun estas elsalutita Elsaluti - Elsaluti el %1$s\? + Elsaluti el %1$s? Ensalutita Ensaluti Ensaluti al %1$s @@ -315,7 +315,7 @@ Reto Kreado de savkopio fiaskis Kreas savkopion - Kion vi volas savkopii\? + Kion vi volas savkopii? %02d min, %02d sek Mankantaj fontoj: Aŭtomata elŝuto @@ -367,7 +367,7 @@ Haltetita Nelegita Ankaŭ apliki al ĉiuj mangaoj en mia biblioteko - Ĉu vi certe volas konservi ĉi-agordoj kiel defaŭltaj\? + Ĉu vi certe volas konservi ĉi-agordoj kiel defaŭltaj? Dato Ordigi laŭ Nevalida ĉapitra formato @@ -431,7 +431,7 @@ Devigi malŝlosi Refreŝi kovrilojn de mangaoj en biblioteko Elekti kovrilbildon - Ĉu uzi ĉi-bildon kiel kovrilo\? + Ĉu uzi ĉi-bildon kiel kovrilo? Aktualigo de kovrilo fiaskis Elekti datumon por inkluzivi Ŝargas paĝojn… diff --git a/i18n/src/commonMain/resources/MR/es/strings.xml b/i18n/src/commonMain/resources/MR/es/strings.xml index a382ea762..7c5a558c1 100644 --- a/i18n/src/commonMain/resources/MR/es/strings.xml +++ b/i18n/src/commonMain/resources/MR/es/strings.xml @@ -124,7 +124,7 @@ Por fecha de subida Descargar Sin leer - ¿Seguro que quieres eliminar los capítulos seleccionados\? + ¿Seguro que quieres eliminar los capítulos seleccionados? Leyendo Completado Abandonado @@ -170,7 +170,7 @@ Añadir No estás conectado a Internet Descarga pausada - ¿Usar esta imagen como portada\? + ¿Usar esta imagen como portada? Apariencia Cuadrícula compacta Lista @@ -206,7 +206,7 @@ Frecuencia de respaldo automático Copia de seguridad creada Restauración completada - ¿De qué quieres hacer una copia de seguridad\? + ¿De qué quieres hacer una copia de seguridad? Restaurando copia de seguridad Creando copia de seguridad No hay más resultados @@ -216,7 +216,7 @@ Seguimiento ¡Ya existe una categoría con este nombre! Categorías eliminadas - ¿Añadir manga a la biblioteca\? + ¿Añadir manga a la biblioteca? Imagen guardada Filtro personalizado Poner como portada @@ -294,7 +294,7 @@ Cerrar sesión Has cerrado sesión Pausado - ¿Cerrar sesión en %1$s\? + ¿Cerrar sesión en %1$s? Más Por capítulo más reciente Ver capítulos @@ -415,7 +415,7 @@ Se han actualizado los ajustes predeterminados de capítulo Establecer como predeterminado Aplicar también a todos los elementos en mi biblioteca - ¿Seguro que quieres guardar estos ajustes como predeterminados\? + ¿Seguro que quieres guardar estos ajustes como predeterminados? Ajustes de capítulo %1$s: %2$s, página %3$d Ajustes de búsqueda @@ -424,7 +424,7 @@ Pausa el historial de lectura Modo incógnito Borrar historial - ¿Estás seguro\? Perderás todo el historial. + ¿Estás seguro? Perderás todo el historial. Historial eliminado Siguiente página Página anterior @@ -605,7 +605,7 @@ Lavanda Sin descripción Borrar categoría - ¿Quieres borrar la categoría «%s»\? + ¿Quieres borrar la categoría «%s»? ErrorInterno: Mira el registro de depuración para más información Identificarse como otro navegador web («user agent») Volver a la identificación de navegador («user agent») original @@ -619,7 +619,7 @@ Solo funciona si el capítulo actual y el que va después ya están descargados. Descargar por adelantado Descarga los capítulos siguientes mientras lees - ¿Estás seguro\? + ¿Estás seguro? La biblioteca se actualizó: %s Estás a punto de borrar «%s» de tu biblioteca Populares @@ -646,11 +646,11 @@ Reindexando descargas Abrir un elemento al azar Parece que esta categoría está vacía - No damos soporte oficial a las versiones de F-Droid. + No damos soporte oficial a las versiones de F-Droid. \nToca para más información. Mostrar el botón de seguir leyendo Al hacer esto %s ya no aparecerá como empezado ese día - ¿Quieres quitar esta fecha\? + ¿Quieres quitar esta fecha? Al hacer esto %s ya no aparecerá como terminado ese día Reconstruir el índice de descargas Forzar a la aplicación a volver a comprobar los capítulos descargados @@ -682,7 +682,7 @@ Están disponibles, pero las fuentes todavía no se han instalado: %s Ya tienes un elemento en la biblioteca con este mismo nombre. \n -\n¿Seguro que quieres continuar\? +\n¿Seguro que quieres continuar? %1$s error: %2$s *necesaria Saltar elementos que ya estén en la biblioteca @@ -730,7 +730,7 @@ Ajustes de la fuente Ajustes de la aplicación Ordenar categorías - ¿Quieres ordenar las categorías de forma alfabética\? + ¿Quieres ordenar las categorías de forma alfabética? No has elegido ningún archivo Nunca Esta transición minimiza las manchas y el efecto de retención de imagen en pantallas de tinta electrónica diff --git a/i18n/src/commonMain/resources/MR/eu/strings.xml b/i18n/src/commonMain/resources/MR/eu/strings.xml index 9674a1604..d3dabbfbd 100644 --- a/i18n/src/commonMain/resources/MR/eu/strings.xml +++ b/i18n/src/commonMain/resources/MR/eu/strings.xml @@ -118,7 +118,7 @@ Ezabatu kapituluak Atzetik hasita laugarren irakurritako kapitulua Saioa hasi gabeko jarraitzaileak: - Zeren babeskopia nahi duzu\? + Zeren babeskopia nahi duzu? Berreskuratzea abian da Ezin izan da babeskopia berreskuratu Babeskopia automatikoak oso gomendagarriak dira. Kopiak beste leku batzuetan ere gorde beharko zenituzke. @@ -126,7 +126,7 @@ Datuak Garbitu kapituluen cachea Garbitu kapituluen cachea aplikazioa ixtean - Ziur zaude\? Kapitulu iraurriak eta liburutegikoak ez diren manga-en aurrerapenak galduko dira + Ziur zaude? Kapitulu iraurriak eta liburutegikoak ez diren manga-en aurrerapenak galduko dira Datu-basea garbi Desgaitu bateriaren optimizazioa Bertsioa @@ -145,7 +145,7 @@ Data Egile ezezaguna Egoera ezezaguna - Deskargatutako kapituluak ezabatu nahi dituzu\? + Deskargatutako kapituluak ezabatu nahi dituzu? Arbelean kopiatu da: \n%1$s Gelditurik @@ -172,12 +172,12 @@ U v%1$s-ra eguneratua Saioa hasia - Itxi saioa %1$s-n\? + Itxi saioa %1$s-n? Liburutegira gehitua - Ziur hautatutako kapituluak ezabatu nahi dituzula\? + Ziur hautatutako kapituluak ezabatu nahi dituzula? Bukaera-data Hasiera-data - Honek kapitulu honen irakurketa-data kenduko du. Ziur zaude\? + Honek kapitulu honen irakurketa-data kenduko du. Ziur zaude? Hurrengoa: Argitalpen honetarako Bukatua: @@ -284,7 +284,7 @@ Fitxak Kapitulu zenbakiaren arabera Igoera dataren arabera - Irudi hau azaleko arte gisa erabili\? + Irudi hau azaleko arte gisa erabili? Ezin izan dira kapituluak deskargatu memorian leku gutxi dagoelako Eguneratze handiek bateriaren erabilera areagotzea eta iturriak motelagoak izatea eragin dezakete Deskarga pausatu da @@ -305,7 +305,7 @@ Mesedez, hasi saioa berriro MAL-en Iturriaren izenburua Iturriaren arabera - Ziur ezarpen hauek lehenetsi gisa gorde nahi dituzula\? + Ziur ezarpen hauek lehenetsi gisa gorde nahi dituzula? Mesedez, eguneratu WebView aplikazioa bateragarritasun hobea izateko Bukatua Izen hori daukan kategoria bat badago dagoeneko! @@ -351,7 +351,7 @@ Liburutegitik kendua Gutxiago Iturria ez dago instalatuta: %1$s - Manga liburutegira gehitu\? + Manga liburutegira gehitu? Hautatu babeskopia fitxategia Ezin izan da azala eguneratu Gehiketa data @@ -422,7 +422,7 @@ Besteak Bilaketa globala… Historia ezabatu da - Ziur zaude\? Historia guztia galduko da. + Ziur zaude? Historia guztia galduko da. Iturrien migrazio gida Hautatu sartu nahi dituzun datuak Kapitulu berriak aurkituak @@ -573,7 +573,7 @@ App hizkuntza Hizkuntza Bilatu… - \"%s\" kategoria ezabatu nahi duzu\? + \"%s\" kategoria ezabatu nahi duzu? Guztia kendu Estatistikak Hasia diff --git a/i18n/src/commonMain/resources/MR/fi/strings.xml b/i18n/src/commonMain/resources/MR/fi/strings.xml index 3cec33782..587de5deb 100644 --- a/i18n/src/commonMain/resources/MR/fi/strings.xml +++ b/i18n/src/commonMain/resources/MR/fi/strings.xml @@ -31,7 +31,7 @@ Sivu: %1$d Seuraavaa lukua ei löydy Kuvaa ei voitu ladata - Käytetäänkö tätä kuvaa kansikuvana\? + Käytetäänkö tätä kuvaa kansikuvana? Valmistunut: Nykyinen: Seuraava: @@ -204,7 +204,7 @@ Varmuuskopioinnin tiheys Varmuuskopio luotu Palautus valmis - Mitä haluat varmuuskopioida\? + Mitä haluat varmuuskopioida? Palautetaan varmuuskopiota Luodaan varmuuskopiota Tyhjennä lukujen välimuisti @@ -215,7 +215,7 @@ Keksit tyhjennetty Tyhjennä tietokanta Poista mangan historia, jota ei ole tallennettu kirjastoosi - Oletko varma\? Luetut luvut ja eteneminen poistetaan muista kun kirjastossa olevista sarjoista + Oletko varma? Luetut luvut ja eteneminen poistetaan muista kun kirjastossa olevista sarjoista Tiedot poistettu Versio Lähetä kaatumisilmoituksia @@ -229,7 +229,7 @@ Päivitetään kategoriaa Ei enempää tuloksia Globaali haku… - Poista ladatut luvut\? + Poista ladatut luvut? Kopioitu leikepöydälle \n%1$s Lähdettä ei ole asennettu: %1$s @@ -242,7 +242,7 @@ Lähteen mukaan Luvun numeron mukaan Lataa - Oletko varma että haluat poistaa valitut luvut\? + Oletko varma että haluat poistaa valitut luvut? Luettavana Päättynyt Arvosana @@ -269,9 +269,9 @@ Pidossa Aikomus lukea Uudelleenluettava - Tämä poistaa luvun lukupäivän. Haluatko jatkaa\? + Tämä poistaa luvun lukupäivän. Haluatko jatkaa? Resetoi kaikki luvut tästä sarjasta - Lisää sarja kirjastoon\? + Lisää sarja kirjastoon? Kuva tallennetu Värisuodattimen sekoitus tyyppi Päällys @@ -290,7 +290,7 @@ Tämä laajennos ei ole enää saatavilla. Se ei välttämättä toimi oikein ja voi aiheuttaa ongelmia Mihonin kanssa. Laajennoksen poistoa suositellaan. Päivämäärän muoto Globaali päivitys - Kirjaa ulos kohteesta %1$s\? + Kirjaa ulos kohteesta %1$s? Kirjaudu ulos Kirjauduttu ulos Pysäytetty @@ -415,7 +415,7 @@ Päivitetyt luvun oletusasetukset Aseta oletukseksi Koskee myös kaikkia kirjastoni mangoja - Haluatko varmasti tallentaa nämä asetukset oletuksena\? + Haluatko varmasti tallentaa nämä asetukset oletuksena? Luvun asetukset %1$s: %2$s, sivu %3$d Hakuasetukset @@ -424,7 +424,7 @@ Keskeyttää lukuhistorian Incognito-tila Tyhjennä historia - Oletko varma\? Koko historia menetetään. + Oletko varma? Koko historia menetetään. Historia poistettu Seuraava sivu Edellinen sivu @@ -555,14 +555,14 @@ Etukäteen lataus Automaattinen lataus luetessa Parantaa lukijan suorituskykyä - Haluatko poistaa kategorian \"%s\"\? + Haluatko poistaa kategorian \"%s\"? Toimii vain kirjastossa oleville sarjoille joissa nykyinen ja seuraava luku on jo ladattu Varmuuskopiointi/palautus ei välttämättä toimi oikein, jos MIUI-optimointi on poistettu käytöstä. Suositut Julkaiseminen päättynyt Ei kuvausta Lukulista - Oletko varma\? + Oletko varma? Keskeneräiset Lähdettä ei löydetty Tallennusoikeuksia ei myönnetty diff --git a/i18n/src/commonMain/resources/MR/fil/strings.xml b/i18n/src/commonMain/resources/MR/fil/strings.xml index 8489e87d4..58c86182b 100644 --- a/i18n/src/commonMain/resources/MR/fil/strings.xml +++ b/i18n/src/commonMain/resources/MR/fil/strings.xml @@ -294,7 +294,7 @@ Pinili kong filter Na-save na ang larawan I-reset ang lahat ng chapters sa entry na ito - Tatanggalin nito ang petsa ng pagbasa sa kabanatang ito. Sigurado ka ba\? + Tatanggalin nito ang petsa ng pagbasa sa kabanatang ito. Sigurado ka ba? Binura na ang mga kategorya Mayroong kapangalan ang kategoryang ito! Uri @@ -311,7 +311,7 @@ Pagbabasa Magdagdag ng tracker Pagta-track - Sigurado ka bang gusto mong burahin ang mga napiling kabanata\? + Sigurado ka bang gusto mong burahin ang mga napiling kabanata? Babasahin I-download Base sa petsa ng pag-upload @@ -323,11 +323,11 @@ Nagka-error Dina-download (%1$d/%2$d) Kabanata %1$s - Idagdag sa Aklatan\? + Idagdag sa Aklatan? Di naka-install ang source: %1$s Kinopya sa clipboard: \n%1$s - Burahin ang mga na-download na kabanata\? + Burahin ang mga na-download na kabanata? Palawakin Bawasan Tinanggal sa Aklatan @@ -356,7 +356,7 @@ Di matukoy na error Naka-logout ka na Mag-logout - Mag-logout sa %1$s\? + Mag-logout sa %1$s? Naka-login ka na Mag-login Password @@ -398,7 +398,7 @@ Rine-restore ang backup Bigong ma-backup Bina-backup - Anong iba-backup mo\? + Anong iba-backup mo? Bina-backup na %02d (na) min, %02d (na) seg Wala kang naka-pin na source @@ -412,7 +412,7 @@ Posibleng may NSFW (18+) content ang mga source mula sa extension na ito 18+ Walang nakitang kabanata - Gusto mo bang i-save at ipagpaubaya ang pagsasaayos na ito\? + Gusto mo bang i-save at ipagpaubaya ang pagsasaayos na ito? Ipagpaubaya Ini-update na ang Ipagpaubaya %1$s: %2$s, pahina %3$d @@ -425,7 +425,7 @@ Nakatago Linisin ang nakaraan Nalinis na ang nakaraan - Sigurado ka ba talaga\? Mawawala ang buong nakaraan. + Sigurado ka ba talaga? Mawawala ang buong nakaraan. Gabay sa Paglipat ng source Abante Balik @@ -606,7 +606,7 @@ Walang paglalarawan Burahin ang kategorya InternalError: Tingnan ang mga crash log para sa karagdagang impormasyon - Gusto mo bang burahin ang kategoryang \"%s\"\? + Gusto mo bang burahin ang kategoryang \"%s\"? I-reset ang default na string ng user agent Default na string ng user agent Burahin lahat @@ -619,7 +619,7 @@ I-download agad Kusang mag-download habang nagbabasa Gumagana lamang sa kasalukuyang kabanata + ang susunod ay na-download na. - Sigurado ka ba\? + Sigurado ka ba? Marami Tatanggalin mo na ang \"%s\" mula sa aklatan mo Huling update sa aklatan: %s @@ -649,7 +649,7 @@ Ang mga build mula sa F-Droid ay hindi opisyal na sinusuportahan. \nI-tap para matuto pa. Magpatuloy na bumasa na button - Tanggalin ang petsa\? + Tanggalin ang petsa? Tatanggalin nito ang huling petsa na ipinili mo na simula sa %s Aalisin nito ang lahat ng mga nauna mong napiling petsa ng kayarian magmula sa %s Ipawalang-bisa ang indise ng mga download @@ -679,7 +679,7 @@ Mayroon ngunit hindi naka-install ang source: %s Mayroon kang entry sa iyong aklatan na magkapareho ng pangalan. \n -\nGusto mo pa bang magpatuloy\? +\nGusto mo pa bang magpatuloy? %1$s Kamalian: %2$s %dd %dh @@ -709,7 +709,7 @@ May mga resulta Burahin ang na-download OK - Tanggalin ang %s tracking\? + Tanggalin ang %s tracking? Tatanggalin nito ang lokal na pag-track. Tanggalin din mula sa %s Nagsi-sync ang aklatan @@ -728,7 +728,7 @@ \"%1$s\" sa halip na \"%2$s\" Mag-ayos ng kategorya Nag-a-update ang aklatan... (%s) - Gusto mo bang mag-ayos ng kategorya ayon sa alpabeto\? + Gusto mo bang mag-ayos ng kategorya ayon sa alpabeto? Mga setting ng source Mga setting ng app Walang napiling file diff --git a/i18n/src/commonMain/resources/MR/fr/strings.xml b/i18n/src/commonMain/resources/MR/fr/strings.xml index abf7d13d2..06b70876d 100644 --- a/i18n/src/commonMain/resources/MR/fr/strings.xml +++ b/i18n/src/commonMain/resources/MR/fr/strings.xml @@ -152,9 +152,9 @@ Statut Chapitres Une catégorie avec ce nom existe déjà ! - Ceci enlèvera la date de lecture de ce chapitre. Êtes-vous sûr(e) \? + Ceci enlèvera la date de lecture de ce chapitre. Êtes-vous sûr(e) ? Réinitialiser tous les chapitres de ce titre - Ajouter à la bibliothèque \? + Ajouter à la bibliothèque ? Image sauvegardée Filtre personnalisé Ajouter comme couverture @@ -184,7 +184,7 @@ Aucune connexion disponible Aucune connexion disponible Êtes-vous sûr(e) ? Les chapitres lus et la progression des entrées hors dans la bibliothèque seront perdues - Supprimer les chapitres sélectionnés \? + Supprimer les chapitres sélectionnés ? Impossible de télécharger le chapitre en raison d\'une erreur inattendue Cinquième chapitre avant le dernier lu Connecté @@ -207,7 +207,7 @@ Ouvrir le fichier Restaurer Restauration terminée - Que voulez-vous sauvegarder \? + Que voulez-vous sauvegarder ? Supprimer les chapitres téléchargés ? Suivi Total de chapitres @@ -290,7 +290,7 @@ Suivant Actualiser Obsolète - Se déconnecter de %1$s \? + Se déconnecter de %1$s ? Se déconnecter Vous êtes maintenant déconnecté(e) En pause @@ -412,7 +412,7 @@ 18+ Ceci n\'empêche pas les extensions de diffuser du contenu +18 dans l\'application. Aucun chapitre trouvé - Appliquer ce paramétrage par défaut \? + Appliquer ce paramétrage par défaut ? Paramètres du chapitre S\'applique également à toutes les entrées de ma bibliothèque Appliquer par défaut @@ -421,7 +421,7 @@ De la bibliothèque Rechercher les paramètres Suspend l\'historique de lecture - Êtes-vous sûr(e) \? Tout l\'historique sera effacé. + Êtes-vous sûr(e) ? Tout l\'historique sera effacé. Mode navigation privée Effacer l\'historique Historique supprimé @@ -605,7 +605,7 @@ Aucune description Lavande Effacer catégorie - Souhaitez-vous supprimer la catégorie « %s » \? + Souhaitez-vous supprimer la catégorie « %s » ? ErreurInterne : Consultez vos rapports de plantage pour plus d\'informations Réinitialiser la liste d\'agents utilisateurs Liste d\'agents utilisateurs par défaut @@ -619,7 +619,7 @@ Téléchargement anticipé Téléchargement anticipé pendant la lecture Fonctionne seulement si le chapitre actuel et le suivant sont déjà téléchargés. - Êtes-vous sûr(e) \? + Êtes-vous sûr(e) ? Populaire Multiple Dernière mise à jour de la bibliothèque le : %s @@ -650,7 +650,7 @@ Vérification des téléchargements Bouton \"Reprendre\" Ceci supprimera la date de début que vous avez précédemment sélectionnée dans la base de données de %s - Retirer la date \? + Retirer la date ? Ceci supprimera la date de fin que vous aviez précédemment sélectionnée dans la base de données de %s Invalider l\'index des téléchargements Forcer l\'application à revérifier les chapitres téléchargés @@ -681,7 +681,7 @@ Ignorer les chapitres en double Un titre de votre bibliothèque porte le même nom. \n -\nVoulez-vous vraiment continuer \? +\nVoulez-vous vraiment continuer ? %1$s erreur : %2$s Disponible mais la source n\'est pas installée : %s *obligatoire diff --git a/i18n/src/commonMain/resources/MR/gl/strings.xml b/i18n/src/commonMain/resources/MR/gl/strings.xml index f5986462e..3c0ee8dfe 100644 --- a/i18n/src/commonMain/resources/MR/gl/strings.xml +++ b/i18n/src/commonMain/resources/MR/gl/strings.xml @@ -261,7 +261,7 @@ Horizontal Mellora o rendemento do lector Páxina web - Estás seguro de querer gardar estes axustes como por defecto\? + Estás seguro de querer gardar estes axustes como por defecto? Imaxe gardada Restaurar a copia de seguridade Frecuencia das copias de seguridade @@ -288,7 +288,7 @@ Só funciona con elementos da biblioteca e se o capítulo actual e o seguinte xa están descargados. Restauración completada %02d min y %02d seg - De que queres facer unha copia de seguridade\? + De que queres facer unha copia de seguridade? Non se concederon os permisos de almacenamento Ningún elemento na biblioteca co que facer a copia de seguridade Deberías gardar duplicados das copias de seguridade fora do teu dispositivo. @@ -305,12 +305,12 @@ Máis recente Engadir á biblioteca Portada personalizada - Estás seguro de querer eliminar os capítulos seleccionados\? + Estás seguro de querer eliminar os capítulos seleccionados? Axustes de capítulo Pausado Portada Portada gardada - Queres eliminar a categoría \"%s\"\? + Queres eliminar a categoría \"%s\"? Descendente Ascendente Se a separación das páxinas anchas non coincide coa dirección de lectura @@ -371,7 +371,7 @@ Pode ser que debas instalar as extensións que falten e iniciar sesión nos servizos de seguemento para usalos. Datos Restableceuse a configuración do lector - Pechar a sesión en %1$s\? + Pechar a sesión en %1$s? Pechouse a sesión Publicación rematada Cancelada @@ -387,7 +387,7 @@ DNS por HTTPS (DoH) Cada 3 días Filtra tódolos elementos da túa biblioteca - Engadir á biblioteca\? + Engadir á biblioteca? Sen descripción Estado É posible que as opcións de copia de seguridade e de restauración non funcionen correctamente se a \"Optimización de MIUI\" está desactivada. @@ -497,7 +497,7 @@ Borrar a caché dos capítulos Borrouse a caché. Elimináronse %1$d ficheiros Forzar á aplicación a comprobar de novo os capítulos descargados - Estás seguro\? Perderanse os capítulos lidos e o progreso dos elementos que non estean na biblioteca + Estás seguro? Perderanse os capítulos lidos e o progreso dos elementos que non estean na biblioteca Restablecer os axustes do lector en cada serie Desactivar a optimización de batería A optimización de batería xa está desactivada @@ -514,7 +514,7 @@ Por número de capítulo Descargar Data de finalización - Queres eliminar a data\? + Queres eliminar a data? Data de comezo Non se puido cargar a imaxe Non hai ningún capítulo anterior @@ -555,7 +555,7 @@ Seguinte: Xa tes un elemento na túa biblioteca co mesmo nome. \n -\nAínda queres continuar\? +\nAínda queres continuar? Copiado ao portapapeis: \n%1$s Non se puido copiar ao portapapeis @@ -590,7 +590,7 @@ Progreso Erro na descarga Menos - Eliminar os capítulos descargados\? + Eliminar os capítulos descargados? En espera Anterior: Non hai ningún capítulo máis @@ -652,16 +652,16 @@ *necesario Páxina anterior Lista de lectura - Estás seguro\? + Estás seguro? Outros Estado descoñecido Fonte non instalada: %1$s Erro %1$s: %2$s Categorías eliminadas - Isto eliminará a data de lectura deste capítulo. Estás seguro\? + Isto eliminará a data de lectura deste capítulo. Estás seguro? Lista de inacabados Non se atoparon coincidencias - Queres usar esta imaxe como portada\? + Queres usar esta imaxe como portada? Xa hai unha categoría con este nome! Reiniciar tódolos capítulos deste elemento Modo de lectura @@ -669,7 +669,7 @@ Actualizando a biblioteca Última actualización da biblioteca: %s Cap. %1$s - %2$s - Estás seguro\? Perderás o historial. + Estás seguro? Perderás o historial. Non se puido abrir o último capítulo lido Lidos %dseg diff --git a/i18n/src/commonMain/resources/MR/he/strings.xml b/i18n/src/commonMain/resources/MR/he/strings.xml index 43020db5d..283ad0a8e 100644 --- a/i18n/src/commonMain/resources/MR/he/strings.xml +++ b/i18n/src/commonMain/resources/MR/he/strings.xml @@ -41,7 +41,7 @@ הבא: נוכחי: סיים: - האם להשתמש בתמונה זו כציור כריכה\? + האם להשתמש בתמונה זו כציור כריכה? לא ניתן היה לפענח את התמונה הפרק הבא לא נמצא עמוד: %1$d @@ -50,7 +50,7 @@ סנן מותאם אישית התמונה נשמרה אפס את כל הפרקים לפריט זה - פעולה זו תסיר את התאריך הקריאה של פרק זה. האם אתה בטוח\? + פעולה זו תסיר את התאריך הקריאה של פרק זה. האם אתה בטוח? הקטגוריות נמחקו קטגוריה עם שם זה כבר קיימת! סוג @@ -64,7 +64,7 @@ בהמתנה הושמט הושלם - האם אתה בטוח שברצונך למחוק את הפרקים שנבחרו\? + האם אתה בטוח שברצונך למחוק את הפרקים שנבחרו? לא נקרא לפי מספר פרק לפי מקור @@ -74,9 +74,9 @@ שגיאה מוריד (%1$d/%2$d) פרקים %1$s - להוסיף לספרייה\? + להוסיף לספרייה? המקור לא הותקן: %1$s - למחוק פרקים שירדו\? + למחוק פרקים שירדו? נוסף לספרייה הסר מהספרייה הוסר מהספרייה @@ -92,7 +92,7 @@ שגיאה לא ידועה אתה עכשיו מנותק התנתק - התנתק מ-%1$s\? + התנתק מ-%1$s? מחובר סיסמה כתובת דוא\"ל @@ -190,7 +190,7 @@ עוזר בעדכוני ספריות רקע וגיבויים השבתת האופטימיזציה של הסוללה הרשומות נמחקו - האם אתה בטוח\? פרקים שנקראו וההתקדמות של פריטים שאינם בספרייה יאבדו + האם אתה בטוח? פרקים שנקראו וההתקדמות של פריטים שאינם בספרייה יאבדו מחק את היסטוריית הפריטים שאינם שמורים בספריה שלך נקה את מסד הנתונים עוגיות נוקו @@ -201,7 +201,7 @@ נקה את זיכרון המטמון של הפרקים מייצר גיבוי משחזר גיבוי - מה אתה רוצה לגבות\? + מה אתה רוצה לגבות? השחזור הושלם גיבוי נוצר תדירות גיבוי @@ -497,7 +497,7 @@ בוטל/ה בתוך הספרייה הגדרות פרק - האם אתה בטוח שאתה רוצה לשמור את ההגדרות האלה כברירת המחדל\? + האם אתה בטוח שאתה רוצה לשמור את ההגדרות האלה כברירת המחדל? כבר מתרחש שחזור לא נמצאו עמודים מקור לא נמצא @@ -538,13 +538,13 @@ Shizuku לא פועל התקן והפעל את Shizuku כדי להשתמש בShizuku כמתקין ההרחבות. הראה את פרישת מיקומי הלחיצה - אתה בטוח\? כל ההיסטוריה תמחק. + אתה בטוח? כל ההיסטוריה תמחק. מדריך נדידת מקורות לא ניתן להוריד פרקים כי אין מספיק מקום אחסון אזהרה: הורדות גדולות עלולות לגרום להאטה במקורות ו/או לחסימת Mihon. לחץ כדי ללמוד עוד. פורמט פרק לא תקין בהפסקה - האם תרצה למחוק את הקטגוריה %s\? + האם תרצה למחוק את הקטגוריה %s? פצל עמודים רחבים כמו ספר אלקטרוני גרסה @@ -602,7 +602,7 @@ נכשל במציאת כתובת עמוד %d כבר קיים פריט בספריה בעל אותו שם. \n -\nלהמשיך בכל זאת\? +\nלהמשיך בכל זאת? הורדה אוטומטית, הורד את הבאים נושא, פורמט תאריך וזמן קטגוריות, עדכון כללי, החלקת פרק @@ -632,7 +632,7 @@ פצל תמונה מוארכת שומר דוח שגיאות לקובץ עבור שיתוף עם המפתחים כריכה מותאמת אישית - להסיר תאריך\? + להסיר תאריך? הורד הלאה פעילות רקע פריטים במעקב @@ -640,7 +640,7 @@ מעבר פרק בעל רישיון - אין פרקים להראות הועתק ללוח כתיבה - האם אתה בטוח\? + האם אתה בטוח? המקור לא נתמך לא נמצאה התאמה לא מותקן @@ -674,7 +674,7 @@ ממשק משתמש טאבלט F-Droid builds לא נתמכים באופן רשמי. \nלחץ כדי ללמוד עוד. - להסיר את %s ממעקב\? + להסיר את %s ממעקב? פעולה זו תסיר את המעקב שלך מקומית. הסר גם מ-%s קבע גם עבור כל הפריטים בספריה diff --git a/i18n/src/commonMain/resources/MR/hi/strings.xml b/i18n/src/commonMain/resources/MR/hi/strings.xml index f167674c8..75f62e210 100644 --- a/i18n/src/commonMain/resources/MR/hi/strings.xml +++ b/i18n/src/commonMain/resources/MR/hi/strings.xml @@ -150,7 +150,7 @@ कुकीज़ को साफ़ किया हुआ डेटाबेस साफ़ करें उन आइटम का इतिहास हटाएं जो आपकी पुस्तकालय में सहेजी नहीं गई हैं - क्या आपको यकीन है\? आपके द्वारा पढ़े गए अध्याय और गैर-पुस्तकालय आइटम की प्रगति खो जाएगी + क्या आपको यकीन है? आपके द्वारा पढ़े गए अध्याय और गैर-पुस्तकालय आइटम की प्रगति खो जाएगी प्रविष्टियां हटाई गईं संस्करण क्रैश रिपोर्ट भेजें @@ -197,7 +197,7 @@ श्रेणियाँ हटाई गयी यह इस अध्याय की पठन तिथि को निकाल देगा क्या आप निश्चित है? इस आइटम के लिए सभी अध्यायों को रीसेट करें - पुस्तकालय में जोड़ें\? + पुस्तकालय में जोड़ें? चित्र सहेजा गया इच्छा अनुसार फिल्टर कवर के रूप में सेट करें @@ -205,7 +205,7 @@ पृष्ठ: %1$d अगले अध्याय नहीं मिला छवि को लोड नहीं किया जा सका - कवर कला के रूप में इस छवि का उपयोग करें\? + कवर कला के रूप में इस छवि का उपयोग करें? अध्याय डाउनलोड नहीं कर सका। आप डाउनलोड अनुभाग में फिर से कोशिश कर सकते हैं नए अध्याय पाए गए कवर को अपडेट करने में विफल @@ -292,7 +292,7 @@ यह एक्सटेंशन अब उपलब्ध नहीं है। यह अब शायद ठीक से काम न करे और मसला खड़ा करे। इसे अनइंस्टॉल करने की सलाह दी गई है। दिनांक स्वरूप वैश्विक अपडेट - %1$s से लॉगआउट करें\? + %1$s से लॉगआउट करें? लॉग आउट अब आप लॉग आउट है रोका हुआ @@ -417,7 +417,7 @@ कोई अध्याय नहीं मिला डिफ़ॉल्ट के रूप में सेट करें मेरी पुस्तकालय में सभी एन्ट्री पर भी लागू करें - क्या आप वाकई इन सेटिंग्स को डिफ़ॉल्ट के रूप में सहेजना चाहते हैं\? + क्या आप वाकई इन सेटिंग्स को डिफ़ॉल्ट के रूप में सहेजना चाहते हैं? अध्याय सेटिंग्स %1$s: %2$s, पृष्ठ %3$d डाउनलोड किए गए अध्याय @@ -426,7 +426,7 @@ इतिहास पढ़ने से रोक देता है इंकॉग्निटो मोड इतिहास मिटा दें - क्या आपको यकीन है\? सारा इतिहास खो जाएगा। + क्या आपको यकीन है? सारा इतिहास खो जाएगा। इतिहास हटाया गया अगला पृष्ठ पिछला पृष्ठ @@ -597,7 +597,7 @@ ऐप की भाषा श्रेणी हटाएँ कोई विवरण नहीं - क्या आप %s कैटेगरी को हटाना चाहते हैं \? + क्या आप %s कैटेगरी को हटाना चाहते हैं ? प्रति श्रृंखला रीडर सेटिंग्स को रीसेट करें सभी श्रृंखलाओं के रीडिंग मोड और ओरिएंटेशन को रीसेट करता है सभी रीडर सेटिंग्स रीसेट करें @@ -621,7 +621,7 @@ टाइडल वेव्ह यूजर एजेंट स्ट्रिंग (User Agent String) खाली नहीं रखा जा सकता मल्टी - क्या आपको यकीन है \? + क्या आपको यकीन है ? ऐप लॉक लागू होने पर विजेट उपलब्ध नहीं होता आप अपनी पुस्तकालय से \"%s\" को निकालने जा रहे हैं एक अपडेट पहले से चल रहा है diff --git a/i18n/src/commonMain/resources/MR/hr/strings.xml b/i18n/src/commonMain/resources/MR/hr/strings.xml index dd70cde6f..ea77daeec 100644 --- a/i18n/src/commonMain/resources/MR/hr/strings.xml +++ b/i18n/src/commonMain/resources/MR/hr/strings.xml @@ -249,7 +249,7 @@ Završeno: Modus čitanja Za ovu seriju - Ovu sliku koristiti kao naslovnicu\? + Ovu sliku koristiti kao naslovnicu? Nije bilo moguće učitati sliku Sljedeće poglavlje nije pronađeno Stranica: %1$d @@ -258,7 +258,7 @@ Prilagođeni filtar Slika spremljena Obnovi sva poglavlja za ovaj unos - Ovo će ukloniti datum čitanja ovog poglavlja. Je li to stvarno želiš\? + Ovo će ukloniti datum čitanja ovog poglavlja. Je li to stvarno želiš? Izbrisane kategorije Kategorija s ovim imenom već postoji! Vrsta @@ -275,7 +275,7 @@ Čitanje Dodaj praćenje Praćenje - Stvarno želiš izbrisati odabrana poglavlja\? + Stvarno želiš izbrisati odabrana poglavlja? Nepročitano Preuzmi Po broju poglavlja @@ -286,11 +286,11 @@ Greška Preuzimanje (%1$d/%2$d) Poglavlje %1$s - Dodati u biblioteku\? + Dodati u biblioteku? Izvor nije instaliran: %1$s Kopirano u međuspremnik: \n%1$s - Izbrisati preuzeta poglavlja\? + Izbrisati preuzeta poglavlja? Manje Više Uklonjeno iz biblioteke @@ -315,7 +315,7 @@ Nepoznata greška Odjava uspjela Odjava - Odjaviti se iz %1$s\? + Odjaviti se iz %1$s? Prijava uspjela Prijava Lozinka @@ -335,7 +335,7 @@ Pomaže pri ažuriranju biblioteke u pozadini i spremanju sigurnosnih kopija Deaktiviraj optimiranje baterije Unosi su izbrisani - Sigurno\? Pročitana poglavlja i tijek unosa koji nisu u biblioteci će se izgubiti + Sigurno? Pročitana poglavlja i tijek unosa koji nisu u biblioteci će se izgubiti Izbriši povijest za unose koji nisu spremljeni u biblioteci Isprazni bazu podataka Kolačići su izbrisani @@ -350,7 +350,7 @@ Obnavljanje je već u tijeku Sigurnosna kopija neuspjela Stvaranje sigurnosne kopije - Što želiš spremiti kao sigurnosnu kopiju\? + Što želiš spremiti kao sigurnosnu kopiju? Spremanje sigurnosne kopije je već u tijeku Neuspjelo zaobilaženje Cloudflare-a Za funkcioniranje programa je potreban WebView @@ -416,7 +416,7 @@ %1$s: %2$s, %3$d. stranica Postavi kao standardno Također primijeni na sve unose u mojoj biblioteci - Stvarno želiš spremiti ove postavke kao standardne\? + Stvarno želiš spremiti ove postavke kao standardne? Postavke poglavlja Postavke pretrage Iz biblioteke @@ -424,7 +424,7 @@ Anonimni modus Zaustavlja čitanje povijesti Izbriši povijest - Sigurno\? Izgubit ćeš svu povijest. + Sigurno? Izgubit ćeš svu povijest. Povijest je izbrisana Sljedeća stranica Prethodna stranica @@ -586,7 +586,7 @@ Samo na mrežom bez ograničenja Broj nepročitanih Izbriši kategoriju - Želiš li izbrisati kategoriju „%s”\? + Želiš li izbrisati kategoriju „%s”? Automatski preuzmi tijekom čitanja Interna greška: Provjeri zapisnike o prekidu programa za daljnje informacije Jezik aplikacije @@ -638,7 +638,7 @@ Pa, ovo je nezgodno Nepoznat naslov Zadnje aktualiziranje biblioteke: %s - Sigurno\? + Sigurno? %s je naišao na neočekivanu grešku. Predlažemo da podijeliš zapis o prekidu programa u našem kanalu za podršku na Discordu. Pregled Dovršene stavke @@ -653,7 +653,7 @@ Ažuriranje je već pokrenuto Neuspjelo obnavljanje postavki čitača Sve postavke čitača su obnovljene - Ukloniti datum\? + Ukloniti datum? Time ćete ukloniti prethodno odabrani datum početka iz %s Time ćete ukloniti prethodno odabrani datum završetka iz %s Nije pronađen instalirani izvor @@ -681,7 +681,7 @@ Dostupne, ali izvor nije instaliran: %s U tvojoj biblioteci imaš unos s istim imenom. \n -\nŽeliš li svejedno nastaviti\? +\nŽeliš li svejedno nastaviti? Preskoči dupla poglavlja %1$s greška: %2$s *obavezno @@ -706,7 +706,7 @@ U redu Predvidi vrijeme sljedećeg izdanja Intervali - Ukloniti praćenje %s\? + Ukloniti praćenje %s? Preskočeno, jer se danas nije očekivalo izdanje Ovo će ukloniti lokalno praćenje. Također ukloni iz %s @@ -728,7 +728,7 @@ „%1$s” umjesto „%2$s” Razvrstaj kategorije Aktualiziranje biblioteke … (%s) - Želiš li razvrstati kategorije po abecedi\? + Želiš li razvrstati kategorije po abecedi? Nije odabrana nijedna datoteka Postavke izvora Postavke aplikacije diff --git a/i18n/src/commonMain/resources/MR/hu/strings.xml b/i18n/src/commonMain/resources/MR/hu/strings.xml index 5f2a28828..370d7bb77 100644 --- a/i18n/src/commonMain/resources/MR/hu/strings.xml +++ b/i18n/src/commonMain/resources/MR/hu/strings.xml @@ -211,7 +211,7 @@ Nyilvántartás Vágólapra másolva: \n%1$s - Törli a kijelölt fejezeteket\? + Törli a kijelölt fejezeteket? Előző oldal Hiba Weboldal @@ -249,10 +249,10 @@ %1$s. fejezet és további %2$d %1$s:%2$s, %3$d. oldal Újraolvasás - Biztosan törölni akarja a kijelölt fejezeteket\? + Biztosan törölni akarja a kijelölt fejezeteket? Olvasatlan Szünetel - Manga könyvtárhoz adása\? + Manga könyvtárhoz adása? Forrás nincs telepítve: %1$s Folyamatban van Utoljára használt @@ -283,7 +283,7 @@ Zárás tétlen állapotban Biometrikus zár Hibák mutatása - Kijelentkezel a következőből: %1$s\? + Kijelentkezel a következőből: %1$s? Böngészés Csökkenő Növekvő @@ -411,7 +411,7 @@ Helyi forrás Legújabb Dátum - Biztos benne\? Minden előzmény elvész. + Biztos benne? Minden előzmény elvész. Kategória törlése Alkalmazás nyelve Olvasatlan @@ -423,12 +423,12 @@ Nincs találat Globális keresés… Feltöltési dátum szerint - Ez eltávolítja a fejezet olvasási dátumát. Biztos benne\? + Ez eltávolítja a fejezet olvasási dátumát. Biztos benne? Olvasatlan fejezet(ek)et tartalmazó elemek kihagyása - Törölni akarja a %s kategóriát \? + Törölni akarja a %s kategóriát ? InternalError: Nézze meg a hibaüzenetet további információért Csak korlátlan hálózatokon - Mit tartalmazzon a biztonsági mentés\? + Mit tartalmazzon a biztonsági mentés? Biztonsági mentés/helyreállítás nem biztos,hogy működik ha a MIUI Optimalizáció ki van kapcsolva. Helyreállítás folyamatban van Felhasznált gyorsítótár: %1$s @@ -446,7 +446,7 @@ Sütik törlése Gyorsítótár törölve: %1$d fájl lett törölve Hiba történt törlés közben - Biztos benne\? Nem könyvtári elemek olvasott fejezetei elvesznek + Biztos benne? Nem könyvtári elemek olvasott fejezetei elvesznek Nem található fájl választó alkalmazás Nincsenek még kategóriáid. Nem sikerült megkerülni a Cloudflare-t @@ -486,7 +486,7 @@ Nem sikerült másolni Fedlap elmentve Fejezet beállítások - Biztos, hogy ezeket a beállításokat alapértelmezettként akarod elmenteni\? + Biztos, hogy ezeket a beállításokat alapértelmezettként akarod elmenteni? Hiba fedlap mentése közben Könyvtárban lévő tartalmakra is Előzmények törlése @@ -605,7 +605,7 @@ Átugorva, mivel a sorozat befejeződött %d oldal nem található felosztás közben Nem található az útvonal a %d-ik oldalhoz - Ez a kép legyen a fedlap\? + Ez a kép legyen a fedlap? Biztonsági mentési fájl kiválasztása Alap hálózati kliens szöveg Alap hálózati kliens szöveg visszaállítása @@ -660,7 +660,7 @@ Tárhely engedély nincs megadva Használt %1$s hiba: %2$s - Biztos vagy benne\? + Biztos vagy benne? Kategória üres Ez eltávolítja az eddig kiválasztott befejezési dátumot a(z) %s szolgáltatásból Olvasási idő @@ -672,7 +672,7 @@ Felkapott A frissités már folyamatban van %s hibába ütközött. A hiba üzenetet kérünk oszd meg velünk a Discord szerverünkön. - Dátum eltávolítása\? + Dátum eltávolítása? Applikáció újrainditása Globális frissités *kötelező diff --git a/i18n/src/commonMain/resources/MR/in/strings.xml b/i18n/src/commonMain/resources/MR/in/strings.xml index ab9e07461..286fe0752 100644 --- a/i18n/src/commonMain/resources/MR/in/strings.xml +++ b/i18n/src/commonMain/resources/MR/in/strings.xml @@ -135,7 +135,7 @@ Frekuensi pencadangan otomatis Cadangan dibuat Pemulihan selesai - Apa saja yang ingin Anda cadangkan\? + Apa saja yang ingin Anda cadangkan? Memulihkan cadangan Membuat cadangan Hapus cache bab @@ -146,7 +146,7 @@ Cookies dihapus Hapus database Hapus riwayat untuk entri yang tidak disimpan di perpustakaan Anda - Apakah Anda yakin\? Bab yang sudah dibaca dan kemajuan entri non-perpustakaan akan hilang + Apakah Anda yakin? Bab yang sudah dibaca dan kemajuan entri non-perpustakaan akan hilang Entri dihapus Versi Kirim laporan kerusakan @@ -164,7 +164,7 @@ Tak diketahui Berlisensi Hapus dari pustaka - Hapus bab terunduh\? + Hapus bab terunduh? Bab %1$s Mengunduh (%1$d/%2$d) Kesalahan @@ -175,7 +175,7 @@ Nomor bab Unduh Belum dibaca - Yakin ingin menghapus bab yang dipilih\? + Yakin ingin menghapus bab yang dipilih? Pelacakan Membaca Selesai @@ -187,9 +187,9 @@ Status Kategori dengan nama ini sudah ada! Kategori dihapus - Ini akan menghapus tanggal baca bab ini. Apakah Anda yakin\? + Ini akan menghapus tanggal baca bab ini. Apakah Anda yakin? Setel ulang semua bab untuk entri ini - Tambahkan ke pustaka\? + Tambahkan ke pustaka? Gambar disimpan Filter khusus Terapkan sebagai sampul @@ -197,7 +197,7 @@ Halaman: %1$d Bab berikutnya tak ditemukan Gambar tidak dapat dimuat - Gunakan gambar ini sebagai sampul\? + Gunakan gambar ini sebagai sampul? Tidak dapat mengunduh bab. Anda bisa mencoba lagi di bagian unduhan Bab baru ditemukan Gagal memperbarui sampul @@ -321,7 +321,7 @@ Optimalisasi baterai sudah dimatikan Alamat surel Amankan layar - Keluar dari %1$s\? + Keluar dari %1$s? Tampilan Bab %1$s Bab %1$s dan %2$d lainnya @@ -413,7 +413,7 @@ Pengaturan bab bawaan diperbarui Terapkan sebagai bawaan Terapkan pula untuk semua entri di pustaka saya - Apakah anda yakin ingin menyimpan pengaturan ini sebagai bawaan\? + Apakah anda yakin ingin menyimpan pengaturan ini sebagai bawaan? Pengaturan bab %1$s: %2$s, halaman %3$d Cari pengaturan @@ -422,7 +422,7 @@ Jeda riwayat membaca Mode penyamaran Hapus riwayat - Apakah anda yakin\? Semua riwayat akan terhapus. + Apakah anda yakin? Semua riwayat akan terhapus. Riwayat telah dihapus Sumber NSFW (18+) Halaman selanjutnya @@ -604,7 +604,7 @@ Bahasa aplikasi lavender Tidak ada deskripsi - Apakah anda ingin menghapus kategori \"%s\"\? + Apakah anda ingin menghapus kategori \"%s\"? InternalError: Periksa log kerusakan untuk informasi lebih lanjut Hapus kategori String agen pengguna default @@ -622,7 +622,7 @@ Kamu akan menghapus \"%s\" dari pustaka Pustaka terakhir diperbarui: %s Semua - Apa kamu yakin\? + Apa kamu yakin? Populer Izin penyimpanan tidak diberikan Dilewati karena seri ini tidak memerlukan pembaruan @@ -649,7 +649,7 @@ Build F-Droid tidak didukung secara resmi. \n Ketuk untuk mempelajari lebih lanjut. Tombol lanjutkan membaca - Hapus tanggal\? + Hapus tanggal? Membatalkan indeks unduhan Paksa aplikasi untuk mengecek ulang bab ter-unduh Ini akan menghapus tanggal mulai yang Anda pilih sebelumnya dari %s @@ -682,7 +682,7 @@ Melewati bab terduplikasi Kamu memiliki entri di pustaka dengan nama yang sama. \n -\nApakah kamu ingin melanjutkan\? +\nApakah kamu ingin melanjutkan? %1$s eror: %2$s *dibutuhkan Semua catatan tersembunyi sudah ada di perpustakaan @@ -706,7 +706,7 @@ Prediksikan waktu rilis selanjutnya Perkirakan setiap Atur untuk memperbarui setiap - Hapus %s pelacakan\? + Hapus %s pelacakan? Oke Ini akan menghapus pelacakan lokal. Hapus juga dari %s @@ -728,7 +728,7 @@ \"%1$s\" seharusnya \"%2$s\" Urutkan kategori Memperbarui pustaka... (%s) - Apakah Anda ingin mengurutkan kategori menurut abjad\? + Apakah Anda ingin mengurutkan kategori menurut abjad? Tidak ada file yang dipilih Pengaturan sumber Pengaturan aplikasi diff --git a/i18n/src/commonMain/resources/MR/it/strings.xml b/i18n/src/commonMain/resources/MR/it/strings.xml index ad504b170..b147c58b4 100644 --- a/i18n/src/commonMain/resources/MR/it/strings.xml +++ b/i18n/src/commonMain/resources/MR/it/strings.xml @@ -120,7 +120,7 @@ Cookie cancellati Pulisci database Elimina la cronologia delle voci che non sono presenti nella libreria - Sei sicuro\? I capitoli letti e i progressi di lettura di voci fuori dalla libreria verranno persi + Sei sicuro? I capitoli letti e i progressi di lettura di voci fuori dalla libreria verranno persi Elementi eliminati Versione Invia segnalazioni crash @@ -145,7 +145,7 @@ Per numero di capitolo Scarica Non letti - Sei sicuro di voler eliminare i capitoli selezionati\? + Sei sicuro di voler eliminare i capitoli selezionati? In lettura Completato Abbandonato @@ -156,7 +156,7 @@ Stato Capitoli Esiste già una categoria con questo nome! - Questo rimuoverà la data lettura di questo capitolo. Sei sicuro\? + Questo rimuoverà la data lettura di questo capitolo. Sei sicuro? Reimposta tutti i capitoli di questa voce Immagine salvata Filtro personalizzato @@ -165,7 +165,7 @@ Pagina: %1$d Capitolo successivo non trovato Non è stato possibile caricare l\'immagine - Usare questa immagine come copertina\? + Usare questa immagine come copertina? Impossibile scaricare i capitoli. Puoi riprovare nella sezione download Nuovi capitoli trovati Impossibile aggiornare la copertina @@ -210,7 +210,7 @@ Frequenza dei backup automatici Backup creato Ripristino completato - Di cosa vuoi fare il backup\? + Di cosa vuoi fare il backup? Ripristino backup Creazione backup Nessun altro risultato @@ -218,7 +218,7 @@ Eliminare i capitoli scaricati? In pausa Tracking - Aggiungere alla libreria\? + Aggiungere alla libreria? Download in pausa Migra Estensioni @@ -290,7 +290,7 @@ Questa estensione non è più disponibile. Potrebbe non funzionare correttamente e causare problemi con l\'app. Si consiglia di disinstallarla. Formato data Aggiornamenti globali - Disconnettersi da %1$s\? + Disconnettersi da %1$s? Disconnetti Ora sei disconnesso In pausa @@ -416,7 +416,7 @@ %1$s: %2$s, pagina %3$d Imposta come predefinito Applica anche a tutte le voci nella mia libreria - Sei sicuro di voler salvare queste impostazioni come predefinite\? + Sei sicuro di voler salvare queste impostazioni come predefinite? Impostazioni del capitolo Ricerca impostazioni Capitoli scaricati @@ -424,7 +424,7 @@ Sospendi cronologia di lettura Modalità incognito Svuota cronologia - Sei sicuro\? Tutta la cronologia verrà persa. + Sei sicuro? Tutta la cronologia verrà persa. Cronologia cancellata Pagina successiva Pagina precedente @@ -607,7 +607,7 @@ Nessuna descrizione Lavanda Errore Interno: Controllare i registri dei crash per ulteriori informazioni - Vuoi eliminare la categoria «%s»\? + Vuoi eliminare la categoria «%s»? Elimina categoria Ripristina la stringa «user agent» del browser Stringa «user agent» del browser @@ -621,7 +621,7 @@ Scarica i prossimi Download automatico durante la lettura Funziona se il capitolo corrente e quello successivo sono già stati scaricati. - Sei sicuro\? + Sei sicuro? Multiple Ultimo aggiornamento libreria: %s Stai per rimuovere «%s» dalla tua libreria @@ -651,7 +651,7 @@ Le versioni F-Droid non sono ufficialmente supportate. \nToccare per saperne di più. Pulsante \"continua a leggere\" - Rimuovere la data\? + Rimuovere la data? Verrà rimossa la data di fine precedentemente selezionata da %s Verrà rimossa la data di inizio precedentemente selezionata da %s Invalida l\'indice dei download @@ -684,7 +684,7 @@ Salta i capitoli duplicati Hai già una voce in libreria con lo stesso nome. \n -\nVuoi comunque continuare\? +\nVuoi comunque continuare? %1$s errore: %2$s *obbligatorio Nascondi le voci già in libreria @@ -708,7 +708,7 @@ Prossimo aggiornamento previsto Imposta l\'aggiornamento ogni Saltato perché oggi non era previsto alcun rilascio - Rimuovere il tracking di %s\? + Rimuovere il tracking di %s? Rimuovi anche da %s OK Questo rimuoverà il tracciamento locale. @@ -732,7 +732,7 @@ Impostazioni delle fonti Aggiornando libreria... (%s) Ordinamento categorie - Vuoi ordinare le categorie alfabeticamente\? + Vuoi ordinare le categorie alfabeticamente? Il selettore di file ha restituito file all\'app Dati e archiviazione Scegli una cartella diff --git a/i18n/src/commonMain/resources/MR/ja/strings.xml b/i18n/src/commonMain/resources/MR/ja/strings.xml index 800ceb8fd..9add17edd 100644 --- a/i18n/src/commonMain/resources/MR/ja/strings.xml +++ b/i18n/src/commonMain/resources/MR/ja/strings.xml @@ -705,7 +705,7 @@ 間隔 今日、連載更新が予想されていないためスキップしました 結果あり - %s の追跡を削除しますか\? + %s の追跡を削除しますか? 毎に評価 ごとに更新するように設定する ローカルの追跡が削除されます。 diff --git a/i18n/src/commonMain/resources/MR/jv/strings.xml b/i18n/src/commonMain/resources/MR/jv/strings.xml index 1592a2048..852bedcf8 100644 --- a/i18n/src/commonMain/resources/MR/jv/strings.xml +++ b/i18n/src/commonMain/resources/MR/jv/strings.xml @@ -262,7 +262,7 @@ Pamisahan kaca kaping pindho Nganyari otomatis watesan piranti Setelan saben kategori kanggo ngurutake lan tampilan - Apa sampeyan pengin mbusak kategori \"%s\"\? + Apa sampeyan pengin mbusak kategori \"%s\"? Busak kategori Tampilake mode saiki kanthi ringkes nalika maca dibukak InternalError: Priksa log kacilakan kanggo informasi luwih lengkap diff --git a/i18n/src/commonMain/resources/MR/ka-rGE/strings.xml b/i18n/src/commonMain/resources/MR/ka-rGE/strings.xml index fc98436a2..6b840fa85 100644 --- a/i18n/src/commonMain/resources/MR/ka-rGE/strings.xml +++ b/i18n/src/commonMain/resources/MR/ka-rGE/strings.xml @@ -193,7 +193,7 @@ აღდგენა შესრულებულია %02d წუთი, %02d წამი მარქაფი უკვე მიმდინარეობს - რას გსურს რომ შეუქმნა რეზერვი\? + რას გსურს რომ შეუქმნა რეზერვი? რეზერვის შექმნა რეზერვის შექმნა ვერ მოხერხდა აღდგენა უკვე მიმდინარეობს @@ -209,7 +209,7 @@ Cookie -ები გასუფთავებულია მონაცემთა ბაზის გასუფთავება ისტორიიდან წაიშლება ჩანაწერები, რომლებიც თქვენს ბიბლიოთეკაში არაა შენახული - დარწმუნებული ბრძანდებით\? წაკითხული თავები და ბიბლიოთეკაში არ არსებული ჩანაწერების პროგრესი დაიკარგება + დარწმუნებული ბრძანდებით? წაკითხული თავები და ბიბლიოთეკაში არ არსებული ჩანაწერების პროგრესი დაიკარგება ჩანაწერები წაშლილია ბიბლიოთეკის მანგების ყდის ცვლილება ელემენტის ოპტიმიზაცუა უკვე გამორთულია @@ -251,11 +251,11 @@ წაშლილია ბიბლიოთეკიდან მეტი ინფორმაციის ჩვენება ნაკლები ინფორმაციის ჩვენება - წაიშალოს გადმოწერილი თავები\? + წაიშალოს გადმოწერილი თავები? დაკოპირებულია ბუფერში: \n%1$s წყარო არ არის დაინსტალირებული: %1$s - დავამატო ბიბლიოთეკაში\? + დავამატო ბიბლიოთეკაში? თავი %1$s მიმდინარეობს გადმოწერა (%1$d/%2$d) შეცდომა diff --git a/i18n/src/commonMain/resources/MR/kk/strings.xml b/i18n/src/commonMain/resources/MR/kk/strings.xml index 795deab72..eb5fa9e7d 100644 --- a/i18n/src/commonMain/resources/MR/kk/strings.xml +++ b/i18n/src/commonMain/resources/MR/kk/strings.xml @@ -254,7 +254,7 @@ Оқылуда Тарау %1$s - %2$s Оқылды деп белгіленген тарауларды өткізіп жіберу - «%s» деген санатты жойғыңыз келеді ме\? + «%s» деген санатты жойғыңыз келеді ме? Санатты жою Ішкі қателік: Қосымша ақпарат алу үшін қателіктер тіркеуін тексеріңіз Лаванда @@ -369,7 +369,7 @@ Қалпына келтіру дайын Әдепкі қалпы бойынша user agent Кірілді - %1$s-ден шығасыз ба\? + %1$s-ден шығасыз ба? Жадыға рұқсаттар берілмеді Жүктелген тараулар Бекітілген @@ -393,7 +393,7 @@ Басылымы яқталды Кітапханаға қосу Қателер тіркеулерімен бөлісу - Сенімдісіз бе\? Кітапханадағы емес жазбалардың оқылған тараулары мен прогрессі жоғалады + Сенімдісіз бе? Кітапханадағы емес жазбалардың оқылған тараулары мен прогрессі жоғалады Жазбалар жойылды Тазалайтын түк жоқ Туындылар мұқабасын жаңарту @@ -409,7 +409,7 @@ Күн Шығып жатыр Қалпына келтіру әлдеқашан орындалуда - Сақтық көшірмесіне нені қалайсыз\? + Сақтық көшірмесіне нені қалайсыз? Сақтық көшірмесін қалпына келтіру Сақтық көшірмесі әлдеқашан орындалуда Оқыма баптауы қалпына келтірілмеді @@ -497,7 +497,7 @@ Сіздің кітапханаңызда санаттар жоқ. Оқылуда Тараулар жоқ - Сенімдісіз бе\? + Сенімдісіз бе? Оқылмаған Кітапхана жаңаруда Ақырғы оқылған тарау ашылмай тұр @@ -509,7 +509,7 @@ Дереккөз қолжетімсіз Сурет сақталмады Келесі: - Сенімдісіз бе\? Бар тарих жоғалады. + Сенімдісіз бе? Бар тарих жоғалады. Дереккөз табылмады %1$d жаңарту сәтсіз өтті Мұқаба жаңартылмады @@ -529,7 +529,7 @@ Оқылмаған Мұқаба жіберілмеді Мұқаба сақталды - Бұл баптауды әдепкі қылып сақтағыңыз келеді ме\? + Бұл баптауды әдепкі қылып сақтағыңыз келеді ме? Көбірек білу үшін басу Жүктеп алу Ортақ @@ -562,7 +562,7 @@ Қазіргі: %1$s: %2$s, бет %3$d Бет: %1$d - Осы суретті мұқаба ретінде қолданайық па\? + Осы суретті мұқаба ретінде қолданайық па? Дереккөз атауы Бұл жазба үшін түгел тарауларды қалпына келтіру Санаттар жойылды @@ -600,7 +600,7 @@ Күйі Басталған күні Аяқталған күні - Бұл осы тараудың оқылған уақытын жояды. Сенімдісіз бе\? + Бұл осы тараудың оқылған уақытын жояды. Сенімдісіз бе? Беттер жүктелуде… Аяқталған: Алдыңғы: @@ -619,7 +619,7 @@ Жеке мұқаба Бағдарламамен қолданып бастау үшін нұсқаулық Дереккөз бойынша - Таңдалған тараулардың жойылуын қалайсыз ба\? + Таңдалған тараулардың жойылуын қалайсыз ба? Жоспарланған Дереккөздерді тасымалдау нұсқаулары Wi-Fi байланысы жоқ @@ -632,12 +632,12 @@ Жаңа тараулар табылды Автоматты жүктеу, алдын ала жүктеу Көбірек - Жүктелген тарауларды жоямыз ба\? + Жүктелген тарауларды жоямыз ба? Буферге көшірілді: \n%1$s Буферге көшірілмеді Дереккөз орнатылмаған: %1$s - Кітапханаға қосайық па\? + Кітапханаға қосайық па? Әдепкі қылып орнату Беттер табылмады Келесі тарау жоқ @@ -655,7 +655,7 @@ «Оқуды жалғастыру» батырмасы Бұл %s ішінен бұрын таңдалған аяқталу күнін жояды Бұл бұрын таңдалған басталу күнін %s ішінен жояды - Күнің жоямыз ба\? + Күнің жоямыз ба? Жүктеулер индексін тазалау Қолданбаны жүктеп алынған тарауларды қайта тексеруге мәжбүрлейді Статистика @@ -686,7 +686,7 @@ Қайталанған бөлімдерді өткізіп жіберу Сіздің кітапханаңызда осымен атауы бірдей жазба бар \n -\nБәрібір жалғастыра бересіз бе\? +\nБәрібір жалғастыра бересіз бе? %1$s қателік: %2$s *қажетті Санатты жаңарту diff --git a/i18n/src/commonMain/resources/MR/kn/strings.xml b/i18n/src/commonMain/resources/MR/kn/strings.xml index fb0b0d9ea..0c8c7c56f 100644 --- a/i18n/src/commonMain/resources/MR/kn/strings.xml +++ b/i18n/src/commonMain/resources/MR/kn/strings.xml @@ -264,7 +264,7 @@ ಮುಗಿದಿದೆ: ಓದುವಿಕೆ ರೀತಿ ಈ ಸರಣಿಗಾಗಿ - ಈ ಚಿತ್ರವನ್ನು ಕವರ್ ಆರ್ಟ್ ಆಗಿ ಬಳಸುವುದೇ\? + ಈ ಚಿತ್ರವನ್ನು ಕವರ್ ಆರ್ಟ್ ಆಗಿ ಬಳಸುವುದೇ? ಈ ಚಿತ್ರವನ್ನು ಲೋಡ್ ಮಾಡಲಾಗಲಿಲ್ಲ ಮುಂದಿನ ಅಧ್ಯಾಯ ಕಂಡುಬಂದಿಲ್ಲ ಪುಟ: %1$d @@ -273,7 +273,7 @@ ಕಸ್ಟಮ್ ಫಿಲ್ಟರ್ ಚಿತ್ರ ಉಳಿಸಲಾಗಿದೆ ಈ ಮಾಂಗಾದ ಎಲ್ಲಾ ಅಧ್ಯಾಯಗಳನ್ನು ಮರುಹೊಂದಿಸಿ - ಇದು ಈ ಅಧ್ಯಾಯದ ಓದುವ ದಿನಾಂಕವನ್ನು ತೆಗೆದುಹಾಕುತ್ತದೆ. ನೀವು ಖಚಿತವಾಗಿರುವಿರಾ\? + ಇದು ಈ ಅಧ್ಯಾಯದ ಓದುವ ದಿನಾಂಕವನ್ನು ತೆಗೆದುಹಾಕುತ್ತದೆ. ನೀವು ಖಚಿತವಾಗಿರುವಿರಾ? ವರ್ಗಗಳನ್ನು ಅಳಿಸಲಾಗಿದೆ ಈ ಹೆಸರಿನ ವರ್ಗವು ಈಗಾಗಲೇ ಅಸ್ತಿತ್ವದಲ್ಲಿದೆ! ಮಾದರಿ @@ -290,7 +290,7 @@ ಓದುತ್ತಿರುವ ಟ್ರ್ಯಾಕಿಂಗ್ ಸೇರಿಸಿ ಟ್ರ್ಯಾಕಿಂಗ್ - ಆಯ್ದ ಅಧ್ಯಾಯಗಳನ್ನು ಅಳಿಸಲು ನೀವು ಖಚಿತವಾಗಿ ಬಯಸುವಿರಾ\? + ಆಯ್ದ ಅಧ್ಯಾಯಗಳನ್ನು ಅಳಿಸಲು ನೀವು ಖಚಿತವಾಗಿ ಬಯಸುವಿರಾ? ಓದದ ಡೌನ್‌ಲೋಡ್ ಅಧ್ಯಾಯದ ಸಂಖ್ಯೆಯ ಪ್ರಕಾರ @@ -301,11 +301,11 @@ ದೋಷ ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ (%1$d/%2$d) ಅಧ್ಯಾಯ %1$s - ಮಾಂಗಾವನ್ನು ಗ್ರಂಥಾಲಯಕ್ಕೆ ಸೇರಿಸುವುದೇ\? + ಮಾಂಗಾವನ್ನು ಗ್ರಂಥಾಲಯಕ್ಕೆ ಸೇರಿಸುವುದೇ? ಮೂಲವನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ: %1$s ಕ್ಲಿಪ್‌ಬೋರ್ಡ್‌ಗೆ ನಕಲಿಸಲಾಗಿದೆ: \n%1$s - ಡೌನ್‌ಲೋಡ್ ಮಾಡಿದ ಅಧ್ಯಾಯಗಳನ್ನು ಅಳಿಸುವುದೇ\? + ಡೌನ್‌ಲೋಡ್ ಮಾಡಿದ ಅಧ್ಯಾಯಗಳನ್ನು ಅಳಿಸುವುದೇ? ಕಡಿಮೆ ಹೆಚ್ಚು ಗ್ರಂಥಾಲಯದಿಂದ ತೆಗೆದುಹಾಕಲಾಗಿದೆ @@ -330,7 +330,7 @@ ಅಜ್ಞಾತ ದೋಷ ನೀವು ಈಗ ಲಾಗ್ ಔಟ್ ಆಗಿದ್ದೀರಿ ಲಾಗ್ ಔಟ್ - %1$s ನಿಂದ ಲಾಗ್ ಔಟ್ ಆಗುವುದೇ\? + %1$s ನಿಂದ ಲಾಗ್ ಔಟ್ ಆಗುವುದೇ? ಲಾಗ್ ಇನ್ ಮಾಡಲಾಗಿದೆ ಲಾಗಿನ್ ಪಾಸ್ ವರ್ಡ್ @@ -351,7 +351,7 @@ ಬ್ಯಾಟರಿ ಆಪ್ಟಿಮೈಸೇಶನ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ ಗ್ರಂಥಾಲಯದ ಮಾಂಗಾ ಕವರ್‌ಗಳನ್ನು ರಿಫ್ರೆಶ್ ಮಾಡಿ ನಮೂದುಗಳನ್ನು ಅಳಿಸಲಾಗಿದೆ - ನೀವು ಖಚಿತವಾಗಿರುವಿರಾ\? ಅಧ್ಯಾಯಗಳು ಗ್ರಂಥಾಲಯೇತರ ಮಾಂಗಾದ ಓದು ಮತ್ತು ಪ್ರಗತಿ ಕಳೆದುಹೋಗುತ್ತದೆ + ನೀವು ಖಚಿತವಾಗಿರುವಿರಾ? ಅಧ್ಯಾಯಗಳು ಗ್ರಂಥಾಲಯೇತರ ಮಾಂಗಾದ ಓದು ಮತ್ತು ಪ್ರಗತಿ ಕಳೆದುಹೋಗುತ್ತದೆ ನಿಮ್ಮ ಗ್ರಂಥಾಲಯದಲ್ಲಿ ಇಲ್ಲದ ಮಾಂಗಾದ ಇತಿಹಾಸವನ್ನು ಅಳಿಸಿ ಡೇಟಾಬೇಸ್ ತೆರವುಗೊಳಿಸಿ ಕುಕೀಗಳನ್ನು ತೆರವುಗೊಳಿಸಲಾಗಿದೆ @@ -366,7 +366,7 @@ ಮರುಸ್ಥಾಪನೆ ಈಗಾಗಲೇ ಪ್ರಗತಿಯಲ್ಲಿದೆ ಬ್ಯಾಕಪ್ ವಿಫಲವಾಗಿದೆ ಬ್ಯಾಕಪ್ ರಚಿಸಲಾಗುತ್ತಿದೆ - ನೀವು ಏನು ಬ್ಯಾಕಪ್ ಮಾಡಲು ಬಯಸುತ್ತೀರಿ\? + ನೀವು ಏನು ಬ್ಯಾಕಪ್ ಮಾಡಲು ಬಯಸುತ್ತೀರಿ? ಬ್ಯಾಕಪ್ ಈಗಾಗಲೇ ಪ್ರಗತಿಯಲ್ಲಿದೆ ಅಪ್‌ಲೋಡ್ ದಿನಾಂಕದ ಮೂಲಕ ಡೇಟಾ @@ -413,7 +413,7 @@ ಯಾವುದೇ ಅಧ್ಯಾಯಗಳು ಕಂಡುಬಂದಿಲ್ಲ ಡೀಫಾಲ್ಟ್ ಆಗಿ ಹೊಂದಿಸಿ ನನ್ನ ಗ್ರಂಥಾಲಯದಲ್ಲಿರುವ ಎಲ್ಲಾ ಮಾಂಗಾಗಳಿಗೂ ಅನ್ವಯಿಸಿ - ಈ ಸೆಟ್ಟಿಂಗ್ ಗಳನ್ನು ಡೀಫಾಲ್ಟ್ ರೂಪದಲ್ಲಿ ಉಳಿಸಲು ನೀವು ಖಚಿತವಾಗಿ ಬಯಸುವಿರಾ\? + ಈ ಸೆಟ್ಟಿಂಗ್ ಗಳನ್ನು ಡೀಫಾಲ್ಟ್ ರೂಪದಲ್ಲಿ ಉಳಿಸಲು ನೀವು ಖಚಿತವಾಗಿ ಬಯಸುವಿರಾ? ಅಧ್ಯಾಯದ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಡೌನ್‌ಲೋಡ್ ಮಾಡಿದ ಅಧ್ಯಾಯಗಳು ಗ್ರಂಥಾಲಯದಿಂದ ಮಾಂಗಾ @@ -431,7 +431,7 @@ ಓದುವ ಇತಿಹಾಸವನ್ನು ವಿರಾಮಗೊಳಿಸುತ್ತದೆ ಅಜ್ಞಾತ ಮೋಡ್ ಇತಿಹಾಸವನ್ನು ತೆರವುಗೊಳಿಸಿ - ನೀವು ಖಚಿತವಾಗಿರುವಿರಾ\? ಎಲ್ಲಾ ಇತಿಹಾಸವೂ ಕಳೆದುಹೋಗುತ್ತದೆ. + ನೀವು ಖಚಿತವಾಗಿರುವಿರಾ? ಎಲ್ಲಾ ಇತಿಹಾಸವೂ ಕಳೆದುಹೋಗುತ್ತದೆ. ಇತಿಹಾಸವನ್ನು ಅಳಿಸಲಾಗಿದೆ ಮೂಲಗಳ ಪಟ್ಟಿಯಲ್ಲಿ ತೋರಿಸಿ ವಯಸ್ಕ (18+) ಮೂಲಗಳು diff --git a/i18n/src/commonMain/resources/MR/ko/strings.xml b/i18n/src/commonMain/resources/MR/ko/strings.xml index 83e27351b..a01a42c1f 100644 --- a/i18n/src/commonMain/resources/MR/ko/strings.xml +++ b/i18n/src/commonMain/resources/MR/ko/strings.xml @@ -167,7 +167,7 @@ 서재에서 제거 서재에 추가되었습니다 서재에서 제거되었습니다 - 다운로드된 회차를 삭제하시겠습니까\? + 다운로드된 회차를 삭제하시겠습니까? 소스 미설치: %1$s %1$s화 다운로드 중 (%1$d/%2$d) @@ -178,16 +178,16 @@ 회차 번호 기준 다운로드 읽지 않은 회차 - 선택된 회차를 삭제하시겠습니까\? + 선택된 회차를 삭제하시겠습니까? 동기화 평점 제목 상태 상태 카테고리 삭제됨 - 이 회차의 읽은 기록을 삭제합니다. 계속하시겠습니까\? + 이 회차의 읽은 기록을 삭제합니다. 계속하시겠습니까? 모든 회차 기록 초기화 - 서재에 추가합니까\? + 서재에 추가합니까? 사진 저장됨 이미지 필터 표지로 설정 @@ -195,7 +195,7 @@ 페이지: %1$d 다음 화가 없습니다 이미지를 로드할 수 없습니다 - 이 이미지를 표지로 사용합니까\? + 이 이미지를 표지로 사용합니까? 완료: 현재: 다음: @@ -243,7 +243,7 @@ 끝에서 세번째 회차 끝에서 네번째 회차 끝에서 다섯번째 회차 - 어떤 항목들을 백업하겠습니까\? + 어떤 항목들을 백업하겠습니까? 항목 삭제됨 로컬 저장소 기타 @@ -286,7 +286,7 @@ 더 보기 소스 완료 - 현재 설정을 기본값으로 설정하시겠습니까\? + 현재 설정을 기본값으로 설정하시겠습니까? 회차 설정 표지 공유 오류 표지 저장 오류 @@ -295,7 +295,7 @@ 업로드 날짜 기준 로그아웃 되었습니다 로그아웃 - %1$s 에서 로그아웃 하시겠습니까\? + %1$s 에서 로그아웃 하시겠습니까? 시크릿 모드 끄기 다운로드가 완료된 항목만 표시 시크릿 모드 @@ -436,7 +436,7 @@ 개발자와 공유할 수 있는 오류 로그 파일을 생성합니다 개인정보 보호 정책 %1$s: %2$s, %3$d페이지 - 계속하시겠습니까\? 모든 기록이 삭제됩니다. + 계속하시겠습니까? 모든 기록이 삭제됩니다. 비활성화 없어진 소스: 로그인 되지않은 트래커: @@ -591,7 +591,7 @@ 설치되지 않음 라벤더 무제한 네트워크에서만 - \"%s\" 카테고리를 삭제하시겠습니까\? + \"%s\" 카테고리를 삭제하시겠습니까? 카테고리 삭제 내부 오류: 더 자세한 정보를 보고 싶으면 충돌 로그를 확인하세요 요약 없음 @@ -624,7 +624,7 @@ 마지막 서재 업데이트: %s 현재 회차 + 다음 회차가 이미 다운로드된 경우에만 작동됩니다. 인기 - 정말로 실행합니까\? + 정말로 실행합니까? 만화에 업데이트가 필요하지 않음으로 건너뜀 검색… 카테고리, 전역 업데이트, 회차 스와이프 @@ -651,7 +651,7 @@ 계속 읽기 버튼 %s에서 이전에 선택한 완료 날짜가 제거됩니다 다운로드 인덱스 제거 - 날짜를 제거 하시겠습니까\? + 날짜를 제거 하시겠습니까? %s에서 이전에 선택한 시작 날짜가 제거됩니다 앱이 다운로드한 회차를 다시 확인하도록 강제 완료된 항목 @@ -683,7 +683,7 @@ %1$s 오류: %2$s 서재에 같은 이름의 항목이 있습니다. \n -\n그래도 계속하시겠습니까\? +\n그래도 계속하시겠습니까? *필수 클립보드에 복사 이미 서재에 있는 항목 숨기기 @@ -707,7 +707,7 @@ %s에서도 제거 OK 오늘 연재가 예상되지 않았기 때문에 건너뛰었습니다 - %s 동기화를 삭제 하시겠습니까\? + %s 동기화를 삭제 하시겠습니까? 다음 연재 기간 예측 다운로드 인덱스를 제거함 다음 업데이트 예정 @@ -726,7 +726,7 @@ %s에 연결할 수 없습니다 카테고리 정렬 서재 업데이트 중...(%s) - 카테고리를 알파벳 순으로 정렬하시겠습니까\? + 카테고리를 알파벳 순으로 정렬하시겠습니까? 소스 설정 앱 설정 확장 앱 설치를 위해 필요합니다. diff --git a/i18n/src/commonMain/resources/MR/lt/strings.xml b/i18n/src/commonMain/resources/MR/lt/strings.xml index 6aa6d7130..056b4a1b7 100644 --- a/i18n/src/commonMain/resources/MR/lt/strings.xml +++ b/i18n/src/commonMain/resources/MR/lt/strings.xml @@ -342,8 +342,8 @@ Istorija ištrinta Nepavyko pakrauti paveikslėlio Puslapių nerasta - Ar tu tuo tikras\? Visa istorija bus prarasta. - Naudoti šį paveiksliuką kaip viršelį\? + Ar tu tuo tikras? Visa istorija bus prarasta. + Naudoti šį paveiksliuką kaip viršelį? Priskirti šiai serijai Baigta: Dabartinis: @@ -383,7 +383,7 @@ Nustatyti kaip viršelį Pristabdyta Šaltinis neįdiegtas: %1$s - Pridėti į biblioteką\? + Pridėti į biblioteką? Тipas Pradžios data Skyrius %1$s @@ -400,14 +400,14 @@ Šaltinio pavadinimas Iš bibliotekos Prašome dar kartą prisijungti prie MAL - Tai pašalins šio skyriaus skaitymo datą. Ar tu tuo tikras\? - Atsijungti nuo %1$s\? + Tai pašalins šio skyriaus skaitymo datą. Ar tu tuo tikras? + Atsijungti nuo %1$s? Šaltinis nepalaikomas Atsijungti Ištrinti kategoriją Neskaitytų skaičius Paskutinis atnaujinimo patikrinimas - Ar tikrai norite ištrinti kategoriją \"%s\"\? + Ar tikrai norite ištrinti kategoriją \"%s\"? Levandos Uždaryti Programos kalba @@ -431,7 +431,7 @@ Galima naudoti dabartinei bibliotekai atkurti Šoninis spaudinėjimas Nėra bibliotekos įrašų, kurių atsargines kopijas būtų galima sukurti - Ar tikrai\? Perskaityti skyriai ir nebibliotekinių įrašų progresas bus prarastas + Ar tikrai? Perskaityti skyriai ir nebibliotekinių įrašų progresas bus prarastas Įrašyti strigčių žurnalus į failą, kad būtų galima juos bendrinti su kūrėjais Atsarginės kopijos kūrimas DNS per HTTPS (DoH) @@ -454,7 +454,7 @@ %1$d nebibliotekiniai įrašai duomenų bazėje Nepavyko atkurti atsarginės kopijos Tinklas - Ką norite įtraukti į atsarginę kopiją\? + Ką norite įtraukti į atsarginę kopiją? Ištrinti įrašai Išvalyti skyriaus talpyklą Naudota: %1$s @@ -523,7 +523,7 @@ Siųsti strigčių ataskaitas Planuojama skaityti Daugiau rezultatų nėra - Ar tikrai norite ištrinti pasirinktus skyrius\? + Ar tikrai norite ištrinti pasirinktus skyrius? Užbaigta Ieškoti programos atnaujinimų Privatumo politika @@ -563,7 +563,7 @@ Netinkamas skyriaus formatas Kita Licencijuotas - Ištrinti atsisiųstus skyrius\? + Ištrinti atsisiųstus skyrius? Nežinoma Nežinomas autorius Nežinoma būsena @@ -591,7 +591,7 @@ Individualus viršelis Įvyko klaida bendrinant viršelį Skyriaus nustatymai - Ar tikrai norite išsaugoti šiuos nustatymus kaip numatytuosius\? + Ar tikrai norite išsaugoti šiuos nustatymus kaip numatytuosius? Pridėti stebėjimą Neskaityta Sulaikytas @@ -643,7 +643,7 @@ Nežinomas pavadinimas Ką tik %s susidūrė su netikėta klaida. Siūlome padaryti šio pranešimo ekrano nuotrauką, išsisaugoti gedimų žurnalus ir pasidalyti tuo mūsų „Discord“ palaikymo kanale. - Ar esate tikri\? + Ar esate tikri? Populiaru Ups! Iš naujo paleiskite programą @@ -654,7 +654,7 @@ Atverti atsitiktinį įrašą Rodyti mygtuką „Tęsti” skaitymą Tai pašalins jūsų anksčiau pasirinktą pabaigos datą iš %s - Pašalinti datą\? + Pašalinti datą? Tai pašalins jūsų anksčiau pasirinktą pradžios datą iš %s Atsisiuntimų indekso panaikinimas Priversti programėlę iš naujo patikrinti atsisiųstus skyrius @@ -686,5 +686,5 @@ Praleisti pasikartojančius skyrius Bibliotekoje jau turite įrašą tokiu pačiu pavadinimu. \n -\nAr vis tiek norite tęsti\? +\nAr vis tiek norite tęsti? \ No newline at end of file diff --git a/i18n/src/commonMain/resources/MR/lv/strings.xml b/i18n/src/commonMain/resources/MR/lv/strings.xml index ea1c7bdf2..9ddae7b85 100644 --- a/i18n/src/commonMain/resources/MR/lv/strings.xml +++ b/i18n/src/commonMain/resources/MR/lv/strings.xml @@ -77,7 +77,7 @@ Atjaunošana jau notiek Dublēšana neizdevās Izveido dublējumu - Ko vēlaties dublēt\? + Ko vēlaties dublēt? Dublēšana jau notiek %02d min, %02d sec Atjaunošana pabeigta @@ -386,7 +386,7 @@ Tīkls Dati Izmantots: %1$s - Vai esi pārliecināts\? Lasītās nodaļas un progress priekš ieraksta, 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 Nav ko tīrīt Atspējot akumulatora optimizāciju Atjaunot bibliotēku no dublējuma faila @@ -432,7 +432,7 @@ Filtrēt visus ierakstus bibliotēkā Vairāk rezultātu nav Pieteicies - Izrakstīties no %1$s\? + Izrakstīties no %1$s? Pieteikties %1$s No bibliotēkas Pēdējo reizi izmantots @@ -484,7 +484,7 @@ Notīrīt vēsturi %1$s: %2$s, lapa %3$d Pievienots bibliotēkai - Pievienot bibliotēkai\? + Pievienot bibliotēkai? Avota nosaukums Vāks Aizturēts @@ -498,7 +498,7 @@ Pabeigts Kļūdas Lūdzu, piesakieties MAL vēlreiz - Tiks noņemts šīs nodaļas lasīšanas datums. Vai esi pārliecināts\? + Tiks noņemts šīs nodaļas lasīšanas datums. Vai esi pārliecināts? Lasīšanas režīms Nākošā nodaļa nav atrasta Nu, šis ir neveikli @@ -507,7 +507,7 @@ Lejupielādē… Progress Attēlu nevarēja ielādēt - Vai izmantot šo attēlu kā vāku\? + Vai izmantot šo attēlu kā vāku? Priekš šīs sērijas Pamests Brīdinājums: liela apjoma lejupielāde var izraisīt to, ka avoti kļūst lēnāki un/vai bloķē Mihon. Pieskarieties, lai uzzinātu vairāk. @@ -568,12 +568,12 @@ 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\? + Vai dzēst lejupielādētās nodaļas? Datums Pārtraukumā Nodaļa %1$s Vāks saglabāts - Vai esat pārliecināts\? Visa vēsture tiks pazaudēta. + Vai esat pārliecināts? Visa vēsture tiks pazaudēta. Nav atrasts avots Neviens instalēts avots nav atrasts Pieskarieties, lai instalētu atjauninājumu @@ -596,12 +596,12 @@ Avots nav atrasts Avota migrācijas rokasgrāmata Migrēt - Vai esat pārliecināts, ka šos iestatījumus vēlaties saglabāt kā noklusējuma\? + Vai esat pārliecināts, ka šos iestatījumus vēlaties saglabāt kā noklusējuma? Dodge / Gaišāks Burn / Tumšāks Pēc augšupielādes datuma Lejupielādēt kļūdu - Vai esat pārliecināts, ka vēlaties dzēst atlasītās nodaļas\? + Vai esat pārliecināts, ka vēlaties dzēst atlasītās nodaļas? Nodaļu iestatījumi Izvēlieties avotu, no kura migrēt Nevarēja lejupielādēt nodaļas. Varat mēģināt vēlreiz lejupielādes sadaļā @@ -621,7 +621,7 @@ Darbojas tikai ja pašreizējā nodaļa + nākošā nodaļa ir jau lejupielādēta. Jūsu bibliotēkā ir ieraksts ar tādu pašu nosaukumu. \n -\nVai joprojām vēlaties turpināt\? +\nVai joprojām vēlaties turpināt? Nederīgs lejupielāžu indekss Pārskats Statistika @@ -640,7 +640,7 @@ %dm %ds Populārs - Vai tu esi pārliecināts\? + Vai tu esi pārliecināts? Tikko Atjaunināt kategoriju Atvērt nejaušu ierakstu @@ -666,7 +666,7 @@ 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\"\? + Vai vēlaties dzēst kategoriju \"%s\"? %1$s kļūda: %2$s Atjauninājums jau darbojas %s radās neparedzēta kļūda. Mēs iesakām dalīties ar avārijas žurnālu mūsu atbalsta kanālā Discord lietotnē. @@ -674,7 +674,7 @@ 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\? + 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 @@ -716,7 +716,7 @@ Intervāli Šis noņems locālo izsekošanu. Izdzēst lejupielādētos - Vai noņemt %s izsekošanu\? + Vai noņemt %s izsekošanu? OK Ārpus paredzamā izlaišanas perioda Dubult-pieskarieties, lai tuvinātu diff --git a/i18n/src/commonMain/resources/MR/mr/strings.xml b/i18n/src/commonMain/resources/MR/mr/strings.xml index 08056d30a..db1bf8a3a 100644 --- a/i18n/src/commonMain/resources/MR/mr/strings.xml +++ b/i18n/src/commonMain/resources/MR/mr/strings.xml @@ -114,10 +114,10 @@ पृष्ठ संक्रमण संजीवित करा कटआउट क्षेत्रात सामग्री दर्शवा हे एक्सटेन्शन उपलब्ध नाही. - हे एक्सटेन्शन एका अविश्वासू प्रमानपत्रासहित आले आहे म्हणून हे स्थापित होऊ शकले नाही. + हे एक्सटेन्शन एका अविश्वासू प्रमानपत्रासहित आले आहे म्हणून हे स्थापित होऊ शकले नाही. \n \nअसे अविश्वासू एक्सटेन्शन आपले लॉगिन बाबत माहिती चोरू शकते. -\n +\n \nव अश्या एक्सटेन्शन वर विश्वास करून तुम्ही ह्या सगद्या जोखीम घेत आहेत. अविश्वासू एक्सटेन्शन विस्थापित करा diff --git a/i18n/src/commonMain/resources/MR/ms/strings.xml b/i18n/src/commonMain/resources/MR/ms/strings.xml index b29446d39..eb4ffe5be 100644 --- a/i18n/src/commonMain/resources/MR/ms/strings.xml +++ b/i18n/src/commonMain/resources/MR/ms/strings.xml @@ -149,7 +149,7 @@ Cookies dihapuskan Hapus pangkalan data Hapus sejarah entri yang tidak disimpan di dalam pustaka - Adakah anda pasti\? Bab dibaca dan kemajuan entri bukan-pustaka akan hilang + Adakah anda pasti? Bab dibaca dan kemajuan entri bukan-pustaka akan hilang Entri dihapuskan Versi Hantar laporan ranap @@ -182,7 +182,7 @@ Mengikut nombor bab Muat turun Belum dibaca - Adakah anda pasti ingin memadamkan bab yang dipilih\? + Adakah anda pasti ingin memadamkan bab yang dipilih? Penjejakan Sedang baca Selesai @@ -196,7 +196,7 @@ Kategori dipadam Tarikh bacaan bab ini akan dipadam. Adakah anda pasti? Set semula semua bab bagi entri ini - Tambah ke pustaka\? + Tambah ke pustaka? Bab dimuat turun Gambar disimpan Tapisan tersuai @@ -205,7 +205,7 @@ Muka surat: %1$d Bab seterusnya tidak dijumpai Imej tidak dapat dimuatkan - Guna imej ini sebagai muka hadapan\? + Guna imej ini sebagai muka hadapan? Memuat turun bab tidak berjaya. Anda boleh mencuba lagi di bahagian muat turun Bab baharu dijumpai Gagal untuk kemas kini muka hadapan @@ -290,7 +290,7 @@ Sambungan ini tidak lagi ada. Ia mungkin tidak berfungsi dengan betul dan boleh menyebabkan masalah dengan aplikasi. Mengenyahpasang adalah disarankan. Format tarikh Kemas kini keseluruhan - Log keluar daripada %1$s\? + Log keluar daripada %1$s? Log keluar Anda telah log keluar Ditangguh @@ -415,7 +415,7 @@ Tetapan bab lalai dikemas kini Tetapkan sebagai lalai Gunakan juga untuk semua entri dalam pustaka - Adakah anda pasti ingin simpan tetapan ini sebagai lalai\? + Adakah anda pasti ingin simpan tetapan ini sebagai lalai? Tetapan bab %1$s: %2$s, muka surat %3$d Carian tetapan @@ -424,7 +424,7 @@ Menjeda sejarah membaca Mod inkognito Padamkan sejarah - Anda pasti\? Semua sejarah akan hilang. + Anda pasti? Semua sejarah akan hilang. Sejarah dipadamkan Muka surat seterusnya Muka surat terdahulu @@ -604,7 +604,7 @@ Bahasa aplikasi Lavender Tiada deskripsi - Adakah anda ingin padam kategori \"%s\"\? + Adakah anda ingin padam kategori \"%s\"? RalatDalam: semak log kerosakan untuk maklumat lanjut Padam kategori Untaian ejen pengguna lalai @@ -619,7 +619,7 @@ Tidal Wave Muat turun maju Hanya berguna sekiranya bab sekarang + seterusnya telah dimuat turun. - Adakah anda pasti\? + Adakah anda pasti? Pelbagai Terakhir pustaka dikemas kini: %s Anda akan membuang \"%s\" ini daripada pustaka anda @@ -650,7 +650,7 @@ \nKetik untuk mengetahui lebih lanjut. Butang sambung membaca Membatalkan indeks muat turun - Buang tarikh\? + Buang tarikh? Ini akan membuang tarikh mula yang dipilih oleh anda sebelum ini dari %s Ini akan membuang tarikh selesai yang dipilih oleh anda sebelum ini dari %s Memaksa aplikasi untuk semak semula bab dimuat turun @@ -683,7 +683,7 @@ %1$s ralat: %2$s Entri di dalam pustaka anda mempunyai nama yang sama. \n -\nAdakah anda ingin meneruskan\? +\nAdakah anda ingin meneruskan? *diperlukan Sembunyikan entri yang sudah ada di dalam pustaka Salin ke papan keratan @@ -706,7 +706,7 @@ Jarak masa Jangka masa keluaran seterusnya Anggaran setiap - Buang penjejakan %s\? + Buang penjejakan %s? Ini akan membuang penjejakan secara lokal. OK Juga buang daripada %s diff --git a/i18n/src/commonMain/resources/MR/nb-rNO/strings.xml b/i18n/src/commonMain/resources/MR/nb-rNO/strings.xml index 05bb16a09..3221848f5 100644 --- a/i18n/src/commonMain/resources/MR/nb-rNO/strings.xml +++ b/i18n/src/commonMain/resources/MR/nb-rNO/strings.xml @@ -156,7 +156,7 @@ Sikkerhetskopieringsfrekvens Sikkerhetskopi opprettet Gjenoppretting fullført - Hva ønsker du å sikkerhetskopiere\? + Hva ønsker du å sikkerhetskopiere? Gjenoppretter sikkerhetskopi Oppretter sikkerhetskopi Tøm kapittelhurtigbuffer @@ -167,7 +167,7 @@ Informasjonskapsler slettet Tøm database Slett historikk for oppføringer som ikke er lagret i biblioteket ditt - Er du sikker\? Leste kapitler og framdrift for oppføringer som ikke er i biblioteket vil gå tapt + Er du sikker? Leste kapitler og framdrift for oppføringer som ikke er i biblioteket vil gå tapt Oppføringer slettet Versjon Send krasjrapporter @@ -191,7 +191,7 @@ Fjern fra bibliotek Lagt til i biblioteket Fjernet fra bibliotek - Slett nedlastede kapitler\? + Slett nedlastede kapitler? Kopiert til utklippstavle: \n%1$s Kilde ikke installert: %1$s @@ -205,7 +205,7 @@ Etter kapittelnummer Last ned Ulest - Er du sikker på at du vil slette de valgte kapitlene\? + Er du sikker på at du vil slette de valgte kapitlene? Sporing Leser Fullført @@ -220,9 +220,9 @@ Type En kategori ved dette navnet finnes allerede! Kategorier slettet - Lesningsdato for dette kapittelet vil bli fjernet. Er du sikker\? + Lesningsdato for dette kapittelet vil bli fjernet. Er du sikker? Tilbakestill alle kapitler for denne oppføringen - Legg til i bibliotek\? + Legg til i bibliotek? Bilde lagret Egendefinert filter Sett som omslag @@ -254,7 +254,7 @@ \nVed å godta denne utvidelsen, aksepterer du disse risikoene. Animasjonshastighet ved dobbelklikk Sider - Bruk dette bildet som omslag\? + Bruk dette bildet som omslag? Kunne ikke laste ned kapitler. Du kan prøve igjen i nedlastingsdelen Legg til oppføringen i biblioteket ditt før du gjør dette Velg omslagsbilde @@ -290,7 +290,7 @@ Denne utvidelsen er ikke lengre tilgjengelig. Den fungerer kankje ikke som den skal og kan forårsake problemer med appen. Avinstallering anbefales. Datoformat Global oppdatering - Logg ut fra %1$s\? + Logg ut fra %1$s? Logg ut Du er utlogget Pauset @@ -404,7 +404,7 @@ Fant ingen kapitler Oppdater WebView-programmet for bedre kompabilitet Oppdaterte standard kapittelinnstillinger - Er du sikker på at du vil lagre disse innstillingene som standard\? + Er du sikker på at du vil lagre disse innstillingene som standard? Søkeinnstillinger Kapitler %1$s Ikke nok lagringsplass til å laste ned kapitler @@ -423,7 +423,7 @@ Forrige side Ingen app for filvelging funnet Veiledning for kilde-flytting - Er du sikker\? All historikk vil gå tapt. + Er du sikker? All historikk vil gå tapt. Historikken er slettet Slett historikk Vennligst logg på MAL igjen @@ -604,7 +604,7 @@ Strengen for brukeragent kan ikke være tom Komplett liste På vent liste - Er du sikker\? + Er du sikker? Du er i ferd med å fjerne \"%s\" fra biblioteket ditt Uferdig liste Last ned i forkant @@ -617,7 +617,7 @@ Forbedrer leserytelsen Tilbakestiller lesemodus og orientering for alle serier Antall uleste - Ønsker du å slette kategorien \"%s\"\? + Ønsker du å slette kategorien \"%s\"? Slett kategori InternalError: Sjekk krasjlogger for mer informasjon Oops! @@ -649,7 +649,7 @@ Fant ingen oppføringer i denne kategorien Åpne tilfeldig oppføring Fortsett å lese-knapp - Fjerne dato\? + Fjerne dato? Dette vil fjerne den tidligere valgte startdatoen fra %s Dette vil fjerne den tidligere valgte sluttdatoen fra %s Rydd ut nedlastingsindeksen @@ -681,7 +681,7 @@ Tilgjengelig, men kilden er ikke installert: %s Du har en oppføring i biblioteket med samme navn. \n -\nØnsker du å fortsette likevel\? +\nØnsker du å fortsette likevel? Hopp over dupliserte kapitler %1$s feil: %2$s *påkrevd @@ -707,7 +707,7 @@ Forutsi neste utgivelsestidspunkt Dobbelttrykk for å zoome Sveip til venstre handling - Vil du fjerne sporing for %s\? + Vil du fjerne sporing for %s? Fjern fra %s også OK Dette vil fjerne sporingen lokalt. @@ -726,7 +726,7 @@ Kunne ikke nå %s Sorter kategorier Oppdaterer bibliotek… (%s) - Vil du sortere kategoriene i alfabetisk rekkefølge\? + Vil du sortere kategoriene i alfabetisk rekkefølge? Kildeinnstillinger Programinnstillinger Relative tidsstempler diff --git a/i18n/src/commonMain/resources/MR/ne/strings.xml b/i18n/src/commonMain/resources/MR/ne/strings.xml index 9dc0527bc..7b110f49c 100644 --- a/i18n/src/commonMain/resources/MR/ne/strings.xml +++ b/i18n/src/commonMain/resources/MR/ne/strings.xml @@ -300,7 +300,7 @@ ट्र्याक रिस्टोर सम्पन्न भयो कुकीहरू खाली गर्नुहोस् - तपाई के ब्याकअप गर्न चाहनुहुन्छ\? + तपाई के ब्याकअप गर्न चाहनुहुन्छ? ब्याकअप असफल भयो HTTPS (DoH) माथि DNS प्रभाव पार्न एप फेरि सुरु गर्न आवश्यक छ @@ -431,7 +431,7 @@ युजरनेम लगइन लगइन हुनुहुन्छ - %1$s बाट लग-आउट गर्ने हो\? + %1$s बाट लग-आउट गर्ने हो? लग-आउट तपाई अब लग-आउट हुनुभएको छ वर्ग अपडेट गर्दै @@ -443,7 +443,7 @@ लाइसेन्स प्राप्त पासवर्ड पुस्तकालयमा नभएका इन्ट्रीको इतिहास हटाउनुहोस् - के तपाईँ निश्चित हुनुहुन्छ\? तपाईंले पढ्नुभएको अध्यायहरू र गैर-पुस्तकालय इन्ट्रीहरूको प्रगति हराउनेछ + के तपाईँ निश्चित हुनुहुन्छ? तपाईंले पढ्नुभएको अध्यायहरू र गैर-पुस्तकालय इन्ट्रीहरूको प्रगति हराउनेछ सफा गर्न केही छैन संस्करण वेबसाइट @@ -469,7 +469,7 @@ कुनै पृष्ठहरू फेला परेनन् स्रोत भेटिएन इतिहास हटाइयो - के तपाईँ निश्चित हुनुहुन्छ\? सबै इतिहास हराउनेछ। + के तपाईँ निश्चित हुनुहुन्छ? सबै इतिहास हराउनेछ। पृष्ठहरू लोड हुन असफल भयो: %1$s स्रोत स्थापना गरिएको छैन: %1$s रोकिएको @@ -481,10 +481,10 @@ आवरण सेभ गर्दा त्रुटि भयो आवरण मात्र ग्रिड आवरण साझा गर्दा त्रुटि भयो - के तपाईँले चयन गर्नुभएको अध्यायहरू हटाउन चाहनुहुन्छ\? + के तपाईँले चयन गर्नुभएको अध्यायहरू हटाउन चाहनुहुन्छ? अध्याय सेटिङ पूर्वनिर्धारित रूपमा सेट गर्नुहोस् - यो छवि आवरण को रूपमा राख्न चाहनुहुन्छ\? + यो छवि आवरण को रूपमा राख्न चाहनुहुन्छ? यो इन्ट्रीको सबै अध्यायहरू रिसेट गर्नुहोस् नपढिएको आवरण सेभ भयो @@ -496,17 +496,17 @@ क्लिपबोर्डमा प्रतिलिपि गर्नुहोस्: \n%1$s क्लिपबोर्डमा प्रतिलिपि गर्न असफल - डाउनलोड गरिएका अध्यायहरू हटाउनु\? + डाउनलोड गरिएका अध्यायहरू हटाउनु? जो सुरु भएको छैन आवरण अपडेट भयो %1$s: %2$s, पृष्ठ %3$d इतिहास हटाउनुहोस् - पुस्तकालयमा राख्ने\? + पुस्तकालयमा राख्ने? अर्को: पढ्ने मोड सकिएको: पुस्तकालय अपडेट गर्दै - के तपाइँ निश्चित रूपमा यी सेटिङहरूलाई पूर्वनिर्धारित रूपमा सेभ गर्न चाहनुहुन्छ\? + के तपाइँ निश्चित रूपमा यी सेटिङहरूलाई पूर्वनिर्धारित रूपमा सेभ गर्न चाहनुहुन्छ? मेरो पुस्तकालयका सबै इन्ट्रीहरूमा पनि लागू गर्नुहोस् अध्यायहरू भेटिएन स्थिति @@ -526,7 +526,7 @@ स्रोत समर्थित छैन सो नाम गरिएको वर्ग पहिले नै अवस्थित छ! वर्गहरू हटाइयो - यसले यस अध्यायको पढेको मिति हटाउनेछ। के तपाईँ निश्चित हुनुहुन्छ\? + यसले यस अध्यायको पढेको मिति हटाउनेछ। के तपाईँ निश्चित हुनुहुन्छ? प्रकार अर्को अध्याय भेटिएन पहिलेको: @@ -578,7 +578,7 @@ बन्द गर्नुहोस् वर्गहरू, ग्लोबल अपडेट, अध्याय स्वाइप पछिल्लो अपडेट जाँच - के तपाई \"%s\" वर्ग हटाउन चाहनु हुन्छ\? + के तपाई \"%s\" वर्ग हटाउन चाहनु हुन्छ? वर्ग हटाउनुहोस् खोज्नुहोस्… सबै हटाउनुहोस् @@ -610,7 +610,7 @@ जुनसुकै इन्ट्री खोल्नुहोस् तपाईको पुस्तकालयमा एउटै नामको इन्ट्री छ। \n -\nके तपाईं अझै जारी राख्न चाहनुहुन्छ\? +\nके तपाईं अझै जारी राख्न चाहनुहुन्छ? पढिरहेको सूची पुस्तकालय पछिल्लो पटक अपडेट गरिएको: %s %s एक अप्रत्याशित त्रुटिमा पर्यो। समर्थन को लागि हामी तपाईंलाई हाम्रो Discord को #support च्यानलमा क्र्यास लगहरू साझेदारी गर्न सुझाव दिन्छौं। @@ -652,14 +652,14 @@ लोकप्रिय रिडर कार्यसम्पादन सुधार गर्दछ प्रति-श्रृङ्खला रिडर सेटिङहरू रिसेट गर्नुहोस् - मिति हटाउने हो\? + मिति हटाउने हो? यसले %s बाट तपाइँको पहिले चयन गरिएको सुरु मिति हटाउनेछ यसले %s बाट तपाइँको पहिले चयन गरिएको समाप्त मिति हटाउनेछ एउटा अपडेट पहिले नै जारी छ ल, यो के भयो स्थापना गरिएको छैन पढ्ने मोड र सबै श्रृङ्खलाहरूको अभिमुखीकरण रिसेट गर्दछ - के तपाईँ निश्चित हुनुहुन्छ\? + के तपाईँ निश्चित हुनुहुन्छ? पृष्ठ विभाजन गर्दा %d फेला परेन वर्ग खाली छ आन्तरिक त्रुटि: थप जानकारीको लागि क्र्यास लगहरू जाँच गर्नुहोस् @@ -710,7 +710,7 @@ अर्को अपेक्षित अपडेट अपेक्षित रिलीज अवधि बाहिर प्रत्येक अनुमान लगाउनुहोस् - %s ट्र्याकिङ हटाउने हो\? + %s ट्र्याकिङ हटाउने हो? %s बाट पनि हटाउनुहोस् यसले लोकल रूपमा ट्र्याकिङ हटाउनेछ। ठिक छ @@ -730,7 +730,7 @@ श्रृङ्खलालाई तल सार्नुहोस् वर्गहरू क्रमबद्ध गर्नुहोस् पुस्तकालय अपडेट गर्दै... (%s) - के तपाई वर्गहरू वर्णमाला अनुसार क्रमबद्ध गर्न चाहनुहुन्छ\? + के तपाई वर्गहरू वर्णमाला अनुसार क्रमबद्ध गर्न चाहनुहुन्छ? स्रोत सेटिङहरू एप सेटिङहरू सापेक्ष टाइमस्ट्याम्पहरू diff --git a/i18n/src/commonMain/resources/MR/nl/strings.xml b/i18n/src/commonMain/resources/MR/nl/strings.xml index 6ffd060ca..9ad64c610 100644 --- a/i18n/src/commonMain/resources/MR/nl/strings.xml +++ b/i18n/src/commonMain/resources/MR/nl/strings.xml @@ -140,7 +140,7 @@ Op hoofdstuknummer Downloaden Ongelezen - Weet je zeker dat je de geselecteerde hoofdstukken wilt verwijderen\? + Weet je zeker dat je de geselecteerde hoofdstukken wilt verwijderen? Tracking Voltooid Score @@ -149,14 +149,14 @@ Er bestaat al een categorie met deze naam! Categorieën verwijderd Stel alle hoofdstukken voor dit item opnieuw in - Toevoegen aan bibliotheek\? + Toevoegen aan bibliotheek? Afbeelding opgeslagen Aangepast filter Instellen als omslagfoto Omslagfoto bijgewerkt Pagina: %1$d Volgend hoofdstuk niet gevonden - Deze afbeelding als omslagfoto gebruiken\? + Deze afbeelding als omslagfoto gebruiken? Nieuwe hoofdstukken gevonden Voeg de manga eerst aan je bibliotheek toe voordat je dit doet Selecteer omslagfoto @@ -206,14 +206,14 @@ Gelezen series Voortgang bijwerken na lezen Scherm aanhouden - Weet je het zeker\? Gelezen hoofdstukken en voortgang voor items die niet in de bibliotheek zitten zullen verloren gaan + Weet je het zeker? Gelezen hoofdstukken en voortgang voor items die niet in de bibliotheek zitten zullen verloren gaan Database geleegd Lopend Verwijder gedownloade hoofdstukken? Aan het lezen Opgegeven Gepauzeerd - Dit verwijdert de datum waarop het hoofdstuk is gelezen. Weet je het zeker\? + Dit verwijdert de datum waarop het hoofdstuk is gelezen. Weet je het zeker? De afbeelding kon niet worden geladen Kon hoofdstukken niet downloaden. Je kan het opnieuw proberen in het downloadscherm Updaten van omslag mislukt @@ -292,7 +292,7 @@ Deze extensie is niet meer beschikbaar. Mogelijk zal deze niet meer naar behoren functioneren en kan problemen binnen de app veroorzaken. Het wordt aangeraden de extensie te verwijderen. Datumformaat Globaal bijwerken - Uitloggen bij %1$s\? + Uitloggen bij %1$s? Uitloggen U bent nu uitgelogd Gepauzeerd @@ -417,7 +417,7 @@ Standaard hoofdstukinstellingen bijgewerkt Instellen als standaard Ook toepassen op alle items in mijn bibliotheek - Weet je zeker dat je deze instellingen als standaardinstellingen wilt opslaan\? + Weet je zeker dat je deze instellingen als standaardinstellingen wilt opslaan? Hoofdstukinstellingen %1$s: %2$s, pagina %3$d Zoekinstellingen @@ -426,7 +426,7 @@ Pauzeert leesgeschiedenis Incognitomodus Geschiedenis wissen - Weet je dit zeker\? Alle geschiedenis zal verloren gaan. + Weet je dit zeker? Alle geschiedenis zal verloren gaan. Geschiedenis verwijderd Volgende pagina Vorige pagina @@ -585,7 +585,7 @@ Lavendel Laatst gecontroleerd op updates Alles verwijderen - Wenst u categorie \"%s\" te verwijderen\? + Wenst u categorie \"%s\" te verwijderen? Categorie verwijderen Sluiten Vloedgolf @@ -624,10 +624,10 @@ F-Droid versies worden niet officieel ondersteund. \nTik hier voor meer informatie. Dubbele hoofdstukken overslaan - Weet je het zeker\? + Weet je het zeker? Je hebt momenteel een item in je bibliotheek met dezelfde naam. \n -\nWil je alsnog verdergaan\? +\nWil je alsnog verdergaan? Leeslijst Geen beschrijving Zet de instellingen voor de leesmodus en het draaitype terug naar de standaardwaarden voor alle series @@ -643,7 +643,7 @@ Recentelijk bijgewerkte items in de bibliotheek bekijken %1$s-fout: %2$s User agent terugzetten naar standaardwaarde - Datum verwijderen\? + Datum verwijderen? Verlanglijst Aangepaste omslag Ongeldige locatie: %s @@ -693,7 +693,7 @@ OK Swipe naar de linker actie Intervallen - Haal %s tracking weg\? + Haal %s tracking weg? Ontgrendel %s Swipe naar de juiste actie %d per rij @@ -736,7 +736,7 @@ Opslaggebruik Bibliotheek bijwerken... (%s) Data en opslag - Wil je de categorieën sorteren op alfabetische volgorde\? + Wil je de categorieën sorteren op alfabetische volgorde? Bestandskiezer gaf geen bestand terug aan de app Bron-instellingen App-instellingen diff --git a/i18n/src/commonMain/resources/MR/nn/strings.xml b/i18n/src/commonMain/resources/MR/nn/strings.xml index 03ddfe0c5..7c8c2a3eb 100644 --- a/i18n/src/commonMain/resources/MR/nn/strings.xml +++ b/i18n/src/commonMain/resources/MR/nn/strings.xml @@ -193,7 +193,7 @@ Gjenopprett bibliotek frå reservekopifil Gjenoppretting fullført %02d min, %02d sek - Kva ønskjer du å ta reservekopi av\? + Kva ønskjer du å ta reservekopi av? Lagar reservekopi Gjenoppretter reservekopi Nettverk @@ -257,7 +257,7 @@ Kva er nytt Fjern frå bibliotek Meir - Legg manga til i bibliotek\? + Legg manga til i bibliotek? Mindre Kunne ikkje kopiera til utklippstavla Kjelde ikkje installert: %1$s diff --git a/i18n/src/commonMain/resources/MR/pl/strings.xml b/i18n/src/commonMain/resources/MR/pl/strings.xml index 1aa9e7429..214d732c6 100644 --- a/i18n/src/commonMain/resources/MR/pl/strings.xml +++ b/i18n/src/commonMain/resources/MR/pl/strings.xml @@ -192,7 +192,7 @@ Pomaga naprawiać błędy. Żadne wrażliwe dane nie będą wysyłane Utworzono kopię zapasową Przywracanie ukończone - Na pewno\? Przeczytane rozdziały i postęp w wpisach spoza biblioteki zostaną utracone + Na pewno? Przeczytane rozdziały i postęp w wpisach spoza biblioteki zostaną utracone Pozycje usunięte Zalogowano Źródło lokalne @@ -208,10 +208,10 @@ Usunięto kategorie To usunie datę przeczytania tego rozdziału. Na pewno? Resetuj wszystkie rozdziały dla tego wpisu - Dodać do biblioteki\? + Dodać do biblioteki? Filtr niestandardowy Nie udało się wczytać obrazka - Ustawić ten obrazek jako okładkę\? + Ustawić ten obrazek jako okładkę? Nie udało się pobrać rozdziałów. Możesz spróbować ponownie w sekcji pobierania Nie udało się zaktualizować okładki Zanim to zrobisz, dodaj ten wpis do biblioteki @@ -292,7 +292,7 @@ To rozszerzenie nie jest już dostępne i może nie funkcjonować prawidłowo, powodując problemy z aplikacją. Zalecamy odinstalowanie go. Format daty Globalna aktualizacja - Wylogować z %1$s\? + Wylogować z %1$s? Wyloguj Wylogowano Wstrzymane @@ -415,7 +415,7 @@ Nie znaleziono rozdziałów Ustaw jako domyślne Dodatkowo zastosuj do wszystkich wpisów w mojej bibliotece - Jesteś pewien, że chcesz zapisać te ustawienia jako domyślne\? + Jesteś pewien, że chcesz zapisać te ustawienia jako domyślne? Ustawienia rozdziałów Nie masz podpiętych źródeł Pobrane rozdziały @@ -423,7 +423,7 @@ Wstrzymuje zapisywanie do historii czytania Tryb incognito Wyczyść historię - Jesteś pewien\? CAŁA historia zostanie usunięta. + Jesteś pewien? CAŁA historia zostanie usunięta. Historia usunięta Serwisy, do których nie zalogowono: Usuwaj rozdziały dodane do zakładek @@ -610,7 +610,7 @@ Nie można było znaleźć ścieżki %d strony Domyślny user agent string Zresetuj domyślny user agent string - Czy chcesz usunąć kategorię \"%s\"\? + Czy chcesz usunąć kategorię \"%s\"? Usuń kategorię InternalError: sprawdź log błędów po więcej informacji Usuń wszystko @@ -622,7 +622,7 @@ Pobieraj podczas czytania Zobacz ostatnio zaktualizowane wpisy Widżet nie jest dostępny kiedy blokada aplikacji jest włączona - Jesteś pewien\? + Jesteś pewien? Działa tylko, jeśli aktualny i następny rozdział są już pobrane. Wielojęzyczne Popularne @@ -651,7 +651,7 @@ Nieznany tytuł Przed chwilą Ups! - Usunąć datę\? + Usunąć datę? To spowoduje usunięcie wcześniej wybranej daty zakończenia z %s Nieprawidłowy ciąg agenta użytkownika Uruchom ponownie aplikację @@ -684,7 +684,7 @@ Skopiowano do schowka Masz w swojej bibliotece pozycję o tej samej nazwie. \n -\nCzy nadal chcesz kontynuować\? +\nCzy nadal chcesz kontynuować? Pomiń zduplikowane rozdziały Dostępne, ale źródło nie jest zainstalowane: %s Błąd %1$s: %2$s @@ -712,7 +712,7 @@ HTTP %d, otwórz w WebView Brak połączenia z internetem Nie można połączyć się z %s - Przestań śledzić %s\? + Przestań śledzić %s? Usuń pobrane Synchronizacja biblioteki zakończona Indeksy pobrań unieważnione diff --git a/i18n/src/commonMain/resources/MR/pt-rBR/strings.xml b/i18n/src/commonMain/resources/MR/pt-rBR/strings.xml index 38776da71..4a03ce56d 100644 --- a/i18n/src/commonMain/resources/MR/pt-rBR/strings.xml +++ b/i18n/src/commonMain/resources/MR/pt-rBR/strings.xml @@ -137,7 +137,7 @@ Frequência de backup automático Backup criado Restauração concluída - Do que você deseja fazer backup\? + Do que você deseja fazer backup? Restaurando backup Criando backup Limpar o cache de capítulos @@ -148,7 +148,7 @@ Cookies limpos Limpar o banco de dados Exclui o histórico de itens que não estão salvos em sua biblioteca - Tem certeza\? Os capítulos lidos e o progresso em itens que não estão na sua biblioteca serão perdidos + Tem certeza? Os capítulos lidos e o progresso em itens que não estão na sua biblioteca serão perdidos Entradas excluídas Versão Enviar relatórios de erro @@ -166,7 +166,7 @@ Desconhecido Licenciado Remover da biblioteca - Excluir os capítulos disponíveis offline\? + Excluir os capítulos disponíveis offline? Capítulo %1$s Fazendo download (%1$d/%2$d) Erro @@ -177,7 +177,7 @@ Pelo número do capítulo Fazer download Não lidos - Você tem certeza que quer excluir os capítulos selecionados\? + Você tem certeza que quer excluir os capítulos selecionados? Monitoramento Lendo Concluído @@ -189,9 +189,9 @@ Estado Já existe uma categoria com este nome! Categorias excluídas - Isso irá remover a data de leitura deste capítulo. Você tem certeza\? + Isso irá remover a data de leitura deste capítulo. Você tem certeza? Redefinir todos os capítulos deste item - Adicionar à biblioteca\? + Adicionar à biblioteca? Imagem salva Filtro personalizado Usar como capa @@ -199,7 +199,7 @@ Página: %1$d Próximo capítulo não encontrado A imagem não pôde ser carregada - Usar esta imagem como capa\? + Usar esta imagem como capa? Não foi possível fazer o download dos capítulos. Você pode tentar novamente na seção de downloads Novos capítulos encontrados Erro ao atualizar a capa @@ -290,7 +290,7 @@ Selecione uma fonte da qual migrar Formato de data Atualização global - Desconectar de %1$s\? + Desconectar de %1$s? Desconectar Você foi desconectado Pausado @@ -415,7 +415,7 @@ Configurações padrões de capítulo atualizadas Definir como padrão Também aplicar a todos os itens na minha biblioteca - Você tem certeza que quer salvar como padrão essas configurações\? + Você tem certeza que quer salvar como padrão essas configurações? Configurações de capítulo %1$s: %2$s, página %3$d Pesquisar nas configurações @@ -424,7 +424,7 @@ Pausa o histórico de leitura Modo anônimo Limpar o histórico - Você tem certeza\? Todo o histórico será perdido. + Você tem certeza? Todo o histórico será perdido. Histórico excluído Próxima página Página anterior @@ -605,7 +605,7 @@ Lavanda Sem descrição Deletar categoria - Você deseja deletar a categoria \"%s\"\? + Você deseja deletar a categoria \"%s\"? Erro interno: Verifique os registros de travamento para mais informações User Agent padrão Redefinir o User Agent padrão @@ -619,7 +619,7 @@ Fazer download a frente Download automático durante a leitura Funciona somente se o capítulo atual e próximo já estão disponíveis offline. - Você tem certeza\? + Você tem certeza? Múltiplos Última atualização da biblioteca: %s Você está prestes a remover \"%s\" de sua biblioteca @@ -649,7 +649,7 @@ Compilações do F-Droid não são suportadas oficialmente. \nToque para saber mais. Botão de continuar a leitura - Remover data\? + Remover data? Isto irá remover sua data de início selecionada anteriormente no %s Isto irá remover sua data de término selecionada anteriormente no %s Invalidar o índice de downloads @@ -682,7 +682,7 @@ Pular capítulos duplicados Você possui um item na sua biblioteca com o mesmo nome. \n -\nVocê ainda deseja continuar\? +\nVocê ainda deseja continuar? Erro em %1$s: %2$s *obrigatório Ocultar títulos que já estão na biblioteca @@ -707,7 +707,7 @@ Definido para atualizar todo Estimar todo OK - Remover o monitoramento do %s\? + Remover o monitoramento do %s? Isso irá remover o monitoramento localmente. Também remover do %s Deletar os disponíveis offline @@ -730,7 +730,7 @@ Atualizando a biblioteca… (%s) Configurações das fontes Ordenar as categorias - Você deseja ordenar as categorias alfabeticamente\? + Você deseja ordenar as categorias alfabeticamente? Nenhum arquivo selecionado Dados e armazenamento Reduz o efeito fantasma em telas e-ink diff --git a/i18n/src/commonMain/resources/MR/pt/strings.xml b/i18n/src/commonMain/resources/MR/pt/strings.xml index 7203697cd..e447f8a33 100644 --- a/i18n/src/commonMain/resources/MR/pt/strings.xml +++ b/i18n/src/commonMain/resources/MR/pt/strings.xml @@ -104,7 +104,7 @@ Cookies limpos Limpar base de dados Exclui o histórico de itens que não estão salvos nasua biblioteca - Tem certeza\? Os capítulos lidos e o progresso em itens que não estão na sua biblioteca serão perdidos + Tem certeza? Os capítulos lidos e o progresso em itens que não estão na sua biblioteca serão perdidos Eliminar entradas Versão Enviar relatórios de erro @@ -129,7 +129,7 @@ Por número de capítulo Transferir Não lidos - Tem certeza de que deseja eliminar os capítulos selecionados\? + Tem certeza de que deseja eliminar os capítulos selecionados? A ler Completo Abandonado @@ -226,7 +226,7 @@ R Cópia de segurança criada Restauro completo - O que pretende fazer cópia de segurança\? + O que pretende fazer cópia de segurança? Capítulos transferidos Confiar Não confiável @@ -241,7 +241,7 @@ Navegar Adicionado à biblioteca Removido da biblioteca - Eliminar capítulos descarregados\? + Eliminar capítulos descarregados? Copiado para área de transferência: \n%1$s Fonte não instalada: %1$s @@ -252,12 +252,12 @@ Tipo Já existe uma categoria com este nome! Categorias eliminadas - Adicionar à biblioteca\? + Adicionar à biblioteca? Imagem guardada Filtro personalizado Usar como capa Capa atualizada - Usar esta imagem como capa\? + Usar esta imagem como capa? Concluído: Atual: Próximo: @@ -292,7 +292,7 @@ Esta extensão não está mais disponível. Ela pode não funcionar corretamente e pode causar problemas na app. É recomendado desinstalá-la. Formato da data Atualização global - Terminar sessão em %1$s\? + Terminar sessão em %1$s? Terminar sessão Sua sessão está agora encerrada Pausado @@ -415,7 +415,7 @@ Isto não impede extensões não oficiais ou incorretamente marcadas de mostrarem conteúdo NSFW (18+) dentro da app. Nenhum capítulo encontrado Definições padrão de capítulo atualizadas - Tem certeza de que quer guardar estas definições como padrão\? + Tem certeza de que quer guardar estas definições como padrão? Definições dos capítulos %1$s: %2$s, página %3$d Definir como padrão @@ -426,7 +426,7 @@ Pausa histórico de leitura Modo incógnito Eliminar histórico - Tem a certeza\? Todo o histórico será perdido. + Tem a certeza? Todo o histórico será perdido. Histórico eliminado Conteúdo 18+ (NSFW) Página seguinte @@ -596,11 +596,11 @@ O formato RARv5 não é suportado Multi Transferência automática durante leitura - Deseja apagar a categoria \"%s\"\? + Deseja apagar a categoria \"%s\"? Apagar categoria InternalError: Verifique registos de falhas para mais informações Cadeia de agente de utilizador não pode estar em branco - Tem a certeza\? + Tem a certeza? Última atualização da biblioteca: %s Veja os seus itens da biblioteca atualizados recentemente Está prestes a remover \"%s\" da sua biblioteca @@ -644,7 +644,7 @@ Copiado para a área de transferência Você tem uma entrada em sua biblioteca com o mesmo nome. \n -\nAinda deseja continuar\? +\nAinda deseja continuar? %dd Agora mesmo Reinicie o aplicativo @@ -680,7 +680,7 @@ Usados %ds A verificar as transferências - Remover data\? + Remover data? Visão geral Itens completos *obrigatório diff --git a/i18n/src/commonMain/resources/MR/ro/strings.xml b/i18n/src/commonMain/resources/MR/ro/strings.xml index 05f744bed..b4b2fdebf 100644 --- a/i18n/src/commonMain/resources/MR/ro/strings.xml +++ b/i18n/src/commonMain/resources/MR/ro/strings.xml @@ -162,7 +162,7 @@ Frecventă de creere a copiilor de rezervă Backup creat Restaurare completă - Doriți să creați o copie de rezervă\? + Doriți să creați o copie de rezervă? Restaurând copia de rezervă Creând copia de rezervă Curățare capitole depozitate @@ -197,7 +197,7 @@ Elimină din bibliotecă Adăugat la bibliotecă Eliminat din bibliotecă - Ștergeți capitolele descărcate\? + Ștergeți capitolele descărcate? Copiat în clipboard: \n%1$s Sursa nu este instalată:%1$s @@ -211,7 +211,7 @@ După numărul capitolului Descarcă Necitit - Sigur doriți să ștergeți capitolele selectate\? + Sigur doriți să ștergeți capitolele selectate? Urmărite Citind Completată @@ -226,9 +226,9 @@ Tip O categorie cu acest nume deja există! Categoriile au fost șterse - Acest lucru va șterge data citirii acestui capitol. Ești sigur\? + Acest lucru va șterge data citirii acestui capitol. Ești sigur? Resetați toate capitolele pentru această intrare - Adăugați la bibliotecă\? + Adăugați la bibliotecă? Imagine salvată Filtru personalizat Stabilește ca și copertă @@ -236,7 +236,7 @@ Pagina: %1$d Capitolul următor nu a fost găsit Imaginea nu a putut să se încarce - Folosiți această imagine ca și copertă\? + Folosiți această imagine ca și copertă? Terminat: Actual: Următorul: @@ -292,7 +292,7 @@ Această extensie nu mai este disponibilă. Este posibil să nu funcționeze corect și poate cauza probleme cu aplicația. Se recomandă dezinstalarea acesteia. Formatul datei Actualizare globală - Deconectează-te de la %1$s\? + Deconectează-te de la %1$s? Deconectează-te Acum ești deconectat Întrerupt @@ -418,7 +418,7 @@ %1$s: %2$s, pagina %3$d Setați ca implicit Se aplică, de asemenea, tuturor intrărilor din biblioteca mea - Sigur doriți să salvați aceste setări ca implicite\? + Sigur doriți să salvați aceste setări ca implicite? Setările capitolului Capitole descărcate Din bibliotecă @@ -426,7 +426,7 @@ Întrerupe citirea istoricului Modul incognito Sterge istoricul - Esti sigur\? Toată istoria va fi pierdută. + Esti sigur? Toată istoria va fi pierdută. Istoricul a fost șters Pagina următoare Pagina anterioară @@ -523,7 +523,7 @@ Instalați și porniți Shizuku pentru a utiliza Shizuku ca instalator de extensii. %1$d înregistrări în baza de datecare nu aparțin bibliotecii Pagina %d nu a fost găsită în timpul divizării - Doriți să ștergeți categoria \"%s\"\? + Doriți să ștergeți categoria \"%s\"? InternalError: Verificați jurnalele de accident pentru informații suplimentare Lavandă Mod întunecat negru pur @@ -578,7 +578,7 @@ Creează dosare în funcție de titlul intrărilor Automat Pornit - Ești sigur\? + Ești sigur? Eroare la salvarea imaginii Actualizează tot Deactivat @@ -652,7 +652,7 @@ Acest lucru va elimina data de finalizare selectată anterior din %s Indexul descărcărilor invalid Descărcări de indexare - Eliminați data\? + Eliminați data? Acest lucru va elimina data de început selectată anterior din %s Versiunile F-Droid nu sunt acceptate oficial. \nApăsați pentru a afla mai multe. @@ -684,7 +684,7 @@ Disponibil, dar sursa nu este instalata: %s Ai deja o intrare in librarie cu acelasi nume. \n -\nEsti sigur ca vrei sa continui\? +\nEsti sigur ca vrei sa continui? %1$s eroare: %2$s *necesar Copiat in clipboard diff --git a/i18n/src/commonMain/resources/MR/ru/strings.xml b/i18n/src/commonMain/resources/MR/ru/strings.xml index ca7229123..bf0909033 100644 --- a/i18n/src/commonMain/resources/MR/ru/strings.xml +++ b/i18n/src/commonMain/resources/MR/ru/strings.xml @@ -51,19 +51,19 @@ Главы Когда заряжается Серии удалены - Вы уверены\? Прочитанные главы и прогресс не библиотечных серий будет потерян + Вы уверены? Прочитанные главы и прогресс не библиотечных серий будет потерян А К З С Завершено - Вы уверены, что хотите удалить выбранные главы\? - Использовать это изображение как обложку\? + Вы уверены, что хотите удалить выбранные главы? + Использовать это изображение как обложку? Cookies очищены Обложка обновлена Пользовательский фильтр Не удалось загрузить изображение - Это действие удалит дату прочтения этой главы. Вы уверены\? + Это действие удалит дату прочтения этой главы. Вы уверены? Сбросить все главы для этой серии Отключено Глава %1$s @@ -162,7 +162,7 @@ Установить как обложку Номер главы Название источника - Добавить в библиотеку\? + Добавить в библиотеку? Номер главы Источник Статус @@ -194,14 +194,14 @@ Справа Восстановить Отменить - Что добавить в резервную копию\? + Что добавить в резервную копию? Резервная копия создана Категории Пауза Создание резервной копии Основная категория Спрашивать - Удалить загруженные главы\? + Удалить загруженные главы? Загрузки приостановлены История Источник на устройстве @@ -290,7 +290,7 @@ Это расширение больше недоступно. Оно может работать неправильно, а также вызвать проблемы с приложением. Рекомендуется его удалить. Формат даты Глобальное обновление - Выйти из %1$s\? + Выйти из %1$s? Выйти Вы успешно вышли На паузе @@ -415,7 +415,7 @@ Настройки главы по умолчанию были обновлены Установить по умолчанию Также применить ко всем сериям в моей библиотеке - Вы уверены, что хотите сохранить эти настройки по умолчанию\? + Вы уверены, что хотите сохранить эти настройки по умолчанию? Настройки главы %1$s: %2$s, страница %3$d Искать настройки @@ -424,7 +424,7 @@ Приостанавливает историю чтения Режим инкогнито Очистить историю - Вы уверены\? Вся история будет удалена. + Вы уверены? Вся история будет удалена. История удалена Следующая страница Предыдущая страница @@ -605,7 +605,7 @@ Лаванда Без описания InternalError: Проверьте журнал с ошибками для дополнительной информации - Хотите ли вы удалить категорию \"%s\"\? + Хотите ли вы удалить категорию \"%s\"? Удалить категорию User agent по умолчанию Сбросить user agent по умолчанию @@ -619,7 +619,7 @@ Загрузить наперёд При чтении Работает только если текущая + следующая главы уже загружены. - Вы уверены\? + Вы уверены? Многоязыковое Вы собираетесь удалить «%s» из вашей библиотеки Последнее обновление библиотеки: %s @@ -650,7 +650,7 @@ \nНажмите, чтобы узнать больше. Кнопка «Продолжить чтение» Это удалит ранее выбранную дату окончания из %s - Удалить дату\? + Удалить дату? Это удалит ранее выбранную дату начала из %s Очистить индекс загрузок Принуждает приложение перепроверить загруженные главы @@ -682,7 +682,7 @@ Пропускать повторяющиеся главы В вашей библиотеке уже есть серия с таким именем. \n -\nВы всё ещё хотите продолжить\? +\nВы всё ещё хотите продолжить? %1$s ошибка: %2$s *необходимо Скрывать серии, уже находящиеся в библиотеке @@ -706,7 +706,7 @@ Предсказать время выхода новых глав Задать обновления каждые Пропущено, т.к сегодня не ожидается выпуска - Удалить отслеживание %s\? + Удалить отслеживание %s? Это удалит отслеживание в приложении. Также удалить из %s ОК @@ -730,7 +730,7 @@ Настройки источников Обновление библиотеки... (%s) Сортировать категории - Хотите ли вы сортировать категории по алфавиту\? + Хотите ли вы сортировать категории по алфавиту? Файл не выбран Данные и хранение Никогда diff --git a/i18n/src/commonMain/resources/MR/sa/strings.xml b/i18n/src/commonMain/resources/MR/sa/strings.xml index 1e22a0f1b..9f57be355 100644 --- a/i18n/src/commonMain/resources/MR/sa/strings.xml +++ b/i18n/src/commonMain/resources/MR/sa/strings.xml @@ -532,12 +532,12 @@ संविधावृत्तलेखं प्रति अतिविस्तीर्णवृत्तलेखान् मुद्रयतु (अनुप्रयोगप्रवृत्तिम् अल्पयति) यादृच्छिकदोषकारणात् अध्यायस्य अवारोपणम् न शक्नुयात् अपि मम ग्रन्थालये सर्वाभ्यः माङ्गाभ्यः आचरतु - त्वं निश्चितं किम् \? चरित्रं सर्वं लुप्तं भविष्यति। + त्वं निश्चितं किम् ? चरित्रं सर्वं लुप्तं भविष्यति। एतत् आण्ड्रायिड्-संस्करणम् अधुना न भरितम् इतोऽपि उत्तमतरसङ्गतिं प्रप्तुं कृपया वेब्-व्यू-अनुप्रयोगं नवीकरोतु तव ग्रन्थालये सर्वाः माङ्गाः शोधनीकरोति तव ग्रन्थालये न रक्षितानां माङ्गानां चरित्रं नाशय - त्वं निश्चितरूपेण सङ्गृहीतान् अध्यायान् नाशयितुम् इच्छसि किम् \? + त्वं निश्चितरूपेण सङ्गृहीतान् अध्यायान् नाशयितुम् इच्छसि किम् ? त्वं निश्चितं किम्। पर-ग्रन्थालय-माङ्गानाम् अपिठाध्यायाः नष्टाः भविष्यन्ते पूर्वसूचना -- महद्भाराणि अवारोपणानि कुर्यात् मूलानि मन्दानि। अपि च टाचीयोमिं निवारयेत्॥ व्युत्पादकेभ्यः प्रसारयितुं दोषदत्तम् एकस्यां सञ्चिकायां रक्षति diff --git a/i18n/src/commonMain/resources/MR/sah/strings.xml b/i18n/src/commonMain/resources/MR/sah/strings.xml index ba1f87335..c5bc7a746 100644 --- a/i18n/src/commonMain/resources/MR/sah/strings.xml +++ b/i18n/src/commonMain/resources/MR/sah/strings.xml @@ -223,7 +223,7 @@ Төнүҥнэрии туола турар Хаппаас куопуйа оҥоһуллубата Хаппаас куопуйа оҥоһуута - Тугу куопуйалаары гынннын\? + Тугу куопуйалаары гынннын? Хаппаас куопуйа оҥоруута туола турар %02d мүнүүтэ %02d сөкүүндэ Төнүҥнэри бүттэ @@ -256,7 +256,7 @@ Түһүмэх кээһин ыраастааһын Билим Куки ыраастанна - Эрэллэххин дуо\? Ааҕыллыбыт түһүмэхтэр уонна бэбэлэтиэкэҕэ суох маанга туруга сүтүө + Эрэллэххин дуо? Ааҕыллыбыт түһүмэхтэр уонна бэбэлэтиэкэҕэ суох маанга туруга сүтүө Эн бэбэлэтиэкэҕэр суох маанга остуоруйатын сотторуу Билим олоҕо ыраастааһына Батарея тупсарыытын араарыы @@ -284,7 +284,7 @@ Биллибэт сыыһыы Эн таҕыстын Тахсыы - %1$s\'тан тахсаҕын\? + %1$s\'тан тахсаҕын? Киирдин Киири Аһарык @@ -295,7 +295,7 @@ Хачайдаммыт эрэ Ханнык баҕар сыаһалары көннөрөргө көмөлөһөр. Бэйэ дааннайдар ылыллыбаттар Охтуу туһунан отчуоту ыытыы - Хачайдаммыт түһүмэхтэри сотторобут\? + Хачайдаммыт түһүмэхтэри сотторобут? Кырата Эбии Бэбэлэтиэкэттэн ылылынна @@ -316,7 +316,7 @@ Кыбытыылах түһүмэхтэри соттор Түһүмэхтэри соттор Кэтээһин - Талыллыбыт түһүмэхтэри сотторорго эрэллээххин дуу\? + Талыллыбыт түһүмэхтэри сотторорго эрэллээххин дуу? Ааҕыллыбата Хачайдаа Хачайдааһын кэминэн @@ -328,7 +328,7 @@ Сыыһааһын Хачайдааһын (%1$d/%2$d) Түһүмэх %1$s - Маанганы Бэбэлэтиэкэҕэ киллэрэбит дуу\? + Маанганы Бэбэлэтиэкэҕэ киллэрэбит дуу? Төрүт туруоруллубатах: %1$s Хоспоххо куопуйаланна: \n %1$s @@ -338,7 +338,7 @@ Туттааччы биилтирэ Хартыыҥка оннунан хаалла Бу маанга бары түһүмэхтэрин түһэрии - Бу дьайыы түһүмэх ааҕыллыбыт кэмин сотторуо. Эрэллээххин дуо\? + Бу дьайыы түһүмэх ааҕыллыбыт кэмин сотторуо. Эрэллээххин дуо? Бөлөхтөр соттороллубуттар Оннук ааттах бөлөх уруккуттан баар! Көрүҥ @@ -355,7 +355,7 @@ Ааҕабын эбии кэтээһини киллэрии Бу сиэриэҕэ - Бу ойууну тас курдук туттаҕын дуо\? + Бу ойууну тас курдук туттаҕын дуо? Ойуу кыайан хачайдаммата Аныгыскы түһүмэх булуллубата Сирэй: %1$d @@ -378,14 +378,14 @@ Түһүмэхтэр суохтар Куолутунан Мин бэбэлэтиэкэбэр баар маангаларга туттуҥ - Бу түстэл куолутунан хааларын баҕараҕын дуо\? + Бу түстэл куолутунан хааларын баҕараҕын дуо? Түһүмэх түстэлэ Хачайдаммыт түһүмэхтэр Бэбэлэтиэкэттэн маанга Ааҕы остуоруйатын тохтотор Кистэлэҥ эрэһиимэ Остуоруйаны сотторуу - Эрэллээххин дуо\? Остуоруйа барыта сотторуллуоҕа. + Эрэллээххин дуо? Остуоруйа барыта сотторуллуоҕа. Остуоруйа сотторулунна Көрдөөһүн түстэлэ Киллэрэр дааннайдары талыы diff --git a/i18n/src/commonMain/resources/MR/sc/strings.xml b/i18n/src/commonMain/resources/MR/sc/strings.xml index a2bfa211d..9ec1b724c 100644 --- a/i18n/src/commonMain/resources/MR/sc/strings.xml +++ b/i18n/src/commonMain/resources/MR/sc/strings.xml @@ -163,7 +163,7 @@ Frecuèntzia de sarvatàgiu de sas còpias de seguresa Còpia de seguresa creada Riprìstinu acabadu - De ite cheres fàghere una còpia de seguresa\? + De ite cheres fàghere una còpia de seguresa? Ripristinende sa còpia de seguresa Creende sa còpia de seguresa Isbòida sa memòria temporànea de sos capìtulos @@ -174,7 +174,7 @@ Cookies iscantzellados Isbòida sa base de datos Iscantzella sa cronologia pro sos elementos chi non sunt sarvados in sa biblioteca tua - Seguru ses\? Sos capìtulos lèghidos e su progressu de sos elementos chi non sunt in sa biblioteca s\'ant a pèrdere + Seguru ses? Sos capìtulos lèghidos e su progressu de sos elementos chi non sunt in sa biblioteca s\'ant a pèrdere Boghes iscantzelladas Versione Imbia raportos a pitzu de sos arrestos anòmalos @@ -198,7 +198,7 @@ Boga dae sa biblioteca Annantu a sa biblioteca Bogadu dae sa biblioteca - Iscantzellare sos capìtulos iscarrigados\? + Iscantzellare sos capìtulos iscarrigados? Copiadu in punta de billete: \n%1$s Fonte no installada: %1$s @@ -212,7 +212,7 @@ Pro nùmeru de capìtulu Iscàrriga Non lèghidos - Ses seguru de chèrrere iscantzellare sos capìtulos ischertados\? + Ses seguru de chèrrere iscantzellare sos capìtulos ischertados? Arrastamentu Leghende Acabadu @@ -227,9 +227,9 @@ Casta Esistit giai una categoria cun custu nùmene! Categorias iscantzelladas - Custu at a bogare sa data de leghidura de custu capìtulu. Seguru ses\? + Custu at a bogare sa data de leghidura de custu capìtulu. Seguru ses? Reseta totu sos capìtulos pro custu elementu - Annànghere a sa biblioteca\? + Annànghere a sa biblioteca? Immàgine sarvada Filtru personalizadu Imposta comente cobertedda @@ -237,7 +237,7 @@ Pàgina: %1$d Capìtulu imbeniente no agatadu No est istadu possìbile carrigare s\'immàgine - Cheres impostare custa immàgine comente cobertedda\? + Cheres impostare custa immàgine comente cobertedda? Acabadu: Atuale: Imbeniente: @@ -292,7 +292,7 @@ Custa estensione no est prus a disponimentu. Diat pòdere non funtzionare prus comente si tocat e causare problemas cun s\'aplicatzione. Ti cussigiamus de la disinstallare. Formadu de sa data Agiornamentu globale - Essire dae %1$s\? + Essire dae %1$s? Essi Ses essidu In pàusa @@ -417,7 +417,7 @@ Impostatziones predefinidas de sos capìtulos agiornadas Imposta comente predefinidu Àplica fintzas a totu sos elementos in sa biblioteca mea - Ses seguru de chèrrere sarvare custas informatziones comente predefinidas\? + Ses seguru de chèrrere sarvare custas informatziones comente predefinidas? Impostatziones de sos capìtulos %1$s: %2$s, pàgina %3$d Impostatziones de chirca @@ -426,7 +426,7 @@ Ponet in pàusa sa registratzione de sa cronologia de leghidura Modalidade anònima Isbòida sa cronologia - Seguru ses\? As a pèrdere totu sa cronologia. + Seguru ses? As a pèrdere totu sa cronologia. Cronologia iscantzellada Pàgina imbeniente Pàgina anteposta @@ -608,7 +608,7 @@ Limba de s\'aplicatzione Peruna descritzione Archemissa - Boles iscantzellare sa categoria \"%s\"\? + Boles iscantzellare sa categoria \"%s\"? Errore internu: verìfica sos registros de sas serradas improvisas pro àteras informatziones Iscantzella sa categoria Istringa de agente de utente predefinida @@ -623,7 +623,7 @@ Iscàrriga sos imbenientes Iscàrriga in automàticu durante sa letura Funtzionat petzi si su capìtulu atuale e s\'imbeniente sunt giai iscarrigados. - Seguru ses\? + Seguru ses? As a bogare \"%s\" dae sa biblioteca tua Mùltiplas Ùrtimu agiornamentu de sa biblioteca: %s @@ -653,7 +653,7 @@ Sas versiones de F-Droid non sunt prus suportadas in manera ufitziale. \nToca pro nde ischire de prus. Butone pro sighire a lèghere - Bogare sa data\? + Bogare sa data? Invàlida s\'ìnditze de sos iscarrigamentos Custu at a bogare sa data de incumintzu seletzionada in antis dae %s Custu at a bogare sa data de fine seletzionada in antis dae %s @@ -684,7 +684,7 @@ Copiadu in punta de billete Tenes giai un\'elementu in sa biblioteca tua cun su matessi nùmene. \n -\nBoles sighire su matessi\? +\nBoles sighire su matessi? Brinca sos capìtulos duplicados A disponimentu ma sa fonte no est installada: %s Errore %1$s: %2$s @@ -711,7 +711,7 @@ Brincadu ca non bi fiat peruna publicatzione prevìdida oe Imposta s\'intervallu AB - Bogare s\'arrastadore de %s\? + Bogare s\'arrastadore de %s? Custu at a bogare s\'arrastamentu locale. Boga fintzas dae %s Iscantzella sos iscarrigados diff --git a/i18n/src/commonMain/resources/MR/sk/strings.xml b/i18n/src/commonMain/resources/MR/sk/strings.xml index 75508fe65..b1b3e461e 100644 --- a/i18n/src/commonMain/resources/MR/sk/strings.xml +++ b/i18n/src/commonMain/resources/MR/sk/strings.xml @@ -172,7 +172,7 @@ Frekvencia zálohovania Záloha bola vytvorená Obnovenie dokončené - Čo chcete zálohovať\? + Čo chcete zálohovať? Obnovuje sa záloha Vytvára sa záloha Vymazať vyrovnávaciu pamäť kapitol @@ -183,7 +183,7 @@ Súbory cookie boli vymazané Vymazať databázu Odstránenie histórie mangy, ktorá nie je uložená vo vašej knižnici - Ste si istý\? Čítanie kapitol a pokrok v mangách mimo vašu knižnicu sa stratí + Ste si istý? Čítanie kapitol a pokrok v mangách mimo vašu knižnicu sa stratí Späť Vpred Obnoviť @@ -213,7 +213,7 @@ Odstrániť z knižnice Pridané do knižnice Odstránené z knižnice - Odstrániť stiahnuté kapitoly\? + Odstrániť stiahnuté kapitoly? Skopírované do schránky: \n%1$s Zdroj nie je nainštalovaný: %1$s @@ -227,7 +227,7 @@ Podľa čísla kapitoly Stiahnuť Neprečítané - Naozaj chcete odstrániť vybrané kapitoly\? + Naozaj chcete odstrániť vybrané kapitoly? Sledovanie Čítanie Dokončený @@ -242,9 +242,9 @@ Typ Kategória s týmto názvom už existuje! Kategórie boli odstránené - Týmto sa odstráni dátum prečítania tejto kapitoly. Si si istý\? + Týmto sa odstráni dátum prečítania tejto kapitoly. Si si istý? Resetovať všetky kapitoly mangy - Pridať mangu do knižnice\? + Pridať mangu do knižnice? Obrázok bol uložený Vlastný filter Nastaviť ako obal @@ -252,7 +252,7 @@ Strana: %1$d Ďalšia kapitola nebola nájdená Obrázok sa nepodarilo načítať - Chcete použiť tento obrázok ako obal\? + Chcete použiť tento obrázok ako obal? Dokončená: Aktuálna: Ďalšia: @@ -389,15 +389,15 @@ Reťazec User-Agent nemôže byť prázdny Obnovenie nastavení čítačky pre jednotlivé série História bola odstránená - Ste si istý\? Celá história sa stratí. - Naozaj chcete tieto nastavenia uložiť ako predvolené\? + Ste si istý? Celá história sa stratí. + Naozaj chcete tieto nastavenia uložiť ako predvolené? Pridať do knižnice Krátko zobrazí aktuálny režim pri otvorení čítačky Odtiene šedej Najvyššie Zálohovanie už prebieha Obnoviť obaly knižnice - Odhlásiť sa z %1$s\? + Odhlásiť sa z %1$s? Kompletný zoznam Nenašla sa žiadna aplikácia na výber súborov Kompletný @@ -522,7 +522,7 @@ Uložiť logy o chybách do súboru na zdieľanie s vývojármi Obal Obal uložený - Prajete si vymazať kategóriu „%s“\? + Prajete si vymazať kategóriu „%s“? Vertikálne Sledovače, do ktorých nie ste prihlásení: DNS cez HTTPS (DoH) @@ -613,7 +613,7 @@ Vlastný obal Presunúť sériu na začiatok Zásady ochrany osobných údajov - Ste si istí\? + Ste si istí? Na výšku Predvolený reťazec pre User-Agent Prihláste sa znova do MAL diff --git a/i18n/src/commonMain/resources/MR/sq/strings.xml b/i18n/src/commonMain/resources/MR/sq/strings.xml index 1fecf51d7..b4e48e6d6 100644 --- a/i18n/src/commonMain/resources/MR/sq/strings.xml +++ b/i18n/src/commonMain/resources/MR/sq/strings.xml @@ -215,7 +215,7 @@ Redakto kategoritë Riemërto kategorinë Vendosni kategori - Dëshironi të fshini kategorinë \"%s\"\? + Dëshironi të fshini kategorinë \"%s\"? Fshi kategorinë Redakto kopertinën Shiko kapitujt @@ -403,7 +403,7 @@ Status i panjohur I licencuar U hoq nga biblioteka - Të fshihen kapitujt e shkarkuar\? + Të fshihen kapitujt e shkarkuar? Kopjimi në kujtesën e fragmenteve dështoi Burimi nuk është i instaluar: %1$s Sipas datës së ngarkimit @@ -473,12 +473,12 @@ Faqe: %1$d Kapitulli tjetër nuk u gjet Imazhi nuk mund të ngarkohej - Të përdoret ky imazh si kopertinë\? + Të përdoret ky imazh si kopertinë? Përfunduar: Aktuale: E mëparshme: I paafte për të hapur kapitullin e lexuar së fundi - A je i sigurt\? E gjithë historia do të humbasë. + A je i sigurt? E gjithë historia do të humbasë. Epo, kjo është e sikletshme Nuk është instaluar Kopjo @@ -494,7 +494,7 @@ Burimet që mungojnë: Rivendosja përfundoi Rezervimi është tashmë në proces - Çfarë dëshironi të bëni rezervë\? + Çfarë dëshironi të bëni rezervë? Duke krijuar rezervë Rezervimi dështoi Lejet e ruajtjes nuk janë dhënë @@ -536,7 +536,7 @@ Modaliteti i fshehtë Ndalon leximin e historisë Filtro të gjitha elementet në bibliotekën tuaj - Dil nga %1$s\? + Dil nga %1$s? Tani keni dalë nga llogaria Gabim i panjohur Kategoria po përditësohet @@ -556,7 +556,7 @@ Në bibliotekë Kopjuar në kujtesën e fragmenteve: \n %1$s - Të shtohet në bibliotekë\? + Të shtohet në bibliotekë? Kapitulli %1$s Po shkarkohet (%1$d/%2$d) Gabim @@ -569,11 +569,11 @@ Kopertina Kopertina u ruajt Gabim në ndarjen e kopertinës - Jeni i sigurt që dëshironi të fshini kapitujt e zgjedhur\? + Jeni i sigurt që dëshironi të fshini kapitujt e zgjedhur? Aplikoni gjithashtu për të gjitha elementet në bibliotekën time Vendose si parësore Nuk u gjet asnjë kapitull - A je i sigurt\? + A je i sigurt? Leximi E përfunduar Ne pritje @@ -643,8 +643,8 @@ Hiqe nga biblioteka Gabim gjatë ruajtjes së kopertinës Ju lutemi shtoni hyrjen në bibliotekën tuaj përpara se ta bëni këtë - Jeni i sigurt që dëshironi t\'i ruani këto cilësime si parazgjedhje\? - Kjo do të heqë datën e leximit të këtij kapitulli. A je i sigurt\? + Jeni i sigurt që dëshironi t\'i ruani këto cilësime si parazgjedhje? + 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ë Faqja e internetit @@ -687,7 +687,7 @@ Kopjo në kujtesën e fragmenteve Ju keni një hyrje në librarni me të njëjtin emër. \n -\nDëshironi të vazhdoni\? +\nDëshironi të vazhdoni? Përditëso kategorinë %1$s gabim: %2$s *kërkohet diff --git a/i18n/src/commonMain/resources/MR/sr/strings.xml b/i18n/src/commonMain/resources/MR/sr/strings.xml index 3d9c8d964..36eb985a0 100644 --- a/i18n/src/commonMain/resources/MR/sr/strings.xml +++ b/i18n/src/commonMain/resources/MR/sr/strings.xml @@ -163,7 +163,7 @@ Учесталост прављења резервних копија Направљена је резервна копија Враћање је завршено - Шта све желите да буде у резервној копији\? + Шта све желите да буде у резервној копији? Враћа се на резервну копију Прави се резервна копија Испразни кеш меморију поглавља @@ -174,7 +174,7 @@ Колачићи су избрисани Избриши базу података Обриши историју за наслове који нису сачувани у твојој колекцији - Да ли сте сигурни\? Изгубићете прочитана поглавља и статус наслова који нису у колекцији + Да ли сте сигурни? Изгубићете прочитана поглавља и статус наслова који нису у колекцији Уноси избрисани Верзија Пошаљи извештаје грешака @@ -198,7 +198,7 @@ Обриши из колекције Додано у колекцији Уклоњено из колекције - Обриши преузета поглавља\? + Обриши преузета поглавља? Копирано: \n%1$s Извор није инсталиран: %1$s @@ -213,7 +213,7 @@ Према броју поглавља Преузми Непрочитано - Да ли сте сигурни да желите да обришете следећа поглавља\? + Да ли сте сигурни да желите да обришете следећа поглавља? Отвори у WebView Прескочи поглавља означена као прочитана Помножи @@ -227,7 +227,7 @@ Овај додатак није више доступан. Можда неће исправно функционисати и може изазвати проблеме са апликацијом. Препоручује се деинсталирање. Формат датума Глобално ажурирање - Излогуј се из %1$s\? + Излогуј се из %1$s? Излогуј се Сада сте излоговани Више @@ -286,14 +286,14 @@ Следеће: Тренутно: Завршено: - Користи ову слику као омот\? + Користи ову слику као омот? Није могуће учитати слику Следеће поглавље није пронађено Страница: %1$d Подесиви филтер Слика је сачувана Ресетуј сва поглавља овог наслова - Ово ће уклонити датум читања овог поглавља. Да ли сте сигурни да то желите\? + Ово ће уклонити датум читања овог поглавља. Да ли сте сигурни да то желите? Категорије обрисане Категорија са овим именом већ постоји! Врста @@ -310,7 +310,7 @@ Читање Додај праћење Праћење - Додати у колекцију\? + Додати у колекцију? Мање Више У колекцији @@ -450,7 +450,7 @@ Омот сачуван Грешка при дељењу омота Постави као подразумевано - Да ли сте сигурни да желите да сачувате ова подешавања као подразумевана\? + Да ли сте сигурни да желите да сачувате ова подешавања као подразумевана? Такође примени на све наслове у мојој колекцији Није пронађено ни једно поглавље Датум почетка @@ -545,7 +545,7 @@ Извор није пронађен Избриши историју Историја је избрисана - Да ли сте сигурни\? Цела историја ће бити изгубљена. + Да ли сте сигурни? Цела историја ће бити изгубљена. Водич за миграцију извора Упозорење: велики број преузимања може довести до успоравања извора и/или блокирања Mihon-а. Додирни да сазнаш више. Велика ажурирања штете изворима и могу довести до споријег ажурирања и повећања потрошње батерије. Кликни да сазнаш више. @@ -600,7 +600,7 @@ Доступно, али извор није преузет: %s F-Droid верзије нису званично подржане. \nДодирните да сазнате више. - Уклони датум\? + Уклони датум? Овим уклањате претходно изабран почетни датум од %s Овим уклањате претходно изабран завршени датум од %s Последње ажурирање колекције било је: %s @@ -608,7 +608,7 @@ Ажурирање је већ у току Имате унос у колекцији са истим именом. \n -\nДа ли још увек желите да наставите\? +\nДа ли још увек желите да наставите? Завршена листа Наслови на чекању Незавршени наслови @@ -623,7 +623,7 @@ Непознат наслов Преглед Листа за читање - Да ли сте сигурни\? + Да ли сте сигурни? Трајање читања У глобалним ажурирањима Наслови @@ -657,7 +657,7 @@ Број непрочитаних Последња провера ажурирања Обриши категорију - Желиш ли обрисати категорију \"%s\"\? + Желиш ли обрисати категорију \"%s\"? Уклони све Упс! Поново покрени апликацију @@ -720,7 +720,7 @@ У реду Синхронизовање колекције завршено Постави да ажурира сваки - Уклони праћење %s\? + Уклони праћење %s? Ово ће уклонити локално праћење. Следеће очекивано ажурирање Синхронизовање колекције diff --git a/i18n/src/commonMain/resources/MR/sv/strings.xml b/i18n/src/commonMain/resources/MR/sv/strings.xml index 695f501a4..5231c7374 100644 --- a/i18n/src/commonMain/resources/MR/sv/strings.xml +++ b/i18n/src/commonMain/resources/MR/sv/strings.xml @@ -160,7 +160,7 @@ Automatisk säkerhetskopieringsfrekvens Säkerhetskopia skapad Återställning slutförd - Vad vill du säkerhetskopiera\? + Vad vill du säkerhetskopiera? Återställer säkerhetskopia Skapar säkerhetskopia Rensa kapitelcache @@ -171,7 +171,7 @@ Cookies rensade Rensa databas Ta bort historik för inlägg som inte finns i ditt bibliotek - Är du säker på det\? Lästa kapitel och framsteg för poster som inte finns i biblioteket kommer att gå förlorade + Är du säker på det? Lästa kapitel och framsteg för poster som inte finns i biblioteket kommer att gå förlorade Inlägg raderade Version Skicka kraschrapporter @@ -195,7 +195,7 @@ Ta bort från biblioteket Lades till i biblioteket Togs bort från biblioteket - Ta bort nedladdade kapitel\? + Ta bort nedladdade kapitel? Kopierat till urklipp: \n%1$s Källa inte installerad: %1$s @@ -209,7 +209,7 @@ Efter kapitelnummer Ladda ner Olästa - Är du säker på att du vill radera valda kapitel\? + Är du säker på att du vill radera valda kapitel? Spårning Läser Färdig @@ -224,9 +224,9 @@ Typ En kategori med detta namn finns redan! Kategorier raderade - Detta kommer att ta bort läs datumet för detta kapitel. Är du säker\? + Detta kommer att ta bort läs datumet för detta kapitel. Är du säker? Återställ alla kapitel för denna post - Lägg till i biblioteket\? + Lägg till i biblioteket? Bild sparad Anpassat filter Ställ in som omslag @@ -234,7 +234,7 @@ Sida: %1$d Nästa kapitel kunde inte hittas Bilden kunde inte laddas - Sätt bild som omslag\? + Sätt bild som omslag? Färdig: Nuvarande: Nästa: @@ -290,7 +290,7 @@ 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\? + Logga ut från %1$s? Logga ut Du är nu utloggad Pausad @@ -412,7 +412,7 @@ Detta förhindrar inte inofficiella eller potentiellt felaktigt flaggade tillägg från att visa NSFW(18+) innehåll i appen. Fel Uppdaterade standardinställningar för kapitel - Vill du spara de här inställningarna som standard\? + Vill du spara de här inställningarna som standard? %1$s: %2$s, sida %3$d Inga kapitel hittades Ange som standard @@ -423,7 +423,7 @@ Pausar läshistoriken Inkognitoläge Rensa historik - Är du säker\? All historik kommer att försvinna. + Är du säker? All historik kommer att försvinna. Historiken har tagits bort Sök inställningar Nästa sida @@ -604,7 +604,7 @@ Lavendel Ingen beskrivning Appspråk - Vill du radera kategorin \"%s\"\? + Vill du radera kategorin \"%s\"? Interntfel: Kontrollera kraschloggar för ytterligare information Ta bort kategori Standardsträng för användaragent @@ -616,7 +616,7 @@ En uppdatering pågår redan Flerspråkig Strängen för användaragent kan inte vara tom - Är du säker\? + Är du säker? Biblioteket uppdaterades senast: %s Populära Du är på väg att ta bort \"%s\" från ditt bibliotek @@ -674,8 +674,8 @@ N/A Du har en post i ditt bibliotek med samma namn. \n -\nVill du fortfarande fortsätta\? - Ta bort datum\? +\nVill du fortfarande fortsätta? + Ta bort datum? Inlägg I global uppdatering Totalt @@ -690,7 +690,7 @@ %1$s fel: %2$s Dubbeltryck för att zooma Detta kommer att ta bort ditt tidigare valda startdatum från %s - Ta bort %s spårning\? + Ta bort %s spårning? Detta kommer att ta bort spårningen lokalt. Detta kommer att ta bort ditt tidigare valda slutdatum från %s Ta även bort från %s @@ -726,7 +726,7 @@ Flytta serien till botten Sortera kategorier Uppdaterar biblioteket... (%s) - Vill du sortera kategorierna i alfabetisk ordning\? + Vill du sortera kategorierna i alfabetisk ordning? Inställningar för källa Appinställningar Relativa tidsstämplar diff --git a/i18n/src/commonMain/resources/MR/te/strings.xml b/i18n/src/commonMain/resources/MR/te/strings.xml index 2ec8dc8cb..4c0e0cc67 100644 --- a/i18n/src/commonMain/resources/MR/te/strings.xml +++ b/i18n/src/commonMain/resources/MR/te/strings.xml @@ -158,7 +158,7 @@ చదవని లెక్క చివరి అప్డేట్ తనిఖీ మొత్తం తీసేయండి - మీరు \"%s\" క్యాటగిరీ తీసివేయాలి అనుకుంటున్నారా\? + మీరు \"%s\" క్యాటగిరీ తీసివేయాలి అనుకుంటున్నారా? క్యాటగిరీ తీసేయండి సిరీస్ పైన పెట్టండి అన్వేషించండి… diff --git a/i18n/src/commonMain/resources/MR/th/strings.xml b/i18n/src/commonMain/resources/MR/th/strings.xml index 033996db9..b2b3da3bf 100644 --- a/i18n/src/commonMain/resources/MR/th/strings.xml +++ b/i18n/src/commonMain/resources/MR/th/strings.xml @@ -161,7 +161,7 @@ ความถี่ในการสำรองข้อมูลอัตโนมัติ สร้างการสำรองข้อมูลแล้ว คืนค่าเสร็จสมบูรณ์ - ต้องการสำรองข้อมูลใดบ้าง\? + ต้องการสำรองข้อมูลใดบ้าง? กำลังคืนค่าการสำรองข้อมูล กำลังสร้างการสำรองข้อมูล ล้างแคชตอน @@ -172,7 +172,7 @@ ล้างคุกกี้แล้ว ล้างฐานข้อมูล ลบประวัติรายการที่ไม่ได้บันทึกไว้ในคลัง - แน่ใจไหม\? ข้อมูลการอ่านของรายการที่ไม่ได้อยู่ในคลังจะหายไป + แน่ใจไหม? ข้อมูลการอ่านของรายการที่ไม่ได้อยู่ในคลังจะหายไป ลบรายการแล้ว เวอร์ชัน ส่งรายงานความผิดพลาด @@ -196,7 +196,7 @@ นำออกจากคลัง เพิ่มไปยังคลังแล้ว นำออกจากคลังแล้ว - ลบตอนที่ดาวน์โหลดแล้วหรือไม่\? + ลบตอนที่ดาวน์โหลดแล้วหรือไม่? คัดลอกไปยังคลิปบอร์ดแล้ว: \n%1$s ไม่ได้ติดตั้งแหลงที่มา: %1$s @@ -210,7 +210,7 @@ ตามหมายเลขตอน ดาวน์โหลด ที่ยังไม่ได้อ่าน - แน่ใจหรือไม่ว่าต้องการลบตอนที่เลือก\? + แน่ใจหรือไม่ว่าต้องการลบตอนที่เลือก? การติดตาม กำลังอ่าน จบแล้ว @@ -233,9 +233,9 @@ ประเภท หมวดหมู่ที่มีชื่อนี้มีอยู่แล้ว! หมวดหมู่ถูกลบแล้ว - การดำเนินการนี้จะลบวันที่อ่านของตอนนี้ คุณแน่ใจไหม\? + การดำเนินการนี้จะลบวันที่อ่านของตอนนี้ คุณแน่ใจไหม? รีเซ็ตตอนทั้งหมดสำหรับรายการนี้ - เพิ่มไปยังคลัง\? + เพิ่มไปยังคลัง? บันทึกรูปภาพแล้ว ตัวกรอง ตั้งเป็นปก @@ -243,7 +243,7 @@ หน้า: %1$d ไม่พบตอนถัดไป ไม่สามารถโหลดภาพนี้ได้ - ใช้ภาพนี้เป็นรูปปก\? + ใช้ภาพนี้เป็นรูปปก? อ่านจบแล้ว: ปัจจุบัน: ตอนถัดไป: @@ -387,7 +387,7 @@ ถัดไป ขอบ คู่มือการโยกย้ายแหล่งที่มา - แน่ใจไหม\? ประวัติทั้งหมดจะหายไป + แน่ใจไหม? ประวัติทั้งหมดจะหายไป ลบประวัติแล้ว ล้างประวัติ ตอน %1$s - %2$s @@ -405,7 +405,7 @@ ไม่พบตอน ตั้งเป็นค่าเริ่มต้น นําไปใช้กับรายการทั้งหมดในคลังด้วย - แน่ใจหรือไม่ว่าต้องการบันทึกการตั้งค่าเหล่านี้เป็นค่าเริ่มต้น\? + แน่ใจหรือไม่ว่าต้องการบันทึกการตั้งค่าเหล่านี้เป็นค่าเริ่มต้น? การตั้งค่าตอน ตามวันที่อัปโหลด ไม่สามารถคัดลอกไปยังคลิปบอร์ด @@ -425,7 +425,7 @@ จากคลัง ออกจากระบบแล้ว ออกจากระบบ - ออกจากระบบจาก %1$s\? + ออกจากระบบจาก %1$s? ที่อยู่อีเมล กรองรายการทั้งหมดในคลังของคุณ ปิดใช้งานโหมดไม่ระบุตัวตน @@ -604,7 +604,7 @@ ภาษาของแอพ ไม่มีคำอธิบาย ลาเวนเดอร์ - ต้องการจะลบหมวดหมู่ \"%s\" ไหม\? + ต้องการจะลบหมวดหมู่ \"%s\" ไหม? ลบหมวดหมู่ InternalError: ตรวจสอบบันทึกการแครชสำหรับข้อมูลเพิ่มเติม ตัวแทนผู้ใช้เริ่มต้น @@ -619,7 +619,7 @@ ดาวน์โหลดล่วงหน้า ดาวน์โหลดอัตโนมัติขณะกำลังอ่าน ใช้ได้เฉพาะกับตอนปัจจุบัน + ตอนถัด ๆ ไปดาวน์โหลดไว้อยู่แล้ว - แน่ใจแล้วใช่หรือไม่\? + แน่ใจแล้วใช่หรือไม่? หลายภาษา อัปเดตคลังล่าสุด: %s คุณกำลังจะนำ \"%s\" ออกจากคลัง @@ -652,7 +652,7 @@ ดัชนีการดาวน์โหลดไม่ถูกต้อง การกระทำนี้จะเป็นการนำวันที่เริ่มอ่านของรายการที่เลือกไว้ก่อนหน้าจาก %s ออก การกระทำนี้จะเป็นการนำวันที่อ่านจบของรายการที่เลือกไว้ก่อนหน้าจาก %s ออก - นำวันที่ออก\? + นำวันที่ออก? บังคับให้ตรวจสอบบทที่ดาวน์โหลดไว้ เริ่มต้น ดาวน์โหลดไว้แล้ว @@ -682,7 +682,7 @@ พร้อมใช้งานแต่ไม่ได้ติดตั้งแหล่งที่มา: %s มีรายการชื่อเดียวกันอยู่ในคลัง \n -\nต้องการดำเนินการต่อหรือไม่\? +\nต้องการดำเนินการต่อหรือไม่? %1$s ข้อผิดพลาด: %2$s *จำเป็น ซ่อนรายการที่มีอยู่ในคลังอยู่แล้ว @@ -707,7 +707,7 @@ ตั้งค่าให้อัพเดตทุกๆ ช่วงเวลา ตกลง - นำการติดตามของ %s ออกไหม\? + นำการติดตามของ %s ออกไหม? การกระทำนี้จะเป็นการลบการติดตามภายในเครื่อง และลบออกจาก %s เช่นกัน ซิงค์คลังเสร็จสมบูรณ์ @@ -728,7 +728,7 @@ แสดง \"%1$s\" แทน \"%2$s\" จัดเรียงหมวดหมู่ กำลังอัปเดตคลัง... (%s) - ต้องการจัดเรียงหมวดหมู่ตามตัวอักษรหรือไม่\? + ต้องการจัดเรียงหมวดหมู่ตามตัวอักษรหรือไม่? การตั้งค่าแหล่งที่มา การตั้งค่าแอป ไม่มีไฟล์ถูกเลือกไว้ diff --git a/i18n/src/commonMain/resources/MR/tr/strings.xml b/i18n/src/commonMain/resources/MR/tr/strings.xml index 7163b3dc7..1aae5877e 100644 --- a/i18n/src/commonMain/resources/MR/tr/strings.xml +++ b/i18n/src/commonMain/resources/MR/tr/strings.xml @@ -171,7 +171,7 @@ Çerezler temizlendi Veritabanını temizle Kitaplığına kaydedilmeyen girdilerin geçmiş bilgisini sil - Emin misin\? Kitaplıkta olmayan girdilerin okunan bölümleri ve ilerlemesi kaybolacak + Emin misin? Kitaplıkta olmayan girdilerin okunan bölümleri ve ilerlemesi kaybolacak Girdiler silindi Sürüm Çökme bildirimi gönder @@ -226,7 +226,7 @@ Kategoriler silindi Bu, bu bölümün okunma tarihini kaldıracak. Emin misiniz? Bu girdi için tüm bölümleri sıfırla - Kitaplığa eklensin mi\? + Kitaplığa eklensin mi? Resim kaydedildi Özel süzgeç Kapak olarak ayarla @@ -234,7 +234,7 @@ Sayfa: %1$d Sonraki bölüm bulunamadı Görsel yüklenemedi - Bu görsel kapak resmi olarak kullanılsın mı\? + Bu görsel kapak resmi olarak kullanılsın mı? Eklenecek veriyi seç Geçiş yap Kopyala @@ -290,7 +290,7 @@ Bu uzantı artık kullanılamıyor. Düzgün çalışmayabilir ve uygulamada sorunlara neden olabilir. Kaldırılması önerilir. Tarih biçimi Genel güncelleme - %1$s \'den çıkılsın mı\? + %1$s \'den çıkılsın mı? Çıkış yap Şimdi çıktınız Duraklatıldı @@ -415,7 +415,7 @@ Öntanımlı bölüm ayarları güncellendi Öntanımlı olarak ayarla Kitaplığımdaki tüm girdilere de uygula - Bu ayarları varsayılan olarak kaydetmek istediğinizden emin misiniz\? + Bu ayarları varsayılan olarak kaydetmek istediğinizden emin misiniz? Bölüm ayarları %1$s: %2$s, sayfa %3$d Arama ayarları @@ -424,7 +424,7 @@ Okuma geçmişini duraklatır Gizli kip Geçmişi sil - Emin misiniz\? Tüm geçmiş silinecek. + Emin misiniz? Tüm geçmiş silinecek. Geçmiş silindi Sonraki sayfa Önceki sayfa @@ -619,7 +619,7 @@ Önceden indir Okurken kendiliğinden indir Yalnızca geçerli bölüm ile bir sonraki bölüm zaten indirildiyse çalışır. - Emin misiniz\? + Emin misiniz? \"%s\"yi kitaplığınızdan kaldırmak üzeresiniz Kitaplık son güncelleme zamanı: %s Çoklu @@ -649,7 +649,7 @@ F-Droid derlemeleri resmi olarak desteklenmemektedir. \nDaha fazla bilgi edinmek için dokunun. Okumayı sürdür düğmesi - Tarih kaldırılsın mı\? + Tarih kaldırılsın mı? Bu, daha önce seçtiğiniz %s başlangıç tarihini kaldıracak Bu, daha önce seçtiğiniz %s bitiş tarihini kaldıracak İndirmeler dizinini geçersiz kıl @@ -682,7 +682,7 @@ Tekrarlanan bölümleri atla Kitaplığınızda aynı ada sahip bir girdiniz var. \n -\nYine de devam etmek istiyor musunuz\? +\nYine de devam etmek istiyor musunuz? %1$s hatası: %2$s *gerekli Zaten kitaplıkta bulunan girdileri gizle @@ -705,7 +705,7 @@ Bir sonraki yayınlama zamanını tahmin et TAMAM Hepsini tahmin et - %s izlemesi kaldırılsın mı\? + %s izlemesi kaldırılsın mı? Atlandı çünkü bugün bir yayın beklenmiyordu Bu, izlemeyi yerel olarak kaldıracak. Ayrıca şuradan da kaldır: %s diff --git a/i18n/src/commonMain/resources/MR/uk/strings.xml b/i18n/src/commonMain/resources/MR/uk/strings.xml index 264cf3b13..d03c85884 100644 --- a/i18n/src/commonMain/resources/MR/uk/strings.xml +++ b/i18n/src/commonMain/resources/MR/uk/strings.xml @@ -160,7 +160,7 @@ Частота автоматичних бекапів Резервна копія створена Відновлення завершено - Що ви хочете зберегти до резервної копії\? + Що ви хочете зберегти до резервної копії? Відновлення резервної копії Створення резервної копії Очистити кеш розділів @@ -171,7 +171,7 @@ Куки очищено Очистити базу даних Видалити історію для записів, котрі не знаходяться в вашій бібліотеці - Ви впевнені\? Прочитані розділи та прогрес не бібліотечних записів будуть втрачені + Ви впевнені? Прочитані розділи та прогрес не бібліотечних записів будуть втрачені Дані видалено Версія Надсилати звіти про падіння @@ -195,7 +195,7 @@ Видалити з бібліотеки Додати до бібліотеки Видалено з бібліотеки - Видаляти завантажені розділи\? + Видаляти завантажені розділи? Скопійовано до буферу \n%1$s Джерело не встановлено: %1$s @@ -209,7 +209,7 @@ За номером розділу Завантажити Непрочитані - Ви впевнені, що бажаєте видалити вибрані розділи\? + Ви впевнені, що бажаєте видалити вибрані розділи? Стежити В процесі читання Завершене @@ -224,9 +224,9 @@ Тип Категорія з таким іменем вже існує! Категорію видалено - Ця дія видалить дату прочитання розділу. Ви впевнені\? + Ця дія видалить дату прочитання розділу. Ви впевнені? Скинути всіх розділи для цього запису - Додати до бібліотеки\? + Додати до бібліотеки? Картинку збережено Користувацький фільтр Встановити як обкладинку @@ -234,7 +234,7 @@ Сторінка %1$d Наступний розділ не знайдено Зображення не вдалося завантажити - Ви бажаєте встановити цю картинку як обкладинку\? + Ви бажаєте встановити цю картинку як обкладинку? Завершено: Поточна: Наступна: @@ -290,7 +290,7 @@ Це розширення більше не доступне. Воно може працювати невірно, а також викликати проблеми із застосунком. Рекомендується його видалити. Формат дати Глобальне оновлення - Вихід із %1$s\? + Вихід із %1$s? Вихід Ви вийшли з системи Призупинено @@ -416,7 +416,7 @@ %1$s: %2$s, сторінка %3$d Зберегти налаштування Також застосувати для усіх записів в моїй бібліотеці - Ви впевнені, що бажаєте зберегти ці налаштування як основні\? + Ви впевнені, що бажаєте зберегти ці налаштування як основні? Налаштування розділу Завантажені розділи З бібліотеки @@ -424,7 +424,7 @@ Призупиняє історію читання Режим інкогніто Очистити історію - Ви впевнені\? Всю історію буде втрачено. + Ви впевнені? Всю історію буде втрачено. Історію видалено Наступна сторінка Попередня сторінка @@ -605,7 +605,7 @@ Відкрити на GitHub Нова версія наявна з офіційних релізів. Тицьніть для того аби дізнатись як мігрувати з неофіційних релізів F-Droid. InternalError: Перевірте журнал халеп задля додаткової інформації - Чи бажаєте ви видалити категорію \"%s\"\? + Чи бажаєте ви видалити категорію \"%s\"? Видалити категорію Переглянути нещодавно оновлені записи бібліотеки Віджет не доступний при ввімкненому блокуванні застосунку @@ -620,7 +620,7 @@ Працює, якщо поточний розділ + наступний вже завантажені. Ви збираєтесь видалити \"%s\" з вашої бібліотеки Приливна Хвиля - Ви впевнені\? + Ви впевнені? Оновлення вже розпочато Рядок User agent не може бути пустим Популярне @@ -650,7 +650,7 @@ \nТицьніть для того, щоб дізнатись більше. Змусити застосунок повторно перевірити завантажені розділи Кнопка \"Продовжити читання\" - Видалити дату\? + Видалити дату? Це призведе до видалення вибраної раніше дати початку з %s Це призведе до видалення раніше вибраної дати завершення з %s Недійсний індекс завантажень @@ -682,7 +682,7 @@ Пропуск повторюваних розділів У бібліотеці є запис з такою ж назвою. \n -\nВи все ще хочете продовжити\? +\nВи все ще хочете продовжити? %1$s помилка: %2$s *необхідно Приховати записи, які вже є в бібліотеці @@ -708,7 +708,7 @@ Пропущено, оскільки сьогодні не очікується жодного релізу ОК Також видалити з %s - Видалити відстеження %s\? + Видалити відстеження %s? Це видалить відстеження локально. Видалити завантажене Має результати diff --git a/i18n/src/commonMain/resources/MR/uz/strings.xml b/i18n/src/commonMain/resources/MR/uz/strings.xml index 17f1ac02f..89c52918a 100644 --- a/i18n/src/commonMain/resources/MR/uz/strings.xml +++ b/i18n/src/commonMain/resources/MR/uz/strings.xml @@ -86,7 +86,7 @@ Bir tomonlama sinxronlash, kengaytirilgan sinxronlash Tizimdagidek Lavanda - \"%s\" toifasini o\'chirishni hohlaysizmi\? + \"%s\" toifasini o\'chirishni hohlaysizmi? Oldingi bob Keyingi bob Ko\'chirish diff --git a/i18n/src/commonMain/resources/MR/vi/strings.xml b/i18n/src/commonMain/resources/MR/vi/strings.xml index 16148241d..ca4675d6e 100644 --- a/i18n/src/commonMain/resources/MR/vi/strings.xml +++ b/i18n/src/commonMain/resources/MR/vi/strings.xml @@ -124,7 +124,7 @@ Thông tin lưu trữ đã được xóa Dọn dẹp cơ sở dữ liệu Xóa lịch sử đọc truyện và các chương không nằm trong thư viện - 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 + 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ác mục đã bị xóa Phiên bản Gửi báo cáo lỗi @@ -166,7 +166,7 @@ Danh mục đã bị xóa Sẽ xóa ngày đọc các chương. Bạn chắc chứ? Làm mới mọi chương của truyện này - Thêm truyện vào thư viện\? + Thêm truyện vào thư viện? Đã lưu ảnh Bộ lọc tùy chỉnh Đặt làm ảnh bìa @@ -242,7 +242,7 @@ Lịch sao lưu Sao lưu đã được tạo Khôi phục hoàn tất - Bạn có muốn sao lưu không\? + Bạn có muốn sao lưu không? Khôi phục sao lưu Đang tạo bản sao lưu Nguồn cục bộ @@ -252,7 +252,7 @@ Duyệt Đã thêm vào thư viện Đã xóa khỏi thư viện - Muốn xóa các chương đã tải\? + Muốn xóa các chương đã tải? Đã sao chép vào bảng tạm: \n%1$s Nguồn truyện không thể cài đặt: %1$s @@ -289,7 +289,7 @@ Đã tạm ngưng Bạn đã đăng xuất Đăng xuất tài khoản - Đăng xuất khỏi %1$s\? + Đăng xuất khỏi %1$s? Địa chỉ email Tối ưu pin đã được tắt Giúp cập nhật và sao lưu thư viện nền @@ -360,7 +360,7 @@ Hướng dẫn di chuyển nguồn Cài đặt tìm kiếm Sao lưu đang trong quá trình thực hiện - Bạn có chắc không\? Tất cả lịch sử sẽ bị xoá. + Bạn có chắc không? Tất cả lịch sử sẽ bị xoá. Truyện trong danh mục bị loại trừ sẽ không được cập nhật. Ngày kết thúc Chế độ làm sáng lên @@ -404,7 +404,7 @@ Không tìm thấy chương Đặt mặc định Ngoài ra có hiệu lực với tất cả truyện trong thư viện - Bạn có chắc chắn muốn lưu lại tuỳ chỉnh này hay không\? + 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 Sao chép vào bảng tạm thất bại @@ -609,7 +609,7 @@ Không thể tìm thấy đường dẫn của trang %d Cài lại chuỗi đại diện người dùng mặc định Không có mô tả - Bạn có muốn xóa thể loại \"%s\"\? + Bạn có muốn xóa thể loại \"%s\"? Tùy chỉnh ảnh bìa Tiện ích không còn khả dụng khi khóa ứng dụng đang bật Xem các bộ truyện được cập nhật gần đây @@ -621,7 +621,7 @@ Tự động tải khi đang đọc Quyền truy cập lưu trữ không được thông qua Nổi Tiếng - Bạn chắc chứ\? + Bạn chắc chứ? Bạn sẽ xóa bỏ \"%s\" này ra khỏi thư viện của bạn Thư viện lần cuối được cập nhật:%s Tải Trước @@ -680,9 +680,9 @@ Đ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. + 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\? +\nBạn có muốn thêm lại không? Đã sao chép vào bảng tạm Sao chép vào bảng tạm %d giây @@ -690,7 +690,7 @@ 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 - Loại bỏ ngày\? + Loại bỏ ngày? %1$s Lỗi: %2$s N/A *bắt buộc From f5a41e9693c0e23bcedcad3b93ca33ee1b8f67db Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 5 Apr 2024 06:20:12 +0600 Subject: [PATCH 146/212] chore(deps): update gradle/actions action to v3.2.0 (#621) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 72310a6e4..d58311c47 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -35,7 +35,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 + uses: gradle/actions/setup-gradle@e24011a3b5db78bd5ab798036042d9312002f252 # v3.2.0 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index af014878b..878382f17 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -33,7 +33,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@417ae3ccd767c252f5661f1ace9f835f9654f2b5 # v3.1.0 + uses: gradle/actions/setup-gradle@e24011a3b5db78bd5ab798036042d9312002f252 # v3.2.0 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest From c3fd2df6f55b70e49088f84209668c4530a9a9c6 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 6 Apr 2024 08:39:32 +0600 Subject: [PATCH 147/212] Disable mpp and agp compability warning --- gradle.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/gradle.properties b/gradle.properties index 22e7dfed5..ec3e136f6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,6 +2,7 @@ android.nonTransitiveRClass=false android.useAndroidX=true kotlin.code.style=official +kotlin.mpp.androidGradlePluginCompatibility.nowarn=true kotlin.mpp.androidSourceSetLayoutVersion=2 org.gradle.caching=true From d77f2f429d2603a5c2b805f2dc7255af41474cf8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 6 Apr 2024 09:47:44 +0600 Subject: [PATCH 148/212] fix(deps): update sqldelight to v2.0.2 (#544) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bfc25a4f1..7a5e8555b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ moko = "0.23.0" okhttp_version = "5.0.0-alpha.12" richtext = "0.20.0" shizuku_version = "12.2.0" -sqldelight = "2.0.0" +sqldelight = "2.0.2" sqlite = "2.4.0" voyager = "1.0.0" detekt = "1.23.6" From aed53d3bdc85ce0e899fbb90b9f9cad0f1b86480 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 6 Apr 2024 11:07:11 +0600 Subject: [PATCH 149/212] Fix build time zone in about screen And slight cleanup --- .../more/settings/screen/about/AboutScreen.kt | 16 +++++++++++----- buildSrc/src/main/kotlin/Commands.kt | 15 ++++++++------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt index 615687aff..ba4e42c7a 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt @@ -55,8 +55,10 @@ import tachiyomi.presentation.core.icons.Reddit import tachiyomi.presentation.core.icons.X import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import java.time.Instant import java.time.LocalDateTime import java.time.ZoneId +import java.time.ZonedDateTime import java.time.format.DateTimeFormatter import java.util.Locale @@ -269,11 +271,15 @@ object AboutScreen : Screen() { internal fun getFormattedBuildTime(): String { return try { - val df = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm'Z'", Locale.US) - .withZone(ZoneId.of("UTC")) - val buildTime = LocalDateTime.from(df.parse(BuildConfig.BUILD_TIME)) - - buildTime!!.toDateTimestampString(UiPreferences.dateFormat(Injekt.get().dateFormat().get())) + LocalDateTime.ofInstant( + Instant.parse(BuildConfig.BUILD_TIME), + ZoneId.systemDefault(), + ) + .toDateTimestampString( + UiPreferences.dateFormat( + Injekt.get().dateFormat().get(), + ), + ) } catch (e: Exception) { BuildConfig.BUILD_TIME } diff --git a/buildSrc/src/main/kotlin/Commands.kt b/buildSrc/src/main/kotlin/Commands.kt index 8e5fe7649..5592c4eef 100644 --- a/buildSrc/src/main/kotlin/Commands.kt +++ b/buildSrc/src/main/kotlin/Commands.kt @@ -1,8 +1,8 @@ import org.gradle.api.Project import java.io.ByteArrayOutputStream -import java.text.SimpleDateFormat -import java.util.TimeZone -import java.util.Date +import java.time.LocalDateTime +import java.time.ZoneOffset +import java.time.format.DateTimeFormatter // Git is needed in your system PATH for these commands to work. // If it's not installed, you can return a random value as a workaround @@ -16,10 +16,11 @@ fun Project.getGitSha(): String { // return "1" } +private val BUILD_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'") + +@Suppress("UnusedReceiverParameter") fun Project.getBuildTime(): String { - val df = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'") - df.timeZone = TimeZone.getTimeZone("UTC") - return df.format(Date()) + return LocalDateTime.now(ZoneOffset.UTC).format(BUILD_TIME_FORMATTER) } fun Project.runCommand(command: String): String { @@ -29,4 +30,4 @@ fun Project.runCommand(command: String): String { standardOutput = byteOut } return String(byteOut.toByteArray()).trim() -} \ No newline at end of file +} From e448e40406e8d9916120a278e42829a6f1b25a7a Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 6 Apr 2024 11:07:11 +0600 Subject: [PATCH 150/212] Rework buildSrc and remove usage of subprojects --- app/build.gradle.kts | 34 +----- build.gradle.kts | 44 ------- buildSrc/build.gradle.kts | 7 +- buildSrc/settings.gradle.kts | 7 +- buildSrc/src/main/kotlin/AndroidConfig.kt | 6 - .../src/main/kotlin/LocalesConfigPlugin.kt | 39 ------ ...hon.android.application.compose.gradle.kts | 11 ++ .../mihon.android.application.gradle.kts | 17 +++ .../main/kotlin/mihon.benchmark.gradle.kts | 13 ++ ...radle.kts => mihon.code.detekt.gradle.kts} | 0 .../kotlin/mihon.library.compose.gradle.kts | 10 ++ .../src/main/kotlin/mihon.library.gradle.kts | 12 ++ .../kotlin/mihon/buildlogic/AndroidConfig.kt | 11 ++ .../kotlin/{ => mihon/buildlogic}/Commands.kt | 6 +- .../mihon/buildlogic/ProjectExtensions.kt | 112 ++++++++++++++++++ .../buildlogic/tasks/LocalesConfigPlugin.kt | 37 ++++++ core-metadata/build.gradle.kts | 2 +- core/common/build.gradle.kts | 2 +- data/build.gradle.kts | 4 +- domain/build.gradle.kts | 2 +- i18n/build.gradle.kts | 11 +- macrobenchmark/build.gradle.kts | 3 +- presentation-core/build.gradle.kts | 12 +- presentation-widget/build.gradle.kts | 11 +- source-api/build.gradle.kts | 2 +- source-local/build.gradle.kts | 2 +- 26 files changed, 262 insertions(+), 155 deletions(-) delete mode 100644 buildSrc/src/main/kotlin/AndroidConfig.kt delete mode 100644 buildSrc/src/main/kotlin/LocalesConfigPlugin.kt create mode 100644 buildSrc/src/main/kotlin/mihon.android.application.compose.gradle.kts create mode 100644 buildSrc/src/main/kotlin/mihon.android.application.gradle.kts create mode 100644 buildSrc/src/main/kotlin/mihon.benchmark.gradle.kts rename buildSrc/src/main/kotlin/{detekt.gradle.kts => mihon.code.detekt.gradle.kts} (100%) create mode 100644 buildSrc/src/main/kotlin/mihon.library.compose.gradle.kts create mode 100644 buildSrc/src/main/kotlin/mihon.library.gradle.kts create mode 100644 buildSrc/src/main/kotlin/mihon/buildlogic/AndroidConfig.kt rename buildSrc/src/main/kotlin/{ => mihon/buildlogic}/Commands.kt (90%) create mode 100644 buildSrc/src/main/kotlin/mihon/buildlogic/ProjectExtensions.kt create mode 100644 buildSrc/src/main/kotlin/mihon/buildlogic/tasks/LocalesConfigPlugin.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3101b6a0e..3d091029a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,11 +1,14 @@ +import mihon.buildlogic.getBuildTime +import mihon.buildlogic.getCommitCount +import mihon.buildlogic.getGitSha import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { - id("com.android.application") + id("mihon.android.application") + id("mihon.android.application.compose") id("com.mikepenz.aboutlibraries.plugin") - kotlin("android") - kotlin("plugin.serialization") id("com.github.zellius.shortcut-helper") + kotlin("plugin.serialization") } if (gradle.startParameter.taskRequests.toString().contains("Standard")) { @@ -119,7 +122,6 @@ android { buildFeatures { viewBinding = true - compose = true buildConfig = true // Disable some unused things @@ -132,10 +134,6 @@ android { abortOnError = false checkReleaseBuilds = false } - - composeOptions { - kotlinCompilerExtensionVersion = compose.versions.compiler.get() - } } dependencies { @@ -150,7 +148,6 @@ dependencies { implementation(projects.presentationWidget) // Compose - implementation(platform(compose.bom)) implementation(compose.activity) implementation(compose.foundation) implementation(compose.material3.core) @@ -295,25 +292,6 @@ tasks { "-opt-in=kotlinx.coroutines.InternalCoroutinesApi", "-opt-in=kotlinx.serialization.ExperimentalSerializationApi", ) - - if (project.findProperty("tachiyomi.enableComposeCompilerMetrics") == "true") { - kotlinOptions.freeCompilerArgs += listOf( - "-P", - "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" + - project.layout.buildDirectory.dir("compose_metrics").get().asFile.absolutePath, - ) - kotlinOptions.freeCompilerArgs += listOf( - "-P", - "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" + - project.layout.buildDirectory.dir("compose_metrics").get().asFile.absolutePath, - ) - } - - // https://developer.android.com/jetpack/androidx/releases/compose-compiler#1.5.9 - kotlinOptions.freeCompilerArgs += listOf( - "-P", - "plugin:androidx.compose.compiler.plugins.kotlin:nonSkippingGroupOptimization=true", - ) } } diff --git a/build.gradle.kts b/build.gradle.kts index dfaf0816e..93146c0a6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,8 +1,3 @@ -import com.android.build.gradle.BaseExtension -import com.android.build.gradle.BasePlugin -import org.gradle.api.tasks.testing.logging.TestLogEvent -import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile - buildscript { dependencies { classpath(libs.android.shortcut.gradle) @@ -17,45 +12,6 @@ plugins { alias(kotlinx.plugins.serialization) apply false } -subprojects { - tasks.withType { - kotlinOptions { - jvmTarget = JavaVersion.VERSION_17.toString() - } - } - - tasks.withType { - useJUnitPlatform() - testLogging { - events(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED) - } - } - - plugins.withType { - plugins.apply("detekt") - configure { - compileSdkVersion(AndroidConfig.compileSdk) - defaultConfig { - minSdk = AndroidConfig.minSdk - targetSdk = AndroidConfig.targetSdk - ndk { - version = AndroidConfig.ndk - } - } - - compileOptions { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - isCoreLibraryDesugaringEnabled = true - } - - dependencies { - add("coreLibraryDesugaring", libs.desugar) - } - } - } -} - tasks.register("clean") { delete(rootProject.layout.buildDirectory) } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index a948efcc7..0ce5d68de 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -4,8 +4,11 @@ plugins { dependencies { implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) - implementation(androidxLibs.gradle) - implementation(kotlinLibs.gradle) + implementation(files(androidx.javaClass.superclass.protectionDomain.codeSource.location)) + implementation(files(compose.javaClass.superclass.protectionDomain.codeSource.location)) + implementation(files(kotlinx.javaClass.superclass.protectionDomain.codeSource.location)) + implementation(androidx.gradle) + implementation(kotlinx.gradle) implementation(libs.detekt.gradlePlugin) implementation(gradleApi()) } diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts index 05a279df0..1b058e0d8 100644 --- a/buildSrc/settings.gradle.kts +++ b/buildSrc/settings.gradle.kts @@ -3,10 +3,13 @@ dependencyResolutionManagement { create("libs") { from(files("../gradle/libs.versions.toml")) } - create("androidxLibs") { + create("androidx") { from(files("../gradle/androidx.versions.toml")) } - create("kotlinLibs") { + create("compose") { + from(files("../gradle/compose.versions.toml")) + } + create("kotlinx") { from(files("../gradle/kotlinx.versions.toml")) } } diff --git a/buildSrc/src/main/kotlin/AndroidConfig.kt b/buildSrc/src/main/kotlin/AndroidConfig.kt deleted file mode 100644 index ba608c7e5..000000000 --- a/buildSrc/src/main/kotlin/AndroidConfig.kt +++ /dev/null @@ -1,6 +0,0 @@ -object AndroidConfig { - const val compileSdk = 34 - const val minSdk = 26 - const val targetSdk = 34 - const val ndk = "26.1.10909125" -} diff --git a/buildSrc/src/main/kotlin/LocalesConfigPlugin.kt b/buildSrc/src/main/kotlin/LocalesConfigPlugin.kt deleted file mode 100644 index a641ef5c5..000000000 --- a/buildSrc/src/main/kotlin/LocalesConfigPlugin.kt +++ /dev/null @@ -1,39 +0,0 @@ -import org.gradle.api.Project -import org.gradle.api.Task -import org.gradle.api.tasks.TaskProvider -import org.gradle.kotlin.dsl.TaskContainerScope - -private val emptyResourcesElement = "\\s*|".toRegex() - -fun TaskContainerScope.registerLocalesConfigTask(project: Project): TaskProvider { - return with(project) { - register("generateLocalesConfig") { - val languages = fileTree("$projectDir/src/commonMain/resources/MR/") - .matching { include("**/strings.xml") } - .filterNot { it.readText().contains(emptyResourcesElement) } - .map { - it.parentFile.name - .replace("base", "en") - .replace("-r", "-") - .replace("+", "-") - .takeIf(String::isNotBlank) ?: "en" - } - .sorted() - .joinToString(separator = "\n") { - " " - } - - val content = """ - - -$languages - - """.trimIndent() - - val localeFile = file("$projectDir/src/androidMain/res/xml/locales_config.xml") - localeFile.parentFile.mkdirs() - localeFile.writeText(content) - } - } -} - diff --git a/buildSrc/src/main/kotlin/mihon.android.application.compose.gradle.kts b/buildSrc/src/main/kotlin/mihon.android.application.compose.gradle.kts new file mode 100644 index 000000000..eda5fb22e --- /dev/null +++ b/buildSrc/src/main/kotlin/mihon.android.application.compose.gradle.kts @@ -0,0 +1,11 @@ +import mihon.buildlogic.AndroidConfig +import mihon.buildlogic.configureCompose + +plugins { + id("com.android.application") + kotlin("android") +} + +android { + configureCompose(this) +} diff --git a/buildSrc/src/main/kotlin/mihon.android.application.gradle.kts b/buildSrc/src/main/kotlin/mihon.android.application.gradle.kts new file mode 100644 index 000000000..3c4c00ac3 --- /dev/null +++ b/buildSrc/src/main/kotlin/mihon.android.application.gradle.kts @@ -0,0 +1,17 @@ +import mihon.buildlogic.AndroidConfig +import mihon.buildlogic.configureAndroid +import mihon.buildlogic.configureTest + +plugins { + id("mihon.code.detekt") + id("com.android.application") + kotlin("android") +} + +android { + defaultConfig { + targetSdk = AndroidConfig.TARGET_SDK + } + configureAndroid(this) + configureTest() +} diff --git a/buildSrc/src/main/kotlin/mihon.benchmark.gradle.kts b/buildSrc/src/main/kotlin/mihon.benchmark.gradle.kts new file mode 100644 index 000000000..b857c4343 --- /dev/null +++ b/buildSrc/src/main/kotlin/mihon.benchmark.gradle.kts @@ -0,0 +1,13 @@ +import mihon.buildlogic.configureAndroid +import mihon.buildlogic.configureTest + +plugins { + id("mihon.code.detekt") + id("com.android.test") + kotlin("android") +} + +android { + configureAndroid(this) + configureTest() +} diff --git a/buildSrc/src/main/kotlin/detekt.gradle.kts b/buildSrc/src/main/kotlin/mihon.code.detekt.gradle.kts similarity index 100% rename from buildSrc/src/main/kotlin/detekt.gradle.kts rename to buildSrc/src/main/kotlin/mihon.code.detekt.gradle.kts diff --git a/buildSrc/src/main/kotlin/mihon.library.compose.gradle.kts b/buildSrc/src/main/kotlin/mihon.library.compose.gradle.kts new file mode 100644 index 000000000..9f39d7737 --- /dev/null +++ b/buildSrc/src/main/kotlin/mihon.library.compose.gradle.kts @@ -0,0 +1,10 @@ +import mihon.buildlogic.configureCompose + +plugins { + id("mihon.code.detekt") + id("com.android.library") +} + +android { + configureCompose(this) +} diff --git a/buildSrc/src/main/kotlin/mihon.library.gradle.kts b/buildSrc/src/main/kotlin/mihon.library.gradle.kts new file mode 100644 index 000000000..0ea496172 --- /dev/null +++ b/buildSrc/src/main/kotlin/mihon.library.gradle.kts @@ -0,0 +1,12 @@ +import mihon.buildlogic.configureAndroid +import mihon.buildlogic.configureTest + +plugins { + id("mihon.code.detekt") + id("com.android.library") +} + +android { + configureAndroid(this) + configureTest() +} diff --git a/buildSrc/src/main/kotlin/mihon/buildlogic/AndroidConfig.kt b/buildSrc/src/main/kotlin/mihon/buildlogic/AndroidConfig.kt new file mode 100644 index 000000000..afea120de --- /dev/null +++ b/buildSrc/src/main/kotlin/mihon/buildlogic/AndroidConfig.kt @@ -0,0 +1,11 @@ +package mihon.buildlogic + +import org.gradle.api.JavaVersion as GradleJavaVersion + +object AndroidConfig { + const val COMPILE_SDK = 34 + const val TARGET_SDK = 34 + const val MIN_SDK = 26 + const val NDK = "26.1.10909125" + val JavaVersion = GradleJavaVersion.VERSION_17 +} diff --git a/buildSrc/src/main/kotlin/Commands.kt b/buildSrc/src/main/kotlin/mihon/buildlogic/Commands.kt similarity index 90% rename from buildSrc/src/main/kotlin/Commands.kt rename to buildSrc/src/main/kotlin/mihon/buildlogic/Commands.kt index 5592c4eef..faa75e2b4 100644 --- a/buildSrc/src/main/kotlin/Commands.kt +++ b/buildSrc/src/main/kotlin/mihon/buildlogic/Commands.kt @@ -1,3 +1,5 @@ +package mihon.buildlogic + import org.gradle.api.Project import java.io.ByteArrayOutputStream import java.time.LocalDateTime @@ -23,9 +25,9 @@ fun Project.getBuildTime(): String { return LocalDateTime.now(ZoneOffset.UTC).format(BUILD_TIME_FORMATTER) } -fun Project.runCommand(command: String): String { +private fun Project.runCommand(command: String): String { val byteOut = ByteArrayOutputStream() - project.exec { + exec { commandLine = command.split(" ") standardOutput = byteOut } diff --git a/buildSrc/src/main/kotlin/mihon/buildlogic/ProjectExtensions.kt b/buildSrc/src/main/kotlin/mihon/buildlogic/ProjectExtensions.kt new file mode 100644 index 000000000..874e556a2 --- /dev/null +++ b/buildSrc/src/main/kotlin/mihon/buildlogic/ProjectExtensions.kt @@ -0,0 +1,112 @@ +package mihon.buildlogic + +import com.android.build.api.dsl.CommonExtension +import org.gradle.accessors.dm.LibrariesForAndroidx +import org.gradle.accessors.dm.LibrariesForCompose +import org.gradle.accessors.dm.LibrariesForKotlinx +import org.gradle.accessors.dm.LibrariesForLibs +import org.gradle.api.Project +import org.gradle.api.tasks.testing.Test +import org.gradle.api.tasks.testing.logging.TestLogEvent +import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.the +import org.gradle.kotlin.dsl.withType +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +val Project.androidx get() = the() +val Project.compose get() = the() +val Project.kotlinx get() = the() +val Project.libs get() = the() + +internal fun Project.configureAndroid(commonExtension: CommonExtension<*, *, *, *, *, *>) { + commonExtension.apply { + compileSdk = AndroidConfig.COMPILE_SDK + + defaultConfig { + minSdk = AndroidConfig.MIN_SDK + ndk { + version = AndroidConfig.NDK + } + } + + compileOptions { + sourceCompatibility = AndroidConfig.JavaVersion + targetCompatibility = AndroidConfig.JavaVersion + isCoreLibraryDesugaringEnabled = true + } + } + + tasks.withType().configureEach { + kotlinOptions { + jvmTarget = AndroidConfig.JavaVersion.toString() + // freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" + // freeCompilerArgs += "-Xcontext-receivers" + + // Treat all Kotlin warnings as errors (disabled by default) + // Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties + // val warningsAsErrors: String? by project + // allWarningsAsErrors = warningsAsErrors.toBoolean() + } + } + + dependencies { + "coreLibraryDesugaring"(libs.desugar) + } +} + +internal fun Project.configureCompose(commonExtension: CommonExtension<*, *, *, *, *, *>) { + commonExtension.apply { + buildFeatures { + compose = true + } + + composeOptions { + kotlinCompilerExtensionVersion = compose.versions.compiler.get() + } + + dependencies { + "implementation"(platform(compose.bom)) + } + } + + tasks.withType().configureEach { + kotlinOptions { + freeCompilerArgs += buildComposeMetricsParameters() + + // Enable experimental compiler opts + // https://developer.android.com/jetpack/androidx/releases/compose-compiler#1.5.9 + freeCompilerArgs += listOf( + "-P", + "plugin:androidx.compose.compiler.plugins.kotlin:nonSkippingGroupOptimization=true", + ) + } + } +} + +private fun Project.buildComposeMetricsParameters(): List { + val rootProjectDir = rootProject.layout.buildDirectory.asFile.get() + val relativePath = projectDir.relativeTo(rootDir) + + val enableMetrics = project.providers.gradleProperty("enableComposeCompilerMetrics").orNull.toBoolean() + val enableReports = project.providers.gradleProperty("enableComposeCompilerReports").orNull.toBoolean() + + return listOfNotNull( + ("metricsDestination" to "compose-metrics").takeIf { enableMetrics }, + ("reportsDestination" to "compose-reports").takeIf { enableReports }, + ).flatMap { (flag, dirName) -> + val buildDirPath = rootProjectDir.resolve(dirName).resolve(relativePath).absolutePath + listOf( + "-P", + "plugin:androidx.compose.compiler.plugins.kotlin:$flag=$buildDirPath" + ) + } +} + +internal fun Project.configureTest() { + tasks.withType { + useJUnitPlatform() + testLogging { + events(TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED) + } + } +} diff --git a/buildSrc/src/main/kotlin/mihon/buildlogic/tasks/LocalesConfigPlugin.kt b/buildSrc/src/main/kotlin/mihon/buildlogic/tasks/LocalesConfigPlugin.kt new file mode 100644 index 000000000..961a3b751 --- /dev/null +++ b/buildSrc/src/main/kotlin/mihon/buildlogic/tasks/LocalesConfigPlugin.kt @@ -0,0 +1,37 @@ +package mihon.buildlogic.tasks + +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.tasks.TaskProvider + +private val emptyResourcesElement = "\\s*|".toRegex() + +fun Project.getLocalesConfigTask(): TaskProvider { + return tasks.register("generateLocalesConfig") { + val locales = fileTree("$projectDir/src/commonMain/resources/MR/") + .matching { include("**/strings.xml") } + .filterNot { it.readText().contains(emptyResourcesElement) } + .map { + it.parentFile.name + .replace("base", "en") + .replace("-r", "-") + .replace("+", "-") + .takeIf(String::isNotBlank) ?: "en" + } + .sorted() + .joinToString("\n") { "| " } + + val content = """ + | + | + $locales + | + """.trimMargin() + + file("$projectDir/src/androidMain/res/xml/locales_config.xml").apply { + parentFile.mkdirs() + writeText(content) + } + } +} + diff --git a/core-metadata/build.gradle.kts b/core-metadata/build.gradle.kts index 5c8605879..dfb3c1943 100644 --- a/core-metadata/build.gradle.kts +++ b/core-metadata/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.android.library") + id("mihon.library") kotlin("android") kotlin("plugin.serialization") } diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts index aaf0f794b..e31015dc4 100644 --- a/core/common/build.gradle.kts +++ b/core/common/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.android.library") + id("mihon.library") kotlin("android") kotlin("plugin.serialization") } diff --git a/data/build.gradle.kts b/data/build.gradle.kts index f4ce0d039..e3f9d9004 100644 --- a/data/build.gradle.kts +++ b/data/build.gradle.kts @@ -1,8 +1,8 @@ plugins { - id("com.android.library") + id("mihon.library") + id("app.cash.sqldelight") kotlin("android") kotlin("plugin.serialization") - id("app.cash.sqldelight") } android { diff --git a/domain/build.gradle.kts b/domain/build.gradle.kts index 63f2b9df9..c49c5bc51 100644 --- a/domain/build.gradle.kts +++ b/domain/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.android.library") + id("mihon.library") kotlin("android") kotlin("plugin.serialization") } diff --git a/i18n/build.gradle.kts b/i18n/build.gradle.kts index 9ed5a6cbf..c782eac0f 100644 --- a/i18n/build.gradle.kts +++ b/i18n/build.gradle.kts @@ -1,7 +1,10 @@ +import mihon.buildlogic.tasks.getLocalesConfigTask +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + plugins { - kotlin("multiplatform") - id("com.android.library") + id("mihon.library") id("dev.icerock.mobile.multiplatform-resources") + kotlin("multiplatform") } kotlin { @@ -41,12 +44,12 @@ multiplatformResources { } tasks { - val localesConfigTask = registerLocalesConfigTask(project) + val localesConfigTask = project.getLocalesConfigTask() preBuild { dependsOn(localesConfigTask) } - withType { + withType { kotlinOptions.freeCompilerArgs += listOf( "-Xexpect-actual-classes", ) diff --git a/macrobenchmark/build.gradle.kts b/macrobenchmark/build.gradle.kts index e8a41c403..ec2d049a5 100644 --- a/macrobenchmark/build.gradle.kts +++ b/macrobenchmark/build.gradle.kts @@ -1,6 +1,5 @@ plugins { - id("com.android.test") - kotlin("android") + id("mihon.benchmark") } android { diff --git a/presentation-core/build.gradle.kts b/presentation-core/build.gradle.kts index e30c5869c..c7b64ad42 100644 --- a/presentation-core/build.gradle.kts +++ b/presentation-core/build.gradle.kts @@ -1,5 +1,6 @@ plugins { - id("com.android.library") + id("mihon.library") + id("mihon.library.compose") kotlin("android") } @@ -10,14 +11,6 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") } - - buildFeatures { - compose = true - } - - composeOptions { - kotlinCompilerExtensionVersion = compose.versions.compiler.get() - } } dependencies { @@ -25,7 +18,6 @@ dependencies { api(projects.i18n) // Compose - implementation(platform(compose.bom)) implementation(compose.activity) implementation(compose.foundation) implementation(compose.material3.core) diff --git a/presentation-widget/build.gradle.kts b/presentation-widget/build.gradle.kts index 896979d98..292b67240 100644 --- a/presentation-widget/build.gradle.kts +++ b/presentation-widget/build.gradle.kts @@ -1,5 +1,6 @@ plugins { - id("com.android.library") + id("mihon.library") + id("mihon.library.compose") kotlin("android") } @@ -10,14 +11,6 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") } - - buildFeatures { - compose = true - } - - composeOptions { - kotlinCompilerExtensionVersion = compose.versions.compiler.get() - } } dependencies { diff --git a/source-api/build.gradle.kts b/source-api/build.gradle.kts index 209136e48..ae28caa14 100644 --- a/source-api/build.gradle.kts +++ b/source-api/build.gradle.kts @@ -1,7 +1,7 @@ plugins { + id("mihon.library") kotlin("multiplatform") kotlin("plugin.serialization") - id("com.android.library") } kotlin { diff --git a/source-local/build.gradle.kts b/source-local/build.gradle.kts index 46de484ce..b63ae8fab 100644 --- a/source-local/build.gradle.kts +++ b/source-local/build.gradle.kts @@ -1,6 +1,6 @@ plugins { + id("mihon.library") kotlin("multiplatform") - id("com.android.library") } kotlin { From 20e4cb26d6c264b13edb357ecfa25a6db21f19b8 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 6 Apr 2024 12:21:25 +0600 Subject: [PATCH 151/212] Remove unused imports --- .../presentation/more/settings/screen/about/AboutScreen.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt index ba4e42c7a..e91a0576c 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt @@ -58,9 +58,6 @@ import uy.kohesive.injekt.api.get import java.time.Instant import java.time.LocalDateTime import java.time.ZoneId -import java.time.ZonedDateTime -import java.time.format.DateTimeFormatter -import java.util.Locale object AboutScreen : Screen() { From 13656959ae0606736f6ca9eb62699dc23e467c2f Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 8 Apr 2024 15:45:38 +0600 Subject: [PATCH 152/212] MangaCoverFetcher: Small cleanups Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> --- app/src/main/java/eu/kanade/tachiyomi/App.kt | 29 ++----------------- .../tachiyomi/data/coil/MangaCoverFetcher.kt | 22 +++++++------- 2 files changed, 13 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index 3ffc83ebd..12170d8a5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -17,8 +17,6 @@ import androidx.lifecycle.ProcessLifecycleOwner import androidx.lifecycle.lifecycleScope import coil3.ImageLoader import coil3.SingletonImageLoader -import coil3.disk.DiskCache -import coil3.disk.directory import coil3.network.okhttp.OkHttpNetworkFetcherFactory import coil3.request.allowRgb565 import coil3.request.crossfade @@ -157,16 +155,14 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor override fun newImageLoader(context: Context): ImageLoader { return ImageLoader.Builder(this).apply { val callFactoryLazy = lazy { Injekt.get().client } - val diskCacheLazy = lazy { CoilDiskCache.get(this@App) } components { add(OkHttpNetworkFetcherFactory(callFactoryLazy::value)) add(TachiyomiImageDecoder.Factory()) - add(MangaCoverFetcher.MangaFactory(callFactoryLazy, diskCacheLazy)) - add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy, diskCacheLazy)) + add(MangaCoverFetcher.MangaFactory(callFactoryLazy)) + add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy)) add(MangaKeyer()) add(MangaCoverKeyer()) } - diskCache(diskCacheLazy::value) crossfade((300 * this@App.animatorDurationScale).toInt()) allowRgb565(DeviceUtil.isLowRamDevice(this@App)) if (networkPreferences.verboseLogging().get()) logger(DebugLogger()) @@ -240,24 +236,3 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor } private const val ACTION_DISABLE_INCOGNITO_MODE = "tachi.action.DISABLE_INCOGNITO_MODE" - -/** - * Direct copy of Coil's internal SingletonDiskCache so that [MangaCoverFetcher] can access it. - */ -private object CoilDiskCache { - - private const val FOLDER_NAME = "image_cache" - private var instance: DiskCache? = null - - @Synchronized - fun get(context: Context): DiskCache { - return instance ?: run { - val safeCacheDir = context.cacheDir.apply { mkdirs() } - // Create the singleton disk cache instance. - DiskCache.Builder() - .directory(safeCacheDir.resolve(FOLDER_NAME)) - .build() - .also { instance = it } - } - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt index 0556d9d1f..11ff6a277 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverFetcher.kt @@ -46,6 +46,7 @@ import java.io.IOException * Available request parameter: * - [USE_CUSTOM_COVER_KEY]: Use custom cover if set by user, default is true */ +@Suppress("LongParameterList") class MangaCoverFetcher( private val url: String?, private val isLibraryManga: Boolean, @@ -55,7 +56,7 @@ class MangaCoverFetcher( private val diskCacheKeyLazy: Lazy, private val sourceLazy: Lazy, private val callFactoryLazy: Lazy, - private val diskCacheLazy: Lazy, + private val imageLoader: ImageLoader, ) : Fetcher { private val diskCacheKey: String @@ -207,7 +208,7 @@ class MangaCoverFetcher( private fun moveSnapshotToCoverCache(snapshot: DiskCache.Snapshot, cacheFile: File?): File? { if (cacheFile == null) return null return try { - diskCacheLazy.value.run { + imageLoader.diskCache?.run { fileSystem.source(snapshot.data).use { input -> writeSourceToCoverCache(input, cacheFile) } @@ -248,7 +249,7 @@ class MangaCoverFetcher( private fun readFromDiskCache(): DiskCache.Snapshot? { return if (options.diskCachePolicy.readEnabled) { - diskCacheLazy.value.openSnapshot(diskCacheKey) + imageLoader.diskCache?.openSnapshot(diskCacheKey) } else { null } @@ -257,9 +258,10 @@ class MangaCoverFetcher( private fun writeToDiskCache( response: Response, ): DiskCache.Snapshot? { - val editor = diskCacheLazy.value.openEditor(diskCacheKey) ?: return null + val diskCache = imageLoader.diskCache + val editor = diskCache?.openEditor(diskCacheKey) ?: return null try { - diskCacheLazy.value.fileSystem.write(editor.data) { + diskCache.fileSystem.write(editor.data) { response.body.source().readAll(this) } return editor.commitAndOpenSnapshot() @@ -299,7 +301,6 @@ class MangaCoverFetcher( class MangaFactory( private val callFactoryLazy: Lazy, - private val diskCacheLazy: Lazy, ) : Fetcher.Factory { private val coverCache: CoverCache by injectLazy() @@ -312,17 +313,16 @@ class MangaCoverFetcher( options = options, coverFileLazy = lazy { coverCache.getCoverFile(data.thumbnailUrl) }, customCoverFileLazy = lazy { coverCache.getCustomCoverFile(data.id) }, - diskCacheKeyLazy = lazy { MangaKeyer().key(data, options) }, + diskCacheKeyLazy = lazy { imageLoader.components.key(data, options)!! }, sourceLazy = lazy { sourceManager.get(data.source) as? HttpSource }, callFactoryLazy = callFactoryLazy, - diskCacheLazy = diskCacheLazy, + imageLoader = imageLoader, ) } } class MangaCoverFactory( private val callFactoryLazy: Lazy, - private val diskCacheLazy: Lazy, ) : Fetcher.Factory { private val coverCache: CoverCache by injectLazy() @@ -335,10 +335,10 @@ class MangaCoverFetcher( options = options, coverFileLazy = lazy { coverCache.getCoverFile(data.url) }, customCoverFileLazy = lazy { coverCache.getCustomCoverFile(data.mangaId) }, - diskCacheKeyLazy = lazy { MangaCoverKeyer().key(data, options) }, + diskCacheKeyLazy = lazy { imageLoader.components.key(data, options)!! }, sourceLazy = lazy { sourceManager.get(data.sourceId) as? HttpSource }, callFactoryLazy = callFactoryLazy, - diskCacheLazy = diskCacheLazy, + imageLoader = imageLoader, ) } } From 56e66e041d22ebd680654df4aefa81578c0f5f11 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 8 Apr 2024 15:48:43 +0600 Subject: [PATCH 153/212] Revert "Update Scaffold fork (#10143)" + Cleanup Causes delay of one frame before actual contentPadding is measured This reverts commit ea15bc782a2cd603c78de7567a59e973dd50fd7f. Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> --- .../core/components/material/Scaffold.kt | 340 +++++++++--------- 1 file changed, 167 insertions(+), 173 deletions(-) diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Scaffold.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Scaffold.kt index fd3cc1f74..6420084a7 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Scaffold.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Scaffold.kt @@ -18,35 +18,32 @@ package tachiyomi.presentation.core.components.material +import androidx.compose.foundation.layout.MutableWindowInsets import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.windowInsetsBottomHeight -import androidx.compose.foundation.layout.windowInsetsEndWidth -import androidx.compose.foundation.layout.windowInsetsStartWidth -import androidx.compose.foundation.layout.windowInsetsTopHeight +import androidx.compose.foundation.layout.asPaddingValues +import androidx.compose.foundation.layout.calculateEndPadding +import androidx.compose.foundation.layout.calculateStartPadding +import androidx.compose.foundation.layout.exclude +import androidx.compose.foundation.layout.onConsumedWindowInsetsChanged import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.FabPosition import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ScaffoldDefaults import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.contentColorFor +import androidx.compose.material3.rememberTopAppBarState import androidx.compose.runtime.Composable import androidx.compose.runtime.Immutable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.layout.Layout +import androidx.compose.ui.layout.SubcomposeLayout import androidx.compose.ui.unit.Constraints -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.offset +import androidx.compose.ui.unit.max import androidx.compose.ui.util.fastForEach import androidx.compose.ui.util.fastMap import androidx.compose.ui.util.fastMaxBy @@ -73,6 +70,8 @@ import kotlin.math.max * * Pass scroll behavior to top bar by default * * Remove height constraint for expanded app bar * * Also take account of fab height when providing inner padding + * * Fixes for fab and snackbar horizontal placements when [contentWindowInsets] is used + * * Handle consumed window insets * * Add startBar slot for Navigation Rail * * @param modifier the [Modifier] to be applied to this scaffold @@ -100,7 +99,9 @@ import kotlin.math.max @Composable fun Scaffold( modifier: Modifier = Modifier, - topBarScrollBehavior: TopAppBarScrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(), + topBarScrollBehavior: TopAppBarScrollBehavior = TopAppBarDefaults.pinnedScrollBehavior( + rememberTopAppBarState(), + ), topBar: @Composable (TopAppBarScrollBehavior) -> Unit = {}, bottomBar: @Composable () -> Unit = {}, startBar: @Composable () -> Unit = {}, @@ -112,9 +113,16 @@ fun Scaffold( contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets, content: @Composable (PaddingValues) -> Unit, ) { + // Tachiyomi: Handle consumed window insets + val remainingWindowInsets = remember { MutableWindowInsets() } androidx.compose.material3.Surface( modifier = Modifier .nestedScroll(topBarScrollBehavior.nestedScrollConnection) + .onConsumedWindowInsetsChanged { + remainingWindowInsets.insets = contentWindowInsets.exclude( + it, + ) + } .then(modifier), color = containerColor, contentColor = contentColor, @@ -126,7 +134,7 @@ fun Scaffold( bottomBar = bottomBar, content = content, snackbar = snackbarHost, - contentWindowInsets = contentWindowInsets, + contentWindowInsets = remainingWindowInsets, fab = floatingActionButton, ) } @@ -144,6 +152,8 @@ fun Scaffold( * @param bottomBar the content to place at the bottom of the [Scaffold], on top of the * [content], typically a [NavigationBar]. */ +@Suppress("CyclomaticComplexMethod") +@OptIn(ExperimentalMaterial3Api::class) @Composable private fun ScaffoldLayout( fabPosition: FabPosition, @@ -155,47 +165,7 @@ private fun ScaffoldLayout( contentWindowInsets: WindowInsets, bottomBar: @Composable () -> Unit, ) { - // Create the backing values for the content padding - // These values will be updated during measurement, but before measuring and placing - // the body content - var topContentPadding by remember { mutableStateOf(0.dp) } - var startContentPadding by remember { mutableStateOf(0.dp) } - var endContentPadding by remember { mutableStateOf(0.dp) } - var bottomContentPadding by remember { mutableStateOf(0.dp) } - - val contentPadding = remember { - object : PaddingValues { - override fun calculateLeftPadding(layoutDirection: LayoutDirection): Dp = - when (layoutDirection) { - LayoutDirection.Ltr -> startContentPadding - LayoutDirection.Rtl -> endContentPadding - } - - override fun calculateTopPadding(): Dp = topContentPadding - - override fun calculateRightPadding(layoutDirection: LayoutDirection): Dp = - when (layoutDirection) { - LayoutDirection.Ltr -> endContentPadding - LayoutDirection.Rtl -> startContentPadding - } - - override fun calculateBottomPadding(): Dp = bottomContentPadding - } - } - Layout( - contents = listOf( - { Spacer(Modifier.windowInsetsTopHeight(contentWindowInsets)) }, - { Spacer(Modifier.windowInsetsBottomHeight(contentWindowInsets)) }, - { Spacer(Modifier.windowInsetsStartWidth(contentWindowInsets)) }, - { Spacer(Modifier.windowInsetsEndWidth(contentWindowInsets)) }, - startBar, - topBar, - snackbar, - fab, - bottomBar, - { content(contentPadding) }, - ), - ) { measurables, constraints -> + SubcomposeLayout { constraints -> val layoutWidth = constraints.maxWidth val layoutHeight = constraints.maxHeight @@ -206,117 +176,116 @@ private fun ScaffoldLayout( */ val topBarConstraints = looseConstraints.copy(maxHeight = Constraints.Infinity) - val topInsetsPlaceables = measurables[0].single() - .measure(looseConstraints) - val bottomInsetsPlaceables = measurables[1].single() - .measure(looseConstraints) - val startInsetsPlaceables = measurables[2].single() - .measure(looseConstraints) - val endInsetsPlaceables = measurables[3].single() - .measure(looseConstraints) - - val startInsetsWidth = startInsetsPlaceables.width - val endInsetsWidth = endInsetsPlaceables.width - - val topInsetsHeight = topInsetsPlaceables.height - val bottomInsetsHeight = bottomInsetsPlaceables.height - - // Tachiyomi: Add startBar slot for Navigation Rail - val startBarPlaceables = measurables[4] - .fastMap { it.measure(looseConstraints) } - - val startBarWidth = startBarPlaceables.fastMaxBy { it.width }?.width ?: 0 - - val topBarPlaceables = measurables[5] - .fastMap { it.measure(topBarConstraints) } - - val topBarHeight = topBarPlaceables.fastMaxBy { it.height }?.height ?: 0 - - val bottomPlaceablesConstraints = looseConstraints.offset( - -startInsetsWidth - endInsetsWidth, - -bottomInsetsHeight, - ) - - val snackbarPlaceables = measurables[6] - .fastMap { it.measure(bottomPlaceablesConstraints) } - - val snackbarHeight = snackbarPlaceables.fastMaxBy { it.height }?.height ?: 0 - val snackbarWidth = snackbarPlaceables.fastMaxBy { it.width }?.width ?: 0 - - val fabPlaceables = measurables[7] - .fastMap { it.measure(bottomPlaceablesConstraints) } - - val fabWidth = fabPlaceables.fastMaxBy { it.width }?.width ?: 0 - val fabHeight = fabPlaceables.fastMaxBy { it.height }?.height ?: 0 - - val fabPlacement = if (fabWidth > 0 && fabHeight > 0) { - // FAB distance from the left of the layout, taking into account LTR / RTL - val fabLeftOffset = when (fabPosition) { - FabPosition.Start -> { - if (layoutDirection == LayoutDirection.Ltr) { - FabSpacing.roundToPx() - } else { - layoutWidth - FabSpacing.roundToPx() - fabWidth - } - } - FabPosition.End, FabPosition.EndOverlay -> { - if (layoutDirection == LayoutDirection.Ltr) { - layoutWidth - FabSpacing.roundToPx() - fabWidth - } else { - FabSpacing.roundToPx() - } - } - else -> (layoutWidth - fabWidth) / 2 - } - - FabPlacement( - left = fabLeftOffset, - width = fabWidth, - height = fabHeight, - ) - } else { - null - } - - val bottomBarPlaceables = measurables[8] - .fastMap { it.measure(looseConstraints) } - - val bottomBarHeight = bottomBarPlaceables.fastMaxBy { it.height }?.height ?: 0 - - val fabOffsetFromBottom = fabPlacement?.let { - if (fabPosition == FabPosition.EndOverlay) { - it.height + FabSpacing.roundToPx() + bottomInsetsHeight - } else { - // Total height is the bottom bar height + the FAB height + the padding - // between the FAB and bottom bar - max(bottomBarHeight, bottomInsetsHeight) + it.height + FabSpacing.roundToPx() - } - } - val snackbarOffsetFromBottom = if (snackbarHeight != 0) { - snackbarHeight + max( - fabOffsetFromBottom ?: 0, - max( - bottomBarHeight, - bottomInsetsHeight, - ), - ) - } else { - 0 - } - - // Update the backing value for the content padding of the body content - // We do this before measuring or placing the body content - topContentPadding = max(topBarHeight, topInsetsHeight).toDp() - bottomContentPadding = max(fabOffsetFromBottom ?: 0, max(bottomBarHeight, bottomInsetsHeight)).toDp() - startContentPadding = max(startBarWidth, startInsetsWidth).toDp() - endContentPadding = endInsetsWidth.toDp() - - val bodyContentPlaceables = measurables[9] - .fastMap { it.measure(looseConstraints) } - layout(layoutWidth, layoutHeight) { - // Inset spacers are just for convenient measurement logic, no need to place them + val leftInset = contentWindowInsets.getLeft(this@SubcomposeLayout, layoutDirection) + val rightInset = contentWindowInsets.getRight(this@SubcomposeLayout, layoutDirection) + val bottomInset = contentWindowInsets.getBottom(this@SubcomposeLayout) + + // Tachiyomi: Add startBar slot for Navigation Rail + val startBarPlaceables = subcompose(ScaffoldLayoutContent.StartBar, startBar).fastMap { + it.measure(looseConstraints) + } + val startBarWidth = startBarPlaceables.fastMaxBy { it.width }?.width ?: 0 + + // Tachiyomi: layoutWidth after horizontal insets + val insetLayoutWidth = layoutWidth - leftInset - rightInset - startBarWidth + + val topBarPlaceables = subcompose(ScaffoldLayoutContent.TopBar, topBar).fastMap { + it.measure(topBarConstraints) + } + + val topBarHeight = topBarPlaceables.fastMaxBy { it.height }?.height ?: 0 + + val snackbarPlaceables = subcompose(ScaffoldLayoutContent.Snackbar, snackbar).fastMap { + it.measure(looseConstraints) + } + + val snackbarHeight = snackbarPlaceables.fastMaxBy { it.height }?.height ?: 0 + val snackbarWidth = snackbarPlaceables.fastMaxBy { it.width }?.width ?: 0 + + // Tachiyomi: Calculate insets for snackbar placement offset + val snackbarLeft = if (snackbarPlaceables.isNotEmpty()) { + (insetLayoutWidth - snackbarWidth) / 2 + leftInset + } else { + 0 + } + + val fabPlaceables = + subcompose(ScaffoldLayoutContent.Fab, fab).fastMap { measurable -> + measurable.measure(looseConstraints) + } + + val fabWidth = fabPlaceables.fastMaxBy { it.width }?.width ?: 0 + val fabHeight = fabPlaceables.fastMaxBy { it.height }?.height ?: 0 + + val fabPlacement = if (fabPlaceables.isNotEmpty() && fabWidth != 0 && fabHeight != 0) { + // FAB distance from the left of the layout, taking into account LTR / RTL + // Tachiyomi: Calculate insets for fab placement offset + val fabLeftOffset = if (fabPosition == FabPosition.End) { + if (layoutDirection == LayoutDirection.Ltr) { + layoutWidth - FabSpacing.roundToPx() - fabWidth - rightInset + } else { + FabSpacing.roundToPx() + leftInset + } + } else { + leftInset + ((insetLayoutWidth - fabWidth) / 2) + } + + FabPlacement( + left = fabLeftOffset, + width = fabWidth, + height = fabHeight, + ) + } else { + null + } + + val bottomBarPlaceables = subcompose(ScaffoldLayoutContent.BottomBar) { + bottomBar() + }.fastMap { it.measure(looseConstraints) } + + val bottomBarHeight = bottomBarPlaceables + .fastMaxBy { it.height } + ?.height + ?.takeIf { it != 0 } + val fabOffsetFromBottom = fabPlacement?.let { + max(bottomBarHeight ?: 0, bottomInset) + it.height + FabSpacing.roundToPx() + } + + val snackbarOffsetFromBottom = if (snackbarHeight != 0) { + snackbarHeight + (fabOffsetFromBottom ?: max(bottomBarHeight ?: 0, bottomInset)) + } else { + 0 + } + + val bodyContentPlaceables = subcompose(ScaffoldLayoutContent.MainContent) { + val insets = contentWindowInsets.asPaddingValues(this@SubcomposeLayout) + val fabOffsetDp = fabOffsetFromBottom?.toDp() ?: 0.dp + val bottomBarHeightPx = bottomBarHeight ?: 0 + val innerPadding = PaddingValues( + top = + if (topBarPlaceables.isEmpty()) { + insets.calculateTopPadding() + } else { + topBarHeight.toDp() + }, + bottom = // Tachiyomi: Also take account of fab height when providing inner padding + if (bottomBarPlaceables.isEmpty() || bottomBarHeightPx == 0) { + max(insets.calculateBottomPadding(), fabOffsetDp) + } else { + max(bottomBarHeightPx.toDp(), fabOffsetDp) + }, + start = max( + insets.calculateStartPadding((this@SubcomposeLayout).layoutDirection), + startBarWidth.toDp(), + ), + end = insets.calculateEndPadding((this@SubcomposeLayout).layoutDirection), + ) + content(innerPadding) + }.fastMap { it.measure(looseConstraints) } + // Placing to control drawing order to match default elevation of each placeable + bodyContentPlaceables.fastForEach { it.place(0, 0) } @@ -328,27 +297,50 @@ private fun ScaffoldLayout( } snackbarPlaceables.fastForEach { it.place( - (layoutWidth - snackbarWidth) / 2 + when (layoutDirection) { - LayoutDirection.Ltr -> startInsetsWidth - LayoutDirection.Rtl -> endInsetsWidth - }, + snackbarLeft, layoutHeight - snackbarOffsetFromBottom, ) } // The bottom bar is always at the bottom of the layout bottomBarPlaceables.fastForEach { - it.place(0, layoutHeight - bottomBarHeight) + it.place(0, layoutHeight - (bottomBarHeight ?: 0)) } // Explicitly not using placeRelative here as `leftOffset` already accounts for RTL - fabPlacement?.let { placement -> - fabPlaceables.fastForEach { - it.place(placement.left, layoutHeight - fabOffsetFromBottom!!) - } + fabPlaceables.fastForEach { + it.place(fabPlacement?.left ?: 0, layoutHeight - (fabOffsetFromBottom ?: 0)) } } } } +/** + * The possible positions for a [FloatingActionButton] attached to a [Scaffold]. + */ +@ExperimentalMaterial3Api +@JvmInline +value class FabPosition internal constructor(@Suppress("unused") private val value: Int) { + companion object { + /** + * Position FAB at the bottom of the screen in the center, above the [NavigationBar] (if it + * exists) + */ + val Center = FabPosition(0) + + /** + * Position FAB at the bottom of the screen at the end, above the [NavigationBar] (if it + * exists) + */ + val End = FabPosition(1) + } + + override fun toString(): String { + return when (this) { + Center -> "FabPosition.Center" + else -> "FabPosition.End" + } + } +} + /** * Placement information for a [FloatingActionButton] inside a [Scaffold]. * @@ -366,3 +358,5 @@ internal class FabPlacement( // FAB spacing above the bottom bar / bottom of the Scaffold private val FabSpacing = 16.dp + +private enum class ScaffoldLayoutContent { TopBar, MainContent, Snackbar, Fab, BottomBar, StartBar } From a1a52ae81a9fa9ec6f93ef2f0db4eddc60b014d4 Mon Sep 17 00:00:00 2001 From: Maddie Witman Date: Fri, 16 Feb 2024 06:09:00 -0500 Subject: [PATCH 154/212] Refactor use of Java.util.date to Java.time.*, to fix localized date issues. (#402) * Add support for localdate based relative times * Update History Screen to use new localdate based relative times * Update Updates Screen to use new localdate based relative times * Cleaned up date util classes * Updated build time display * Code cleanup * Fixed crash in settings * Updated Preferences item * Worker Info works * Fixed Tracker date display * Code changes to pass detekt --- .../java/eu/kanade/domain/ui/UiPreferences.kt | 10 ++-- .../presentation/components/DateText.kt | 25 +++++---- .../presentation/history/HistoryScreen.kt | 4 +- .../HistoryScreenModelStateProvider.kt | 5 +- .../screen/SettingsAppearanceScreen.kt | 4 +- .../more/settings/screen/about/AboutScreen.kt | 19 ++----- .../settings/screen/debug/WorkerInfoScreen.kt | 20 +++++-- .../presentation/track/TrackInfoDialogHome.kt | 9 +-- .../TrackInfoDialogHomePreviewProvider.kt | 7 ++- .../presentation/updates/UpdatesScreen.kt | 4 +- .../ui/history/HistoryScreenModel.kt | 12 ++-- .../ui/updates/UpdatesScreenModel.kt | 14 ++--- .../tachiyomi/util/lang/DateExtensions.kt | 56 ++++++------------- 13 files changed, 89 insertions(+), 100 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt b/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt index 606a1048d..2efefb517 100644 --- a/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt +++ b/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt @@ -7,8 +7,8 @@ import eu.kanade.tachiyomi.util.system.DeviceUtil import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable import tachiyomi.core.common.preference.PreferenceStore import tachiyomi.core.common.preference.getEnum -import java.text.DateFormat -import java.text.SimpleDateFormat +import java.time.format.DateTimeFormatter +import java.time.format.FormatStyle import java.util.Locale class UiPreferences( @@ -31,9 +31,9 @@ class UiPreferences( fun tabletUiMode() = preferenceStore.getEnum("tablet_ui_mode", TabletUiMode.AUTOMATIC) companion object { - fun dateFormat(format: String): DateFormat = when (format) { - "" -> DateFormat.getDateInstance(DateFormat.SHORT) - else -> SimpleDateFormat(format, Locale.getDefault()) + fun dateFormat(format: String): DateTimeFormatter = when (format) { + "" -> DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT) + else -> DateTimeFormatter.ofPattern(format, Locale.getDefault()) } } } diff --git a/app/src/main/java/eu/kanade/presentation/components/DateText.kt b/app/src/main/java/eu/kanade/presentation/components/DateText.kt index 9fa8c85d1..59708a049 100644 --- a/app/src/main/java/eu/kanade/presentation/components/DateText.kt +++ b/app/src/main/java/eu/kanade/presentation/components/DateText.kt @@ -4,25 +4,31 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import eu.kanade.domain.ui.UiPreferences -import eu.kanade.tachiyomi.util.lang.toRelativeString +import eu.kanade.tachiyomi.util.lang.toRelativeSting import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.util.Date +import java.time.Instant +import java.time.LocalDate +import java.time.ZoneId @Composable fun relativeDateText( dateEpochMillis: Long, ): String { return relativeDateText( - date = Date(dateEpochMillis).takeIf { dateEpochMillis > 0L }, + localDate = LocalDate.ofInstant( + Instant.ofEpochMilli(dateEpochMillis), + ZoneId.systemDefault(), + ) + .takeIf { dateEpochMillis > 0L }, ) } @Composable fun relativeDateText( - date: Date?, + localDate: LocalDate?, ): String { val context = LocalContext.current @@ -30,11 +36,10 @@ fun relativeDateText( val relativeTime = remember { preferences.relativeTime().get() } val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) } - return date - ?.toRelativeString( - context = context, - relative = relativeTime, - dateFormat = dateFormat, - ) + return localDate?.toRelativeSting( + context = context, + relative = relativeTime, + dateFormat = dateFormat, + ) ?: stringResource(MR.strings.not_applicable) } diff --git a/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt b/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt index ec25769dc..c15604829 100644 --- a/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt @@ -28,7 +28,7 @@ import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.LoadingScreen -import java.util.Date +import java.time.LocalDate @Composable fun HistoryScreen( @@ -133,7 +133,7 @@ private fun HistoryScreenContent( } sealed interface HistoryUiModel { - data class Header(val date: Date) : HistoryUiModel + data class Header(val date: LocalDate) : HistoryUiModel data class Item(val item: HistoryWithRelations) : HistoryUiModel } diff --git a/app/src/main/java/eu/kanade/presentation/history/HistoryScreenModelStateProvider.kt b/app/src/main/java/eu/kanade/presentation/history/HistoryScreenModelStateProvider.kt index 2c07b81e0..4ffa15dd1 100644 --- a/app/src/main/java/eu/kanade/presentation/history/HistoryScreenModelStateProvider.kt +++ b/app/src/main/java/eu/kanade/presentation/history/HistoryScreenModelStateProvider.kt @@ -5,6 +5,7 @@ import eu.kanade.tachiyomi.ui.history.HistoryScreenModel import tachiyomi.domain.history.model.HistoryWithRelations import tachiyomi.domain.manga.model.MangaCover import java.time.Instant +import java.time.LocalDate import java.time.temporal.ChronoUnit import java.util.Date import kotlin.random.Random @@ -71,10 +72,10 @@ class HistoryScreenModelStateProvider : PreviewParameterProvider Instant = { it }) = - HistoryUiModel.Header(Date.from(instantBuilder(Instant.now()))) + HistoryUiModel.Header(LocalDate.from(instantBuilder(Instant.now()))) fun items() = sequence { var count = 1 diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt index d1474bf6a..10ad5e7bd 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt @@ -26,7 +26,7 @@ import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.collectAsState import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.time.Instant +import java.time.LocalDate object SettingsAppearanceScreen : SearchableSettings { @@ -101,7 +101,7 @@ object SettingsAppearanceScreen : SearchableSettings { val context = LocalContext.current val navigator = LocalNavigator.currentOrThrow - val now = remember { Instant.now().toEpochMilli() } + val now = remember { LocalDate.now() } val dateFormat by uiPreferences.dateFormat().collectAsState() val formattedNow = remember(dateFormat) { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt index 58f690569..615687aff 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt @@ -55,10 +55,10 @@ import tachiyomi.presentation.core.icons.Reddit import tachiyomi.presentation.core.icons.X import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.text.DateFormat -import java.text.SimpleDateFormat +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.format.DateTimeFormatter import java.util.Locale -import java.util.TimeZone object AboutScreen : Screen() { @@ -269,16 +269,9 @@ object AboutScreen : Screen() { internal fun getFormattedBuildTime(): String { return try { - val inputDf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'", Locale.US) - inputDf.timeZone = TimeZone.getTimeZone("UTC") - val buildTime = inputDf.parse(BuildConfig.BUILD_TIME) - - val outputDf = DateFormat.getDateTimeInstance( - DateFormat.MEDIUM, - DateFormat.SHORT, - Locale.getDefault(), - ) - outputDf.timeZone = TimeZone.getDefault() + val df = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm'Z'", Locale.US) + .withZone(ZoneId.of("UTC")) + val buildTime = LocalDateTime.from(df.parse(BuildConfig.BUILD_TIME)) buildTime!!.toDateTimestampString(UiPreferences.dateFormat(Injekt.get().dateFormat().get())) } catch (e: Exception) { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt index ea8db81e8..dbe82830d 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/debug/WorkerInfoScreen.kt @@ -42,7 +42,9 @@ import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.plus import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.util.Date +import java.time.Instant +import java.time.LocalDateTime +import java.time.ZoneId class WorkerInfoScreen : Screen() { @@ -149,11 +151,17 @@ class WorkerInfoScreen : Screen() { appendLine("State: ${workInfo.state}") if (workInfo.state == WorkInfo.State.ENQUEUED) { appendLine( - "Next scheduled run: ${Date(workInfo.nextScheduleTimeMillis).toDateTimestampString( - UiPreferences.dateFormat( - Injekt.get().dateFormat().get(), - ), - )}", + "Next scheduled run: ${ + LocalDateTime.ofInstant( + Instant.ofEpochMilli(workInfo.nextScheduleTimeMillis), + ZoneId.systemDefault(), + ) + .toDateTimestampString( + UiPreferences.dateFormat( + Injekt.get().dateFormat().get(), + ), + ) + }", ) appendLine("Attempt #${workInfo.runAttemptCount + 1}") } diff --git a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHome.kt b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHome.kt index 11f2b9a06..993532180 100644 --- a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHome.kt +++ b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHome.kt @@ -52,17 +52,18 @@ import eu.kanade.presentation.theme.TachiyomiPreviewTheme import eu.kanade.presentation.track.components.TrackLogoIcon import eu.kanade.tachiyomi.data.track.Tracker import eu.kanade.tachiyomi.ui.manga.track.TrackItem +import eu.kanade.tachiyomi.util.lang.toLocalDate import eu.kanade.tachiyomi.util.system.copyToClipboard import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource -import java.text.DateFormat +import java.time.format.DateTimeFormatter private const val UnsetStatusTextAlpha = 0.5F @Composable fun TrackInfoDialogHome( trackItems: List, - dateFormat: DateFormat, + dateFormat: DateTimeFormatter, onStatusClick: (TrackItem) -> Unit, onChapterClick: (TrackItem) -> Unit, onScoreClick: (TrackItem) -> Unit, @@ -104,11 +105,11 @@ fun TrackInfoDialogHome( .takeIf { supportsScoring && item.track.score != 0.0 }, onScoreClick = { onScoreClick(item) } .takeIf { supportsScoring }, - startDate = remember(item.track.startDate) { dateFormat.format(item.track.startDate) } + startDate = remember(item.track.startDate) { dateFormat.format(item.track.startDate.toLocalDate()) } .takeIf { supportsReadingDates && item.track.startDate != 0L }, onStartDateClick = { onStartDateEdit(item) } // TODO .takeIf { supportsReadingDates }, - endDate = dateFormat.format(item.track.finishDate) + endDate = dateFormat.format(item.track.finishDate.toLocalDate()) .takeIf { supportsReadingDates && item.track.finishDate != 0L }, onEndDateClick = { onEndDateEdit(item) } .takeIf { supportsReadingDates }, diff --git a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHomePreviewProvider.kt b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHomePreviewProvider.kt index 51b7ca3f8..aa58403ea 100644 --- a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHomePreviewProvider.kt +++ b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogHomePreviewProvider.kt @@ -5,7 +5,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider import eu.kanade.tachiyomi.ui.manga.track.TrackItem import eu.kanade.test.DummyTracker import tachiyomi.domain.track.model.Track -import java.text.DateFormat +import java.time.format.DateTimeFormatter +import java.time.format.FormatStyle internal class TrackInfoDialogHomePreviewProvider : PreviewParameterProvider<@Composable () -> Unit> { @@ -46,7 +47,7 @@ internal class TrackInfoDialogHomePreviewProvider : trackItemWithoutTrack, trackItemWithTrack, ), - dateFormat = DateFormat.getDateInstance(), + dateFormat = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM), onStatusClick = {}, onChapterClick = {}, onScoreClick = {}, @@ -61,7 +62,7 @@ internal class TrackInfoDialogHomePreviewProvider : private val noTrackers = @Composable { TrackInfoDialogHome( trackItems = listOf(), - dateFormat = DateFormat.getDateInstance(), + dateFormat = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM), onStatusClick = {}, onChapterClick = {}, onScoreClick = {}, diff --git a/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt b/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt index ca1e5632c..ac97de83f 100644 --- a/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt @@ -36,7 +36,7 @@ import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.screens.EmptyScreen import tachiyomi.presentation.core.screens.LoadingScreen -import java.util.Date +import java.time.LocalDate import kotlin.time.Duration.Companion.seconds @Composable @@ -206,6 +206,6 @@ private fun UpdatesBottomBar( } sealed interface UpdatesUiModel { - data class Header(val date: Date) : UpdatesUiModel + data class Header(val date: LocalDate) : UpdatesUiModel data class Item(val item: UpdatesItem) : UpdatesUiModel } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt index a344fcda3..7b0cdcf72 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt @@ -5,7 +5,7 @@ import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.screenModelScope import eu.kanade.core.util.insertSeparators import eu.kanade.presentation.history.HistoryUiModel -import eu.kanade.tachiyomi.util.lang.toDateKey +import eu.kanade.tachiyomi.util.lang.toLocalDate import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow @@ -28,7 +28,7 @@ import tachiyomi.domain.history.interactor.RemoveHistory import tachiyomi.domain.history.model.HistoryWithRelations import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.util.Date +import java.time.LocalDate class HistoryScreenModel( private val getHistory: GetHistory = Injekt.get(), @@ -60,10 +60,12 @@ class HistoryScreenModel( private fun List.toHistoryUiModels(): List { return map { HistoryUiModel.Item(it) } .insertSeparators { before, after -> - val beforeDate = before?.item?.readAt?.time?.toDateKey() ?: Date(0) - val afterDate = after?.item?.readAt?.time?.toDateKey() ?: Date(0) + val beforeDate = before?.item?.readAt?.time?.toLocalDate() ?: LocalDate.MIN + val afterDate = after?.item?.readAt?.time?.toLocalDate() ?: LocalDate.MIN when { - beforeDate.time != afterDate.time && afterDate.time != 0L -> HistoryUiModel.Header(afterDate) + beforeDate.isAfter(afterDate) + or afterDate.equals(LocalDate.MIN) + or beforeDate.equals(LocalDate.MIN) -> HistoryUiModel.Header(afterDate) // Return null to avoid adding a separator between two items. else -> null } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt index f76ac45fe..2b2c1c25a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt @@ -16,7 +16,7 @@ import eu.kanade.tachiyomi.data.download.DownloadCache import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.library.LibraryUpdateJob -import eu.kanade.tachiyomi.util.lang.toDateKey +import eu.kanade.tachiyomi.util.lang.toLocalDate import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.mutate import kotlinx.collections.immutable.persistentListOf @@ -45,8 +45,8 @@ import tachiyomi.domain.updates.interactor.GetUpdates import tachiyomi.domain.updates.model.UpdatesWithRelations import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import java.time.LocalDate import java.time.ZonedDateTime -import java.util.Date class UpdatesScreenModel( private val sourceManager: SourceManager = Injekt.get(), @@ -374,12 +374,12 @@ class UpdatesScreenModel( return items .map { UpdatesUiModel.Item(it) } .insertSeparators { before, after -> - val beforeDate = before?.item?.update?.dateFetch?.toDateKey() ?: Date(0) - val afterDate = after?.item?.update?.dateFetch?.toDateKey() ?: Date(0) + val beforeDate = before?.item?.update?.dateFetch?.toLocalDate() ?: LocalDate.MIN + val afterDate = after?.item?.update?.dateFetch?.toLocalDate() ?: LocalDate.MIN when { - beforeDate.time != afterDate.time && afterDate.time != 0L -> { - UpdatesUiModel.Header(afterDate) - } + beforeDate.isAfter(afterDate) + or afterDate.equals(LocalDate.MIN) + or beforeDate.equals(LocalDate.MIN) -> UpdatesUiModel.Header(afterDate) // Return null to avoid adding a separator between two items. else -> null } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt index a313f5a2d..e1a1ea62f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt @@ -6,15 +6,17 @@ import tachiyomi.core.common.i18n.stringResource import tachiyomi.i18n.MR import java.text.DateFormat import java.time.Instant +import java.time.LocalDate import java.time.LocalDateTime import java.time.ZoneId +import java.time.format.DateTimeFormatter +import java.time.format.FormatStyle import java.time.temporal.ChronoUnit -import java.util.Calendar import java.util.Date -fun Date.toDateTimestampString(dateFormatter: DateFormat): String { - val date = dateFormatter.format(this) - val time = DateFormat.getTimeInstance(DateFormat.SHORT).format(this) +fun LocalDateTime.toDateTimestampString(dateTimeFormatter: DateTimeFormatter): String { + val date = dateTimeFormatter.format(this) + val time = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT).format(this) return "$date $time" } @@ -32,52 +34,28 @@ fun Long.convertEpochMillisZone( .toEpochMilli() } -/** - * Get date as time key - * - * @param date desired date - * @return date as time key - */ -fun Long.toDateKey(): Date { - val instant = Instant.ofEpochMilli(this) - return Date.from(instant.truncatedTo(ChronoUnit.DAYS)) +fun Long.toLocalDate(): LocalDate { + return LocalDate.ofInstant(Instant.ofEpochMilli(this), ZoneId.systemDefault()) } -fun Date.toRelativeString( +fun LocalDate.toRelativeSting( context: Context, relative: Boolean = true, - dateFormat: DateFormat = DateFormat.getDateInstance(DateFormat.SHORT), + dateFormat: DateTimeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT), ): String { if (!relative) { return dateFormat.format(this) } - val now = Date() - val difference = now.timeWithOffset.floorNearest(MILLISECONDS_IN_DAY) - - this.timeWithOffset.floorNearest(MILLISECONDS_IN_DAY) - val days = difference.floorDiv(MILLISECONDS_IN_DAY).toInt() + val now = LocalDate.now() + val difference = ChronoUnit.DAYS.between(this, now) return when { - difference < 0 -> dateFormat.format(this) - difference < MILLISECONDS_IN_DAY -> context.stringResource(MR.strings.relative_time_today) - difference < MILLISECONDS_IN_DAY.times(7) -> context.pluralStringResource( + difference < 0 -> difference.toString() + difference < 1 -> context.stringResource(MR.strings.relative_time_today) + difference < 7 -> context.pluralStringResource( MR.plurals.relative_time, - days, - days, + difference.toInt(), + difference.toInt(), ) else -> dateFormat.format(this) } } - -private const val MILLISECONDS_IN_DAY = 86_400_000L - -private val Date.timeWithOffset: Long - get() { - return Calendar.getInstance().run { - time = this@timeWithOffset - val dstOffset = get(Calendar.DST_OFFSET) - this@timeWithOffset.time + timeZone.rawOffset + dstOffset - } - } - -private fun Long.floorNearest(to: Long): Long { - return this.floorDiv(to) * to -} From 1ee54d74a4626bfb05996ba1c338fea8df85c766 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Fri, 16 Feb 2024 17:18:53 +0600 Subject: [PATCH 155/212] Ignore detekt [LongParameterList] for composables --- config/detekt/detekt.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 112fcf3f0..0e21d99d4 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -10,6 +10,7 @@ complexity: functionThreshold: 6 constructorThreshold: 7 ignoreDefaultParameters: true + ignoreAnnotated: [ 'Composable' ] style: MagicNumber: From 1e28999e1387c47080d2dc868021b3f257e5325e Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 17 Feb 2024 03:28:48 +0600 Subject: [PATCH 156/212] Revert a mishap in 7ff95e21babda98dd1b479912278d6029cd15f0d --- .../main/java/eu/kanade/presentation/components/DateText.kt | 4 ++-- .../main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/components/DateText.kt b/app/src/main/java/eu/kanade/presentation/components/DateText.kt index 59708a049..4f2d988af 100644 --- a/app/src/main/java/eu/kanade/presentation/components/DateText.kt +++ b/app/src/main/java/eu/kanade/presentation/components/DateText.kt @@ -4,7 +4,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext import eu.kanade.domain.ui.UiPreferences -import eu.kanade.tachiyomi.util.lang.toRelativeSting +import eu.kanade.tachiyomi.util.lang.toRelativeString import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource import uy.kohesive.injekt.Injekt @@ -36,7 +36,7 @@ fun relativeDateText( val relativeTime = remember { preferences.relativeTime().get() } val dateFormat = remember { UiPreferences.dateFormat(preferences.dateFormat().get()) } - return localDate?.toRelativeSting( + return localDate?.toRelativeString( context = context, relative = relativeTime, dateFormat = dateFormat, diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt index e1a1ea62f..9e61555ec 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt @@ -38,7 +38,7 @@ fun Long.toLocalDate(): LocalDate { return LocalDate.ofInstant(Instant.ofEpochMilli(this), ZoneId.systemDefault()) } -fun LocalDate.toRelativeSting( +fun LocalDate.toRelativeString( context: Context, relative: Boolean = true, dateFormat: DateTimeFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT), From 0bfacf5570784f78ce66da76a691cda5e8c1a49b Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 17 Feb 2024 04:25:41 +0600 Subject: [PATCH 157/212] Tweak detekt config --- config/detekt/detekt.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 0e21d99d4..11c42e80d 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -6,9 +6,9 @@ naming: constantPattern: '[A-Z][A-Za-z0-9]*' complexity: + LongMethod: + ignoreAnnotated: [ 'Composable' ] LongParameterList: - functionThreshold: 6 - constructorThreshold: 7 ignoreDefaultParameters: true ignoreAnnotated: [ 'Composable' ] @@ -16,5 +16,7 @@ style: MagicNumber: ignorePropertyDeclaration: true ignoreCompanionObjectPropertyDeclaration: true + ReturnCount: + excludeGuardClauses: true UnusedPrivateMember: ignoreAnnotated: [ 'Preview' ] From 3910ffdd9e99f5511cf1541cefcb990d96c2708b Mon Sep 17 00:00:00 2001 From: MajorTanya <39014446+MajorTanya@users.noreply.github.com> Date: Sun, 18 Feb 2024 19:57:50 +0100 Subject: [PATCH 158/212] Fix DelayedTrackingUpdateJob spam on update errors (#411) * Fix DelayedTrackingUpdateJob spam on update errors DelayedTrackingUpdateJob would start spamming when it encountered an error (e.g. a tracker has an issue) and never stop. This seems to stem from a circular dependency between the Job's `doWork` and TrackChapter's `await`. TrackChapter sets up a completely new instance of the DelayedTrackingUpdateJob if any Exception was thrown during the track update. This causes the Job to get replaced (as per the WorkManager's set ExistingWorkPolicy). Because of this, the guard clause at the start of doWork would never trigger, as all instances of the Job would report being the 0th try (because they were completely new instances). This simple fix introduces a boolean `isRetry` parameter to TrackChapter's await method, which is set to `false` by default. DelayedTrackingUpdateJob however sets this parameter to `true`, which means TrackChapter won't try to set up the Job again. * Rename isRetry parameter to setupJobOnFailure This also inverts the logic, so true & false were swapped. --- .../java/eu/kanade/domain/track/interactor/TrackChapter.kt | 6 ++++-- .../kanade/domain/track/service/DelayedTrackingUpdateJob.kt | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt b/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt index a942d5f94..fdf24ec4e 100644 --- a/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt +++ b/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt @@ -21,7 +21,7 @@ class TrackChapter( private val delayedTrackingStore: DelayedTrackingStore, ) { - suspend fun await(context: Context, mangaId: Long, chapterNumber: Double) { + suspend fun await(context: Context, mangaId: Long, chapterNumber: Double, setupJobOnFailure: Boolean = true) { withNonCancellableContext { val tracks = getTracks.await(mangaId) if (tracks.isEmpty()) return@withNonCancellableContext @@ -43,7 +43,9 @@ class TrackChapter( delayedTrackingStore.remove(track.id) } catch (e: Exception) { delayedTrackingStore.add(track.id, chapterNumber) - DelayedTrackingUpdateJob.setupTask(context) + if (setupJobOnFailure) { + DelayedTrackingUpdateJob.setupTask(context) + } throw e } } diff --git a/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt b/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt index 1f4e246dc..50589ae9d 100644 --- a/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt +++ b/app/src/main/java/eu/kanade/domain/track/service/DelayedTrackingUpdateJob.kt @@ -45,7 +45,7 @@ class DelayedTrackingUpdateJob(private val context: Context, workerParams: Worke logcat(LogPriority.DEBUG) { "Updating delayed track item: ${track.mangaId}, last chapter read: ${track.lastChapterRead}" } - trackChapter.await(context, track.mangaId, track.lastChapterRead) + trackChapter.await(context, track.mangaId, track.lastChapterRead, setupJobOnFailure = false) } } From 42ebf017e412df292672f3982beefe020c872c44 Mon Sep 17 00:00:00 2001 From: Maddie Witman Date: Sat, 24 Feb 2024 14:33:46 -0500 Subject: [PATCH 159/212] Fix some issues from 7ff95e2 (#415) * Fixed extra header introduced in 7ff95e2 * Removed parentheses to make detekt happy * Updated relative date display for dates in the future * Small cleanup for header creation logic * replaced "and" with "&&" for better formatting --- .../kanade/tachiyomi/ui/history/HistoryScreenModel.kt | 9 +++------ .../kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt | 9 +++------ .../eu/kanade/tachiyomi/util/lang/DateExtensions.kt | 10 +++++++++- i18n/src/commonMain/resources/MR/base/plurals.xml | 5 +++++ 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt index 7b0cdcf72..c0c2b555f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/history/HistoryScreenModel.kt @@ -28,7 +28,6 @@ import tachiyomi.domain.history.interactor.RemoveHistory import tachiyomi.domain.history.model.HistoryWithRelations import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.time.LocalDate class HistoryScreenModel( private val getHistory: GetHistory = Injekt.get(), @@ -60,12 +59,10 @@ class HistoryScreenModel( private fun List.toHistoryUiModels(): List { return map { HistoryUiModel.Item(it) } .insertSeparators { before, after -> - val beforeDate = before?.item?.readAt?.time?.toLocalDate() ?: LocalDate.MIN - val afterDate = after?.item?.readAt?.time?.toLocalDate() ?: LocalDate.MIN + val beforeDate = before?.item?.readAt?.time?.toLocalDate() + val afterDate = after?.item?.readAt?.time?.toLocalDate() when { - beforeDate.isAfter(afterDate) - or afterDate.equals(LocalDate.MIN) - or beforeDate.equals(LocalDate.MIN) -> HistoryUiModel.Header(afterDate) + beforeDate != afterDate && afterDate != null -> HistoryUiModel.Header(afterDate) // Return null to avoid adding a separator between two items. else -> null } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt index 2b2c1c25a..c5385d1f0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt @@ -45,7 +45,6 @@ import tachiyomi.domain.updates.interactor.GetUpdates import tachiyomi.domain.updates.model.UpdatesWithRelations import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -import java.time.LocalDate import java.time.ZonedDateTime class UpdatesScreenModel( @@ -374,12 +373,10 @@ class UpdatesScreenModel( return items .map { UpdatesUiModel.Item(it) } .insertSeparators { before, after -> - val beforeDate = before?.item?.update?.dateFetch?.toLocalDate() ?: LocalDate.MIN - val afterDate = after?.item?.update?.dateFetch?.toLocalDate() ?: LocalDate.MIN + val beforeDate = before?.item?.update?.dateFetch?.toLocalDate() + val afterDate = after?.item?.update?.dateFetch?.toLocalDate() when { - beforeDate.isAfter(afterDate) - or afterDate.equals(LocalDate.MIN) - or beforeDate.equals(LocalDate.MIN) -> UpdatesUiModel.Header(afterDate) + beforeDate != afterDate && afterDate != null -> UpdatesUiModel.Header(afterDate) // Return null to avoid adding a separator between two items. else -> null } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt index 9e61555ec..8326fe2f8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/lang/DateExtensions.kt @@ -13,6 +13,7 @@ import java.time.format.DateTimeFormatter import java.time.format.FormatStyle import java.time.temporal.ChronoUnit import java.util.Date +import kotlin.math.absoluteValue fun LocalDateTime.toDateTimestampString(dateTimeFormatter: DateTimeFormatter): String { val date = dateTimeFormatter.format(this) @@ -49,13 +50,20 @@ fun LocalDate.toRelativeString( val now = LocalDate.now() val difference = ChronoUnit.DAYS.between(this, now) return when { - difference < 0 -> difference.toString() + difference < -7 -> dateFormat.format(this) + difference < 0 -> context.pluralStringResource( + MR.plurals.upcoming_relative_time, + difference.toInt().absoluteValue, + difference.toInt().absoluteValue, + ) + difference < 1 -> context.stringResource(MR.strings.relative_time_today) difference < 7 -> context.pluralStringResource( MR.plurals.relative_time, difference.toInt(), difference.toInt(), ) + else -> dateFormat.format(this) } } diff --git a/i18n/src/commonMain/resources/MR/base/plurals.xml b/i18n/src/commonMain/resources/MR/base/plurals.xml index 2f10b004c..d9c958afc 100644 --- a/i18n/src/commonMain/resources/MR/base/plurals.xml +++ b/i18n/src/commonMain/resources/MR/base/plurals.xml @@ -10,6 +10,11 @@ %1$d days ago + + Tomorrow + In %1$d days + + %d category %d categories From ab067209661eceefc04c65f6bdbfcaa8a1264651 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 2 Mar 2024 18:41:56 +0600 Subject: [PATCH 160/212] Upgrade Compose --- gradle/compose.versions.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 778c43c47..54a808a29 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,7 +1,7 @@ [versions] -compiler = "1.5.8" -compose-bom = "2024.01.00-alpha03" -accompanist = "0.34.0" +compiler = "1.5.10" +compose-bom = "2024.02.00-alpha02" +accompanist = "0.35.0-alpha" [libraries] activity = "androidx.activity:activity-compose:1.8.2" From c348fac78fac479fb123bd617c01c78b9ca851d5 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Thu, 14 Mar 2024 03:28:48 +0600 Subject: [PATCH 161/212] Fix crash in track date selection dialog Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> --- .../eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt | 1 + domain/src/main/java/tachiyomi/domain/track/model/Track.kt | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt index e0ad25182..1ba697f24 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackInfoDialog.kt @@ -427,6 +427,7 @@ private data class TrackDateSelectorScreen( private val start: Boolean, ) : Screen() { + @Transient private val selectableDates = object : SelectableDates { override fun isSelectableDate(utcTimeMillis: Long): Boolean { val dateToCheck = Instant.ofEpochMilli(utcTimeMillis) diff --git a/domain/src/main/java/tachiyomi/domain/track/model/Track.kt b/domain/src/main/java/tachiyomi/domain/track/model/Track.kt index 1a656fcad..a92390494 100644 --- a/domain/src/main/java/tachiyomi/domain/track/model/Track.kt +++ b/domain/src/main/java/tachiyomi/domain/track/model/Track.kt @@ -1,5 +1,7 @@ package tachiyomi.domain.track.model +import java.io.Serializable + data class Track( val id: Long, val mangaId: Long, @@ -14,4 +16,4 @@ data class Track( val remoteUrl: String, val startDate: Long, val finishDate: Long, -) +) : Serializable From fa6fa1f53adb3ed55b7c7f6bd44b8dda59d62c50 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 17 Mar 2024 19:36:14 +0600 Subject: [PATCH 162/212] Disable `SerialVersionUIDInSerializableClass` detekt rule --- config/detekt/detekt.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 11c42e80d..d1a3e3dbb 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -18,5 +18,7 @@ style: ignoreCompanionObjectPropertyDeclaration: true ReturnCount: excludeGuardClauses: true + SerialVersionUIDInSerializableClass: + active: false UnusedPrivateMember: ignoreAnnotated: [ 'Preview' ] From 35f8eda8c57b34befe51bed741276599b9305069 Mon Sep 17 00:00:00 2001 From: MajorTanya <39014446+MajorTanya@users.noreply.github.com> Date: Thu, 21 Mar 2024 06:37:17 +0100 Subject: [PATCH 163/212] Switch to seconds for DATE_MODIFIED of saved pages (#552) While most Android skins are seemingly able to handle the millisecond format, the documentation technically specifies seconds. This seems to be causing issues on Samsung devices using the Samsung Gallery app, which renders the millisecond timestamps as if they were second ones, causing the dates to be set at some point in the year 56189. This change should fix that issue on Samsung devices and have no real impact on the rest. --- app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt index beefba353..b7b53d835 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/saver/ImageSaver.kt @@ -79,7 +79,7 @@ class ImageSaver( MediaStore.Images.Media.RELATIVE_PATH to relativePath, MediaStore.Images.Media.DISPLAY_NAME to image.name, MediaStore.Images.Media.MIME_TYPE to type.mime, - MediaStore.Images.Media.DATE_MODIFIED to Instant.now().toEpochMilli(), + MediaStore.Images.Media.DATE_MODIFIED to Instant.now().epochSecond, ) val picture = findUriOrDefault(relativePath, filename) { From 7862088b940f9881bf59c3d8460baccf5c48c5b0 Mon Sep 17 00:00:00 2001 From: w Date: Thu, 21 Mar 2024 00:20:29 -0700 Subject: [PATCH 164/212] Update image-decoder, color management (#523) * Update image-decoder, color management * move display profile pref * remove true color pref * Move Display Profile settings to a new section * Partially revert "remove true color pref" This partially reverts commit e1a75816950e100936699279e1618adb2fa341aa. * Tweak label --------- Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- .../eu/kanade/domain/base/BasePreferences.kt | 2 ++ .../settings/screen/SettingsAdvancedScreen.kt | 31 +++++++++++++++++++ .../settings/screen/SettingsReaderScreen.kt | 6 +--- .../data/coil/TachiyomiImageDecoder.kt | 2 +- .../tachiyomi/ui/reader/ReaderActivity.kt | 24 +++++++++----- .../ui/reader/setting/ReaderPreferences.kt | 3 -- .../ui/reader/viewer/ViewerConfig.kt | 4 --- gradle/libs.versions.toml | 4 +-- .../commonMain/resources/MR/base/strings.xml | 3 +- 9 files changed, 54 insertions(+), 25 deletions(-) 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 9fa63d2cd..1700f0a02 100644 --- a/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt +++ b/app/src/main/java/eu/kanade/domain/base/BasePreferences.kt @@ -28,4 +28,6 @@ class BasePreferences( SHIZUKU(MR.strings.ext_installer_shizuku, false), PRIVATE(MR.strings.ext_installer_private, false), } + + fun displayProfile() = preferenceStore.getString("pref_display_profile_key", "") } 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 509f2196a..646c8c042 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 @@ -6,6 +6,8 @@ import android.content.Intent import android.provider.Settings import android.webkit.WebStorage import android.webkit.WebView +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.material3.AlertDialog import androidx.compose.material3.Text import androidx.compose.material3.TextButton @@ -123,6 +125,7 @@ object SettingsAdvancedScreen : SearchableSettings { getDataGroup(), getNetworkGroup(networkPreferences = networkPreferences), getLibraryGroup(), + getReaderGroup(basePreferences = basePreferences), getExtensionsGroup(basePreferences = basePreferences), ) } @@ -313,6 +316,34 @@ object SettingsAdvancedScreen : SearchableSettings { ) } + @Composable + private fun getReaderGroup( + basePreferences: BasePreferences, + ): Preference.PreferenceGroup { + val context = LocalContext.current + val chooseColorProfile = rememberLauncherForActivityResult( + contract = ActivityResultContracts.OpenDocument(), + ) { uri -> + uri?.let { + val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION + context.contentResolver.takePersistableUriPermission(uri, flags) + basePreferences.displayProfile().set(uri.toString()) + } + } + return Preference.PreferenceGroup( + title = stringResource(MR.strings.pref_category_reader), + preferenceItems = persistentListOf( + Preference.PreferenceItem.TextPreference( + title = stringResource(MR.strings.pref_display_profile), + subtitle = basePreferences.displayProfile().get(), + onClick = { + chooseColorProfile.launch(arrayOf("*/*")) + }, + ), + ) + ) + } + @Composable private fun getExtensionsGroup( basePreferences: BasePreferences, 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 bc60037e9..86bdd97c2 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 @@ -29,6 +29,7 @@ object SettingsReaderScreen : SearchableSettings { @Composable override fun getPreferences(): List { val readerPref = remember { Injekt.get() } + return listOf( Preference.PreferenceItem.ListPreference( pref = readerPref.defaultReadingMode(), @@ -56,11 +57,6 @@ object SettingsReaderScreen : SearchableSettings { title = stringResource(MR.strings.pref_show_navigation_mode), subtitle = stringResource(MR.strings.pref_show_navigation_mode_summary), ), - Preference.PreferenceItem.SwitchPreference( - pref = readerPref.trueColor(), - title = stringResource(MR.strings.pref_true_color), - subtitle = stringResource(MR.strings.pref_true_color_summary), - ), Preference.PreferenceItem.SwitchPreference( pref = readerPref.pageTransitions(), title = stringResource(MR.strings.pref_page_transitions), diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt index acbda8cc5..a984fc919 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt @@ -24,7 +24,7 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti check(decoder != null && decoder.width > 0 && decoder.height > 0) { "Failed to initialize decoder" } - val bitmap = decoder.decode(rgb565 = options.allowRgb565) + val bitmap = decoder.decode() decoder.recycle() check(bitmap != null) { "Failed to decode image" } 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 f170f7e1a..197d1f9dc 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 @@ -38,6 +38,7 @@ import androidx.lifecycle.lifecycleScope import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.google.android.material.elevation.SurfaceColors import com.google.android.material.transition.platform.MaterialContainerTransform +import com.hippo.unifile.UniFile import dev.chrisbanes.insetter.applyInsetter import eu.kanade.domain.base.BasePreferences import eu.kanade.presentation.reader.DisplayRefreshHost @@ -92,6 +93,7 @@ import tachiyomi.i18n.MR import tachiyomi.presentation.core.util.collectAsState import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import java.io.ByteArrayOutputStream class ReaderActivity : BaseActivity() { @@ -795,8 +797,8 @@ class ReaderActivity : BaseActivity() { } .launchIn(lifecycleScope) - readerPreferences.trueColor().changes() - .onEach(::setTrueColor) + preferences.displayProfile().changes() + .onEach { setDisplayProfile(it) } .launchIn(lifecycleScope) readerPreferences.cutoutShort().changes() @@ -835,13 +837,19 @@ class ReaderActivity : BaseActivity() { } /** - * Sets the 32-bit color mode according to [enabled]. + * Sets the display profile to [path]. */ - private fun setTrueColor(enabled: Boolean) { - if (enabled) { - SubsamplingScaleImageView.setPreferredBitmapConfig(Bitmap.Config.ARGB_8888) - } else { - SubsamplingScaleImageView.setPreferredBitmapConfig(Bitmap.Config.RGB_565) + private fun setDisplayProfile(path: String) { + val file = UniFile.fromUri(baseContext, path.toUri()) + if (file != null && file.exists()) { + val inputStream = file.openInputStream() + val outputStream = ByteArrayOutputStream() + inputStream.use { input -> + outputStream.use { output -> + input.copyTo(output) + } + } + SubsamplingScaleImageView.setDisplayProfile(outputStream.toByteArray()) } } 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 6c079b836..39045175f 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 @@ -23,9 +23,6 @@ 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) fun cutoutShort() = preferenceStore.getBoolean("cutout_short", true) 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 0d973d239..c84f8f51b 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 @@ -22,7 +22,6 @@ abstract class ViewerConfig(readerPreferences: ReaderPreferences, private val sc var doubleTapAnimDuration = 500 var volumeKeysEnabled = false var volumeKeysInverted = false - var trueColor = false var alwaysShowChapterTransition = true var navigationMode = 0 protected set @@ -62,9 +61,6 @@ abstract class ViewerConfig(readerPreferences: ReaderPreferences, private val sc readerPreferences.readWithVolumeKeysInverted() .register({ volumeKeysInverted = it }) - readerPreferences.trueColor() - .register({ trueColor = it }, { imagePropertyChangedListener?.invoke() }) - readerPreferences.alwaysShowChapterTransition() .register({ alwaysShowChapterTransition = it }) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 576d08176..bd306d843 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -47,8 +47,8 @@ coil-core = { module = "io.coil-kt:coil" } coil-gif = { module = "io.coil-kt:coil-gif" } coil-compose = { module = "io.coil-kt:coil-compose" } -subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:7e57335" -image-decoder = "com.github.tachiyomiorg:image-decoder:fbd6601290" +subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:aeaa170036" +image-decoder = "com.github.tachiyomiorg:image-decoder:e08e9be535" natural-comparator = "com.github.gpanther:java-nat-sort:natural-comparator-1.1" diff --git a/i18n/src/commonMain/resources/MR/base/strings.xml b/i18n/src/commonMain/resources/MR/base/strings.xml index 5214e41a8..08fe9eeda 100644 --- a/i18n/src/commonMain/resources/MR/base/strings.xml +++ b/i18n/src/commonMain/resources/MR/base/strings.xml @@ -364,8 +364,7 @@ Show page number Show reading mode Briefly show current mode when reader is opened - 32-bit color - Reduces banding, but may impact performance + Custom display profile Crop borders Custom brightness Grayscale From 6a8a9c6bbf89658920d92b414b510ec2af1f74cb Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Fri, 22 Mar 2024 18:43:36 +0600 Subject: [PATCH 165/212] Address detekt issues --- .../main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt | 1 - 1 file changed, 1 deletion(-) 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 197d1f9dc..159a6d3fc 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 @@ -4,7 +4,6 @@ import android.annotation.SuppressLint import android.app.assist.AssistContent import android.content.Context import android.content.Intent -import android.graphics.Bitmap import android.graphics.Color import android.graphics.ColorMatrix import android.graphics.ColorMatrixColorFilter From 2755d1f35ed683d508e5b503ae81c2c1c96248ed Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Mar 2024 23:05:13 +0600 Subject: [PATCH 166/212] chore(deps): update dependency gradle to v8.7 (#567) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.jar | Bin 63375 -> 43453 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 17 +++++++++-------- gradlew.bat | 20 ++++++++++---------- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 033e24c4cdf41af1ab109bc7f253b2b887023340..e6441136f3d4ba8a0da8d277868979cfbc8ad796 100644 GIT binary patch literal 43453 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 yu?e%!_ptzGB|8GrCfu%p?(_ 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

NnX)EH+Nua)3Y(c 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≫L+crb^Bz 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 /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -201,11 +202,11 @@ 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 -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/gradlew.bat b/gradlew.bat index 93e3f59f1..25da30dbd 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail From a7509b3a3c3a69cf5cf5fa852624dbd49131853b Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 6 Apr 2024 11:07:11 +0600 Subject: [PATCH 167/212] Fix build time zone in about screen And slight cleanup --- .../more/settings/screen/about/AboutScreen.kt | 16 +++++++++++----- buildSrc/src/main/kotlin/Commands.kt | 15 ++++++++------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt index 615687aff..ba4e42c7a 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt @@ -55,8 +55,10 @@ import tachiyomi.presentation.core.icons.Reddit import tachiyomi.presentation.core.icons.X import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import java.time.Instant import java.time.LocalDateTime import java.time.ZoneId +import java.time.ZonedDateTime import java.time.format.DateTimeFormatter import java.util.Locale @@ -269,11 +271,15 @@ object AboutScreen : Screen() { internal fun getFormattedBuildTime(): String { return try { - val df = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm'Z'", Locale.US) - .withZone(ZoneId.of("UTC")) - val buildTime = LocalDateTime.from(df.parse(BuildConfig.BUILD_TIME)) - - buildTime!!.toDateTimestampString(UiPreferences.dateFormat(Injekt.get().dateFormat().get())) + LocalDateTime.ofInstant( + Instant.parse(BuildConfig.BUILD_TIME), + ZoneId.systemDefault(), + ) + .toDateTimestampString( + UiPreferences.dateFormat( + Injekt.get().dateFormat().get(), + ), + ) } catch (e: Exception) { BuildConfig.BUILD_TIME } diff --git a/buildSrc/src/main/kotlin/Commands.kt b/buildSrc/src/main/kotlin/Commands.kt index 8e5fe7649..5592c4eef 100644 --- a/buildSrc/src/main/kotlin/Commands.kt +++ b/buildSrc/src/main/kotlin/Commands.kt @@ -1,8 +1,8 @@ import org.gradle.api.Project import java.io.ByteArrayOutputStream -import java.text.SimpleDateFormat -import java.util.TimeZone -import java.util.Date +import java.time.LocalDateTime +import java.time.ZoneOffset +import java.time.format.DateTimeFormatter // Git is needed in your system PATH for these commands to work. // If it's not installed, you can return a random value as a workaround @@ -16,10 +16,11 @@ fun Project.getGitSha(): String { // return "1" } +private val BUILD_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'") + +@Suppress("UnusedReceiverParameter") fun Project.getBuildTime(): String { - val df = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'") - df.timeZone = TimeZone.getTimeZone("UTC") - return df.format(Date()) + return LocalDateTime.now(ZoneOffset.UTC).format(BUILD_TIME_FORMATTER) } fun Project.runCommand(command: String): String { @@ -29,4 +30,4 @@ fun Project.runCommand(command: String): String { standardOutput = byteOut } return String(byteOut.toByteArray()).trim() -} \ No newline at end of file +} From 9a6559b013320dcfde2e12c7f8fab10ed72888bf Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 6 Apr 2024 12:21:25 +0600 Subject: [PATCH 168/212] Remove unused imports --- .../presentation/more/settings/screen/about/AboutScreen.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt index ba4e42c7a..e91a0576c 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/about/AboutScreen.kt @@ -58,9 +58,6 @@ import uy.kohesive.injekt.api.get import java.time.Instant import java.time.LocalDateTime import java.time.ZoneId -import java.time.ZonedDateTime -import java.time.format.DateTimeFormatter -import java.util.Locale object AboutScreen : Screen() { From 2a01a2ac6b13bad8c277b2dee7ba9561fe734416 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Tue, 9 Apr 2024 13:48:14 +0600 Subject: [PATCH 169/212] Release v0.16.5 --- .github/ISSUE_TEMPLATE/report_issue.yml | 4 ++-- .github/ISSUE_TEMPLATE/request_feature.yml | 2 +- app/build.gradle.kts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/report_issue.yml b/.github/ISSUE_TEMPLATE/report_issue.yml index 0835f86cb..30ac12739 100644 --- a/.github/ISSUE_TEMPLATE/report_issue.yml +++ b/.github/ISSUE_TEMPLATE/report_issue.yml @@ -53,7 +53,7 @@ body: label: Mihon version description: You can find your Mihon version in **More → About**. placeholder: | - Example: "0.16.4" + Example: "0.16.5" validations: required: true @@ -96,7 +96,7 @@ body: required: true - label: I have gone through the [FAQ](https://mihon.app/docs/faq/general) and [troubleshooting guide](https://mihon.app/docs/guides/troubleshooting/). required: true - - label: I have updated the app to version **[0.16.4](https://github.com/mihonapp/mihon/releases/latest)**. + - label: I have updated the app to version **[0.16.5](https://github.com/mihonapp/mihon/releases/latest)**. required: true - label: I have updated all installed extensions. required: true diff --git a/.github/ISSUE_TEMPLATE/request_feature.yml b/.github/ISSUE_TEMPLATE/request_feature.yml index 48071fc91..a653bfda0 100644 --- a/.github/ISSUE_TEMPLATE/request_feature.yml +++ b/.github/ISSUE_TEMPLATE/request_feature.yml @@ -31,7 +31,7 @@ body: required: true - label: I have written a short but informative title. required: true - - label: I have updated the app to version **[0.16.4](https://github.com/mihonapp/mihon/releases/latest)**. + - label: I have updated the app to version **[0.16.5](https://github.com/mihonapp/mihon/releases/latest)**. required: true - label: I will fill out all of the requested information in this form. required: true diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 00dc1b691..0fb94e5f8 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,8 +22,8 @@ android { defaultConfig { applicationId = "app.mihon" - versionCode = 5 - versionName = "0.16.4" + versionCode = 6 + versionName = "0.16.5" buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"") From 015620711d4d6b060a5e416995efd2c6e6b20bbc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 12:56:45 +0600 Subject: [PATCH 170/212] Update gradle/actions action to v3.2.1 (#657) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index d58311c47..585cd2a7b 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -35,7 +35,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@e24011a3b5db78bd5ab798036042d9312002f252 # v3.2.0 + uses: gradle/actions/setup-gradle@1168cd3d07c1876a65e1724114de42ccbdfa7b78 # v3.2.1 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 878382f17..218778a7b 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -33,7 +33,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@e24011a3b5db78bd5ab798036042d9312002f252 # v3.2.0 + uses: gradle/actions/setup-gradle@1168cd3d07c1876a65e1724114de42ccbdfa7b78 # v3.2.1 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest From ea0fe2414e1e30b6e82ddf65144035283b31a5c4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 12:57:10 +0600 Subject: [PATCH 171/212] Update dependency com.android.tools.build:gradle to v8.3.2 (#655) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 2357dfc0b..879f5e846 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -1,5 +1,5 @@ [versions] -agp_version = "8.3.1" +agp_version = "8.3.2" lifecycle_version = "2.7.0" paging_version = "3.2.1" From 4c43a0ef66e9c8a321fb745b860319aaa074c57f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 12:57:35 +0600 Subject: [PATCH 172/212] Update aboutlib.version to v11.1.3 (#654) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7a5e8555b..5f663ad82 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -aboutlib_version = "11.1.1" +aboutlib_version = "11.1.3" leakcanary = "2.13" moko = "0.23.0" okhttp_version = "5.0.0-alpha.12" From c94d212ef471012990bfe02ba43044de476ab9cd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 13:21:30 +0600 Subject: [PATCH 173/212] chore(deps): update gradle/wrapper-validation-action action to v2.1.3 (#658) Update gradle/wrapper-validation-action action to v2.1.3 Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 585cd2a7b..75864d31f 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@b231772637bb498f11fdbc86052b6e8a8dc9fc92 # v2.1.2 + uses: gradle/wrapper-validation-action@b5418f5a58f5fd2eb486dd7efb368fe7be7eae45 # v2.1.3 - name: Dependency Review uses: actions/dependency-review-action@5bbc3ba658137598168acb2ab73b21c432dd411b # v4.2.5 diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 218778a7b..d64c3c569 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@b231772637bb498f11fdbc86052b6e8a8dc9fc92 # v2.1.2 + uses: gradle/wrapper-validation-action@b5418f5a58f5fd2eb486dd7efb368fe7be7eae45 # v2.1.3 - name: Setup Android SDK run: | From 5378c4c5d6407064ee7f170a0224a748a6f45c61 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 13 Apr 2024 21:55:56 +0600 Subject: [PATCH 174/212] chore(deps): update gradle/wrapper-validation-action action to v3 (#661) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 75864d31f..1e680c32d 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@b5418f5a58f5fd2eb486dd7efb368fe7be7eae45 # v2.1.3 + uses: gradle/wrapper-validation-action@460a3ca55fc5d559238a0efc7fa9f7465df8585d # v3.3.0 - name: Dependency Review uses: actions/dependency-review-action@5bbc3ba658137598168acb2ab73b21c432dd411b # v4.2.5 diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index d64c3c569..34c85991b 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@b5418f5a58f5fd2eb486dd7efb368fe7be7eae45 # v2.1.3 + uses: gradle/wrapper-validation-action@460a3ca55fc5d559238a0efc7fa9f7465df8585d # v3.3.0 - name: Setup Android SDK run: | From 8bba9268918138106aa156db4e530b66c54f85d2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 13 Apr 2024 21:56:30 +0600 Subject: [PATCH 175/212] chore(deps): update gradle/actions action to v3.3.0 (#659) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 1e680c32d..74488ccfd 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -35,7 +35,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@1168cd3d07c1876a65e1724114de42ccbdfa7b78 # v3.2.1 + uses: gradle/actions/setup-gradle@6cec5d49d4d6d4bb982fbed7047db31ea6d38f11 # v3.3.0 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 34c85991b..700c0466c 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -33,7 +33,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@1168cd3d07c1876a65e1724114de42ccbdfa7b78 # v3.2.1 + uses: gradle/actions/setup-gradle@6cec5d49d4d6d4bb982fbed7047db31ea6d38f11 # v3.3.0 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest From f080a4937e61d3dde5473876c34db8f16844e30c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 13 Apr 2024 21:57:21 +0600 Subject: [PATCH 176/212] fix(deps): update dependency com.google.firebase:firebase-analytics-ktx to v21.6.2 (#656) Update dependency com.google.firebase:firebase-analytics-ktx to v21.6.2 Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5f663ad82..47446f893 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -73,7 +73,7 @@ moko-gradle = { module = "dev.icerock.moko:resources-generator", version.ref = " logcat = "com.squareup.logcat:logcat:0.1" -firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.6.1" +firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.6.2" aboutLibraries-gradle = { module = "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin", version.ref = "aboutlib_version" } aboutLibraries-compose = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "aboutlib_version" } From 843daa5304d0b1a93ba69f8cc69791e446a58596 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Wed, 17 Apr 2024 15:21:09 +0800 Subject: [PATCH 177/212] Update compose bom and fix renovate config for it (#674) --- .github/renovate.json5 | 10 +++++++++- gradle/compose.versions.toml | 3 ++- .../presentation/core/components/AdaptiveSheet.kt | 4 ++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 4cc36b133..751500ef3 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -6,12 +6,20 @@ "schedule": ["every friday"], "labels": ["Dependencies"], "packageRules": [ + { + "groupName": "Compose BOM", + "matchPackageNames": [ + "dev.chrisbanes.compose:compose-bom" + ], + "ignoreUnstable": false + }, { // Compiler plugins are tightly coupled to Kotlin version "groupName": "Kotlin", "matchPackagePrefixes": [ "androidx.compose.compiler", - "org.jetbrains.kotlin", + "org.jetbrains.kotlin.", + "org.jetbrains.kotlin:" ], } ] diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index b37960e0a..cd0e14211 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,6 +1,7 @@ [versions] compiler = "1.5.11" -compose-bom = "2024.02.00-alpha02" +# 2024.04.00-alpha01 has several bugs with the new animateItem() modifier +compose-bom = "2024.03.00-alpha02" accompanist = "0.35.0-alpha" [libraries] 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 568332ca7..5ee802edc 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 @@ -122,7 +122,7 @@ fun AdaptiveSheet( ) } val internalOnDismissRequest = { - if (anchoredDraggableState.currentValue == 0) { + if (anchoredDraggableState.settledValue == 0) { scope.launch { anchoredDraggableState.animateTo(1) } } } @@ -192,7 +192,7 @@ fun AdaptiveSheet( LaunchedEffect(anchoredDraggableState) { scope.launch { anchoredDraggableState.animateTo(0) } - snapshotFlow { anchoredDraggableState.currentValue } + snapshotFlow { anchoredDraggableState.settledValue } .drop(1) .filter { it == 1 } .collectLatest { From f27ca3b1b2f92258c213bca6b27d8eff4c7363ad Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Wed, 17 Apr 2024 15:21:24 +0800 Subject: [PATCH 178/212] Use m3 ripple and clean up interactionSource usage (#675) Also remove a leftover of scoped storage adaptation. --- app/build.gradle.kts | 1 - .../presentation/browse/ExtensionDetailsScreen.kt | 5 +---- .../manga/components/ChapterDownloadIndicator.kt | 2 +- .../manga/components/MangaBottomActionMenu.kt | 5 ++--- .../presentation/core/components/AdaptiveSheet.kt | 10 ++++------ .../core/components/material/Surface.kt | 2 +- .../tachiyomi/presentation/core/util/Modifier.kt | 15 ++++++--------- 7 files changed, 15 insertions(+), 25 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a4b166fa1..5a16ab034 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -286,7 +286,6 @@ tasks { "-opt-in=androidx.compose.animation.ExperimentalAnimationApi", "-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi", "-opt-in=coil3.annotation.ExperimentalCoilApi", - "-opt-in=com.google.accompanist.permissions.ExperimentalPermissionsApi", "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", "-opt-in=kotlinx.coroutines.FlowPreview", "-opt-in=kotlinx.coroutines.InternalCoroutinesApi", diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt index 807b68b20..dcd5067d3 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt @@ -5,7 +5,6 @@ import android.net.Uri import android.provider.Settings import android.util.DisplayMetrics import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues @@ -354,10 +353,8 @@ private fun InfoText( primaryTextStyle: TextStyle = MaterialTheme.typography.bodyLarge, onClick: (() -> Unit)? = null, ) { - val interactionSource = remember { MutableInteractionSource() } - val clickableModifier = if (onClick != null) { - Modifier.clickable(interactionSource, indication = null) { onClick() } + Modifier.clickable(interactionSource = null, indication = null, onClick = onClick) } else { Modifier } diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt index b1a1474ec..518f09d4b 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt @@ -9,13 +9,13 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.outlined.ArrowDownward import androidx.compose.material.icons.outlined.ErrorOutline -import androidx.compose.material.ripple import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ProgressIndicatorDefaults import androidx.compose.material3.Text +import androidx.compose.material3.ripple import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt index d67a406e7..a78383590 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaBottomActionMenu.kt @@ -8,7 +8,6 @@ import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.combinedClickable -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -30,11 +29,11 @@ import androidx.compose.material.icons.outlined.Delete import androidx.compose.material.icons.outlined.DoneAll import androidx.compose.material.icons.outlined.Download import androidx.compose.material.icons.outlined.RemoveDone -import androidx.compose.material.ripple import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.ripple import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateListOf @@ -191,7 +190,7 @@ private fun RowScope.Button( .size(48.dp) .weight(animatedWeight) .combinedClickable( - interactionSource = remember { MutableInteractionSource() }, + interactionSource = null, indication = ripple(bounded = false), onLongClick = onLongClick, onClick = onClick, 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 5ee802edc..fa6eb375a 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 @@ -10,7 +10,6 @@ import androidx.compose.foundation.gestures.DraggableAnchors import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.anchoredDraggable import androidx.compose.foundation.gestures.animateTo -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.navigationBarsPadding @@ -78,8 +77,7 @@ fun AdaptiveSheet( Box( modifier = Modifier .clickable( - enabled = true, - interactionSource = remember { MutableInteractionSource() }, + interactionSource = null, indication = null, onClick = internalOnDismissRequest, ) @@ -91,7 +89,7 @@ fun AdaptiveSheet( modifier = Modifier .requiredWidthIn(max = 460.dp) .clickable( - interactionSource = remember { MutableInteractionSource() }, + interactionSource = null, indication = null, onClick = {}, ) @@ -129,7 +127,7 @@ fun AdaptiveSheet( Box( modifier = Modifier .clickable( - interactionSource = remember { MutableInteractionSource() }, + interactionSource = null, indication = null, onClick = internalOnDismissRequest, ) @@ -147,7 +145,7 @@ fun AdaptiveSheet( modifier = Modifier .widthIn(max = 460.dp) .clickable( - interactionSource = remember { MutableInteractionSource() }, + interactionSource = null, indication = null, onClick = {}, ) diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Surface.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Surface.kt index e472e5127..0e857ef75 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Surface.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Surface.kt @@ -6,13 +6,13 @@ import androidx.compose.foundation.border import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box -import androidx.compose.material.ripple import androidx.compose.material3.ColorScheme import androidx.compose.material3.LocalAbsoluteTonalElevation import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.contentColorFor import androidx.compose.material3.minimumInteractiveComponentSize +import androidx.compose.material3.ripple import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.NonRestartableComposable 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 411fc9983..875cd4583 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 @@ -1,7 +1,6 @@ package tachiyomi.presentation.core.util 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 @@ -42,14 +41,12 @@ fun Modifier.secondaryItemAlpha(): Modifier = this.alpha(SecondaryItemAlpha) fun Modifier.clickableNoIndication( onLongClick: (() -> Unit)? = null, onClick: () -> Unit, -): Modifier = composed { - Modifier.combinedClickable( - interactionSource = remember { MutableInteractionSource() }, - indication = null, - onLongClick = onLongClick, - onClick = onClick, - ) -} +) = this.combinedClickable( + interactionSource = null, + indication = null, + onLongClick = onLongClick, + onClick = onClick, +) /** * For TextField, the provided [action] will be invoked when From b152e3881bffd9050a8a0ed4030823886e3fe04f Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Sat, 20 Apr 2024 12:52:40 +0800 Subject: [PATCH 179/212] Use Okio instead of `java.io` for image processing (#691) --- app/src/main/java/eu/kanade/tachiyomi/App.kt | 2 + .../data/coil/BufferedSourceFetcher.kt | 38 +++++++++ .../ui/reader/viewer/ReaderPageImageView.kt | 26 +++--- .../ui/reader/viewer/pager/PagerPageHolder.kt | 81 +++++++++---------- .../viewer/webtoon/WebtoonPageHolder.kt | 39 ++++----- .../core/common/util/system/ImageUtil.kt | 73 +++++++---------- 6 files changed, 132 insertions(+), 127 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/coil/BufferedSourceFetcher.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index 12170d8a5..558ad213e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -27,6 +27,7 @@ import eu.kanade.domain.ui.UiPreferences import eu.kanade.domain.ui.model.setAppCompatDelegateThemeMode import eu.kanade.tachiyomi.crash.CrashActivity import eu.kanade.tachiyomi.crash.GlobalExceptionHandler +import eu.kanade.tachiyomi.data.coil.BufferedSourceFetcher import eu.kanade.tachiyomi.data.coil.MangaCoverFetcher import eu.kanade.tachiyomi.data.coil.MangaCoverKeyer import eu.kanade.tachiyomi.data.coil.MangaKeyer @@ -162,6 +163,7 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor add(MangaCoverFetcher.MangaCoverFactory(callFactoryLazy)) add(MangaKeyer()) add(MangaCoverKeyer()) + add(BufferedSourceFetcher.Factory()) } crossfade((300 * this@App.animatorDurationScale).toInt()) allowRgb565(DeviceUtil.isLowRamDevice(this@App)) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/BufferedSourceFetcher.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/BufferedSourceFetcher.kt new file mode 100644 index 000000000..4bee925ed --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/BufferedSourceFetcher.kt @@ -0,0 +1,38 @@ +package eu.kanade.tachiyomi.data.coil + +import coil3.ImageLoader +import coil3.decode.DataSource +import coil3.decode.ImageSource +import coil3.fetch.FetchResult +import coil3.fetch.Fetcher +import coil3.fetch.SourceFetchResult +import coil3.request.Options +import okio.BufferedSource + +class BufferedSourceFetcher( + private val data: BufferedSource, + private val options: Options, +) : Fetcher { + + override suspend fun fetch(): FetchResult { + return SourceFetchResult( + source = ImageSource( + source = data, + fileSystem = options.fileSystem, + ), + mimeType = null, + dataSource = DataSource.MEMORY, + ) + } + + class Factory : Fetcher.Factory { + + override fun create( + data: BufferedSource, + options: Options, + imageLoader: ImageLoader, + ): Fetcher { + return BufferedSourceFetcher(data, options) + } + } +} 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 2cd0f2840..0c83d97ea 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 @@ -33,8 +33,7 @@ import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonSubsamplingImageView import eu.kanade.tachiyomi.util.system.GLUtil import eu.kanade.tachiyomi.util.system.animatorDurationScale import eu.kanade.tachiyomi.util.view.isVisibleOnScreen -import java.io.InputStream -import java.nio.ByteBuffer +import okio.BufferedSource /** * A wrapper view for showing page image. @@ -146,14 +145,14 @@ open class ReaderPageImageView @JvmOverloads constructor( } } - fun setImage(inputStream: InputStream, isAnimated: Boolean, config: Config) { + fun setImage(source: BufferedSource, isAnimated: Boolean, config: Config) { this.config = config if (isAnimated) { prepareAnimatedImageView() - setAnimatedImage(inputStream, config) + setAnimatedImage(source, config) } else { prepareNonAnimatedImageView() - setNonAnimatedImage(inputStream, config) + setNonAnimatedImage(source, config) } } @@ -262,7 +261,7 @@ open class ReaderPageImageView @JvmOverloads constructor( } private fun setNonAnimatedImage( - image: Any, + data: Any, config: Config, ) = (pageView as? SubsamplingScaleImageView)?.apply { setDoubleTapZoomDuration(config.zoomDuration.getSystemScaledDuration()) @@ -283,10 +282,10 @@ open class ReaderPageImageView @JvmOverloads constructor( }, ) - when (image) { - is BitmapDrawable -> setImage(ImageSource.bitmap(image.bitmap)) - is InputStream -> setImage(ImageSource.inputStream(image)) - else -> throw IllegalArgumentException("Not implemented for class ${image::class.simpleName}") + when (data) { + is BitmapDrawable -> setImage(ImageSource.bitmap(data.bitmap)) + is BufferedSource -> setImage(ImageSource.inputStream(data.inputStream())) + else -> throw IllegalArgumentException("Not implemented for class ${data::class.simpleName}") } isVisible = true } @@ -331,18 +330,13 @@ open class ReaderPageImageView @JvmOverloads constructor( } private fun setAnimatedImage( - image: Any, + data: Any, config: Config, ) = (pageView as? AppCompatImageView)?.apply { if (this is PhotoView) { setZoomTransitionDuration(config.zoomDuration.getSystemScaledDuration()) } - val data = when (image) { - is Drawable -> image - is InputStream -> ByteBuffer.wrap(image.readBytes()) - else -> throw IllegalArgumentException("Not implemented for class ${image::class.simpleName}") - } val request = ImageRequest.Builder(context) .data(data) .memoryCachePolicy(CachePolicy.DISABLED) 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 7b6d1dd6a..71d499c38 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 @@ -18,14 +18,13 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope import logcat.LogPriority +import okio.Buffer +import okio.BufferedSource import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.logcat -import java.io.BufferedInputStream -import java.io.ByteArrayInputStream -import java.io.InputStream /** * View of the ViewPager that contains a page of a chapter. @@ -139,38 +138,30 @@ class PagerPageHolder( val streamFn = page.stream ?: return try { - val (bais, isAnimated, background) = withIOContext { - streamFn().buffered(16).use { stream -> - process(item, stream).use { itemStream -> - val bais = ByteArrayInputStream(itemStream.readBytes()) - val isAnimated = ImageUtil.isAnimatedAndSupported(bais) - bais.reset() - val background = if (!isAnimated && viewer.config.automaticBackground) { - ImageUtil.chooseBackground(context, bais) - } else { - null - } - bais.reset() - Triple(bais, isAnimated, background) - } + val (source, isAnimated, background) = withIOContext { + val source = streamFn().use { process(item, Buffer().readFrom(it)) } + val isAnimated = ImageUtil.isAnimatedAndSupported(source) + val background = if (!isAnimated && viewer.config.automaticBackground) { + ImageUtil.chooseBackground(context, source.peek().inputStream()) + } else { + null } + Triple(source, isAnimated, background) } withUIContext { - bais.use { - setImage( - it, - isAnimated, - Config( - zoomDuration = viewer.config.doubleTapAnimDuration, - minimumScaleType = viewer.config.imageScaleType, - cropBorders = viewer.config.imageCropBorders, - zoomStartPosition = viewer.config.imageZoomType, - landscapeZoom = viewer.config.landscapeZoom, - ), - ) - if (!isAnimated) { - pageBackground = background - } + setImage( + source, + isAnimated, + Config( + zoomDuration = viewer.config.doubleTapAnimDuration, + minimumScaleType = viewer.config.imageScaleType, + cropBorders = viewer.config.imageCropBorders, + zoomStartPosition = viewer.config.imageZoomType, + landscapeZoom = viewer.config.landscapeZoom, + ), + ) + if (!isAnimated) { + pageBackground = background } removeErrorLayout() } @@ -182,40 +173,40 @@ class PagerPageHolder( } } - private fun process(page: ReaderPage, imageStream: BufferedInputStream): InputStream { + private fun process(page: ReaderPage, imageSource: BufferedSource): BufferedSource { if (viewer.config.dualPageRotateToFit) { - return rotateDualPage(imageStream) + return rotateDualPage(imageSource) } if (!viewer.config.dualPageSplit) { - return imageStream + return imageSource } if (page is InsertPage) { - return splitInHalf(imageStream) + return splitInHalf(imageSource) } - val isDoublePage = ImageUtil.isWideImage(imageStream) + val isDoublePage = ImageUtil.isWideImage(imageSource) if (!isDoublePage) { - return imageStream + return imageSource } onPageSplit(page) - return splitInHalf(imageStream) + return splitInHalf(imageSource) } - private fun rotateDualPage(imageStream: BufferedInputStream): InputStream { - val isDoublePage = ImageUtil.isWideImage(imageStream) + private fun rotateDualPage(imageSource: BufferedSource): BufferedSource { + val isDoublePage = ImageUtil.isWideImage(imageSource) return if (isDoublePage) { val rotation = if (viewer.config.dualPageRotateToFitInvert) -90f else 90f - ImageUtil.rotateImage(imageStream, rotation) + ImageUtil.rotateImage(imageSource, rotation) } else { - imageStream + imageSource } } - private fun splitInHalf(imageStream: InputStream): InputStream { + private fun splitInHalf(imageSource: BufferedSource): BufferedSource { var side = when { viewer is L2RPagerViewer && page is InsertPage -> ImageUtil.Side.RIGHT viewer !is L2RPagerViewer && page is InsertPage -> ImageUtil.Side.LEFT @@ -231,7 +222,7 @@ class PagerPageHolder( } } - return ImageUtil.splitInHalf(imageStream, side) + return ImageUtil.splitInHalf(imageSource, side) } private fun onPageSplit(page: ReaderPage) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt index b501725f7..488db7bb6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt @@ -22,15 +22,14 @@ import kotlinx.coroutines.MainScope import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope -import kotlinx.coroutines.suspendCancellableCoroutine import logcat.LogPriority +import okio.Buffer +import okio.BufferedSource import tachiyomi.core.common.util.lang.launchIO import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.core.common.util.system.logcat -import java.io.BufferedInputStream -import java.io.InputStream /** * Holder of the webtoon reader for a single page of a chapter. @@ -188,16 +187,14 @@ class WebtoonPageHolder( val streamFn = page?.stream ?: return try { - val (openStream, isAnimated) = withIOContext { - val stream = streamFn().buffered(16) - val openStream = process(stream) - - val isAnimated = ImageUtil.isAnimatedAndSupported(stream) - Pair(openStream, isAnimated) + val (source, isAnimated) = withIOContext { + val source = streamFn().use { process(Buffer().readFrom(it)) } + val isAnimated = ImageUtil.isAnimatedAndSupported(source) + Pair(source, isAnimated) } withUIContext { frame.setImage( - openStream, + source, isAnimated, ReaderPageImageView.Config( zoomDuration = viewer.config.doubleTapAnimDuration, @@ -207,10 +204,6 @@ class WebtoonPageHolder( ) removeErrorLayout() } - // Suspend the coroutine to close the input stream only when the WebtoonPageHolder is recycled - suspendCancellableCoroutine { continuation -> - continuation.invokeOnCancellation { openStream.close() } - } } catch (e: Throwable) { logcat(LogPriority.ERROR, e) withUIContext { @@ -219,29 +212,29 @@ class WebtoonPageHolder( } } - private fun process(imageStream: BufferedInputStream): InputStream { + private fun process(imageSource: BufferedSource): BufferedSource { if (viewer.config.dualPageRotateToFit) { - return rotateDualPage(imageStream) + return rotateDualPage(imageSource) } if (viewer.config.dualPageSplit) { - val isDoublePage = ImageUtil.isWideImage(imageStream) + val isDoublePage = ImageUtil.isWideImage(imageSource) if (isDoublePage) { val upperSide = if (viewer.config.dualPageInvert) ImageUtil.Side.LEFT else ImageUtil.Side.RIGHT - return ImageUtil.splitAndMerge(imageStream, upperSide) + return ImageUtil.splitAndMerge(imageSource, upperSide) } } - return imageStream + return imageSource } - private fun rotateDualPage(imageStream: BufferedInputStream): InputStream { - val isDoublePage = ImageUtil.isWideImage(imageStream) + private fun rotateDualPage(imageSource: BufferedSource): BufferedSource { + val isDoublePage = ImageUtil.isWideImage(imageSource) return if (isDoublePage) { val rotation = if (viewer.config.dualPageRotateToFitInvert) -90f else 90f - ImageUtil.rotateImage(imageStream, rotation) + ImageUtil.rotateImage(imageSource, rotation) } else { - imageStream + imageSource } } diff --git a/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt b/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt index 03a2f2d60..f5e9a8098 100644 --- a/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt +++ b/core/common/src/main/kotlin/tachiyomi/core/common/util/system/ImageUtil.kt @@ -24,11 +24,10 @@ import androidx.core.graphics.green import androidx.core.graphics.red import com.hippo.unifile.UniFile import logcat.LogPriority +import okio.Buffer +import okio.BufferedSource import tachiyomi.decoder.Format import tachiyomi.decoder.ImageDecoder -import java.io.BufferedInputStream -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream import java.io.InputStream import java.net.URLConnection import java.util.Locale @@ -76,9 +75,9 @@ object ImageUtil { ?: "jpg" } - fun isAnimatedAndSupported(stream: InputStream): Boolean { + fun isAnimatedAndSupported(source: BufferedSource): Boolean { return try { - val type = getImageType(stream) ?: return false + val type = getImageType(source.peek().inputStream()) ?: return false // https://coil-kt.github.io/coil/getting_started/#supported-image-formats when (type.format) { Format.Gif -> true @@ -125,18 +124,16 @@ object ImageUtil { * * @return true if the width is greater than the height */ - fun isWideImage(imageStream: BufferedInputStream): Boolean { - val options = extractImageOptions(imageStream) + fun isWideImage(imageSource: BufferedSource): Boolean { + val options = extractImageOptions(imageSource) return options.outWidth > options.outHeight } /** - * Extract the 'side' part from imageStream and return it as InputStream. + * Extract the 'side' part from [BufferedSource] and return it as [BufferedSource]. */ - fun splitInHalf(imageStream: InputStream, side: Side): InputStream { - val imageBytes = imageStream.readBytes() - - val imageBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size) + fun splitInHalf(imageSource: BufferedSource, side: Side): BufferedSource { + val imageBitmap = BitmapFactory.decodeStream(imageSource.inputStream()) val height = imageBitmap.height val width = imageBitmap.width @@ -150,22 +147,20 @@ object ImageUtil { half.applyCanvas { drawBitmap(imageBitmap, part, singlePage, null) } - val output = ByteArrayOutputStream() - half.compress(Bitmap.CompressFormat.JPEG, 100, output) + val output = Buffer() + half.compress(Bitmap.CompressFormat.JPEG, 100, output.outputStream()) - return ByteArrayInputStream(output.toByteArray()) + return output } - fun rotateImage(imageStream: InputStream, degrees: Float): InputStream { - val imageBytes = imageStream.readBytes() - - val imageBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size) + fun rotateImage(imageSource: BufferedSource, degrees: Float): BufferedSource { + val imageBitmap = BitmapFactory.decodeStream(imageSource.inputStream()) val rotated = rotateBitMap(imageBitmap, degrees) - val output = ByteArrayOutputStream() - rotated.compress(Bitmap.CompressFormat.JPEG, 100, output) + val output = Buffer() + rotated.compress(Bitmap.CompressFormat.JPEG, 100, output.outputStream()) - return ByteArrayInputStream(output.toByteArray()) + return output } private fun rotateBitMap(bitmap: Bitmap, degrees: Float): Bitmap { @@ -176,10 +171,8 @@ object ImageUtil { /** * Split the image into left and right parts, then merge them into a new image. */ - fun splitAndMerge(imageStream: InputStream, upperSide: Side): InputStream { - val imageBytes = imageStream.readBytes() - - val imageBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size) + fun splitAndMerge(imageSource: BufferedSource, upperSide: Side): BufferedSource { + val imageBitmap = BitmapFactory.decodeStream(imageSource.inputStream()) val height = imageBitmap.height val width = imageBitmap.width @@ -201,9 +194,9 @@ object ImageUtil { drawBitmap(imageBitmap, leftPart, bottomPart, null) } - val output = ByteArrayOutputStream() - result.compress(Bitmap.CompressFormat.JPEG, 100, output) - return ByteArrayInputStream(output.toByteArray()) + val output = Buffer() + result.compress(Bitmap.CompressFormat.JPEG, 100, output.outputStream()) + return output } enum class Side { @@ -216,8 +209,8 @@ object ImageUtil { * * @return true if the height:width ratio is greater than 3. */ - private fun isTallImage(imageStream: InputStream): Boolean { - val options = extractImageOptions(imageStream, resetAfterExtraction = false) + private fun isTallImage(imageSource: BufferedSource): Boolean { + val options = extractImageOptions(imageSource) return (options.outHeight / options.outWidth) > 3 } @@ -225,17 +218,18 @@ object ImageUtil { * Splits tall images to improve performance of reader */ fun splitTallImage(tmpDir: UniFile, imageFile: UniFile, filenamePrefix: String): Boolean { - if (isAnimatedAndSupported(imageFile.openInputStream()) || !isTallImage(imageFile.openInputStream())) { + val imageSource = imageFile.openInputStream().use { Buffer().readFrom(it) } + if (isAnimatedAndSupported(imageSource) || !isTallImage(imageSource)) { return true } - val bitmapRegionDecoder = getBitmapRegionDecoder(imageFile.openInputStream()) + val bitmapRegionDecoder = getBitmapRegionDecoder(imageSource.peek().inputStream()) if (bitmapRegionDecoder == null) { logcat { "Failed to create new instance of BitmapRegionDecoder" } return false } - val options = extractImageOptions(imageFile.openInputStream(), resetAfterExtraction = false).apply { + val options = extractImageOptions(imageSource).apply { inJustDecodeBounds = false } @@ -548,16 +542,9 @@ object ImageUtil { /** * Used to check an image's dimensions without loading it in the memory. */ - private fun extractImageOptions( - imageStream: InputStream, - resetAfterExtraction: Boolean = true, - ): BitmapFactory.Options { - imageStream.mark(Int.MAX_VALUE) - - val imageBytes = imageStream.readBytes() + private fun extractImageOptions(imageSource: BufferedSource): BitmapFactory.Options { val options = BitmapFactory.Options().apply { inJustDecodeBounds = true } - BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size, options) - if (resetAfterExtraction) imageStream.reset() + BitmapFactory.decodeStream(imageSource.peek().inputStream(), null, options) return options } From 7fd8f653529b8e1488dd57c051000abf2a80ed12 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Apr 2024 10:52:56 +0600 Subject: [PATCH 180/212] fix(deps): update dependency androidx.core:core-ktx to v1.13.0 (#690) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 879f5e846..1f455723b 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -10,7 +10,7 @@ annotation = "androidx.annotation:annotation:1.7.1" appcompat = "androidx.appcompat:appcompat:1.6.1" biometricktx = "androidx.biometric:biometric-ktx:1.2.0-alpha05" constraintlayout = "androidx.constraintlayout:constraintlayout:2.1.4" -corektx = "androidx.core:core-ktx:1.12.0" +corektx = "androidx.core:core-ktx:1.13.0" splashscreen = "androidx.core:core-splashscreen:1.0.1" recyclerview = "androidx.recyclerview:recyclerview:1.3.2" viewpager = "androidx.viewpager:viewpager:1.1.0-alpha01" From 2ad462b4d882c4a03359092515aa6b8d3cb4fd5d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Apr 2024 10:53:19 +0600 Subject: [PATCH 181/212] fix(deps): update dependency androidx.activity:activity-compose to v1.9.0 (#689) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/compose.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index cd0e14211..18a102af6 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -7,7 +7,7 @@ accompanist = "0.35.0-alpha" [libraries] compiler = { module = "androidx.compose.compiler:compiler", version.ref = "compiler" } -activity = "androidx.activity:activity-compose:1.8.2" +activity = "androidx.activity:activity-compose:1.9.0" bom = { group = "dev.chrisbanes.compose", name = "compose-bom", version.ref = "compose-bom" } foundation = { module = "androidx.compose.foundation:foundation" } animation = { module = "androidx.compose.animation:animation" } From 935c0c7e2e706c512726b1b603cb84b0c56ba4e3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Apr 2024 10:53:35 +0600 Subject: [PATCH 182/212] chore(deps): update gradle/actions action to v3.3.1 (#682) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 74488ccfd..2f350a463 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -35,7 +35,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@6cec5d49d4d6d4bb982fbed7047db31ea6d38f11 # v3.3.0 + uses: gradle/actions/setup-gradle@750cdda3edd6d51b7fdfc069d2e2818cf3c44f4c # v3.3.1 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 700c0466c..e56565552 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -33,7 +33,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@6cec5d49d4d6d4bb982fbed7047db31ea6d38f11 # v3.3.0 + uses: gradle/actions/setup-gradle@750cdda3edd6d51b7fdfc069d2e2818cf3c44f4c # v3.3.1 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest From e82a2f5f9f79cefed84d4c340a1aa4c0282f7973 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Apr 2024 10:53:55 +0600 Subject: [PATCH 183/212] chore(deps): update gradle/wrapper-validation-action action to v3.3.1 (#683) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 2f350a463..7cf283dd5 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@460a3ca55fc5d559238a0efc7fa9f7465df8585d # v3.3.0 + uses: gradle/wrapper-validation-action@5188e9b5527a0a094cee21e2fe9a8ca44b4629af # v3.3.1 - name: Dependency Review uses: actions/dependency-review-action@5bbc3ba658137598168acb2ab73b21c432dd411b # v4.2.5 diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index e56565552..5fa0c075f 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@460a3ca55fc5d559238a0efc7fa9f7465df8585d # v3.3.0 + uses: gradle/wrapper-validation-action@5188e9b5527a0a094cee21e2fe9a8ca44b4629af # v3.3.1 - name: Setup Android SDK run: | From 25570147a1ca8ac374a75d7f29cf105bd686954b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Apr 2024 10:54:09 +0600 Subject: [PATCH 184/212] fix(deps): update dependency androidx.benchmark:benchmark-macro-junit4 to v1.2.4 (#684) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 1f455723b..3f78c27c1 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -25,7 +25,7 @@ workmanager = "androidx.work:work-runtime:2.9.0" paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "paging_version" } paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging_version" } -benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.3" +benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.4" test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha03" test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha03" test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0" From 213effa169e28e144f3e323290d865b02d0bf94b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Apr 2024 10:54:26 +0600 Subject: [PATCH 185/212] fix(deps): update dependency androidx.compose.compiler:compiler to v1.5.12 (#685) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/compose.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 18a102af6..58c1ed191 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,5 +1,5 @@ [versions] -compiler = "1.5.11" +compiler = "1.5.12" # 2024.04.00-alpha01 has several bugs with the new animateItem() modifier compose-bom = "2024.03.00-alpha02" accompanist = "0.35.0-alpha" From 9a3ffe2ea6cbf7ef2c2966c304a54b715a5fa682 Mon Sep 17 00:00:00 2001 From: MajorTanya <39014446+MajorTanya@users.noreply.github.com> Date: Tue, 23 Apr 2024 07:38:16 +0200 Subject: [PATCH 186/212] Add MyAnimeList issue autoclose (#703) [skip ci] Add MyAnimeList issue autoclose This rule is intended to automatically close issues that report problems with linking MAL that would be solved with the standard solution of updating & changing the default UA. The RegEx might be too general, but there isn't any neat pattern in the previously filed issues. --- .github/workflows/issue_moderator.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/issue_moderator.yml b/.github/workflows/issue_moderator.yml index 70292069d..1a4fbe18f 100644 --- a/.github/workflows/issue_moderator.yml +++ b/.github/workflows/issue_moderator.yml @@ -30,6 +30,12 @@ jobs: "ignoreCase": true, "labels": ["Cloudflare protected"], "message": "Refer to the **Solving Cloudflare issues** section at https://mihon.app/docs/guides/troubleshooting/#cloudflare. If it doesn't work, migrate to other sources or wait until they lower their protection." + }, + { + "type": "both", + "regex": "^.*(myanimelist|mal).*$", + "ignoreCase": true, + "message": "For issues with linking MyAnimeList, please follow these steps:\n1. Update Mihon to version 0.16.4 or newer\n2. Change your default User-Agent (`More → Settings → Advanced → Default user agent string`)\n3. Close and restart Mihon\n4. Attempt to link MyAnimeList again\n\nIf you had MyAnimeList linked before, try to unlink it first before trying these steps." } ] auto-close-ignore-label: do-not-autoclose From e85ce6456a364a82cdde898be01bc62271c788cc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Apr 2024 14:50:15 +0600 Subject: [PATCH 187/212] chore(deps): update actions/checkout action to v4.1.4 (#710) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 7cf283dd5..885898b18 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Clone repo - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Validate Gradle Wrapper uses: gradle/wrapper-validation-action@5188e9b5527a0a094cee21e2fe9a8ca44b4629af # v3.3.1 diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 5fa0c075f..aa5c2cc8c 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -17,7 +17,7 @@ jobs: steps: - name: Clone repo - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Validate Gradle Wrapper uses: gradle/wrapper-validation-action@5188e9b5527a0a094cee21e2fe9a8ca44b4629af # v3.3.1 From dd932e136256e7f420a6926aaed1559ec63badc3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Apr 2024 14:50:29 +0600 Subject: [PATCH 188/212] chore(deps): update gradle/actions action to v3.3.2 (#711) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 885898b18..f3b51a6ee 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -35,7 +35,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@750cdda3edd6d51b7fdfc069d2e2818cf3c44f4c # v3.3.1 + uses: gradle/actions/setup-gradle@db19848a5fa7950289d3668fb053140cf3028d43 # v3.3.2 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index aa5c2cc8c..37952e40d 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -33,7 +33,7 @@ jobs: distribution: adopt - name: Set up gradle - uses: gradle/actions/setup-gradle@750cdda3edd6d51b7fdfc069d2e2818cf3c44f4c # v3.3.1 + uses: gradle/actions/setup-gradle@db19848a5fa7950289d3668fb053140cf3028d43 # v3.3.2 - name: Build app and run unit tests run: ./gradlew detekt assembleStandardRelease testReleaseUnitTest From 5289830a840c8812ee46ea639e095d849182a53d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Apr 2024 14:50:41 +0600 Subject: [PATCH 189/212] chore(deps): update gradle/wrapper-validation-action action to v3.3.2 (#713) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- .github/workflows/build_push.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index f3b51a6ee..30407dbfc 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@5188e9b5527a0a094cee21e2fe9a8ca44b4629af # v3.3.1 + uses: gradle/wrapper-validation-action@216d1ad2b3710bf005dc39237337b9673fd8fcd5 # v3.3.2 - name: Dependency Review uses: actions/dependency-review-action@5bbc3ba658137598168acb2ab73b21c432dd411b # v4.2.5 diff --git a/.github/workflows/build_push.yml b/.github/workflows/build_push.yml index 37952e40d..0686e1889 100644 --- a/.github/workflows/build_push.yml +++ b/.github/workflows/build_push.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@5188e9b5527a0a094cee21e2fe9a8ca44b4629af # v3.3.1 + uses: gradle/wrapper-validation-action@216d1ad2b3710bf005dc39237337b9673fd8fcd5 # v3.3.2 - name: Setup Android SDK run: | From f1ce205d002aa7ee92d69ceaaab910e6b037239a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Apr 2024 14:50:52 +0600 Subject: [PATCH 190/212] fix(deps): update dependency dev.chrisbanes.compose:compose-bom to v2024.04.00-alpha02 (#714) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/compose.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 58c1ed191..0d5ae0aa4 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,7 +1,7 @@ [versions] compiler = "1.5.12" # 2024.04.00-alpha01 has several bugs with the new animateItem() modifier -compose-bom = "2024.03.00-alpha02" +compose-bom = "2024.04.00-alpha02" accompanist = "0.35.0-alpha" [libraries] From 2ad98520aa869cf27a4acdce41a9dc8f5a986419 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Fri, 26 Apr 2024 19:04:49 +0600 Subject: [PATCH 191/212] Revert "fix(deps): update dependency dev.chrisbanes.compose:compose-bom to v2024.04.00-alpha02 (#714)" This reverts commit f1ce205d002aa7ee92d69ceaaab910e6b037239a. --- gradle/compose.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 0d5ae0aa4..58c1ed191 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,7 +1,7 @@ [versions] compiler = "1.5.12" # 2024.04.00-alpha01 has several bugs with the new animateItem() modifier -compose-bom = "2024.04.00-alpha02" +compose-bom = "2024.03.00-alpha02" accompanist = "0.35.0-alpha" [libraries] From c3e7bb12f4cccf42dd3ea169111c771876e640fe Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Wed, 1 May 2024 15:07:30 +0800 Subject: [PATCH 192/212] Use Coil pipeline instead of SSIV for image decode (#692) --- .../data/coil/TachiyomiImageDecoder.kt | 46 +++++++++++++++++-- .../eu/kanade/tachiyomi/data/coil/Utils.kt | 44 ++++++++++++++++++ .../tachiyomi/ui/reader/ReaderActivity.kt | 5 +- .../ui/reader/viewer/ReaderPageImageView.kt | 39 ++++++++++++++-- 4 files changed, 123 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/data/coil/Utils.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt index 2f0c3df49..2929f4da3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/TachiyomiImageDecoder.kt @@ -1,12 +1,16 @@ package eu.kanade.tachiyomi.data.coil +import android.graphics.Bitmap import coil3.ImageLoader import coil3.asCoilImage import coil3.decode.DecodeResult +import coil3.decode.DecodeUtils import coil3.decode.Decoder import coil3.decode.ImageSource import coil3.fetch.SourceFetchResult import coil3.request.Options +import coil3.request.bitmapConfig +import eu.kanade.tachiyomi.util.system.GLUtil import okio.BufferedSource import tachiyomi.core.common.util.system.ImageUtil import tachiyomi.decoder.ImageDecoder @@ -18,27 +22,55 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti override suspend fun decode(): DecodeResult { val decoder = resources.sourceOrNull()?.use { - ImageDecoder.newInstance(it.inputStream()) + ImageDecoder.newInstance(it.inputStream(), options.cropBorders, displayProfile) } check(decoder != null && decoder.width > 0 && decoder.height > 0) { "Failed to initialize decoder" } - val bitmap = decoder.decode() + val srcWidth = decoder.width + val srcHeight = decoder.height + + val dstWidth = options.size.widthPx(options.scale) { srcWidth } + val dstHeight = options.size.heightPx(options.scale) { srcHeight } + + val sampleSize = DecodeUtils.calculateInSampleSize( + srcWidth = srcWidth, + srcHeight = srcHeight, + dstWidth = dstWidth, + dstHeight = dstHeight, + scale = options.scale, + ) + + var bitmap = decoder.decode(sampleSize = sampleSize) decoder.recycle() check(bitmap != null) { "Failed to decode image" } + if ( + options.bitmapConfig == Bitmap.Config.HARDWARE && + maxOf(bitmap.width, bitmap.height) <= GLUtil.maxTextureSize + ) { + val hwBitmap = bitmap.copy(Bitmap.Config.HARDWARE, false) + if (hwBitmap != null) { + bitmap.recycle() + bitmap = hwBitmap + } + } + return DecodeResult( image = bitmap.asCoilImage(), - isSampled = false, + isSampled = sampleSize > 1, ) } class Factory : Decoder.Factory { override fun create(result: SourceFetchResult, options: Options, imageLoader: ImageLoader): Decoder? { - if (!isApplicable(result.source.source())) return null - return TachiyomiImageDecoder(result.source, options) + return if (options.customDecoder || isApplicable(result.source.source())) { + TachiyomiImageDecoder(result.source, options) + } else { + null + } } private fun isApplicable(source: BufferedSource): Boolean { @@ -55,4 +87,8 @@ class TachiyomiImageDecoder(private val resources: ImageSource, private val opti override fun hashCode() = javaClass.hashCode() } + + companion object { + var displayProfile: ByteArray? = null + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/Utils.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/Utils.kt new file mode 100644 index 000000000..7a920bf39 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/Utils.kt @@ -0,0 +1,44 @@ +package eu.kanade.tachiyomi.data.coil + +import coil3.Extras +import coil3.getExtra +import coil3.request.ImageRequest +import coil3.request.Options +import coil3.size.Dimension +import coil3.size.Scale +import coil3.size.Size +import coil3.size.isOriginal +import coil3.size.pxOrElse + +internal inline fun Size.widthPx(scale: Scale, original: () -> Int): Int { + return if (isOriginal) original() else width.toPx(scale) +} + +internal inline fun Size.heightPx(scale: Scale, original: () -> Int): Int { + return if (isOriginal) original() else height.toPx(scale) +} + +internal fun Dimension.toPx(scale: Scale): Int = pxOrElse { + when (scale) { + Scale.FILL -> Int.MIN_VALUE + Scale.FIT -> Int.MAX_VALUE + } +} + +fun ImageRequest.Builder.cropBorders(enable: Boolean) = apply { + extras[cropBordersKey] = enable +} + +val Options.cropBorders: Boolean + get() = getExtra(cropBordersKey) + +private val cropBordersKey = Extras.Key(default = false) + +fun ImageRequest.Builder.customDecoder(enable: Boolean) = apply { + extras[customDecoderKey] = enable +} + +val Options.customDecoder: Boolean + get() = getExtra(customDecoderKey) + +private val customDecoderKey = Extras.Key(default = false) 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 62bf23646..ae471a025 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 @@ -51,6 +51,7 @@ import eu.kanade.presentation.reader.ReadingModeSelectDialog import eu.kanade.presentation.reader.appbars.ReaderAppBars import eu.kanade.presentation.reader.settings.ReaderSettingsDialog import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.coil.TachiyomiImageDecoder import eu.kanade.tachiyomi.data.notification.NotificationReceiver import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.databinding.ReaderActivityBinding @@ -872,7 +873,9 @@ class ReaderActivity : BaseActivity() { input.copyTo(output) } } - SubsamplingScaleImageView.setDisplayProfile(outputStream.toByteArray()) + val data = outputStream.toByteArray() + SubsamplingScaleImageView.setDisplayProfile(data) + TachiyomiImageDecoder.displayProfile = data } } 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 0c83d97ea..17b973a7e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ReaderPageImageView.kt @@ -18,17 +18,22 @@ import androidx.annotation.StyleRes import androidx.appcompat.widget.AppCompatImageView import androidx.core.os.postDelayed import androidx.core.view.isVisible +import coil3.BitmapImage import coil3.dispose import coil3.imageLoader import coil3.request.CachePolicy import coil3.request.ImageRequest import coil3.request.crossfade +import coil3.size.Precision +import coil3.size.ViewSizeResolver import com.davemorrissey.labs.subscaleview.ImageSource import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_IN_OUT_QUAD import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.EASE_OUT_QUAD import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE import com.github.chrisbanes.photoview.PhotoView +import eu.kanade.tachiyomi.data.coil.cropBorders +import eu.kanade.tachiyomi.data.coil.customDecoder import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonSubsamplingImageView import eu.kanade.tachiyomi.util.system.GLUtil import eu.kanade.tachiyomi.util.system.animatorDurationScale @@ -282,12 +287,36 @@ open class ReaderPageImageView @JvmOverloads constructor( }, ) - when (data) { - is BitmapDrawable -> setImage(ImageSource.bitmap(data.bitmap)) - is BufferedSource -> setImage(ImageSource.inputStream(data.inputStream())) - else -> throw IllegalArgumentException("Not implemented for class ${data::class.simpleName}") + if (isWebtoon) { + val request = ImageRequest.Builder(context) + .data(data) + .memoryCachePolicy(CachePolicy.DISABLED) + .diskCachePolicy(CachePolicy.DISABLED) + .target( + onSuccess = { result -> + val image = result as BitmapImage + setImage(ImageSource.bitmap(image.bitmap)) + isVisible = true + }, + onError = { + this@ReaderPageImageView.onImageLoadError() + }, + ) + .size(ViewSizeResolver(this@ReaderPageImageView)) + .precision(Precision.INEXACT) + .cropBorders(config.cropBorders) + .customDecoder(true) + .crossfade(false) + .build() + context.imageLoader.enqueue(request) + } else { + when (data) { + is BitmapDrawable -> setImage(ImageSource.bitmap(data.bitmap)) + is BufferedSource -> setImage(ImageSource.inputStream(data.inputStream())) + else -> throw IllegalArgumentException("Not implemented for class ${data::class.simpleName}") + } + isVisible = true } - isVisible = true } private fun prepareAnimatedImageView() { From 80461d883f7d6ca2203ae7455223ff49e8ef96ab Mon Sep 17 00:00:00 2001 From: w Date: Wed, 1 May 2024 00:09:19 -0700 Subject: [PATCH 193/212] Update subsampling-scale-image-view (#687) Update libs.versions.toml --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 47446f893..1e6ab9e7e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -49,7 +49,7 @@ coil-gif = { module = "io.coil-kt.coil3:coil-gif" } coil-compose = { module = "io.coil-kt.coil3:coil-compose" } coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp" } -subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:aeaa170036" +subsamplingscaleimageview = "com.github.tachiyomiorg:subsampling-scale-image-view:b8e1b0ed2b" image-decoder = "com.github.tachiyomiorg:image-decoder:e08e9be535" natural-comparator = "com.github.gpanther:java-nat-sort:natural-comparator-1.1" From a3d438e2f5b427eb8b4c391ab9fe10c5a83baf29 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Fri, 3 May 2024 02:59:29 +0800 Subject: [PATCH 194/212] Log app crash exceptions in dumped crash logs (#742) --- app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt | 2 +- app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt b/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt index fd8b27030..5be7a2ca5 100644 --- a/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/crash/CrashScreen.kt @@ -37,7 +37,7 @@ fun CrashScreen( acceptText = stringResource(MR.strings.pref_dump_crash_logs), onAcceptClick = { scope.launch { - CrashLogUtil(context).dumpLogs() + CrashLogUtil(context).dumpLogs(exception) } }, rejectText = stringResource(MR.strings.crash_screen_restart_application), 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 56e6c278b..ed27314ef 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/CrashLogUtil.kt @@ -19,12 +19,13 @@ class CrashLogUtil( private val extensionManager: ExtensionManager = Injekt.get(), ) { - suspend fun dumpLogs() = withNonCancellableContext { + suspend fun dumpLogs(exception: Throwable? = null) = withNonCancellableContext { try { val file = context.createFileInCacheDir("mihon_crash_logs.txt") file.appendText(getDebugInfo() + "\n\n") getExtensionsInfo()?.let { file.appendText("$it\n\n") } + exception?.let { file.appendText("$it\n\n") } Runtime.getRuntime().exec("logcat *:E -d -f ${file.absolutePath}").waitFor() From 6290cf222df922240575e2199459ab7b707d6ae2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 14:19:59 +0600 Subject: [PATCH 195/212] fix(deps): update aboutlib.version to v11.1.4 (#744) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1e6ab9e7e..3c81f6603 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -aboutlib_version = "11.1.3" +aboutlib_version = "11.1.4" leakcanary = "2.13" moko = "0.23.0" okhttp_version = "5.0.0-alpha.12" From c6a1412f18cb16f89c4ddeadb3448c141a49072e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 14:20:16 +0600 Subject: [PATCH 196/212] fix(deps): update dependency androidx.test.ext:junit-ktx to v1.2.0-alpha04 (#751) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 3f78c27c1..407c5cec5 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -26,7 +26,7 @@ paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "pag paging-compose = { module = "androidx.paging:paging-compose", version.ref = "paging_version" } benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.4" -test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha03" +test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha04" test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha03" test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0" From b720f34267e4111466cdabf3a298d006231e4b55 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 14:20:46 +0600 Subject: [PATCH 197/212] fix(deps): update dependency androidx.core:core-ktx to v1.13.1 (#748) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 407c5cec5..67e1f970a 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -10,7 +10,7 @@ annotation = "androidx.annotation:annotation:1.7.1" appcompat = "androidx.appcompat:appcompat:1.6.1" biometricktx = "androidx.biometric:biometric-ktx:1.2.0-alpha05" constraintlayout = "androidx.constraintlayout:constraintlayout:2.1.4" -corektx = "androidx.core:core-ktx:1.13.0" +corektx = "androidx.core:core-ktx:1.13.1" splashscreen = "androidx.core:core-splashscreen:1.0.1" recyclerview = "androidx.recyclerview:recyclerview:1.3.2" viewpager = "androidx.viewpager:viewpager:1.1.0-alpha01" From 4e5cbbc96b5c7be1e51fc65c55103733f1eb12aa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 14:21:08 +0600 Subject: [PATCH 198/212] fix(deps): update dependency androidx.compose.compiler:compiler to v1.5.13 (#745) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/compose.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 58c1ed191..642d5600c 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,5 +1,5 @@ [versions] -compiler = "1.5.12" +compiler = "1.5.13" # 2024.04.00-alpha01 has several bugs with the new animateItem() modifier compose-bom = "2024.03.00-alpha02" accompanist = "0.35.0-alpha" From a2f7d47a0a65bf88ac609b2227d440a7a2f841bf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 14:21:52 +0600 Subject: [PATCH 199/212] fix(deps): update dependency androidx.test.espresso:espresso-core to v3.6.0-alpha04 (#749) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index 67e1f970a..eac7f8955 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -27,7 +27,7 @@ paging-compose = { module = "androidx.paging:paging-compose", version.ref = "pag benchmark-macro = "androidx.benchmark:benchmark-macro-junit4:1.2.4" test-ext = "androidx.test.ext:junit-ktx:1.2.0-alpha04" -test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha03" +test-espresso-core = "androidx.test.espresso:espresso-core:3.6.0-alpha04" test-uiautomator = "androidx.test.uiautomator:uiautomator:2.3.0" [bundles] From 47ee2b45a879b730407e65fcd6599e94a86cf832 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 14:22:13 +0600 Subject: [PATCH 200/212] chore(deps): update actions/dependency-review-action action to v4.3.2 (#752) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build_pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_pull_request.yml b/.github/workflows/build_pull_request.yml index 30407dbfc..be47b2425 100644 --- a/.github/workflows/build_pull_request.yml +++ b/.github/workflows/build_pull_request.yml @@ -26,7 +26,7 @@ jobs: uses: gradle/wrapper-validation-action@216d1ad2b3710bf005dc39237337b9673fd8fcd5 # v3.3.2 - name: Dependency Review - uses: actions/dependency-review-action@5bbc3ba658137598168acb2ab73b21c432dd411b # v4.2.5 + uses: actions/dependency-review-action@0c155c5e8556a497adf53f2c18edabf945ed8e70 # v4.3.2 - name: Set up JDK uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 From 8a51d56c594e1f7ae4ebc01fc6a639292dde78bd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 19:08:36 +0600 Subject: [PATCH 201/212] fix(deps): update dependency com.android.tools.build:gradle to v8.4.0 (#753) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/androidx.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/androidx.versions.toml b/gradle/androidx.versions.toml index eac7f8955..247035a9f 100644 --- a/gradle/androidx.versions.toml +++ b/gradle/androidx.versions.toml @@ -1,5 +1,5 @@ [versions] -agp_version = "8.3.2" +agp_version = "8.4.0" lifecycle_version = "2.7.0" paging_version = "3.2.1" From fa6dba6cc76f0b08cbc9bf222b0e087f4fb16d76 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 3 May 2024 19:34:33 +0600 Subject: [PATCH 202/212] fix(deps): update leakcanary to v2.14 (#715) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3c81f6603..a2f4303db 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] aboutlib_version = "11.1.4" -leakcanary = "2.13" +leakcanary = "2.14" moko = "0.23.0" okhttp_version = "5.0.0-alpha.12" richtext = "0.20.0" From 134e4648a9353fc91b4b2a7a40f12807eaee1d92 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 4 May 2024 13:33:12 +0600 Subject: [PATCH 203/212] Revert "fix(deps): update dependency androidx.compose.compiler:compiler to v1.5.13 (#745)" This reverts commit 4e5cbbc96b5c7be1e51fc65c55103733f1eb12aa. --- gradle/compose.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 642d5600c..58c1ed191 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,5 +1,5 @@ [versions] -compiler = "1.5.13" +compiler = "1.5.12" # 2024.04.00-alpha01 has several bugs with the new animateItem() modifier compose-bom = "2024.03.00-alpha02" accompanist = "0.35.0-alpha" From 21145144cdf550aa775047603e06e261951ebc42 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 4 May 2024 16:08:38 +0600 Subject: [PATCH 204/212] Fix some extension related issue and cleanups - Extension being marked as not installed instead of untrusted after updating with private installer - Extension update counter not updating due to extension being marked as untrusted - Minimize `Key "extension-XXX-YYY" was already used` crash --- .../interactor/GetExtensionsByType.kt | 6 +- .../tachiyomi/extension/ExtensionManager.kt | 151 ++++++++---------- .../util/ExtensionInstallReceiver.kt | 53 +++--- 3 files changed, 91 insertions(+), 119 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionsByType.kt b/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionsByType.kt index dadbd35f6..489403407 100644 --- a/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionsByType.kt +++ b/app/src/main/java/eu/kanade/domain/extension/interactor/GetExtensionsByType.kt @@ -20,7 +20,7 @@ class GetExtensionsByType( extensionManager.installedExtensionsFlow, extensionManager.untrustedExtensionsFlow, extensionManager.availableExtensionsFlow, - ) { _activeLanguages, _installed, _untrusted, _available -> + ) { enabledLanguages, _installed, _untrusted, _available -> val (updates, installed) = _installed .filter { (showNsfwSources || !it.isNsfw) } .sortedWith( @@ -40,9 +40,9 @@ class GetExtensionsByType( } .flatMap { ext -> if (ext.sources.isEmpty()) { - return@flatMap if (ext.lang in _activeLanguages) listOf(ext) else emptyList() + return@flatMap if (ext.lang in enabledLanguages) listOf(ext) else emptyList() } - ext.sources.filter { it.lang in _activeLanguages } + ext.sources.filter { it.lang in enabledLanguages } .map { ext.copy( name = it.name, diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt index 15f386d38..5c5e359c2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt @@ -13,14 +13,17 @@ import eu.kanade.tachiyomi.extension.util.ExtensionInstallReceiver import eu.kanade.tachiyomi.extension.util.ExtensionInstaller import eu.kanade.tachiyomi.extension.util.ExtensionLoader import eu.kanade.tachiyomi.util.system.toast -import kotlinx.coroutines.async +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.emptyFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn import logcat.LogPriority -import tachiyomi.core.common.util.lang.launchNow import tachiyomi.core.common.util.lang.withUIContext import tachiyomi.core.common.util.system.logcat import tachiyomi.domain.source.model.StubSource @@ -42,6 +45,8 @@ class ExtensionManager( private val trustExtension: TrustExtension = Injekt.get(), ) { + val scope = CoroutineScope(SupervisorJob()) + private val _isInitialized = MutableStateFlow(false) val isInitialized: StateFlow = _isInitialized.asStateFlow() @@ -57,24 +62,35 @@ class ExtensionManager( private val iconMap = mutableMapOf() - private val _installedExtensionsFlow = MutableStateFlow(emptyList()) - val installedExtensionsFlow = _installedExtensionsFlow.asStateFlow() + private val _installedExtensionsMapFlow = MutableStateFlow(emptyMap()) + val installedExtensionsFlow = _installedExtensionsMapFlow.mapExtensions(scope) + + private val _availableExtensionsMapFlow = MutableStateFlow(emptyMap()) + val availableExtensionsFlow = _availableExtensionsMapFlow.mapExtensions(scope) + + private val _untrustedExtensionsMapFlow = MutableStateFlow(emptyMap()) + val untrustedExtensionsFlow = _untrustedExtensionsMapFlow.mapExtensions(scope) + + init { + initExtensions() + ExtensionInstallReceiver(InstallationListener()).register(context) + } private var subLanguagesEnabledOnFirstRun = preferences.enabledLanguages().isSet() fun getAppIconForSource(sourceId: Long): Drawable? { - val pkgName = _installedExtensionsFlow.value.find { ext -> ext.sources.any { it.id == sourceId } }?.pkgName - if (pkgName != null) { - return iconMap[pkgName] ?: iconMap.getOrPut(pkgName) { - ExtensionLoader.getExtensionPackageInfoFromPkgName(context, pkgName)!!.applicationInfo - .loadIcon(context.packageManager) + val pkgName = _installedExtensionsMapFlow.value.values + .find { ext -> + ext.sources.any { it.id == sourceId } } - } - return null - } + ?.pkgName + ?: return null - private val _availableExtensionsFlow = MutableStateFlow(emptyList()) - val availableExtensionsFlow = _availableExtensionsFlow.asStateFlow() + return iconMap[pkgName] ?: iconMap.getOrPut(pkgName) { + ExtensionLoader.getExtensionPackageInfoFromPkgName(context, pkgName)!!.applicationInfo + .loadIcon(context.packageManager) + } + } private var availableExtensionsSourcesData: Map = emptyMap() @@ -87,33 +103,25 @@ class ExtensionManager( fun getSourceData(id: Long) = availableExtensionsSourcesData[id] - private val _untrustedExtensionsFlow = MutableStateFlow(emptyList()) - val untrustedExtensionsFlow = _untrustedExtensionsFlow.asStateFlow() - - init { - initExtensions() - ExtensionInstallReceiver(InstallationListener()).register(context) - } - /** * Loads and registers the installed extensions. */ private fun initExtensions() { val extensions = ExtensionLoader.loadExtensions(context) - _installedExtensionsFlow.value = extensions + _installedExtensionsMapFlow.value = extensions .filterIsInstance() - .map { it.extension } + .associate { it.extension.pkgName to it.extension } - _untrustedExtensionsFlow.value = extensions + _untrustedExtensionsMapFlow.value = extensions .filterIsInstance() - .map { it.extension } + .associate { it.extension.pkgName to it.extension } _isInitialized.value = true } /** - * Finds the available extensions in the [api] and updates [availableExtensions]. + * Finds the available extensions in the [api] and updates [_availableExtensionsMapFlow]. */ suspend fun findAvailableExtensions() { val extensions: List = try { @@ -126,7 +134,7 @@ class ExtensionManager( enableAdditionalSubLanguages(extensions) - _availableExtensionsFlow.value = extensions + _availableExtensionsMapFlow.value = extensions.associateBy { it.pkgName } updatedInstalledExtensionsStatuses(extensions) setupAvailableExtensionsSourcesDataMap(extensions) } @@ -172,35 +180,31 @@ class ExtensionManager( return } - val mutInstalledExtensions = _installedExtensionsFlow.value.toMutableList() + val installedExtensionsMap = _installedExtensionsMapFlow.value.toMutableMap() var changed = false - - for ((index, installedExt) in mutInstalledExtensions.withIndex()) { - val pkgName = installedExt.pkgName + for ((pkgName, extension) in installedExtensionsMap) { val availableExt = availableExtensions.find { it.pkgName == pkgName } - if (availableExt == null && !installedExt.isObsolete) { - mutInstalledExtensions[index] = installedExt.copy(isObsolete = true) + if (availableExt == null && !extension.isObsolete) { + installedExtensionsMap[pkgName] = extension.copy(isObsolete = true) changed = true } else if (availableExt != null) { - val hasUpdate = installedExt.updateExists(availableExt) - - if (installedExt.hasUpdate != hasUpdate) { - mutInstalledExtensions[index] = installedExt.copy( + val hasUpdate = extension.updateExists(availableExt) + if (extension.hasUpdate != hasUpdate) { + installedExtensionsMap[pkgName] = extension.copy( hasUpdate = hasUpdate, repoUrl = availableExt.repoUrl, ) - changed = true } else { - mutInstalledExtensions[index] = installedExt.copy( + installedExtensionsMap[pkgName] = extension.copy( repoUrl = availableExt.repoUrl, ) - changed = true } + changed = true } } if (changed) { - _installedExtensionsFlow.value = mutInstalledExtensions + _installedExtensionsMapFlow.value = installedExtensionsMap } updatePendingUpdatesCount() } @@ -224,8 +228,7 @@ class ExtensionManager( * @param extension The extension to be updated. */ fun updateExtension(extension: Extension.Installed): Flow { - val availableExt = _availableExtensionsFlow.value.find { it.pkgName == extension.pkgName } - ?: return emptyFlow() + val availableExt = _availableExtensionsMapFlow.value[extension.pkgName] ?: return emptyFlow() return installExtension(availableExt) } @@ -262,23 +265,15 @@ class ExtensionManager( * @param extension the extension to trust */ fun trust(extension: Extension.Untrusted) { - val untrustedPkgNames = _untrustedExtensionsFlow.value.map { it.pkgName }.toSet() - if (extension.pkgName !in untrustedPkgNames) return + _untrustedExtensionsMapFlow.value[extension.pkgName] ?: return trustExtension.trust(extension.pkgName, extension.versionCode, extension.signatureHash) - val nowTrustedExtensions = _untrustedExtensionsFlow.value - .filter { it.pkgName == extension.pkgName && it.versionCode == extension.versionCode } - _untrustedExtensionsFlow.value -= nowTrustedExtensions + _untrustedExtensionsMapFlow.value -= extension.pkgName - launchNow { - nowTrustedExtensions - .map { extension -> - async { ExtensionLoader.loadExtensionFromPkgName(context, extension.pkgName) }.await() - } - .filterIsInstance() - .forEach { registerNewExtension(it.extension) } - } + ExtensionLoader.loadExtensionFromPkgName(context, extension.pkgName) + .let { it as? LoadResult.Success } + ?.let { registerNewExtension(it.extension) } } /** @@ -287,7 +282,7 @@ class ExtensionManager( * @param extension The extension to be registered. */ private fun registerNewExtension(extension: Extension.Installed) { - _installedExtensionsFlow.value += extension + _installedExtensionsMapFlow.value += extension } /** @@ -297,13 +292,7 @@ class ExtensionManager( * @param extension The extension to be registered. */ private fun registerUpdatedExtension(extension: Extension.Installed) { - val mutInstalledExtensions = _installedExtensionsFlow.value.toMutableList() - val oldExtension = mutInstalledExtensions.find { it.pkgName == extension.pkgName } - if (oldExtension != null) { - mutInstalledExtensions -= oldExtension - } - mutInstalledExtensions += extension - _installedExtensionsFlow.value = mutInstalledExtensions + _installedExtensionsMapFlow.value += extension } /** @@ -313,14 +302,8 @@ class ExtensionManager( * @param pkgName The package name of the uninstalled application. */ private fun unregisterExtension(pkgName: String) { - val installedExtension = _installedExtensionsFlow.value.find { it.pkgName == pkgName } - if (installedExtension != null) { - _installedExtensionsFlow.value -= installedExtension - } - val untrustedExtension = _untrustedExtensionsFlow.value.find { it.pkgName == pkgName } - if (untrustedExtension != null) { - _untrustedExtensionsFlow.value -= untrustedExtension - } + _installedExtensionsMapFlow.value -= pkgName + _untrustedExtensionsMapFlow.value -= pkgName } /** @@ -339,14 +322,9 @@ class ExtensionManager( } override fun onExtensionUntrusted(extension: Extension.Untrusted) { - val installedExtension = _installedExtensionsFlow.value - .find { it.pkgName == extension.pkgName } - - if (installedExtension != null) { - _installedExtensionsFlow.value -= installedExtension - } else { - _untrustedExtensionsFlow.value += extension - } + _installedExtensionsMapFlow.value -= extension.pkgName + _untrustedExtensionsMapFlow.value += extension + updatePendingUpdatesCount() } override fun onPackageUninstalled(pkgName: String) { @@ -368,17 +346,24 @@ class ExtensionManager( } private fun Extension.Installed.updateExists(availableExtension: Extension.Available? = null): Boolean { - val availableExt = availableExtension ?: _availableExtensionsFlow.value.find { it.pkgName == pkgName } + val availableExt = availableExtension + ?: _availableExtensionsMapFlow.value[pkgName] ?: return false return (availableExt.versionCode > versionCode || availableExt.libVersion > libVersion) } private fun updatePendingUpdatesCount() { - val pendingUpdateCount = _installedExtensionsFlow.value.count { it.hasUpdate } + val pendingUpdateCount = _installedExtensionsMapFlow.value.values.count { it.hasUpdate } preferences.extensionUpdatesCount().set(pendingUpdateCount) if (pendingUpdateCount == 0) { ExtensionUpdateNotifier(context).dismiss() } } + + private operator fun Map.plus(extension: T) = plus(extension.pkgName to extension) + + private fun StateFlow>.mapExtensions(scope: CoroutineScope): StateFlow> { + return map { it.values.toList() }.stateIn(scope, SharingStarted.Lazily, value.values.toList()) + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt index e0a008e1c..be9a60a75 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt @@ -9,12 +9,7 @@ import androidx.core.content.ContextCompat import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.extension.model.LoadResult -import kotlinx.coroutines.CoroutineStart -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.async import logcat.LogPriority -import tachiyomi.core.common.util.lang.launchNow import tachiyomi.core.common.util.system.logcat /** @@ -23,8 +18,7 @@ import tachiyomi.core.common.util.system.logcat * * @param listener The listener that should be notified of extension installation events. */ -internal class ExtensionInstallReceiver(private val listener: Listener) : - BroadcastReceiver() { +internal class ExtensionInstallReceiver(private val listener: Listener) : BroadcastReceiver() { /** * Registers this broadcast receiver @@ -36,16 +30,15 @@ internal class ExtensionInstallReceiver(private val listener: Listener) : /** * Returns the intent filter this receiver should subscribe to. */ - private val filter - get() = IntentFilter().apply { - addAction(Intent.ACTION_PACKAGE_ADDED) - addAction(Intent.ACTION_PACKAGE_REPLACED) - addAction(Intent.ACTION_PACKAGE_REMOVED) - addAction(ACTION_EXTENSION_ADDED) - addAction(ACTION_EXTENSION_REPLACED) - addAction(ACTION_EXTENSION_REMOVED) - addDataScheme("package") - } + private val filter = IntentFilter().apply { + addAction(Intent.ACTION_PACKAGE_ADDED) + addAction(Intent.ACTION_PACKAGE_REPLACED) + addAction(Intent.ACTION_PACKAGE_REMOVED) + addAction(ACTION_EXTENSION_ADDED) + addAction(ACTION_EXTENSION_REPLACED) + addAction(ACTION_EXTENSION_REMOVED) + addDataScheme("package") + } /** * Called when one of the events of the [filter] is received. When the package is an extension, @@ -58,21 +51,17 @@ internal class ExtensionInstallReceiver(private val listener: Listener) : Intent.ACTION_PACKAGE_ADDED, ACTION_EXTENSION_ADDED -> { if (isReplacing(intent)) return - launchNow { - when (val result = getExtensionFromIntent(context, intent)) { - is LoadResult.Success -> listener.onExtensionInstalled(result.extension) - is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension) - else -> {} - } + when (val result = getExtensionFromIntent(context, intent)) { + is LoadResult.Success -> listener.onExtensionInstalled(result.extension) + is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension) + else -> {} } } Intent.ACTION_PACKAGE_REPLACED, ACTION_EXTENSION_REPLACED -> { - launchNow { - when (val result = getExtensionFromIntent(context, intent)) { - is LoadResult.Success -> listener.onExtensionUpdated(result.extension) - is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension) - else -> {} - } + when (val result = getExtensionFromIntent(context, intent)) { + is LoadResult.Success -> listener.onExtensionUpdated(result.extension) + is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension) + else -> {} } } Intent.ACTION_PACKAGE_REMOVED, ACTION_EXTENSION_REMOVED -> { @@ -101,15 +90,13 @@ internal class ExtensionInstallReceiver(private val listener: Listener) : * @param context The application context. * @param intent The intent containing the package name of the extension. */ - private suspend fun getExtensionFromIntent(context: Context, intent: Intent?): LoadResult { + private fun getExtensionFromIntent(context: Context, intent: Intent?): LoadResult { val pkgName = getPackageNameFromIntent(intent) if (pkgName == null) { logcat(LogPriority.WARN) { "Package name not found" } return LoadResult.Error } - return GlobalScope.async(Dispatchers.Default, CoroutineStart.DEFAULT) { - ExtensionLoader.loadExtensionFromPkgName(context, pkgName) - }.await() + return ExtensionLoader.loadExtensionFromPkgName(context, pkgName) } /** From 70cd688ac245a70a3146e2ac7374f24b0c3453ab Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 4 May 2024 16:26:45 +0600 Subject: [PATCH 205/212] Trust extension by repo (#570) --- .../java/eu/kanade/domain/DomainModule.kt | 2 +- .../extension/interactor/TrustExtension.kt | 13 ++++---- .../tachiyomi/extension/ExtensionManager.kt | 2 +- .../util/ExtensionInstallReceiver.kt | 33 ++++++++++--------- .../extension/util/ExtensionLoader.kt | 7 ++-- .../browse/extension/ExtensionsScreenModel.kt | 5 ++- 6 files changed, 35 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt index 48c183a92..4c769f703 100644 --- a/app/src/main/java/eu/kanade/domain/DomainModule.kt +++ b/app/src/main/java/eu/kanade/domain/DomainModule.kt @@ -179,7 +179,7 @@ class DomainModule : InjektModule { addFactory { ToggleLanguage(get()) } addFactory { ToggleSource(get()) } addFactory { ToggleSourcePin(get()) } - addFactory { TrustExtension(get()) } + addFactory { TrustExtension(get(), get()) } addSingletonFactory { ExtensionRepoRepositoryImpl(get()) } addFactory { ExtensionRepoService(get(), get()) } diff --git a/app/src/main/java/eu/kanade/domain/extension/interactor/TrustExtension.kt b/app/src/main/java/eu/kanade/domain/extension/interactor/TrustExtension.kt index c3daec5f8..b4259945d 100644 --- a/app/src/main/java/eu/kanade/domain/extension/interactor/TrustExtension.kt +++ b/app/src/main/java/eu/kanade/domain/extension/interactor/TrustExtension.kt @@ -3,15 +3,18 @@ package eu.kanade.domain.extension.interactor import android.content.pm.PackageInfo import androidx.core.content.pm.PackageInfoCompat import eu.kanade.domain.source.service.SourcePreferences +import mihon.domain.extensionrepo.repository.ExtensionRepoRepository import tachiyomi.core.common.preference.getAndSet class TrustExtension( + private val extensionRepoRepository: ExtensionRepoRepository, private val preferences: SourcePreferences, ) { - fun isTrusted(pkgInfo: PackageInfo, signatureHash: String): Boolean { - val key = "${pkgInfo.packageName}:${PackageInfoCompat.getLongVersionCode(pkgInfo)}:$signatureHash" - return key in preferences.trustedExtensions().get() + suspend fun isTrusted(pkgInfo: PackageInfo, fingerprints: List): Boolean { + val trustedFingerprints = extensionRepoRepository.getAll().map { it.signingKeyFingerprint }.toHashSet() + val key = "${pkgInfo.packageName}:${PackageInfoCompat.getLongVersionCode(pkgInfo)}:${fingerprints.last()}" + return trustedFingerprints.any { fingerprints.contains(it) } || key in preferences.trustedExtensions().get() } fun trust(pkgName: String, versionCode: Long, signatureHash: String) { @@ -19,9 +22,7 @@ class TrustExtension( // Remove previously trusted versions val removed = exts.filterNot { it.startsWith("$pkgName:") }.toMutableSet() - removed.also { - it += "$pkgName:$versionCode:$signatureHash" - } + removed.also { it += "$pkgName:$versionCode:$signatureHash" } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt index 5c5e359c2..968263b5d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/ExtensionManager.kt @@ -264,7 +264,7 @@ class ExtensionManager( * * @param extension the extension to trust */ - fun trust(extension: Extension.Untrusted) { + suspend fun trust(extension: Extension.Untrusted) { _untrustedExtensionsMapFlow.value[extension.pkgName] ?: return trustExtension.trust(extension.pkgName, extension.versionCode, extension.signatureHash) diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt index be9a60a75..a0ccb23fb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionInstallReceiver.kt @@ -9,6 +9,9 @@ import androidx.core.content.ContextCompat import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.extension.model.Extension import eu.kanade.tachiyomi.extension.model.LoadResult +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch import logcat.LogPriority import tachiyomi.core.common.util.system.logcat @@ -20,16 +23,12 @@ import tachiyomi.core.common.util.system.logcat */ internal class ExtensionInstallReceiver(private val listener: Listener) : BroadcastReceiver() { - /** - * Registers this broadcast receiver - */ + val scope = CoroutineScope(SupervisorJob()) + fun register(context: Context) { ContextCompat.registerReceiver(context, this, filter, ContextCompat.RECEIVER_NOT_EXPORTED) } - /** - * Returns the intent filter this receiver should subscribe to. - */ private val filter = IntentFilter().apply { addAction(Intent.ACTION_PACKAGE_ADDED) addAction(Intent.ACTION_PACKAGE_REPLACED) @@ -51,17 +50,21 @@ internal class ExtensionInstallReceiver(private val listener: Listener) : Broadc Intent.ACTION_PACKAGE_ADDED, ACTION_EXTENSION_ADDED -> { if (isReplacing(intent)) return - when (val result = getExtensionFromIntent(context, intent)) { - is LoadResult.Success -> listener.onExtensionInstalled(result.extension) - is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension) - else -> {} + scope.launch { + when (val result = getExtensionFromIntent(context, intent)) { + is LoadResult.Success -> listener.onExtensionInstalled(result.extension) + is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension) + else -> {} + } } } Intent.ACTION_PACKAGE_REPLACED, ACTION_EXTENSION_REPLACED -> { - when (val result = getExtensionFromIntent(context, intent)) { - is LoadResult.Success -> listener.onExtensionUpdated(result.extension) - is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension) - else -> {} + scope.launch { + when (val result = getExtensionFromIntent(context, intent)) { + is LoadResult.Success -> listener.onExtensionUpdated(result.extension) + is LoadResult.Untrusted -> listener.onExtensionUntrusted(result.extension) + else -> {} + } } } Intent.ACTION_PACKAGE_REMOVED, ACTION_EXTENSION_REMOVED -> { @@ -90,7 +93,7 @@ internal class ExtensionInstallReceiver(private val listener: Listener) : Broadc * @param context The application context. * @param intent The intent containing the package name of the extension. */ - private fun getExtensionFromIntent(context: Context, intent: Intent?): LoadResult { + private suspend fun getExtensionFromIntent(context: Context, intent: Intent?): LoadResult { val pkgName = getPackageNameFromIntent(intent) if (pkgName == null) { logcat(LogPriority.WARN) { "Package name not found" } diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt index 0468c45e5..50ab94279 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/util/ExtensionLoader.kt @@ -172,7 +172,7 @@ internal object ExtensionLoader { * Attempts to load an extension from the given package name. It checks if the extension * contains the required feature flag before trying to load it. */ - fun loadExtensionFromPkgName(context: Context, pkgName: String): LoadResult { + suspend fun loadExtensionFromPkgName(context: Context, pkgName: String): LoadResult { val extensionPackage = getExtensionInfoFromPkgName(context, pkgName) if (extensionPackage == null) { logcat(LogPriority.ERROR) { "Extension package is not found ($pkgName)" } @@ -223,7 +223,8 @@ internal object ExtensionLoader { * @param context The application context. * @param extensionInfo The extension to load. */ - private fun loadExtension(context: Context, extensionInfo: ExtensionInfo): LoadResult { + @Suppress("LongMethod", "CyclomaticComplexMethod", "ReturnCount") + private suspend fun loadExtension(context: Context, extensionInfo: ExtensionInfo): LoadResult { val pkgManager = context.packageManager val pkgInfo = extensionInfo.packageInfo val appInfo = pkgInfo.applicationInfo @@ -252,7 +253,7 @@ internal object ExtensionLoader { if (signatures.isNullOrEmpty()) { logcat(LogPriority.WARN) { "Package $pkgName isn't signed" } return LoadResult.Error - } else if (!trustExtension.isTrusted(pkgInfo, signatures.last())) { + } else if (!trustExtension.isTrusted(pkgInfo, signatures)) { val extension = Extension.Untrusted( extName, pkgName, diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt index e71d65997..feb69c5ce 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/ExtensionsScreenModel.kt @@ -27,6 +27,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch import tachiyomi.core.common.util.lang.launchIO import tachiyomi.i18n.MR import uy.kohesive.injekt.Injekt @@ -196,7 +197,9 @@ class ExtensionsScreenModel( } fun trustExtension(extension: Extension.Untrusted) { - extensionManager.trust(extension) + screenModelScope.launch { + extensionManager.trust(extension) + } } @Immutable From 28dca3b7b818ad095008e7cd49ec07a82b0ebcad Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 4 May 2024 16:30:53 +0600 Subject: [PATCH 206/212] Address firebase ktx module deprecation --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a2f4303db..2685b5344 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -73,7 +73,7 @@ moko-gradle = { module = "dev.icerock.moko:resources-generator", version.ref = " logcat = "com.squareup.logcat:logcat:0.1" -firebase-analytics = "com.google.firebase:firebase-analytics-ktx:21.6.2" +firebase-analytics = "com.google.firebase:firebase-analytics:22.0.0" aboutLibraries-gradle = { module = "com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin", version.ref = "aboutlib_version" } aboutLibraries-compose = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "aboutlib_version" } From e55e5f6f64f872475d370d6ce0c186e2601776e4 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sat, 4 May 2024 22:26:45 +0600 Subject: [PATCH 207/212] Remove some legacy folder/file name lookup for download Related to #705 --- .../tachiyomi/data/download/DownloadProvider.kt | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt index 001395af1..2c978bed1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt @@ -160,21 +160,12 @@ class DownloadProvider( */ fun getValidChapterDirNames(chapterName: String, chapterScanlator: String?): List { val chapterDirName = getChapterDirName(chapterName, chapterScanlator) - return buildList(4) { + return buildList(2) { // Folder of images add(chapterDirName) // Archived chapters add("$chapterDirName.cbz") - - if (chapterScanlator.isNullOrBlank()) { - // Previously null scanlator fields were converted to "" due to a bug - add("_$chapterDirName") - add("_$chapterDirName.cbz") - } else { - // Legacy chapter directory name used in v0.9.2 and before - add(DiskUtil.buildValidFilename(chapterName)) - } } } } From 7ec2108812fbe0483111dbe996e29e5a621b583a Mon Sep 17 00:00:00 2001 From: Radon Rosborough Date: Sat, 4 May 2024 09:40:41 -0700 Subject: [PATCH 208/212] Massively improve findFile performance (#728) * Massively improve findFile performance * Update libs.versions.toml --------- Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> --- .../eu/kanade/tachiyomi/data/download/DownloadProvider.kt | 8 ++++---- .../java/eu/kanade/tachiyomi/data/download/Downloader.kt | 2 +- gradle/libs.versions.toml | 2 +- .../kotlin/tachiyomi/source/local/LocalSource.kt | 4 ++-- .../tachiyomi/source/local/io/LocalSourceFileSystem.kt | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt index 2c978bed1..2ab5e55d9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadProvider.kt @@ -57,7 +57,7 @@ class DownloadProvider( * @param source the source to query. */ fun findSourceDir(source: Source): UniFile? { - return downloadsDir?.findFile(getSourceDirName(source), true) + return downloadsDir?.findFile(getSourceDirName(source)) } /** @@ -68,7 +68,7 @@ class DownloadProvider( */ fun findMangaDir(mangaTitle: String, source: Source): UniFile? { val sourceDir = findSourceDir(source) - return sourceDir?.findFile(getMangaDirName(mangaTitle), true) + return sourceDir?.findFile(getMangaDirName(mangaTitle)) } /** @@ -82,7 +82,7 @@ class DownloadProvider( fun findChapterDir(chapterName: String, chapterScanlator: String?, mangaTitle: String, source: Source): UniFile? { val mangaDir = findMangaDir(mangaTitle, source) return getValidChapterDirNames(chapterName, chapterScanlator).asSequence() - .mapNotNull { mangaDir?.findFile(it, true) } + .mapNotNull { mangaDir?.findFile(it) } .firstOrNull() } @@ -97,7 +97,7 @@ class DownloadProvider( val mangaDir = findMangaDir(manga.title, source) ?: return null to emptyList() return mangaDir to chapters.mapNotNull { chapter -> getValidChapterDirNames(chapter.name, chapter.scanlator).asSequence() - .mapNotNull { mangaDir.findFile(it, true) } + .mapNotNull { mangaDir.findFile(it) } .firstOrNull() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index 7c8238d5a..11bfe28d1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -645,7 +645,7 @@ class Downloader( ) // Remove the old file - dir.findFile(COMIC_INFO_FILE, true)?.delete() + dir.findFile(COMIC_INFO_FILE)?.delete() dir.createFile(COMIC_INFO_FILE)!!.openOutputStream().use { val comicInfoString = xml.encodeToString(ComicInfo.serializer(), comicInfo) it.write(comicInfoString.toByteArray()) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2685b5344..b6776f997 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -31,7 +31,7 @@ quickjs-android = "app.cash.quickjs:quickjs-android:0.9.2" jsoup = "org.jsoup:jsoup:1.17.2" disklrucache = "com.jakewharton:disklrucache:2.0.2" -unifile = "com.github.tachiyomiorg:unifile:7c257e1c64" +unifile = "com.github.tachiyomiorg:unifile:e0def6b3dc" common-compress = "org.apache.commons:commons-compress:1.26.1" junrar = "com.github.junrar:junrar:7.5.5" diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt index 9e2aa8406..06ed4551d 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt @@ -300,8 +300,8 @@ actual class LocalSource( try { val (mangaDirName, chapterName) = chapter.url.split('/', limit = 2) return fileSystem.getBaseDirectory() - ?.findFile(mangaDirName, true) - ?.findFile(chapterName, true) + ?.findFile(mangaDirName) + ?.findFile(chapterName) ?.let(Format.Companion::valueOf) ?: throw Exception(context.stringResource(MR.strings.chapter_not_found)) } catch (e: Format.UnknownFormatException) { diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt index 402df7e00..69faaa2b7 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/io/LocalSourceFileSystem.kt @@ -17,7 +17,7 @@ actual class LocalSourceFileSystem( actual fun getMangaDirectory(name: String): UniFile? { return getBaseDirectory() - ?.findFile(name, true) + ?.findFile(name) ?.takeIf { it.isDirectory } } From 263e467cdeb948b8f3679e2ea0282a291cf2f131 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 5 May 2024 01:15:07 +0600 Subject: [PATCH 209/212] Fix badge count getting cut off on tab title Fixes #335 --- .../tachiyomi/presentation/core/components/material/Tabs.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Tabs.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Tabs.kt index 30f6766d8..c0c0dea2b 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Tabs.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Tabs.kt @@ -6,6 +6,7 @@ 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.text.style.TextOverflow import androidx.compose.ui.unit.sp import tachiyomi.presentation.core.components.Pill @@ -21,6 +22,7 @@ fun TabText(text: String, badgeCount: Int? = null) { text = text, maxLines = 1, overflow = TextOverflow.Ellipsis, + modifier = Modifier.weight(1f, fill = false), ) if (badgeCount != null) { Pill( From e473c7f09fc009161145aca94bd70027f042b0bf Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Mon, 8 Apr 2024 15:35:17 +0600 Subject: [PATCH 210/212] Bump compose version Co-authored-by: Ivan Iskandar <12537387+ivaniskandar@users.noreply.github.com> --- .../browse/ExtensionDetailsScreen.kt | 2 +- .../browse/ExtensionFilterScreen.kt | 2 +- .../presentation/browse/ExtensionsScreen.kt | 6 +- .../presentation/browse/GlobalSearchScreen.kt | 2 + .../browse/MigrateSourceScreen.kt | 2 +- .../browse/SourcesFilterScreen.kt | 4 +- .../presentation/browse/SourcesScreen.kt | 4 +- .../components/GlobalSearchResultItems.kt | 3 +- .../presentation/category/CategoryScreen.kt | 2 +- .../CategoryFloatingActionButton.kt | 5 +- .../presentation/history/HistoryScreen.kt | 4 +- .../kanade/presentation/manga/MangaScreen.kt | 7 +- .../manga/components/MangaInfoHeader.kt | 4 +- .../manga/components/ScanlatorFilterDialog.kt | 6 +- .../more/onboarding/PermissionStep.kt | 2 +- .../components/ExtensionReposContent.kt | 2 +- .../settings/widget/ListPreferenceWidget.kt | 6 +- .../settings/widget/TriStateListDialog.kt | 14 +- .../track/TrackInfoDialogSelector.kt | 6 +- .../presentation/updates/UpdatesUiItem.kt | 6 +- .../kanade/presentation/util/Permissions.kt | 2 +- gradle/compose.versions.toml | 3 +- .../core/components/AdaptiveSheet.kt | 100 ++++--- .../core/components/material/PullRefresh.kt | 270 ++---------------- .../presentation/core/util/LazyListState.kt | 59 +--- 25 files changed, 112 insertions(+), 411 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt index dcd5067d3..277dc5e65 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionDetailsScreen.kt @@ -190,7 +190,7 @@ private fun ExtensionDetails( key = { it.source.id }, ) { source -> SourceSwitchPreference( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), source = source, onClickSourcePreferences = onClickSourcePreferences, onClickSource = onClickSource, diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionFilterScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionFilterScreen.kt index c65f0d0b1..7f7e18baa 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionFilterScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionFilterScreen.kt @@ -58,7 +58,7 @@ private fun ExtensionFilterContent( ) { items(state.languages) { language -> SwitchPreferenceWidget( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), title = LocaleHelper.getSourceDisplayName(language, context), checked = language in state.enabledLanguages, onCheckedChanged = { onClickLang(language) }, diff --git a/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt index d08a2d93b..a3cb94800 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/ExtensionsScreen.kt @@ -187,14 +187,14 @@ private fun ExtensionContent( } ExtensionHeader( textRes = header.textRes, - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), action = action, ) } is ExtensionUiModel.Header.Text -> { ExtensionHeader( text = header.text, - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), ) } } @@ -212,7 +212,7 @@ private fun ExtensionContent( }, ) { item -> ExtensionItem( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), item = item, onClickItem = { when (it) { diff --git a/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt index 7f79dd3ed..da4db5e98 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.runtime.Composable import androidx.compose.runtime.State +import androidx.compose.ui.Modifier import eu.kanade.presentation.browse.components.GlobalSearchCardRow import eu.kanade.presentation.browse.components.GlobalSearchErrorResultItem import eu.kanade.presentation.browse.components.GlobalSearchLoadingResultItem @@ -79,6 +80,7 @@ internal fun GlobalSearchContent( } ?: source.name, subtitle = LocaleHelper.getLocalizedDisplayName(source.lang), onClick = { onClickSource(source) }, + modifier = Modifier.animateItem(), ) { when (result) { SearchItemResult.Loading -> { diff --git a/app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt index 404d11476..3f8a67c4c 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/MigrateSourceScreen.kt @@ -133,7 +133,7 @@ private fun MigrateSourceList( key = { (source, _) -> "migrate-${source.id}" }, ) { (source, count) -> MigrateSourceItem( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), source = source, count = count, onClickItem = { onClickItem(source) }, diff --git a/app/src/main/java/eu/kanade/presentation/browse/SourcesFilterScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/SourcesFilterScreen.kt index e334a451e..9f74c3799 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/SourcesFilterScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/SourcesFilterScreen.kt @@ -68,7 +68,7 @@ private fun SourcesFilterContent( contentType = "source-filter-header", ) { SourcesFilterHeader( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), language = language, enabled = enabled, onClickItem = onClickLanguage, @@ -81,7 +81,7 @@ private fun SourcesFilterContent( contentType = { "source-filter-item" }, ) { source -> SourcesFilterItem( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), source = source, enabled = "${source.id}" !in state.disabledSources, onClickItem = onClickSource, diff --git a/app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt index d0411b7a8..56644b3d8 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/SourcesScreen.kt @@ -74,12 +74,12 @@ fun SourcesScreen( when (model) { is SourceUiModel.Header -> { SourceHeader( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), language = model.language, ) } is SourceUiModel.Item -> SourceItem( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), source = model.source, onClickItem = onClickItem, onLongClickItem = onLongClickItem, diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchResultItems.kt b/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchResultItems.kt index 29ef3b97b..5de9a2142 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchResultItems.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchResultItems.kt @@ -32,9 +32,10 @@ fun GlobalSearchResultItem( title: String, subtitle: String, onClick: () -> Unit, + modifier: Modifier = Modifier, content: @Composable () -> Unit, ) { - Column { + Column(modifier = modifier) { Row( modifier = Modifier .padding( diff --git a/app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt b/app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt index 34a6a1217..df553a203 100644 --- a/app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/category/CategoryScreen.kt @@ -107,7 +107,7 @@ private fun CategoryContent( key = { _, category -> "category-${category.id}" }, ) { index, category -> CategoryListItem( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), category = category, canMoveUp = index != 0, canMoveDown = index != categories.lastIndex, diff --git a/app/src/main/java/eu/kanade/presentation/category/components/CategoryFloatingActionButton.kt b/app/src/main/java/eu/kanade/presentation/category/components/CategoryFloatingActionButton.kt index 58d7f163a..a151e9b2f 100644 --- a/app/src/main/java/eu/kanade/presentation/category/components/CategoryFloatingActionButton.kt +++ b/app/src/main/java/eu/kanade/presentation/category/components/CategoryFloatingActionButton.kt @@ -10,8 +10,7 @@ import androidx.compose.ui.Modifier import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton import tachiyomi.presentation.core.i18n.stringResource -import tachiyomi.presentation.core.util.isScrolledToEnd -import tachiyomi.presentation.core.util.isScrollingUp +import tachiyomi.presentation.core.util.shouldExpandFAB @Composable fun CategoryFloatingActionButton( @@ -23,7 +22,7 @@ fun CategoryFloatingActionButton( text = { Text(text = stringResource(MR.strings.action_add)) }, icon = { Icon(imageVector = Icons.Outlined.Add, contentDescription = null) }, onClick = onCreate, - expanded = lazyListState.isScrollingUp() || lazyListState.isScrolledToEnd(), + expanded = lazyListState.shouldExpandFAB(), modifier = modifier, ) } diff --git a/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt b/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt index c15604829..dba526b54 100644 --- a/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/history/HistoryScreen.kt @@ -113,14 +113,14 @@ private fun HistoryScreenContent( when (item) { is HistoryUiModel.Header -> { ListGroupHeader( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), text = relativeDateText(item.date), ) } is HistoryUiModel.Item -> { val value = item.item HistoryItem( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), history = value, onClickCover = { onClickCover(value) }, onClickResume = { onClickResume(value) }, diff --git a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt index 9a6e3f6fd..efe44ceb4 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt @@ -75,8 +75,7 @@ import tachiyomi.presentation.core.components.material.ExtendedFloatingActionBut import tachiyomi.presentation.core.components.material.PullRefresh import tachiyomi.presentation.core.components.material.Scaffold import tachiyomi.presentation.core.i18n.stringResource -import tachiyomi.presentation.core.util.isScrolledToEnd -import tachiyomi.presentation.core.util.isScrollingUp +import tachiyomi.presentation.core.util.shouldExpandFAB import tachiyomi.source.local.isLocal import java.time.Instant @@ -346,7 +345,7 @@ private fun MangaScreenSmallImpl( }, icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) }, onClick = onContinueReading, - expanded = chapterListState.isScrollingUp() || chapterListState.isScrolledToEnd(), + expanded = chapterListState.shouldExpandFAB(), ) } }, @@ -594,7 +593,7 @@ fun MangaScreenLargeImpl( }, icon = { Icon(imageVector = Icons.Filled.PlayArrow, contentDescription = null) }, onClick = onContinueReading, - expanded = chapterListState.isScrollingUp() || chapterListState.isScrolledToEnd(), + expanded = chapterListState.shouldExpandFAB(), ) } }, diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt index ac4946c99..f1b660626 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt @@ -42,7 +42,7 @@ import androidx.compose.material.icons.outlined.Sync import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.LocalContentColor -import androidx.compose.material3.LocalMinimumInteractiveComponentEnforcement +import androidx.compose.material3.LocalMinimumInteractiveComponentSize import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ProvideTextStyle @@ -649,7 +649,7 @@ private fun TagsChip( modifier: Modifier = Modifier, onClick: () -> Unit, ) { - CompositionLocalProvider(LocalMinimumInteractiveComponentEnforcement provides false) { + CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) { SuggestionChip( modifier = modifier, onClick = onClick, diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/ScanlatorFilterDialog.kt b/app/src/main/java/eu/kanade/presentation/manga/components/ScanlatorFilterDialog.kt index d7c46e90d..747ac09e0 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/ScanlatorFilterDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/ScanlatorFilterDialog.kt @@ -31,8 +31,6 @@ import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.TextButton import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource -import tachiyomi.presentation.core.util.isScrolledToEnd -import tachiyomi.presentation.core.util.isScrolledToStart @Composable fun ScanlatorFilterDialog( @@ -96,8 +94,8 @@ fun ScanlatorFilterDialog( } } } - if (!state.isScrolledToStart()) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter)) - if (!state.isScrolledToEnd()) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter)) + if (state.canScrollBackward) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter)) + if (state.canScrollForward) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter)) } }, properties = DialogProperties( diff --git a/app/src/main/java/eu/kanade/presentation/more/onboarding/PermissionStep.kt b/app/src/main/java/eu/kanade/presentation/more/onboarding/PermissionStep.kt index 79e45159f..8b3d9c07b 100644 --- a/app/src/main/java/eu/kanade/presentation/more/onboarding/PermissionStep.kt +++ b/app/src/main/java/eu/kanade/presentation/more/onboarding/PermissionStep.kt @@ -28,11 +28,11 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.unit.dp import androidx.core.content.getSystemService import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.compose.LocalLifecycleOwner import eu.kanade.presentation.util.rememberRequestPackageInstallsPermissionState import eu.kanade.tachiyomi.util.system.launchRequestPackageInstallsPermission import tachiyomi.i18n.MR diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposContent.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposContent.kt index 20a924d11..e6b2a227f 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposContent.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposContent.kt @@ -46,7 +46,7 @@ fun ExtensionReposContent( repos.forEach { item { ExtensionRepoListItem( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), repo = it, onOpenWebsite = { onOpenWebsite(it) }, onDelete = { onClickDelete(it.baseUrl) }, diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/ListPreferenceWidget.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/ListPreferenceWidget.kt index c8e757491..7daf65168 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/ListPreferenceWidget.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/ListPreferenceWidget.kt @@ -26,8 +26,6 @@ import androidx.compose.ui.unit.dp import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.ScrollbarLazyColumn import tachiyomi.presentation.core.i18n.stringResource -import tachiyomi.presentation.core.util.isScrolledToEnd -import tachiyomi.presentation.core.util.isScrolledToStart @Composable fun ListPreferenceWidget( @@ -69,8 +67,8 @@ fun ListPreferenceWidget( } } } - if (!state.isScrolledToStart()) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter)) - if (!state.isScrolledToEnd()) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter)) + if (state.canScrollBackward) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter)) + if (state.canScrollForward) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter)) } }, confirmButton = { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/TriStateListDialog.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/TriStateListDialog.kt index f6e195e4d..be5029ac3 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/TriStateListDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/TriStateListDialog.kt @@ -30,8 +30,6 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp import tachiyomi.i18n.MR import tachiyomi.presentation.core.i18n.stringResource -import tachiyomi.presentation.core.util.isScrolledToEnd -import tachiyomi.presentation.core.util.isScrolledToStart private enum class State { CHECKED, INVERSED, UNCHECKED @@ -115,16 +113,8 @@ fun TriStateListDialog( } } - if (!listState.isScrolledToStart()) { - HorizontalDivider( - modifier = Modifier.align(Alignment.TopCenter), - ) - } - if (!listState.isScrolledToEnd()) { - HorizontalDivider( - modifier = Modifier.align(Alignment.BottomCenter), - ) - } + if (listState.canScrollBackward) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter)) + if (listState.canScrollForward) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter)) } } }, 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 053ba7bbc..c6fda5cc5 100644 --- a/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogSelector.kt +++ b/app/src/main/java/eu/kanade/presentation/track/TrackInfoDialogSelector.kt @@ -43,8 +43,6 @@ import tachiyomi.presentation.core.components.WheelTextPicker import tachiyomi.presentation.core.components.material.AlertDialogContent import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource -import tachiyomi.presentation.core.util.isScrolledToEnd -import tachiyomi.presentation.core.util.isScrolledToStart @Composable fun TrackStatusSelector( @@ -86,8 +84,8 @@ fun TrackStatusSelector( } } } - if (!state.isScrolledToStart()) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter)) - if (!state.isScrolledToEnd()) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter)) + if (state.canScrollBackward) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter)) + if (state.canScrollForward) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter)) }, onConfirm = onConfirm, onDismissRequest = onDismissRequest, diff --git a/app/src/main/java/eu/kanade/presentation/updates/UpdatesUiItem.kt b/app/src/main/java/eu/kanade/presentation/updates/UpdatesUiItem.kt index a69252ffc..9f5c92479 100644 --- a/app/src/main/java/eu/kanade/presentation/updates/UpdatesUiItem.kt +++ b/app/src/main/java/eu/kanade/presentation/updates/UpdatesUiItem.kt @@ -54,7 +54,7 @@ internal fun LazyListScope.updatesLastUpdatedItem( item(key = "updates-lastUpdated") { Box( modifier = Modifier - .animateItemPlacement() + .animateItem() .padding(horizontal = MaterialTheme.padding.medium, vertical = MaterialTheme.padding.small), ) { Text( @@ -91,14 +91,14 @@ internal fun LazyListScope.updatesUiItems( when (item) { is UpdatesUiModel.Header -> { ListGroupHeader( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), text = relativeDateText(item.date), ) } is UpdatesUiModel.Item -> { val updatesItem = item.item UpdatesUiItem( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), update = updatesItem.update, selected = updatesItem.selected, readProgress = updatesItem.update.lastPageRead diff --git a/app/src/main/java/eu/kanade/presentation/util/Permissions.kt b/app/src/main/java/eu/kanade/presentation/util/Permissions.kt index 6bee9203e..80c2f90b9 100644 --- a/app/src/main/java/eu/kanade/presentation/util/Permissions.kt +++ b/app/src/main/java/eu/kanade/presentation/util/Permissions.kt @@ -7,9 +7,9 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.compose.LocalLifecycleOwner @Composable fun rememberRequestPackageInstallsPermissionState(initialValue: Boolean = false): Boolean { diff --git a/gradle/compose.versions.toml b/gradle/compose.versions.toml index 58c1ed191..1249e384f 100644 --- a/gradle/compose.versions.toml +++ b/gradle/compose.versions.toml @@ -1,7 +1,6 @@ [versions] compiler = "1.5.12" -# 2024.04.00-alpha01 has several bugs with the new animateItem() modifier -compose-bom = "2024.03.00-alpha02" +compose-bom = "2024.05.00-alpha01" accompanist = "0.35.0-alpha" [libraries] 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 fa6eb375a..54d2ad700 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 @@ -153,7 +153,9 @@ fun AdaptiveSheet( if (enableSwipeDismiss) { Modifier.nestedScroll( remember(anchoredDraggableState) { - anchoredDraggableState.preUpPostDownNestedScrollConnection() + anchoredDraggableState.preUpPostDownNestedScrollConnection( + onFling = { scope.launch { anchoredDraggableState.settle(it) } } + ) }, ) } else { @@ -201,55 +203,51 @@ fun AdaptiveSheet( } } -private fun AnchoredDraggableState.preUpPostDownNestedScrollConnection() = - object : NestedScrollConnection { - override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { - val delta = available.toFloat() - return if (delta < 0 && source == NestedScrollSource.Drag) { - dispatchRawDelta(delta).toOffset() - } else { - Offset.Zero - } +private fun AnchoredDraggableState.preUpPostDownNestedScrollConnection( + onFling: (velocity: Float) -> Unit +) = object : NestedScrollConnection { + override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { + val delta = available.toFloat() + return if (delta < 0 && source == NestedScrollSource.UserInput) { + dispatchRawDelta(delta).toOffset() + } else { + Offset.Zero } - - override fun onPostScroll( - consumed: Offset, - available: Offset, - source: NestedScrollSource, - ): Offset { - return if (source == NestedScrollSource.Drag) { - dispatchRawDelta(available.toFloat()).toOffset() - } else { - Offset.Zero - } - } - - override suspend fun onPreFling(available: Velocity): Velocity { - val toFling = available.toFloat() - return if (toFling < 0 && offset > anchors.minAnchor()) { - settle(toFling) - // since we go to the anchor with tween settling, consume all for the best UX - available - } else { - Velocity.Zero - } - } - - override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { - val toFling = available.toFloat() - return if (toFling > 0) { - settle(toFling) - available - } else { - Velocity.Zero - } - } - - private fun Float.toOffset(): Offset = Offset(0f, this) - - @JvmName("velocityToFloat") - private fun Velocity.toFloat() = this.y - - @JvmName("offsetToFloat") - private fun Offset.toFloat(): Float = this.y } + + override fun onPostScroll( + consumed: Offset, + available: Offset, + source: NestedScrollSource, + ): Offset { + return if (source == NestedScrollSource.UserInput) { + dispatchRawDelta(available.toFloat()).toOffset() + } else { + Offset.Zero + } + } + + override suspend fun onPreFling(available: Velocity): Velocity { + val toFling = available.toFloat() + return if (toFling < 0 && offset > anchors.minAnchor()) { + onFling(toFling) + // since we go to the anchor with tween settling, consume all for the best UX + available + } else { + Velocity.Zero + } + } + + override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { + onFling(available.toFloat()) + return available + } + + private fun Float.toOffset(): Offset = Offset(0f, this) + + @JvmName("velocityToFloat") + private fun Velocity.toFloat() = this.y + + @JvmName("offsetToFloat") + private fun Offset.toFloat(): Float = this.y +} diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/PullRefresh.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/PullRefresh.kt index 9642cec71..b1987185b 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/PullRefresh.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/PullRefresh.kt @@ -1,34 +1,16 @@ package tachiyomi.presentation.core.components.material -import androidx.compose.animation.core.animate import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.pulltorefresh.PullToRefreshContainer -import androidx.compose.material3.pulltorefresh.PullToRefreshState +import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults +import androidx.compose.material3.pulltorefresh.pullToRefresh +import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableFloatStateOf -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.Saver -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.geometry.Offset -import androidx.compose.ui.input.nestedscroll.NestedScrollConnection -import androidx.compose.ui.input.nestedscroll.NestedScrollSource -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.LayoutDirection -import androidx.compose.ui.unit.Velocity import androidx.compose.ui.unit.dp -import kotlin.math.abs -import kotlin.math.pow /** * @param refreshing Whether the layout is currently refreshing @@ -46,242 +28,26 @@ fun PullRefresh( indicatorPadding: PaddingValues = PaddingValues(0.dp), content: @Composable () -> Unit, ) { - val state = rememberPullToRefreshState( - isRefreshing = refreshing, - extraVerticalOffset = indicatorPadding.calculateTopPadding(), - enabled = enabled, - onRefresh = onRefresh, - ) - - Box(modifier.nestedScroll(state.nestedScrollConnection)) { + val state = rememberPullToRefreshState() + Box( + modifier = modifier + .pullToRefresh( + isRefreshing = refreshing, + state = state, + enabled = enabled, + onRefresh = onRefresh, + ) + ) { content() - val contentPadding = remember(indicatorPadding) { - object : PaddingValues { - override fun calculateLeftPadding(layoutDirection: LayoutDirection): Dp = - indicatorPadding.calculateLeftPadding(layoutDirection) - - override fun calculateTopPadding(): Dp = 0.dp - - override fun calculateRightPadding(layoutDirection: LayoutDirection): Dp = - indicatorPadding.calculateRightPadding(layoutDirection) - - override fun calculateBottomPadding(): Dp = - indicatorPadding.calculateBottomPadding() - } - } - PullToRefreshContainer( - state = state, + PullToRefreshDefaults.Indicator( modifier = Modifier .align(Alignment.TopCenter) - .padding(contentPadding), + .padding(indicatorPadding), + isRefreshing = refreshing, + state = state, containerColor = MaterialTheme.colorScheme.surfaceVariant, - contentColor = MaterialTheme.colorScheme.onSurfaceVariant, + color = MaterialTheme.colorScheme.onSurfaceVariant, ) } } - -@Composable -private fun rememberPullToRefreshState( - isRefreshing: Boolean, - extraVerticalOffset: Dp, - positionalThreshold: Dp = 64.dp, - enabled: () -> Boolean = { true }, - onRefresh: () -> Unit, -): PullToRefreshStateImpl { - val density = LocalDensity.current - val extraVerticalOffsetPx = with(density) { extraVerticalOffset.toPx() } - val positionalThresholdPx = with(density) { positionalThreshold.toPx() } - return rememberSaveable( - extraVerticalOffset, - positionalThresholdPx, - enabled, - onRefresh, - saver = PullToRefreshStateImpl.Saver( - extraVerticalOffset = extraVerticalOffsetPx, - positionalThreshold = positionalThresholdPx, - enabled = enabled, - onRefresh = onRefresh, - ), - ) { - PullToRefreshStateImpl( - initialRefreshing = isRefreshing, - extraVerticalOffset = extraVerticalOffsetPx, - positionalThreshold = positionalThresholdPx, - enabled = enabled, - onRefresh = onRefresh, - ) - }.also { - LaunchedEffect(isRefreshing) { - if (isRefreshing && !it.isRefreshing) { - it.startRefreshAnimated() - } else if (!isRefreshing && it.isRefreshing) { - it.endRefreshAnimated() - } - } - } -} - -/** - * Creates a [PullToRefreshState]. - * - * @param positionalThreshold The positional threshold, in pixels, in which a refresh is triggered - * @param extraVerticalOffset Extra vertical offset, in pixels, for the "refreshing" state - * @param initialRefreshing The initial refreshing value of [PullToRefreshState] - * @param enabled a callback used to determine whether scroll events are to be handled by this - * @param onRefresh a callback to run when pull-to-refresh action is triggered by user - * [PullToRefreshState] - */ -private class PullToRefreshStateImpl( - initialRefreshing: Boolean, - private val extraVerticalOffset: Float, - override val positionalThreshold: Float, - enabled: () -> Boolean, - private val onRefresh: () -> Unit, -) : PullToRefreshState { - - override val progress get() = adjustedDistancePulled / positionalThreshold - override var verticalOffset by mutableFloatStateOf(if (initialRefreshing) refreshingVerticalOffset else 0f) - - override var isRefreshing by mutableStateOf(initialRefreshing) - - private val refreshingVerticalOffset: Float - get() = positionalThreshold + extraVerticalOffset - - override fun startRefresh() { - isRefreshing = true - verticalOffset = refreshingVerticalOffset - } - - suspend fun startRefreshAnimated() { - isRefreshing = true - animateTo(refreshingVerticalOffset) - } - - override fun endRefresh() { - verticalOffset = 0f - isRefreshing = false - } - - suspend fun endRefreshAnimated() { - animateTo(0f) - isRefreshing = false - } - - override var nestedScrollConnection = object : NestedScrollConnection { - override fun onPreScroll( - available: Offset, - source: NestedScrollSource, - ): Offset = when { - !enabled() -> Offset.Zero - // Swiping up - source == NestedScrollSource.Drag && available.y < 0 -> { - consumeAvailableOffset(available) - } - else -> Offset.Zero - } - - override fun onPostScroll( - consumed: Offset, - available: Offset, - source: NestedScrollSource, - ): Offset = when { - !enabled() -> Offset.Zero - // Swiping down - source == NestedScrollSource.Drag && available.y > 0 -> { - consumeAvailableOffset(available) - } - else -> Offset.Zero - } - - override suspend fun onPreFling(available: Velocity): Velocity { - return Velocity(0f, onRelease(available.y)) - } - } - - /** Helper method for nested scroll connection */ - fun consumeAvailableOffset(available: Offset): Offset { - val y = if (isRefreshing) { - 0f - } else { - val newOffset = (distancePulled + available.y).coerceAtLeast(0f) - val dragConsumed = newOffset - distancePulled - distancePulled = newOffset - verticalOffset = calculateVerticalOffset() + (extraVerticalOffset * progress.coerceIn(0f, 1f)) - dragConsumed - } - return Offset(0f, y) - } - - /** Helper method for nested scroll connection. Calls onRefresh callback when triggered */ - suspend fun onRelease(velocity: Float): Float { - if (isRefreshing) return 0f // Already refreshing, do nothing - // Trigger refresh - if (adjustedDistancePulled > positionalThreshold) { - onRefresh() - startRefreshAnimated() - } else { - animateTo(0f) - } - - val consumed = when { - // We are flinging without having dragged the pull refresh (for example a fling inside - // a list) - don't consume - distancePulled == 0f -> 0f - // If the velocity is negative, the fling is upwards, and we don't want to prevent the - // the list from scrolling - velocity < 0f -> 0f - // We are showing the indicator, and the fling is downwards - consume everything - else -> velocity - } - distancePulled = 0f - return consumed - } - - suspend fun animateTo(offset: Float) { - animate(initialValue = verticalOffset, targetValue = offset) { value, _ -> - verticalOffset = value - } - } - - /** Provides custom vertical offset behavior for [PullToRefreshContainer] */ - fun calculateVerticalOffset(): Float = when { - // If drag hasn't gone past the threshold, the position is the adjustedDistancePulled. - adjustedDistancePulled <= positionalThreshold -> adjustedDistancePulled - else -> { - // How far beyond the threshold pull has gone, as a percentage of the threshold. - val overshootPercent = abs(progress) - 1.0f - // Limit the overshoot to 200%. Linear between 0 and 200. - val linearTension = overshootPercent.coerceIn(0f, 2f) - // Non-linear tension. Increases with linearTension, but at a decreasing rate. - val tensionPercent = linearTension - linearTension.pow(2) / 4 - // The additional offset beyond the threshold. - val extraOffset = positionalThreshold * tensionPercent - positionalThreshold + extraOffset - } - } - - companion object { - /** The default [Saver] for [PullToRefreshStateImpl]. */ - fun Saver( - extraVerticalOffset: Float, - positionalThreshold: Float, - enabled: () -> Boolean, - onRefresh: () -> Unit, - ) = Saver( - save = { it.isRefreshing }, - restore = { isRefreshing -> - PullToRefreshStateImpl( - initialRefreshing = isRefreshing, - extraVerticalOffset = extraVerticalOffset, - positionalThreshold = positionalThreshold, - enabled = enabled, - onRefresh = onRefresh, - ) - }, - ) - } - - private var distancePulled by mutableFloatStateOf(0f) - private val adjustedDistancePulled: Float get() = distancePulled * 0.5f -} diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/util/LazyListState.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/util/LazyListState.kt index 1abb3205a..cdabf1b49 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/util/LazyListState.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/util/LazyListState.kt @@ -3,63 +3,16 @@ package tachiyomi.presentation.core.util import androidx.compose.foundation.lazy.LazyListState import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue @Composable -fun LazyListState.isScrolledToStart(): Boolean { +fun LazyListState.shouldExpandFAB(): Boolean { return remember { derivedStateOf { - val firstItem = layoutInfo.visibleItemsInfo.firstOrNull() - firstItem == null || firstItem.offset == layoutInfo.viewportStartOffset + (firstVisibleItemIndex == 0 && firstVisibleItemScrollOffset == 0) || + lastScrolledBackward || + !canScrollForward } - }.value -} - -@Composable -fun LazyListState.isScrolledToEnd(): Boolean { - return remember { - derivedStateOf { - val lastItem = layoutInfo.visibleItemsInfo.lastOrNull() - lastItem == null || lastItem.size + lastItem.offset <= layoutInfo.viewportEndOffset - } - }.value -} - -@Composable -fun LazyListState.isScrollingUp(): Boolean { - var previousIndex by remember { mutableIntStateOf(firstVisibleItemIndex) } - var previousScrollOffset by remember { mutableIntStateOf(firstVisibleItemScrollOffset) } - return remember { - derivedStateOf { - if (previousIndex != firstVisibleItemIndex) { - previousIndex > firstVisibleItemIndex - } else { - previousScrollOffset >= firstVisibleItemScrollOffset - }.also { - previousIndex = firstVisibleItemIndex - previousScrollOffset = firstVisibleItemScrollOffset - } - } - }.value -} - -@Composable -fun LazyListState.isScrollingDown(): Boolean { - var previousIndex by remember { mutableIntStateOf(firstVisibleItemIndex) } - var previousScrollOffset by remember { mutableIntStateOf(firstVisibleItemScrollOffset) } - return remember { - derivedStateOf { - if (previousIndex != firstVisibleItemIndex) { - previousIndex < firstVisibleItemIndex - } else { - previousScrollOffset <= firstVisibleItemScrollOffset - }.also { - previousIndex = firstVisibleItemIndex - previousScrollOffset = firstVisibleItemScrollOffset - } - } - }.value + } + .value } From dbcc4a7d714cf36b48a41d6074ef671f3c039823 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 5 May 2024 03:31:50 +0600 Subject: [PATCH 211/212] Revert "Fix badge count getting cut off on tab title" This reverts commit 263e467cdeb948b8f3679e2ea0282a291cf2f131. --- .../tachiyomi/presentation/core/components/material/Tabs.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Tabs.kt b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Tabs.kt index c0c0dea2b..30f6766d8 100644 --- a/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Tabs.kt +++ b/presentation-core/src/main/java/tachiyomi/presentation/core/components/material/Tabs.kt @@ -6,7 +6,6 @@ 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.text.style.TextOverflow import androidx.compose.ui.unit.sp import tachiyomi.presentation.core.components.Pill @@ -22,7 +21,6 @@ fun TabText(text: String, badgeCount: Int? = null) { text = text, maxLines = 1, overflow = TextOverflow.Ellipsis, - modifier = Modifier.weight(1f, fill = false), ) if (badgeCount != null) { Pill( From 550f1197e818c35c7c05fd6184e69c7d29559e9f Mon Sep 17 00:00:00 2001 From: Reagan Date: Sun, 5 May 2024 22:23:09 +1200 Subject: [PATCH 212/212] Change keyboard type in extension repo dialog (#764) --- .../settings/screen/browse/components/ExtensionReposDialogs.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposDialogs.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposDialogs.kt index 5022f44f0..239f917d0 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposDialogs.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/browse/components/ExtensionReposDialogs.kt @@ -1,6 +1,7 @@ package eu.kanade.presentation.more.settings.screen.browse.components import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.AlertDialog import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text @@ -14,6 +15,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.text.input.KeyboardType import kotlinx.collections.immutable.ImmutableSet import kotlinx.coroutines.delay import mihon.domain.extensionrepo.model.ExtensionRepo @@ -74,6 +76,7 @@ fun ExtensionRepoCreateDialog( Text(text = stringResource(msgRes)) }, isError = name.isNotEmpty() && nameAlreadyExists, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Uri), singleLine = true, ) }