Compare commits

...

34 Commits

Author SHA1 Message Date
f590378761 Release 0.10.9 2021-02-12 16:01:23 -05:00
f5f592be91 Require minimum WebView v88, try to catch fatal errors too 2021-02-12 12:42:33 -05:00
7a373fb43a Minor download icon optimizations 2021-02-12 12:27:40 -05:00
aded11e599 Make backup restoring logic more sequential 2021-02-12 12:27:40 -05:00
41d7cee020 Remove ExperimentalSerializationApi opt-in annotations 2021-02-12 12:27:40 -05:00
f2ef6a20e6 Weblate translations (#4378)
Co-authored-by: Adaś <adam.prosniak@gmail.com>
Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Co-authored-by: Alex <linuxrf@gmail.com>
Co-authored-by: Ava <Sasu.ruotsalainen@live.fi>
Co-authored-by: Crazyom <naxom@laposte.net>
Co-authored-by: Cream π <f.t.nayeem014@gmail.com>
Co-authored-by: DarKCroX <darkcrox.2020@outlook.com>
Co-authored-by: Eduard Ereza Martínez <eduard@ereza.cat>
Co-authored-by: Eric <spice2wolf@gmail.com>
Co-authored-by: Eugene <eugcheung94@gmail.com>
Co-authored-by: Flamm <robindevaux25@gmail.com>
Co-authored-by: Habibur Rahman <habiburr016@gmail.com>
Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Huang Zhiyi <hzy980512@126.com>
Co-authored-by: Iuri Jikidze <ijiki16@freeuni.edu.ge>
Co-authored-by: Jimly Asshiddiqy <j_mly@ymail.com>
Co-authored-by: Kurocon <weblate@kurocon.nl>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Matteo Gaeta <matteo.gaeta.1998@gmail.com>
Co-authored-by: Michalis <michalisntovas@yahoo.gr>
Co-authored-by: Murilo Simionato Arnemann <murilo2110@hotmail.com>
Co-authored-by: Oğuz Ersen <oguzersen@protonmail.com>
Co-authored-by: Paulo Pinho <kebrus@gmail.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Rocco Casadei <roccobot@gmail.com>
Co-authored-by: Rostyslav <info@ubilling.net.ua>
Co-authored-by: Samuel Carvalho de Araújo <samuelnegro12345@gmail.com>
Co-authored-by: Soitora <simon.mattila@protonmail.com>
Co-authored-by: darkbeast13 <nikhil15mps@gmail.com>
Co-authored-by: Роман <Rozhenkov69@gmail.com>
Co-authored-by: 赤城 悠 <hapipon815@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/bn/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ca/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/de/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/el/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fi/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/fr/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/hi/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/id/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/it/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ja/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ka/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ms/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/nl/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/pl/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/pt/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/pt_BR/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/ru/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/sc/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/sv/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/tr/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/uk/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/zh_Hans/
Translate-URL: https://hosted.weblate.org/projects/tachiyomi/strings/zh_Hant/
Translation: Tachiyomi/Tachiyomi 0.x

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Co-authored-by: Adaś <adam.prosniak@gmail.com>
Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Co-authored-by: Alex <linuxrf@gmail.com>
Co-authored-by: Ava <Sasu.ruotsalainen@live.fi>
Co-authored-by: Crazyom <naxom@laposte.net>
Co-authored-by: Cream π <f.t.nayeem014@gmail.com>
Co-authored-by: DarKCroX <darkcrox.2020@outlook.com>
Co-authored-by: Eduard Ereza Martínez <eduard@ereza.cat>
Co-authored-by: Eric <spice2wolf@gmail.com>
Co-authored-by: Eugene <eugcheung94@gmail.com>
Co-authored-by: Flamm <robindevaux25@gmail.com>
Co-authored-by: Habibur Rahman <habiburr016@gmail.com>
Co-authored-by: Huang Zhiyi <hzy980512@126.com>
Co-authored-by: Iuri Jikidze <ijiki16@freeuni.edu.ge>
Co-authored-by: Jimly Asshiddiqy <j_mly@ymail.com>
Co-authored-by: Kurocon <weblate@kurocon.nl>
Co-authored-by: Lyfja <yassinelaoud@gmail.com>
Co-authored-by: Lzmxya <lzmxya@gmail.com>
Co-authored-by: Matteo Gaeta <matteo.gaeta.1998@gmail.com>
Co-authored-by: Michalis <michalisntovas@yahoo.gr>
Co-authored-by: Murilo Simionato Arnemann <murilo2110@hotmail.com>
Co-authored-by: Oğuz Ersen <oguzersen@protonmail.com>
Co-authored-by: Paulo Pinho <kebrus@gmail.com>
Co-authored-by: Pitpe11 <giorgos2550@gmail.com>
Co-authored-by: Rocco Casadei <roccobot@gmail.com>
Co-authored-by: Rostyslav <info@ubilling.net.ua>
Co-authored-by: Samuel Carvalho de Araújo <samuelnegro12345@gmail.com>
Co-authored-by: Soitora <simon.mattila@protonmail.com>
Co-authored-by: darkbeast13 <nikhil15mps@gmail.com>
Co-authored-by: Роман <Rozhenkov69@gmail.com>
Co-authored-by: 赤城 悠 <hapipon815@gmail.com>
2021-02-12 12:27:32 -05:00
a398c3fb81 Handle link for multisource extension commits (closes #4432) 2021-02-11 17:35:15 -05:00
2a454b44cc Adjust some scopes 2021-02-09 19:14:38 -05:00
7b66ece895 Fix invisible overflow icon in chapter filter sheet in light blue theme 2021-02-09 19:12:44 -05:00
b5017eebbf Added dual page split setting (#4252)
* Add DualPageSplit option

* remove extra line

* Split double-page into two pages

* Remove !isAnimated check and add (ALPHA) to the label

* Fix missing insert pages

* Pager cleanup

* Add dual split to Webtoon and fix Vertical

* Fix L2R/R2L

* Add comments and refactor code in ImageUtil

* Use a simpler split solution in webtoon mode

Co-authored-by: weng <>
Co-authored-by: Andreas E <andreas.everos@gmail.com>
2021-02-09 17:54:44 -05:00
aa67229daf Add weekly to library update frequency options (closes #4422) 2021-02-09 17:49:02 -05:00
5af68186d6 Clean up LibraryUpdateService a bit 2021-02-09 17:44:22 -05:00
545bc0e605 Open manga when clicking thumbnail in migration list (closes #4152) 2021-02-08 17:47:44 -05:00
291168f4de Remove unnecessary LayoutContainer implementations 2021-02-08 17:45:42 -05:00
9facb51f22 Add action to directly share crash log file from notification 2021-02-07 23:05:13 -05:00
5b7d8c5e37 Show locales in list of sources to migrate 2021-02-07 22:54:13 -05:00
5945937e4b Update AboutLibraries 2021-02-07 22:51:23 -05:00
9f9f9872eb Fix legacy backups
(cherry picked from commit ded58541f5903c109b70799683829e26018d2af6)
2021-02-07 22:33:07 -05:00
3566072f4a Revert attempt to programmatically determine user agent string; fallback to Edge 2021-02-07 17:54:28 -05:00
b85cd86b24 Add Esperanto locale 2021-02-07 16:55:44 -05:00
79c3767fff Chapter backup optimization
From fc6d9aaf51 (diff-9872ccc3c9af14d2872ec99199409e60a11cb754ab23e733b1d45843778f7c95R24)
2021-02-07 16:20:07 -05:00
cf1609a429 Massage user agent string from WebView a bit more 2021-02-07 16:19:13 -05:00
3aeac7e7b5 Fix selected tab in sheets not being the accent color 2021-02-07 10:54:35 -05:00
1557f713f4 Don't restrict filter sheet height anymore 2021-02-07 10:49:08 -05:00
b63d24ac1a Add Right and Left navigation (#4392)
and remove default navigation classes in favor of the navigation classes
2021-02-06 23:26:56 -05:00
348c1ff29d Avoid some unnecessary re-renderings of download icons 2021-02-06 23:25:39 -05:00
717e55497f Fix downloads getting deleted when marked as unread 2021-02-06 22:48:06 -05:00
d84b5e8b46 Show help action when source fails to load 2021-02-06 13:09:56 -05:00
5f9ddf9ff5 Use AndroidX version of ContextThemeWrapper 2021-02-06 12:51:40 -05:00
bbee093c63 Remove some logic around old legacy backup versions + minor optimizations 2021-02-06 12:15:34 -05:00
e8c35ae4e1 Do a regular return to cancel update jobs instead of throwing an exception 2021-02-06 12:14:55 -05:00
1607658c30 Set clip data when sharing content URIs (closes #4198) 2021-02-06 09:43:33 -05:00
2e9ef373f3 Minor optimizations for restoring full backups
Based on fc6d9aaf51
2021-02-06 09:32:00 -05:00
ec6eef6d37 Switch back to new image decoder for preview builds 2021-02-06 09:31:18 -05:00
119 changed files with 851 additions and 560 deletions

View File

@ -2,7 +2,7 @@
I acknowledge that: I acknowledge that:
- I have updated to the latest version of the app (stable is v0.10.8) - I have updated to the latest version of the app (stable is v0.10.9)
- I have updated all extensions - I have updated all extensions
- If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-extensions - If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-extensions

View File

@ -9,7 +9,7 @@ labels: "bug"
I acknowledge that: I acknowledge that:
- I have updated to the latest version of the app (stable is v0.10.8) - I have updated to the latest version of the app (stable is v0.10.9)
- I have updated all extensions - I have updated all extensions
- If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-extensions - If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-extensions

View File

@ -9,7 +9,7 @@ labels: "feature"
I acknowledge that: I acknowledge that:
- I have updated to the latest version of the app (stable is v0.10.8) - I have updated to the latest version of the app (stable is v0.10.9)
- I have updated all extensions - I have updated all extensions
- If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-extensions - If this is an issue with an extension, that I should be opening an issue in https://github.com/tachiyomiorg/tachiyomi-extensions

View File

@ -9,7 +9,6 @@ plugins {
id("com.mikepenz.aboutlibraries.plugin") id("com.mikepenz.aboutlibraries.plugin")
kotlin("android") kotlin("android")
kotlin("kapt") kotlin("kapt")
kotlin("plugin.parcelize")
kotlin("plugin.serialization") kotlin("plugin.serialization")
id("com.github.zellius.shortcut-helper") id("com.github.zellius.shortcut-helper")
} }
@ -30,8 +29,8 @@ android {
minSdkVersion(AndroidConfig.minSdk) minSdkVersion(AndroidConfig.minSdk)
targetSdkVersion(AndroidConfig.targetSdk) targetSdkVersion(AndroidConfig.targetSdk)
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
versionCode = 55 versionCode = 56
versionName = "0.10.8" versionName = "0.10.9"
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"") buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")

View File

@ -80,6 +80,13 @@ abstract class AbstractBackupManager(protected val context: Context) {
databaseHelper.updateChaptersBackup(chapters).executeAsBlocking() databaseHelper.updateChaptersBackup(chapters).executeAsBlocking()
} }
/**
* Updates a list of chapters with known database ids
*/
protected fun updateKnownChapters(chapters: List<Chapter>) {
databaseHelper.updateKnownChaptersBackup(chapters).executeAsBlocking()
}
/** /**
* Return number of backups. * Return number of backups.
* *

View File

@ -37,9 +37,9 @@ abstract class AbstractBackupRestore<T : AbstractBackupManager>(protected val co
protected val errors = mutableListOf<Pair<Date, String>>() protected val errors = mutableListOf<Pair<Date, String>>()
abstract fun performRestore(uri: Uri): Boolean abstract suspend fun performRestore(uri: Uri): Boolean
fun restoreBackup(uri: Uri): Boolean { suspend fun restoreBackup(uri: Uri): Boolean {
val startTime = System.currentTimeMillis() val startTime = System.currentTimeMillis()
restoreProgress = 0 restoreProgress = 0
errors.clear() errors.clear()

View File

@ -111,7 +111,7 @@ class BackupCreateService : Service() {
val backupFileUri = backupManager.createBackup(uri, backupFlags, false)?.toUri() val backupFileUri = backupManager.createBackup(uri, backupFlags, false)?.toUri()
val unifile = UniFile.fromUri(this, backupFileUri) val unifile = UniFile.fromUri(this, backupFileUri)
notifier.showBackupComplete(unifile) notifier.showBackupComplete(unifile, backupType == BackupConst.BACKUP_TYPE_LEGACY)
} catch (e: Exception) { } catch (e: Exception) {
notifier.showBackupError(e.message) notifier.showBackupError(e.message)
} }

View File

@ -60,7 +60,7 @@ class BackupNotifier(private val context: Context) {
} }
} }
fun showBackupComplete(unifile: UniFile) { fun showBackupComplete(unifile: UniFile, isLegacyFormat: Boolean) {
context.notificationManager.cancel(Notifications.ID_BACKUP_PROGRESS) context.notificationManager.cancel(Notifications.ID_BACKUP_PROGRESS)
with(completeNotificationBuilder) { with(completeNotificationBuilder) {
@ -73,7 +73,7 @@ class BackupNotifier(private val context: Context) {
addAction( addAction(
R.drawable.ic_share_24dp, R.drawable.ic_share_24dp,
context.getString(R.string.action_share), context.getString(R.string.action_share),
NotificationReceiver.shareBackupPendingBroadcast(context, unifile.uri, Notifications.ID_BACKUP_COMPLETE) NotificationReceiver.shareBackupPendingBroadcast(context, unifile.uri, isLegacyFormat, Notifications.ID_BACKUP_COMPLETE)
) )
show(Notifications.ID_BACKUP_COMPLETE) show(Notifications.ID_BACKUP_COMPLETE)

View File

@ -14,7 +14,10 @@ import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.util.system.acquireWakeLock import eu.kanade.tachiyomi.util.system.acquireWakeLock
import eu.kanade.tachiyomi.util.system.isServiceRunning import eu.kanade.tachiyomi.util.system.isServiceRunning
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
@ -68,12 +71,14 @@ class BackupRestoreService : Service() {
*/ */
private lateinit var wakeLock: PowerManager.WakeLock private lateinit var wakeLock: PowerManager.WakeLock
private lateinit var ioScope: CoroutineScope
private var backupRestore: AbstractBackupRestore<*>? = null private var backupRestore: AbstractBackupRestore<*>? = null
private lateinit var notifier: BackupNotifier private lateinit var notifier: BackupNotifier
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
ioScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
notifier = BackupNotifier(this) notifier = BackupNotifier(this)
wakeLock = acquireWakeLock(javaClass.name) wakeLock = acquireWakeLock(javaClass.name)
@ -92,6 +97,7 @@ class BackupRestoreService : Service() {
private fun destroyJob() { private fun destroyJob() {
backupRestore?.job?.cancel() backupRestore?.job?.cancel()
ioScope?.cancel()
if (wakeLock.isHeld) { if (wakeLock.isHeld) {
wakeLock.release() wakeLock.release()
} }
@ -122,6 +128,7 @@ class BackupRestoreService : Service() {
BackupConst.BACKUP_TYPE_FULL -> FullBackupRestore(this, notifier, online) BackupConst.BACKUP_TYPE_FULL -> FullBackupRestore(this, notifier, online)
else -> LegacyBackupRestore(this, notifier) else -> LegacyBackupRestore(this, notifier)
} }
val handler = CoroutineExceptionHandler { _, exception -> val handler = CoroutineExceptionHandler { _, exception ->
Timber.e(exception) Timber.e(exception)
backupRestore?.writeErrorLog() backupRestore?.writeErrorLog()
@ -129,14 +136,15 @@ class BackupRestoreService : Service() {
notifier.showRestoreError(exception.message) notifier.showRestoreError(exception.message)
stopSelf(startId) stopSelf(startId)
} }
backupRestore?.job = GlobalScope.launch(handler) { val job = ioScope.launch(handler) {
if (backupRestore?.restoreBackup(uri) == false) { if (backupRestore?.restoreBackup(uri) == false) {
notifier.showRestoreError(getString(R.string.restoring_backup_canceled)) notifier.showRestoreError(getString(R.string.restoring_backup_canceled))
} }
} }
backupRestore?.job?.invokeOnCompletion { job.invokeOnCompletion {
stopSelf(startId) stopSelf(startId)
} }
backupRestore?.job = job
return START_NOT_STICKY return START_NOT_STICKY
} }

View File

@ -29,7 +29,6 @@ import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.database.models.toMangaInfo import eu.kanade.tachiyomi.data.database.models.toMangaInfo
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.toSManga import eu.kanade.tachiyomi.source.model.toSManga
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.protobuf.ProtoBuf import kotlinx.serialization.protobuf.ProtoBuf
import okio.buffer import okio.buffer
import okio.gzip import okio.gzip
@ -37,7 +36,6 @@ import okio.sink
import timber.log.Timber import timber.log.Timber
import kotlin.math.max import kotlin.math.max
@OptIn(ExperimentalSerializationApi::class)
class FullBackupManager(context: Context) : AbstractBackupManager(context) { class FullBackupManager(context: Context) : AbstractBackupManager(context) {
val parser = ProtoBuf val parser = ProtoBuf
@ -247,7 +245,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
*/ */
internal fun restoreCategoriesForManga(manga: Manga, categories: List<Int>, backupCategories: List<BackupCategory>) { internal fun restoreCategoriesForManga(manga: Manga, categories: List<Int>, backupCategories: List<BackupCategory>) {
val dbCategories = databaseHelper.getCategories().executeAsBlocking() val dbCategories = databaseHelper.getCategories().executeAsBlocking()
val mangaCategoriesToUpdate = mutableListOf<MangaCategory>() val mangaCategoriesToUpdate = ArrayList<MangaCategory>(categories.size)
categories.forEach { backupCategoryOrder -> categories.forEach { backupCategoryOrder ->
backupCategories.firstOrNull { backupCategories.firstOrNull {
it.order == backupCategoryOrder it.order == backupCategoryOrder
@ -274,7 +272,7 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
*/ */
internal fun restoreHistoryForManga(history: List<BackupHistory>) { internal fun restoreHistoryForManga(history: List<BackupHistory>) {
// List containing history to be updated // List containing history to be updated
val historyToBeUpdated = mutableListOf<History>() val historyToBeUpdated = ArrayList<History>(history.size)
for ((url, lastRead) in history) { for ((url, lastRead) in history) {
val dbHistory = databaseHelper.getHistoryByChapterUrl(url).executeAsBlocking() val dbHistory = databaseHelper.getHistoryByChapterUrl(url).executeAsBlocking()
// Check if history already in database and update // Check if history already in database and update
@ -358,9 +356,8 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
} }
chapters.forEach { chapter -> chapters.forEach { chapter ->
val pos = dbChapters.indexOfFirst { it.url == chapter.url } val dbChapter = dbChapters.find { it.url == chapter.url }
if (pos != -1) { if (dbChapter != null) {
val dbChapter = dbChapters[pos]
chapter.id = dbChapter.id chapter.id = dbChapter.id
chapter.copyFrom(dbChapter) chapter.copyFrom(dbChapter)
if (dbChapter.read && !chapter.read) { if (dbChapter.read && !chapter.read) {
@ -373,12 +370,13 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
chapter.bookmark = dbChapter.bookmark chapter.bookmark = dbChapter.bookmark
} }
} }
}
// Filter the chapters that couldn't be found.
chapters.filter { it.id != null }
chapters.map { it.manga_id = manga.id }
updateChapters(chapters) chapter.manga_id = manga.id
}
// Filter the chapters that couldn't be found.
updateChapters(chapters.filter { it.id != null })
return true return true
} }
@ -386,9 +384,8 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
val dbChapters = databaseHelper.getChapters(manga).executeAsBlocking() val dbChapters = databaseHelper.getChapters(manga).executeAsBlocking()
chapters.forEach { chapter -> chapters.forEach { chapter ->
val pos = dbChapters.indexOfFirst { it.url == chapter.url } val dbChapter = dbChapters.find { it.url == chapter.url }
if (pos != -1) { if (dbChapter != null) {
val dbChapter = dbChapters[pos]
chapter.id = dbChapter.id chapter.id = dbChapter.id
chapter.copyFrom(dbChapter) chapter.copyFrom(dbChapter)
if (dbChapter.read && !chapter.read) { if (dbChapter.read && !chapter.read) {
@ -401,10 +398,12 @@ class FullBackupManager(context: Context) : AbstractBackupManager(context) {
chapter.bookmark = dbChapter.bookmark chapter.bookmark = dbChapter.bookmark
} }
} }
}
chapters.map { it.manga_id = manga.id }
updateChapters(chapters.filter { it.id != null }) chapter.manga_id = manga.id
insertChapters(chapters.filter { it.id == null }) }
val newChapters = chapters.groupBy { it.id != null }
newChapters[true]?.let { updateKnownChapters(it) }
newChapters[false]?.let { insertChapters(it) }
} }
} }

View File

@ -13,17 +13,14 @@ import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.util.lang.launchIO
import kotlinx.serialization.ExperimentalSerializationApi
import okio.buffer import okio.buffer
import okio.gzip import okio.gzip
import okio.source import okio.source
import java.util.Date import java.util.Date
@OptIn(ExperimentalSerializationApi::class)
class FullBackupRestore(context: Context, notifier: BackupNotifier, private val online: Boolean) : AbstractBackupRestore<FullBackupManager>(context, notifier) { class FullBackupRestore(context: Context, notifier: BackupNotifier, private val online: Boolean) : AbstractBackupRestore<FullBackupManager>(context, notifier) {
override fun performRestore(uri: Uri): Boolean { override suspend fun performRestore(uri: Uri): Boolean {
backupManager = FullBackupManager(context) backupManager = FullBackupManager(context)
val backupString = context.contentResolver.openInputStream(uri)!!.source().gzip().buffer().use { it.readByteArray() } val backupString = context.contentResolver.openInputStream(uri)!!.source().gzip().buffer().use { it.readByteArray() }
@ -60,7 +57,7 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.categories)) showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.categories))
} }
private fun restoreManga(backupManga: BackupManga, backupCategories: List<BackupCategory>, online: Boolean) { private suspend fun restoreManga(backupManga: BackupManga, backupCategories: List<BackupCategory>, online: Boolean) {
val manga = backupManga.getMangaImpl() val manga = backupManga.getMangaImpl()
val chapters = backupManga.getChaptersImpl() val chapters = backupManga.getChaptersImpl()
val categories = backupManga.categories val categories = backupManga.categories
@ -94,7 +91,7 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
* @param history history data from json * @param history history data from json
* @param tracks tracking data from json * @param tracks tracking data from json
*/ */
private fun restoreMangaData( private suspend fun restoreMangaData(
manga: Manga, manga: Manga,
source: Source?, source: Source?,
chapters: List<Chapter>, chapters: List<Chapter>,
@ -126,7 +123,7 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
* @param chapters chapters of manga that needs updating * @param chapters chapters of manga that needs updating
* @param categories categories that need updating * @param categories categories that need updating
*/ */
private fun restoreMangaFetch( private suspend fun restoreMangaFetch(
source: Source?, source: Source?,
manga: Manga, manga: Manga,
chapters: List<Chapter>, chapters: List<Chapter>,
@ -136,27 +133,25 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
backupCategories: List<BackupCategory>, backupCategories: List<BackupCategory>,
online: Boolean online: Boolean
) { ) {
launchIO { try {
try { val fetchedManga = backupManager.restoreMangaFetch(source, manga, online)
val fetchedManga = backupManager.restoreMangaFetch(source, manga, online) fetchedManga.id ?: return
fetchedManga.id ?: (return@launchIO)
if (online && source != null) { if (online && source != null) {
updateChapters(source, fetchedManga, chapters) updateChapters(source, fetchedManga, chapters)
} else { } else {
backupManager.restoreChaptersForMangaOffline(fetchedManga, chapters) backupManager.restoreChaptersForMangaOffline(fetchedManga, chapters)
}
restoreExtraForManga(fetchedManga, categories, history, tracks, backupCategories)
updateTracking(fetchedManga, tracks)
} catch (e: Exception) {
errors.add(Date() to "${manga.title} - ${e.message}")
} }
restoreExtraForManga(fetchedManga, categories, history, tracks, backupCategories)
updateTracking(fetchedManga, tracks)
} catch (e: Exception) {
errors.add(Date() to "${manga.title} - ${e.message}")
} }
} }
private fun restoreMangaNoFetch( private suspend fun restoreMangaNoFetch(
source: Source?, source: Source?,
backupManga: Manga, backupManga: Manga,
chapters: List<Chapter>, chapters: List<Chapter>,
@ -166,19 +161,17 @@ class FullBackupRestore(context: Context, notifier: BackupNotifier, private val
backupCategories: List<BackupCategory>, backupCategories: List<BackupCategory>,
online: Boolean online: Boolean
) { ) {
launchIO { if (online && source != null) {
if (online && source != null) { if (!backupManager.restoreChaptersForManga(backupManga, chapters)) {
if (!backupManager.restoreChaptersForManga(backupManga, chapters)) { updateChapters(source, backupManga, chapters)
updateChapters(source, backupManga, chapters)
}
} else {
backupManager.restoreChaptersForMangaOffline(backupManga, chapters)
} }
} else {
restoreExtraForManga(backupManga, categories, history, tracks, backupCategories) backupManager.restoreChaptersForMangaOffline(backupManga, chapters)
updateTracking(backupManga, tracks)
} }
restoreExtraForManga(backupManga, categories, history, tracks, backupCategories)
updateTracking(backupManga, tracks)
} }
private fun restoreExtraForManga(manga: Manga, categories: List<Int>, history: List<BackupHistory>, tracks: List<Track>, backupCategories: List<BackupCategory>) { private fun restoreExtraForManga(manga: Manga, categories: List<Int>, history: List<BackupHistory>, tracks: List<Track>, backupCategories: List<BackupCategory>) {

View File

@ -5,12 +5,10 @@ import android.net.Uri
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.backup.AbstractBackupRestoreValidator import eu.kanade.tachiyomi.data.backup.AbstractBackupRestoreValidator
import eu.kanade.tachiyomi.data.backup.full.models.BackupSerializer import eu.kanade.tachiyomi.data.backup.full.models.BackupSerializer
import kotlinx.serialization.ExperimentalSerializationApi
import okio.buffer import okio.buffer
import okio.gzip import okio.gzip
import okio.source import okio.source
@OptIn(ExperimentalSerializationApi::class)
class FullBackupRestoreValidator : AbstractBackupRestoreValidator() { class FullBackupRestoreValidator : AbstractBackupRestoreValidator() {
/** /**
* Checks for critical backup file data. * Checks for critical backup file data.

View File

@ -53,30 +53,14 @@ import kotlin.math.max
class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : AbstractBackupManager(context) { class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : AbstractBackupManager(context) {
var parserVersion: Int = version val parser: Gson = when (version) {
private set 2 -> GsonBuilder()
.registerTypeAdapter<MangaImpl>(MangaTypeAdapter.build())
var parser: Gson = initParser() .registerTypeHierarchyAdapter<ChapterImpl>(ChapterTypeAdapter.build())
.registerTypeAdapter<CategoryImpl>(CategoryTypeAdapter.build())
/** .registerTypeAdapter<DHistory>(HistoryTypeAdapter.build())
* Set version of parser .registerTypeHierarchyAdapter<TrackImpl>(TrackTypeAdapter.build())
* .create()
* @param version version of parser
*/
internal fun setVersion(version: Int) {
this.parserVersion = version
parser = initParser()
}
private fun initParser(): Gson = when (parserVersion) {
2 ->
GsonBuilder()
.registerTypeAdapter<MangaImpl>(MangaTypeAdapter.build())
.registerTypeHierarchyAdapter<ChapterImpl>(ChapterTypeAdapter.build())
.registerTypeAdapter<CategoryImpl>(CategoryTypeAdapter.build())
.registerTypeAdapter<DHistory>(HistoryTypeAdapter.build())
.registerTypeHierarchyAdapter<TrackImpl>(TrackTypeAdapter.build())
.create()
else -> throw Exception("Unknown backup version") else -> throw Exception("Unknown backup version")
} }
@ -308,7 +292,7 @@ class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : Ab
*/ */
internal fun restoreCategoriesForManga(manga: Manga, categories: List<String>) { internal fun restoreCategoriesForManga(manga: Manga, categories: List<String>) {
val dbCategories = databaseHelper.getCategories().executeAsBlocking() val dbCategories = databaseHelper.getCategories().executeAsBlocking()
val mangaCategoriesToUpdate = mutableListOf<MangaCategory>() val mangaCategoriesToUpdate = ArrayList<MangaCategory>(categories.size)
for (backupCategoryStr in categories) { for (backupCategoryStr in categories) {
for (dbCategory in dbCategories) { for (dbCategory in dbCategories) {
if (backupCategoryStr == dbCategory.name) { if (backupCategoryStr == dbCategory.name) {
@ -332,7 +316,7 @@ class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : Ab
*/ */
internal fun restoreHistoryForManga(history: List<DHistory>) { internal fun restoreHistoryForManga(history: List<DHistory>) {
// List containing history to be updated // List containing history to be updated
val historyToBeUpdated = mutableListOf<History>() val historyToBeUpdated = ArrayList<History>(history.size)
for ((url, lastRead) in history) { for ((url, lastRead) in history) {
val dbHistory = databaseHelper.getHistoryByChapterUrl(url).executeAsBlocking() val dbHistory = databaseHelper.getHistoryByChapterUrl(url).executeAsBlocking()
// Check if history already in database and update // Check if history already in database and update
@ -361,14 +345,14 @@ class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : Ab
* @param tracks the track list to restore. * @param tracks the track list to restore.
*/ */
internal fun restoreTrackForManga(manga: Manga, tracks: List<Track>) { internal fun restoreTrackForManga(manga: Manga, tracks: List<Track>) {
// Fix foreign keys with the current manga id
tracks.map { it.manga_id = manga.id!! }
// Get tracks from database // Get tracks from database
val dbTracks = databaseHelper.getTracks(manga).executeAsBlocking() val dbTracks = databaseHelper.getTracks(manga).executeAsBlocking()
val trackToUpdate = mutableListOf<Track>() val trackToUpdate = ArrayList<Track>(tracks.size)
tracks.forEach { track -> tracks.forEach { track ->
// Fix foreign keys with the current manga id
track.manga_id = manga.id!!
val service = trackManager.getService(track.sync_id) val service = trackManager.getService(track.sync_id)
if (service != null && service.isLogged) { if (service != null && service.isLogged) {
var isInDatabase = false var isInDatabase = false
@ -423,12 +407,13 @@ class LegacyBackupManager(context: Context, version: Int = CURRENT_VERSION) : Ab
chapter.copyFrom(dbChapter) chapter.copyFrom(dbChapter)
break break
} }
}
// Filter the chapters that couldn't be found.
chapters.filter { it.id != null }
chapters.map { it.manga_id = manga.id }
updateChapters(chapters) chapter.manga_id = manga.id
}
// Filter the chapters that couldn't be found.
updateChapters(chapters.filter { it.id != null })
return true return true
} }
} }

View File

@ -21,12 +21,11 @@ import eu.kanade.tachiyomi.data.database.models.MangaImpl
import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.database.models.TrackImpl import eu.kanade.tachiyomi.data.database.models.TrackImpl
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.util.lang.launchIO
import java.util.Date import java.util.Date
class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBackupRestore<LegacyBackupManager>(context, notifier) { class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : AbstractBackupRestore<LegacyBackupManager>(context, notifier) {
override fun performRestore(uri: Uri): Boolean { override suspend fun performRestore(uri: Uri): Boolean {
val reader = JsonReader(context.contentResolver.openInputStream(uri)!!.bufferedReader()) val reader = JsonReader(context.contentResolver.openInputStream(uri)!!.bufferedReader())
val json = JsonParser.parseReader(reader).asJsonObject val json = JsonParser.parseReader(reader).asJsonObject
@ -63,7 +62,7 @@ class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : Abstract
showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.categories)) showRestoreProgress(restoreProgress, restoreAmount, context.getString(R.string.categories))
} }
private fun restoreManga(mangaJson: JsonObject) { private suspend fun restoreManga(mangaJson: JsonObject) {
val manga = backupManager.parser.fromJson<MangaImpl>( val manga = backupManager.parser.fromJson<MangaImpl>(
mangaJson.get( mangaJson.get(
Backup.MANGA Backup.MANGA
@ -113,7 +112,7 @@ class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : Abstract
* @param history history data from json * @param history history data from json
* @param tracks tracking data from json * @param tracks tracking data from json
*/ */
private fun restoreMangaData( private suspend fun restoreMangaData(
manga: Manga, manga: Manga,
source: Source, source: Source,
chapters: List<Chapter>, chapters: List<Chapter>,
@ -143,7 +142,7 @@ class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : Abstract
* @param chapters chapters of manga that needs updating * @param chapters chapters of manga that needs updating
* @param categories categories that need updating * @param categories categories that need updating
*/ */
private fun restoreMangaFetch( private suspend fun restoreMangaFetch(
source: Source, source: Source,
manga: Manga, manga: Manga,
chapters: List<Chapter>, chapters: List<Chapter>,
@ -151,23 +150,21 @@ class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : Abstract
history: List<DHistory>, history: List<DHistory>,
tracks: List<Track> tracks: List<Track>
) { ) {
launchIO { try {
try { val fetchedManga = backupManager.fetchManga(source, manga)
val fetchedManga = backupManager.fetchManga(source, manga) fetchedManga.id ?: return
fetchedManga.id ?: (return@launchIO)
updateChapters(source, fetchedManga, chapters) updateChapters(source, fetchedManga, chapters)
restoreExtraForManga(fetchedManga, categories, history, tracks) restoreExtraForManga(fetchedManga, categories, history, tracks)
updateTracking(fetchedManga, tracks) updateTracking(fetchedManga, tracks)
} catch (e: Exception) { } catch (e: Exception) {
errors.add(Date() to "${manga.title} - ${e.message}") errors.add(Date() to "${manga.title} - ${e.message}")
}
} }
} }
private fun restoreMangaNoFetch( private suspend fun restoreMangaNoFetch(
source: Source, source: Source,
backupManga: Manga, backupManga: Manga,
chapters: List<Chapter>, chapters: List<Chapter>,
@ -175,15 +172,13 @@ class LegacyBackupRestore(context: Context, notifier: BackupNotifier) : Abstract
history: List<DHistory>, history: List<DHistory>,
tracks: List<Track> tracks: List<Track>
) { ) {
launchIO { if (!backupManager.restoreChaptersForManga(backupManga, chapters)) {
if (!backupManager.restoreChaptersForManga(backupManga, chapters)) { updateChapters(source, backupManga, chapters)
updateChapters(source, backupManga, chapters)
}
restoreExtraForManga(backupManga, categories, history, tracks)
updateTracking(backupManga, tracks)
} }
restoreExtraForManga(backupManga, categories, history, tracks)
updateTracking(backupManga, tracks)
} }
private fun restoreExtraForManga(manga: Manga, categories: List<String>, history: List<DHistory>, tracks: List<Track>) { private fun restoreExtraForManga(manga: Manga, categories: List<String>, history: List<DHistory>, tracks: List<Track>) {

View File

@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaChapter import eu.kanade.tachiyomi.data.database.models.MangaChapter
import eu.kanade.tachiyomi.data.database.resolvers.ChapterBackupPutResolver import eu.kanade.tachiyomi.data.database.resolvers.ChapterBackupPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.ChapterKnownBackupPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.ChapterProgressPutResolver import eu.kanade.tachiyomi.data.database.resolvers.ChapterProgressPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.ChapterSourceOrderPutResolver import eu.kanade.tachiyomi.data.database.resolvers.ChapterSourceOrderPutResolver
import eu.kanade.tachiyomi.data.database.resolvers.MangaChapterGetResolver import eu.kanade.tachiyomi.data.database.resolvers.MangaChapterGetResolver
@ -84,6 +85,11 @@ interface ChapterQueries : DbProvider {
.withPutResolver(ChapterBackupPutResolver()) .withPutResolver(ChapterBackupPutResolver())
.prepare() .prepare()
fun updateKnownChaptersBackup(chapters: List<Chapter>) = db.put()
.objects(chapters)
.withPutResolver(ChapterKnownBackupPutResolver())
.prepare()
fun updateChapterProgress(chapter: Chapter) = db.put() fun updateChapterProgress(chapter: Chapter) = db.put()
.`object`(chapter) .`object`(chapter)
.withPutResolver(ChapterProgressPutResolver()) .withPutResolver(ChapterProgressPutResolver())

View File

@ -0,0 +1,34 @@
package eu.kanade.tachiyomi.data.database.resolvers
import androidx.core.content.contentValuesOf
import com.pushtorefresh.storio.sqlite.StorIOSQLite
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
import eu.kanade.tachiyomi.data.database.inTransactionReturn
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.tables.ChapterTable
class ChapterKnownBackupPutResolver : PutResolver<Chapter>() {
override fun performPut(db: StorIOSQLite, chapter: Chapter) = db.inTransactionReturn {
val updateQuery = mapToUpdateQuery(chapter)
val contentValues = mapToContentValues(chapter)
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
}
fun mapToUpdateQuery(chapter: Chapter) = UpdateQuery.builder()
.table(ChapterTable.TABLE)
.where("${ChapterTable.COL_ID} = ?")
.whereArgs(chapter.id)
.build()
fun mapToContentValues(chapter: Chapter) =
contentValuesOf(
ChapterTable.COL_READ to chapter.read,
ChapterTable.COL_BOOKMARK to chapter.bookmark,
ChapterTable.COL_LAST_PAGE_READ to chapter.last_page_read
)
}

View File

@ -16,8 +16,8 @@ import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.fetchAllImageUrlsFromPageList import eu.kanade.tachiyomi.source.online.fetchAllImageUrlsFromPageList
import eu.kanade.tachiyomi.util.lang.RetryWithDelay import eu.kanade.tachiyomi.util.lang.RetryWithDelay
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchNow import eu.kanade.tachiyomi.util.lang.launchNow
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.lang.plusAssign import eu.kanade.tachiyomi.util.lang.plusAssign
import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.storage.saveTo import eu.kanade.tachiyomi.util.storage.saveTo
@ -228,8 +228,8 @@ class Downloader(
* @param chapters the list of chapters to download. * @param chapters the list of chapters to download.
* @param autoStart whether to start the downloader after enqueing the chapters. * @param autoStart whether to start the downloader after enqueing the chapters.
*/ */
fun queueChapters(manga: Manga, chapters: List<Chapter>, autoStart: Boolean) = launchUI { fun queueChapters(manga: Manga, chapters: List<Chapter>, autoStart: Boolean) = launchIO {
val source = sourceManager.get(manga.source) as? HttpSource ?: return@launchUI val source = sourceManager.get(manga.source) as? HttpSource ?: return@launchIO
val wasEmpty = queue.isEmpty() val wasEmpty = queue.isEmpty()
// Called in background thread, the operation can be slow with SAF. // Called in background thread, the operation can be slow with SAF.
val chaptersWithoutDir = async { val chaptersWithoutDir = async {

View File

@ -33,10 +33,10 @@ import eu.kanade.tachiyomi.util.storage.getUriCompat
import eu.kanade.tachiyomi.util.system.acquireWakeLock import eu.kanade.tachiyomi.util.system.acquireWakeLock
import eu.kanade.tachiyomi.util.system.createFileInCacheDir import eu.kanade.tachiyomi.util.system.createFileInCacheDir
import eu.kanade.tachiyomi.util.system.isServiceRunning import eu.kanade.tachiyomi.util.system.isServiceRunning
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async import kotlinx.coroutines.async
@ -158,8 +158,8 @@ class LibraryUpdateService(
* lock. * lock.
*/ */
override fun onDestroy() { override fun onDestroy() {
ioScope?.cancel()
updateJob?.cancel() updateJob?.cancel()
ioScope?.cancel()
if (wakeLock.isHeld) { if (wakeLock.isHeld) {
wakeLock.release() wakeLock.release()
} }
@ -254,48 +254,35 @@ class LibraryUpdateService(
val failedUpdates = mutableListOf<Pair<Manga, String?>>() val failedUpdates = mutableListOf<Pair<Manga, String?>>()
var hasDownloads = false var hasDownloads = false
mangaToUpdate mangaToUpdate.forEach { manga ->
.map { manga -> if (updateJob?.isActive != true) {
if (updateJob?.isActive != true) { return
throw CancellationException() }
}
// Notify manga that will update. notifier.showProgressNotification(manga, progressCount.andIncrement, mangaToUpdate.size)
notifier.showProgressNotification(manga, progressCount.andIncrement, mangaToUpdate.size)
// Update the chapters of the manga try {
try { val (newChapters, _) = updateManga(manga)
val newChapters = updateManga(manga).first
Pair(manga, newChapters) if (newChapters.isNotEmpty()) {
} catch (e: Throwable) { if (manga.shouldDownloadNewChapters(db, preferences)) {
// If there's any error, return empty update and continue. downloadChapters(manga, newChapters)
val errorMessage = if (e is NoChaptersException) { hasDownloads = true
getString(R.string.no_chapters_error)
} else {
e.message
} }
failedUpdates.add(Pair(manga, errorMessage))
Pair(manga, emptyList())
}
}
// Filter out mangas without new chapters (or failed).
.filter { (_, newChapters) -> newChapters.isNotEmpty() }
.forEach { (manga, newChapters) ->
if (manga.shouldDownloadNewChapters(db, preferences)) {
downloadChapters(manga, newChapters)
hasDownloads = true
}
// Convert to the manga that contains new chapters. // Convert to the manga that contains new chapters
newUpdates.add( newUpdates.add(manga to newChapters.sortedByDescending { ch -> ch.source_order }.toTypedArray())
Pair( }
manga, } catch (e: Throwable) {
newChapters.sortedByDescending { ch -> ch.source_order }.toTypedArray() val errorMessage = if (e is NoChaptersException) {
) getString(R.string.no_chapters_error)
) } else {
e.message
}
failedUpdates.add(manga to errorMessage)
} }
}
// Notify result of the overall update.
notifier.cancelProgressNotification() notifier.cancelProgressNotification()
if (newUpdates.isNotEmpty()) { if (newUpdates.isNotEmpty()) {
@ -334,7 +321,7 @@ class LibraryUpdateService(
val handler = CoroutineExceptionHandler { _, exception -> val handler = CoroutineExceptionHandler { _, exception ->
Timber.e(exception) Timber.e(exception)
} }
ioScope.launch(handler) { GlobalScope.launch(Dispatchers.IO + handler) {
val updatedManga = source.getMangaDetails(manga.toMangaInfo()) val updatedManga = source.getMangaDetails(manga.toMangaInfo())
val sManga = updatedManga.toSManga() val sManga = updatedManga.toSManga()
// Avoid "losing" existing cover // Avoid "losing" existing cover
@ -360,7 +347,7 @@ class LibraryUpdateService(
mangaToUpdate.forEach { manga -> mangaToUpdate.forEach { manga ->
if (updateJob?.isActive != true) { if (updateJob?.isActive != true) {
throw CancellationException() return
} }
notifier.showProgressNotification(manga, progressCount++, mangaToUpdate.size) notifier.showProgressNotification(manga, progressCount++, mangaToUpdate.size)
@ -394,7 +381,7 @@ class LibraryUpdateService(
mangaToUpdate.forEach { manga -> mangaToUpdate.forEach { manga ->
if (updateJob?.isActive != true) { if (updateJob?.isActive != true) {
throw CancellationException() return
} }
// Notify manga that will update. // Notify manga that will update.

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.notification
import android.app.PendingIntent import android.app.PendingIntent
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.ClipData
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
@ -69,9 +70,10 @@ class NotificationReceiver : BroadcastReceiver() {
) )
// Share backup file // Share backup file
ACTION_SHARE_BACKUP -> ACTION_SHARE_BACKUP ->
shareBackup( shareFile(
context, context,
intent.getParcelableExtra(EXTRA_URI), intent.getParcelableExtra(EXTRA_URI),
if (intent.getBooleanExtra(EXTRA_IS_LEGACY_BACKUP, false)) "application/json" else "application/octet-stream+gzip",
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1) intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)
) )
ACTION_CANCEL_RESTORE -> cancelRestore( ACTION_CANCEL_RESTORE -> cancelRestore(
@ -100,6 +102,14 @@ class NotificationReceiver : BroadcastReceiver() {
markAsRead(urls, mangaId) markAsRead(urls, mangaId)
} }
} }
// Share crash dump file
ACTION_SHARE_CRASH_LOG ->
shareFile(
context,
intent.getParcelableExtra(EXTRA_URI),
"text/plain",
intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1)
)
} }
} }
@ -120,14 +130,13 @@ class NotificationReceiver : BroadcastReceiver() {
* @param notificationId id of notification * @param notificationId id of notification
*/ */
private fun shareImage(context: Context, path: String, notificationId: Int) { private fun shareImage(context: Context, path: String, notificationId: Int) {
// Create intent
val intent = Intent(Intent.ACTION_SEND).apply { val intent = Intent(Intent.ACTION_SEND).apply {
val uri = File(path).getUriCompat(context) val uri = File(path).getUriCompat(context)
putExtra(Intent.EXTRA_STREAM, uri) putExtra(Intent.EXTRA_STREAM, uri)
clipData = ClipData.newRawUri(null, uri)
type = "image/*" type = "image/*"
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
} }
// Dismiss notification
dismissNotification(context, notificationId) dismissNotification(context, notificationId)
// Launch share activity // Launch share activity
context.startActivity(intent) context.startActivity(intent)
@ -140,10 +149,11 @@ class NotificationReceiver : BroadcastReceiver() {
* @param path path of file * @param path path of file
* @param notificationId id of notification * @param notificationId id of notification
*/ */
private fun shareBackup(context: Context, uri: Uri, notificationId: Int) { private fun shareFile(context: Context, uri: Uri, fileMimeType: String, notificationId: Int) {
val sendIntent = Intent(Intent.ACTION_SEND).apply { val sendIntent = Intent(Intent.ACTION_SEND).apply {
putExtra(Intent.EXTRA_STREAM, uri) putExtra(Intent.EXTRA_STREAM, uri)
type = "application/json" clipData = ClipData.newRawUri(null, uri)
type = fileMimeType
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
} }
// Dismiss notification // Dismiss notification
@ -244,59 +254,34 @@ class NotificationReceiver : BroadcastReceiver() {
companion object { companion object {
private const val NAME = "NotificationReceiver" private const val NAME = "NotificationReceiver"
// Called to launch share intent.
private const val ACTION_SHARE_IMAGE = "$ID.$NAME.SHARE_IMAGE" private const val ACTION_SHARE_IMAGE = "$ID.$NAME.SHARE_IMAGE"
// Called to delete image.
private const val ACTION_DELETE_IMAGE = "$ID.$NAME.DELETE_IMAGE" private const val ACTION_DELETE_IMAGE = "$ID.$NAME.DELETE_IMAGE"
// Called to launch send intent.
private const val ACTION_SHARE_BACKUP = "$ID.$NAME.SEND_BACKUP" private const val ACTION_SHARE_BACKUP = "$ID.$NAME.SEND_BACKUP"
// Called to cancel backup restore job. private const val ACTION_SHARE_CRASH_LOG = "$ID.$NAME.SEND_CRASH_LOG"
private const val ACTION_CANCEL_RESTORE = "$ID.$NAME.CANCEL_RESTORE" private const val ACTION_CANCEL_RESTORE = "$ID.$NAME.CANCEL_RESTORE"
// Called to cancel library update.
private const val ACTION_CANCEL_LIBRARY_UPDATE = "$ID.$NAME.CANCEL_LIBRARY_UPDATE" private const val ACTION_CANCEL_LIBRARY_UPDATE = "$ID.$NAME.CANCEL_LIBRARY_UPDATE"
// Called to mark manga chapters as read.
private const val ACTION_MARK_AS_READ = "$ID.$NAME.MARK_AS_READ" private const val ACTION_MARK_AS_READ = "$ID.$NAME.MARK_AS_READ"
// Called to open chapter.
private const val ACTION_OPEN_CHAPTER = "$ID.$NAME.ACTION_OPEN_CHAPTER" private const val ACTION_OPEN_CHAPTER = "$ID.$NAME.ACTION_OPEN_CHAPTER"
// Value containing file location.
private const val EXTRA_FILE_LOCATION = "$ID.$NAME.FILE_LOCATION"
// Called to resume downloads.
private const val ACTION_RESUME_DOWNLOADS = "$ID.$NAME.ACTION_RESUME_DOWNLOADS" private const val ACTION_RESUME_DOWNLOADS = "$ID.$NAME.ACTION_RESUME_DOWNLOADS"
// Called to pause downloads.
private const val ACTION_PAUSE_DOWNLOADS = "$ID.$NAME.ACTION_PAUSE_DOWNLOADS" private const val ACTION_PAUSE_DOWNLOADS = "$ID.$NAME.ACTION_PAUSE_DOWNLOADS"
// Called to clear downloads.
private const val ACTION_CLEAR_DOWNLOADS = "$ID.$NAME.ACTION_CLEAR_DOWNLOADS" private const val ACTION_CLEAR_DOWNLOADS = "$ID.$NAME.ACTION_CLEAR_DOWNLOADS"
// Called to dismiss notification.
private const val ACTION_DISMISS_NOTIFICATION = "$ID.$NAME.ACTION_DISMISS_NOTIFICATION" private const val ACTION_DISMISS_NOTIFICATION = "$ID.$NAME.ACTION_DISMISS_NOTIFICATION"
// Value containing uri. private const val EXTRA_FILE_LOCATION = "$ID.$NAME.FILE_LOCATION"
private const val EXTRA_URI = "$ID.$NAME.URI" private const val EXTRA_URI = "$ID.$NAME.URI"
// Value containing notification id.
private const val EXTRA_NOTIFICATION_ID = "$ID.$NAME.NOTIFICATION_ID" private const val EXTRA_NOTIFICATION_ID = "$ID.$NAME.NOTIFICATION_ID"
// Value containing group id.
private const val EXTRA_GROUP_ID = "$ID.$NAME.EXTRA_GROUP_ID" private const val EXTRA_GROUP_ID = "$ID.$NAME.EXTRA_GROUP_ID"
// Value containing manga id.
private const val EXTRA_MANGA_ID = "$ID.$NAME.EXTRA_MANGA_ID" private const val EXTRA_MANGA_ID = "$ID.$NAME.EXTRA_MANGA_ID"
// Value containing chapter id.
private const val EXTRA_CHAPTER_ID = "$ID.$NAME.EXTRA_CHAPTER_ID" private const val EXTRA_CHAPTER_ID = "$ID.$NAME.EXTRA_CHAPTER_ID"
// Value containing chapter url.
private const val EXTRA_CHAPTER_URL = "$ID.$NAME.EXTRA_CHAPTER_URL" private const val EXTRA_CHAPTER_URL = "$ID.$NAME.EXTRA_CHAPTER_URL"
private const val EXTRA_IS_LEGACY_BACKUP = "$ID.$NAME.EXTRA_IS_LEGACY_BACKUP"
/** /**
* Returns a [PendingIntent] that resumes the download of a chapter * Returns a [PendingIntent] that resumes the download of a chapter
@ -509,10 +494,11 @@ class NotificationReceiver : BroadcastReceiver() {
* @param notificationId id of notification * @param notificationId id of notification
* @return [PendingIntent] * @return [PendingIntent]
*/ */
internal fun shareBackupPendingBroadcast(context: Context, uri: Uri, notificationId: Int): PendingIntent { internal fun shareBackupPendingBroadcast(context: Context, uri: Uri, isLegacyFormat: Boolean, notificationId: Int): PendingIntent {
val intent = Intent(context, NotificationReceiver::class.java).apply { val intent = Intent(context, NotificationReceiver::class.java).apply {
action = ACTION_SHARE_BACKUP action = ACTION_SHARE_BACKUP
putExtra(EXTRA_URI, uri) putExtra(EXTRA_URI, uri)
putExtra(EXTRA_IS_LEGACY_BACKUP, isLegacyFormat)
putExtra(EXTRA_NOTIFICATION_ID, notificationId) putExtra(EXTRA_NOTIFICATION_ID, notificationId)
} }
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
@ -534,6 +520,23 @@ class NotificationReceiver : BroadcastReceiver() {
return PendingIntent.getActivity(context, 0, intent, 0) return PendingIntent.getActivity(context, 0, intent, 0)
} }
/**
* Returns [PendingIntent] that starts a share activity for a crash log dump file.
*
* @param context context of application
* @param uri uri of file
* @param notificationId id of notification
* @return [PendingIntent]
*/
internal fun shareCrashLogPendingBroadcast(context: Context, uri: Uri, notificationId: Int): PendingIntent {
val intent = Intent(context, NotificationReceiver::class.java).apply {
action = ACTION_SHARE_CRASH_LOG
putExtra(EXTRA_URI, uri)
putExtra(EXTRA_NOTIFICATION_ID, notificationId)
}
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
/** /**
* Returns [PendingIntent] that cancels a backup restore job. * Returns [PendingIntent] that cancels a backup restore job.
* *

View File

@ -23,6 +23,8 @@ object PreferenceKeys {
const val showPageNumber = "pref_show_page_number_key" const val showPageNumber = "pref_show_page_number_key"
const val dualPageSplit = "pref_dual_page_split"
const val showReadingMode = "pref_show_reading_mode" const val showReadingMode = "pref_show_reading_mode"
const val trueColor = "pref_true_color_key" const val trueColor = "pref_true_color_key"

View File

@ -89,6 +89,8 @@ class PreferencesHelper(val context: Context) {
fun showPageNumber() = flowPrefs.getBoolean(Keys.showPageNumber, true) fun showPageNumber() = flowPrefs.getBoolean(Keys.showPageNumber, true)
fun dualPageSplit() = flowPrefs.getBoolean(Keys.dualPageSplit, false)
fun showReadingMode() = prefs.getBoolean(Keys.showReadingMode, true) fun showReadingMode() = prefs.getBoolean(Keys.showReadingMode, true)
fun trueColor() = flowPrefs.getBoolean(Keys.trueColor, false) fun trueColor() = flowPrefs.getBoolean(Keys.trueColor, false)

View File

@ -18,6 +18,7 @@ sealed class Extension {
override val versionCode: Int, override val versionCode: Int,
override val lang: String, override val lang: String,
override val isNsfw: Boolean, override val isNsfw: Boolean,
val pkgFactory: String?,
val sources: List<Source>, val sources: List<Source>,
val hasUpdate: Boolean = false, val hasUpdate: Boolean = false,
val isObsolete: Boolean = false, val isObsolete: Boolean = false,

View File

@ -31,6 +31,7 @@ internal object ExtensionLoader {
private const val EXTENSION_FEATURE = "tachiyomi.extension" private const val EXTENSION_FEATURE = "tachiyomi.extension"
private const val METADATA_SOURCE_CLASS = "tachiyomi.extension.class" private const val METADATA_SOURCE_CLASS = "tachiyomi.extension.class"
private const val METADATA_SOURCE_FACTORY = "tachiyomi.extension.factory"
private const val METADATA_NSFW = "tachiyomi.extension.nsfw" private const val METADATA_NSFW = "tachiyomi.extension.nsfw"
const val LIB_VERSION_MIN = 1.2 const val LIB_VERSION_MIN = 1.2
const val LIB_VERSION_MAX = 1.2 const val LIB_VERSION_MAX = 1.2
@ -184,7 +185,8 @@ internal object ExtensionLoader {
versionCode, versionCode,
lang, lang,
isNsfw, isNsfw,
sources, sources = sources,
pkgFactory = appInfo.metaData.getString(METADATA_SOURCE_FACTORY),
isUnofficial = signatureHash != officialSignature isUnofficial = signatureHash != officialSignature
) )
return LoadResult.Success(extension) return LoadResult.Success(extension)

View File

@ -9,6 +9,7 @@ import android.webkit.WebSettings
import android.webkit.WebView import android.webkit.WebView
import android.widget.Toast import android.widget.Toast
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.WebViewClientCompat import eu.kanade.tachiyomi.util.system.WebViewClientCompat
import eu.kanade.tachiyomi.util.system.WebViewUtil import eu.kanade.tachiyomi.util.system.WebViewUtil
@ -98,7 +99,7 @@ class CloudflareInterceptor(private val context: Context) : Interceptor {
// Avoid sending empty User-Agent, Chromium WebView will reset to default if empty // Avoid sending empty User-Agent, Chromium WebView will reset to default if empty
webview.settings.userAgentString = request.header("User-Agent") webview.settings.userAgentString = request.header("User-Agent")
?: WebViewUtil.DEFAULT_USER_AGENT ?: HttpSource.DEFAULT_USER_AGENT
webview.webViewClient = object : WebViewClientCompat() { webview.webViewClient = object : WebViewClientCompat() {
override fun onPageFinished(view: WebView, url: String) { override fun onPageFinished(view: WebView, url: String) {

View File

@ -1,6 +1,6 @@
package eu.kanade.tachiyomi.network package eu.kanade.tachiyomi.network
import eu.kanade.tachiyomi.util.system.WebViewUtil import eu.kanade.tachiyomi.source.online.HttpSource
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.Response import okhttp3.Response
@ -12,7 +12,7 @@ class UserAgentInterceptor : Interceptor {
val newRequest = originalRequest val newRequest = originalRequest
.newBuilder() .newBuilder()
.removeHeader("User-Agent") .removeHeader("User-Agent")
.addHeader("User-Agent", WebViewUtil.DEFAULT_USER_AGENT) .addHeader("User-Agent", HttpSource.DEFAULT_USER_AGENT)
.build() .build()
chain.proceed(newRequest) chain.proceed(newRequest)
} else { } else {

View File

@ -10,7 +10,6 @@ import eu.kanade.tachiyomi.source.model.MangasPage
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.util.system.WebViewUtil
import okhttp3.Headers import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
@ -75,7 +74,7 @@ abstract class HttpSource : CatalogueSource {
* Headers builder for requests. Implementations can override this method for custom headers. * Headers builder for requests. Implementations can override this method for custom headers.
*/ */
protected open fun headersBuilder() = Headers.Builder().apply { protected open fun headersBuilder() = Headers.Builder().apply {
add("User-Agent", WebViewUtil.DEFAULT_USER_AGENT) add("User-Agent", DEFAULT_USER_AGENT)
} }
/** /**
@ -370,4 +369,8 @@ abstract class HttpSource : CatalogueSource {
* Returns the list of filters for the source. * Returns the list of filters for the source.
*/ */
override fun getFilterList() = FilterList() override fun getFilterList() = FilterList()
companion object {
const val DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36 Edg/88.0.705.63"
}
} }

View File

@ -11,15 +11,13 @@ import com.bluelinelabs.conductor.Controller
import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType import com.bluelinelabs.conductor.ControllerChangeType
import com.bluelinelabs.conductor.RestoreViewOnCreateController import com.bluelinelabs.conductor.RestoreViewOnCreateController
import kotlinx.android.extensions.LayoutContainer
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import timber.log.Timber import timber.log.Timber
abstract class BaseController<VB : ViewBinding>(bundle: Bundle? = null) : abstract class BaseController<VB : ViewBinding>(bundle: Bundle? = null) :
RestoreViewOnCreateController(bundle), RestoreViewOnCreateController(bundle) {
LayoutContainer {
lateinit var binding: VB lateinit var binding: VB
@ -53,9 +51,6 @@ abstract class BaseController<VB : ViewBinding>(bundle: Bundle? = null) :
) )
} }
override val containerView: View?
get() = view
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedViewState: Bundle?): View { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedViewState: Bundle?): View {
return inflateView(inflater, container) return inflateView(inflater, container)
} }

View File

@ -1,11 +0,0 @@
package eu.kanade.tachiyomi.ui.base.holder
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.extensions.LayoutContainer
abstract class BaseViewHolder(view: View) : RecyclerView.ViewHolder(view), LayoutContainer {
override val containerView: View?
get() = itemView
}

View File

@ -208,7 +208,11 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
private fun openCommitHistory() { private fun openCommitHistory() {
val pkgName = presenter.extension!!.pkgName.substringAfter("eu.kanade.tachiyomi.extension.") val pkgName = presenter.extension!!.pkgName.substringAfter("eu.kanade.tachiyomi.extension.")
val url = "https://github.com/tachiyomiorg/tachiyomi-extensions/commits/master/src/${pkgName.replace(".", "/")}" val pkgFactory = presenter.extension!!.pkgFactory
val url = when {
!pkgFactory.isNullOrEmpty() -> "$URL_EXTENSION_COMMITS/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/$pkgFactory"
else -> "$URL_EXTENSION_COMMITS/src/${pkgName.replace(".", "/")}"
}
val intent = Intent(Intent.ACTION_VIEW, url.toUri()) val intent = Intent(Intent.ACTION_VIEW, url.toUri())
startActivity(intent) startActivity(intent)
} }
@ -232,5 +236,7 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
private companion object { private companion object {
const val PKGNAME_KEY = "pkg_name" const val PKGNAME_KEY = "pkg_name"
private const val URL_EXTENSION_COMMITS = "https://github.com/tachiyomiorg/tachiyomi-extensions/commits/master"
} }
} }

View File

@ -4,10 +4,10 @@ import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.util.TypedValue import android.util.TypedValue
import android.view.ContextThemeWrapper
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.preference.DialogPreference import androidx.preference.DialogPreference
import androidx.preference.EditTextPreference import androidx.preference.EditTextPreference

View File

@ -0,0 +1,14 @@
package eu.kanade.tachiyomi.ui.browse.migration.manga
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible
class MigrationMangaAdapter(controller: MigrationMangaController) :
FlexibleAdapter<IFlexible<*>>(null, controller, true) {
val coverClickListener: OnCoverClickListener = controller
interface OnCoverClickListener {
fun onCoverClick(position: Int)
}
}

View File

@ -7,17 +7,18 @@ import android.view.ViewGroup
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.databinding.MigrationMangaControllerBinding import eu.kanade.tachiyomi.databinding.MigrationMangaControllerBinding
import eu.kanade.tachiyomi.ui.base.controller.NucleusController import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.migration.search.SearchController import eu.kanade.tachiyomi.ui.browse.migration.search.SearchController
import eu.kanade.tachiyomi.ui.manga.MangaController
class MigrationMangaController : class MigrationMangaController :
NucleusController<MigrationMangaControllerBinding, MigrationMangaPresenter>, NucleusController<MigrationMangaControllerBinding, MigrationMangaPresenter>,
FlexibleAdapter.OnItemClickListener { FlexibleAdapter.OnItemClickListener,
MigrationMangaAdapter.OnCoverClickListener {
private var adapter: FlexibleAdapter<IFlexible<*>>? = null private var adapter: MigrationMangaAdapter? = null
constructor(sourceId: Long, sourceName: String?) : super( constructor(sourceId: Long, sourceName: String?) : super(
bundleOf( bundleOf(
@ -51,7 +52,7 @@ class MigrationMangaController :
override fun onViewCreated(view: View) { override fun onViewCreated(view: View) {
super.onViewCreated(view) super.onViewCreated(view)
adapter = FlexibleAdapter<IFlexible<*>>(null, this) adapter = MigrationMangaAdapter(this)
binding.recycler.layoutManager = LinearLayoutManager(view.context) binding.recycler.layoutManager = LinearLayoutManager(view.context)
binding.recycler.adapter = adapter binding.recycler.adapter = adapter
adapter?.fastScroller = binding.fastScroller adapter?.fastScroller = binding.fastScroller
@ -62,17 +63,22 @@ class MigrationMangaController :
super.onDestroyView(view) super.onDestroyView(view)
} }
fun setManga(manga: List<MangaItem>) { fun setManga(manga: List<MigrationMangaItem>) {
adapter?.updateDataSet(manga) adapter?.updateDataSet(manga)
} }
override fun onItemClick(view: View, position: Int): Boolean { override fun onItemClick(view: View, position: Int): Boolean {
val item = adapter?.getItem(position) as? MangaItem ?: return false val item = adapter?.getItem(position) as? MigrationMangaItem ?: return false
val controller = SearchController(item.manga) val controller = SearchController(item.manga)
router.pushController(controller.withFadeTransaction()) router.pushController(controller.withFadeTransaction())
return false return false
} }
override fun onCoverClick(position: Int) {
val mangaItem = adapter?.getItem(position) as? MigrationMangaItem ?: return
router.pushController(MangaController(mangaItem.manga).withFadeTransaction())
}
companion object { companion object {
const val SOURCE_ID_EXTRA = "source_id_extra" const val SOURCE_ID_EXTRA = "source_id_extra"
const val SOURCE_NAME_EXTRA = "source_name_extra" const val SOURCE_NAME_EXTRA = "source_name_extra"

View File

@ -5,29 +5,27 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.resource.bitmap.CenterCrop import com.bumptech.glide.load.resource.bitmap.CenterCrop
import com.bumptech.glide.load.resource.bitmap.RoundedCorners import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.viewholders.FlexibleViewHolder import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.data.glide.toMangaThumbnail import eu.kanade.tachiyomi.data.glide.toMangaThumbnail
import eu.kanade.tachiyomi.databinding.SourceListItemBinding import eu.kanade.tachiyomi.databinding.SourceListItemBinding
class MangaHolder( class MigrationMangaHolder(
view: View, view: View,
adapter: FlexibleAdapter<*> private val adapter: MigrationMangaAdapter
) : FlexibleViewHolder(view, adapter) { ) : FlexibleViewHolder(view, adapter) {
private val binding = SourceListItemBinding.bind(view) private val binding = SourceListItemBinding.bind(view)
fun bind(item: MangaItem) { init {
// Update the title of the manga.
binding.title.text = item.manga.title
// Create thumbnail onclick to simulate long click
binding.thumbnail.setOnClickListener { binding.thumbnail.setOnClickListener {
// Simulate long click on this view to enter selection mode adapter.coverClickListener.onCoverClick(bindingAdapterPosition)
onLongClick(itemView)
} }
}
fun bind(item: MigrationMangaItem) {
binding.title.text = item.manga.title
// Update the cover. // Update the cover.
GlideApp.with(itemView.context).clear(binding.thumbnail) GlideApp.with(itemView.context).clear(binding.thumbnail)

View File

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.ui.browse.migration.manga package eu.kanade.tachiyomi.ui.browse.migration.manga
import android.os.Parcelable
import android.view.View import android.view.View
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
@ -8,25 +7,20 @@ import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import kotlinx.parcelize.Parcelize
@Parcelize class MigrationMangaItem(val manga: Manga) : AbstractFlexibleItem<MigrationMangaHolder>() {
class MangaItem(val manga: Manga) : AbstractFlexibleItem<MangaHolder>(), Parcelable {
override fun getLayoutRes(): Int { override fun getLayoutRes(): Int {
return R.layout.source_list_item return R.layout.source_list_item
} }
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): MangaHolder { override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): MigrationMangaHolder {
return MangaHolder( return MigrationMangaHolder(view, adapter as MigrationMangaAdapter)
view,
adapter
)
} }
override fun bindViewHolder( override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
holder: MangaHolder, holder: MigrationMangaHolder,
position: Int, position: Int,
payloads: List<Any?>? payloads: List<Any?>?
) { ) {
@ -34,7 +28,7 @@ class MangaItem(val manga: Manga) : AbstractFlexibleItem<MangaHolder>(), Parcela
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other is MangaItem) { if (other is MigrationMangaItem) {
return manga.id == other.manga.id return manga.id == other.manga.id
} }
return false return false

View File

@ -23,9 +23,9 @@ class MigrationMangaPresenter(
.subscribeLatestCache(MigrationMangaController::setManga) .subscribeLatestCache(MigrationMangaController::setManga)
} }
private fun libraryToMigrationItem(library: List<Manga>): List<MangaItem> { private fun libraryToMigrationItem(library: List<Manga>): List<MigrationMangaItem> {
return library.filter { it.source == sourceId } return library.filter { it.source == sourceId }
.sortedBy { it.title } .sortedBy { it.title }
.map { MangaItem(it) } .map { MigrationMangaItem(it) }
} }
} }

View File

@ -1,9 +1,11 @@
package eu.kanade.tachiyomi.ui.browse.migration.sources package eu.kanade.tachiyomi.ui.browse.migration.sources
import android.view.View import android.view.View
import androidx.core.view.isVisible
import eu.davidea.viewholders.FlexibleViewHolder import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.databinding.SourceMainControllerCardItemBinding import eu.kanade.tachiyomi.databinding.SourceMainControllerCardItemBinding
import eu.kanade.tachiyomi.source.icon import eu.kanade.tachiyomi.source.icon
import eu.kanade.tachiyomi.util.system.LocaleHelper
class SourceHolder(view: View, val adapter: SourceAdapter) : class SourceHolder(view: View, val adapter: SourceAdapter) :
FlexibleViewHolder(view, adapter) { FlexibleViewHolder(view, adapter) {
@ -13,10 +15,10 @@ class SourceHolder(view: View, val adapter: SourceAdapter) :
fun bind(item: SourceItem) { fun bind(item: SourceItem) {
val source = item.source val source = item.source
// Set source name
binding.title.text = source.name binding.title.text = source.name
binding.subtitle.isVisible = true
binding.subtitle.text = LocaleHelper.getDisplayName(source.lang)
// Set source icon
itemView.post { itemView.post {
binding.image.setImageDrawable(source.icon()) binding.image.setImageDrawable(source.icon())
} }

View File

@ -39,6 +39,7 @@ import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog
import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.ui.more.MoreController
import eu.kanade.tachiyomi.ui.webview.WebViewActivity import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.system.connectivityManager import eu.kanade.tachiyomi.util.system.connectivityManager
import eu.kanade.tachiyomi.util.system.openInBrowser import eu.kanade.tachiyomi.util.system.openInBrowser
@ -391,16 +392,16 @@ open class BrowseSourceController(bundle: Bundle) :
} }
if (adapter.isEmpty) { if (adapter.isEmpty) {
val actions = emptyList<EmptyView.Action>().toMutableList() val actions = if (presenter.source is LocalSource) {
listOf(
if (presenter.source is LocalSource) { EmptyView.Action(R.string.local_source_help_guide, R.drawable.ic_help_24dp) { openLocalSourceHelpGuide() }
actions += EmptyView.Action(R.string.local_source_help_guide) { openLocalSourceHelpGuide() } )
} else { } else {
actions += EmptyView.Action(R.string.action_retry, retryAction) listOf(
} EmptyView.Action(R.string.action_retry, R.drawable.ic_refresh_24dp, retryAction),
EmptyView.Action(R.string.action_open_in_web_view, R.drawable.ic_public_24dp) { openInWebView() },
if (presenter.source is HttpSource) { EmptyView.Action(R.string.label_help, R.drawable.ic_help_24dp) { activity?.openInBrowser(MoreController.URL_HELP) }
actions += EmptyView.Action(R.string.action_open_in_web_view) { openInWebView() } )
} }
binding.emptyView.show(message, actions) binding.emptyView.show(message, actions)

View File

@ -316,17 +316,26 @@ class MangaPresenter(
private fun observeDownloads() { private fun observeDownloads() {
observeDownloadsStatusSubscription?.let { remove(it) } observeDownloadsStatusSubscription?.let { remove(it) }
observeDownloadsStatusSubscription = downloadManager.queue.getStatusObservable() observeDownloadsStatusSubscription = downloadManager.queue.getStatusObservable()
.observeOn(AndroidSchedulers.mainThread()) .observeOn(Schedulers.io())
.onBackpressureLatest()
.filter { download -> download.manga.id == manga.id } .filter { download -> download.manga.id == manga.id }
.doOnNext { onDownloadStatusChange(it) } .observeOn(AndroidSchedulers.mainThread())
.subscribeLatestCache(MangaController::onChapterDownloadUpdate) { _, error -> .subscribeLatestCache(
Timber.e(error) { view, it ->
} onDownloadStatusChange(it)
view.onChapterDownloadUpdate(it)
},
{ _, error ->
Timber.e(error)
}
)
observeDownloadsPageSubscription?.let { remove(it) } observeDownloadsPageSubscription?.let { remove(it) }
observeDownloadsPageSubscription = downloadManager.queue.getProgressObservable() observeDownloadsPageSubscription = downloadManager.queue.getProgressObservable()
.observeOn(AndroidSchedulers.mainThread()) .observeOn(Schedulers.io())
.onBackpressureLatest()
.filter { download -> download.manga.id == manga.id } .filter { download -> download.manga.id == manga.id }
.observeOn(AndroidSchedulers.mainThread())
.subscribeLatestCache(MangaController::onChapterDownloadUpdate) { _, error -> .subscribeLatestCache(MangaController::onChapterDownloadUpdate) { _, error ->
Timber.e(error) Timber.e(error)
} }
@ -484,7 +493,7 @@ class MangaPresenter(
db.updateChaptersProgress(chapters).executeAsBlocking() db.updateChaptersProgress(chapters).executeAsBlocking()
if (preferences.removeAfterMarkedAsRead()) { if (preferences.removeAfterMarkedAsRead()) {
deleteChapters(chapters) deleteChapters(chapters.filter { it.read })
} }
} }
} }

View File

@ -14,6 +14,9 @@ class ChapterDownloadView @JvmOverloads constructor(context: Context, attrs: Att
private val binding: ChapterDownloadViewBinding private val binding: ChapterDownloadViewBinding
private var state = Download.State.NOT_DOWNLOADED
private var progress = 0
private var downloadIconAnimator: ObjectAnimator? = null private var downloadIconAnimator: ObjectAnimator? = null
private var isAnimating = false private var isAnimating = false
@ -23,6 +26,17 @@ class ChapterDownloadView @JvmOverloads constructor(context: Context, attrs: Att
} }
fun setState(state: Download.State, progress: Int = 0) { fun setState(state: Download.State, progress: Int = 0) {
val isDirty = this.state.value != state.value || this.progress != progress
this.state = state
this.progress = progress
if (isDirty) {
updateLayout()
}
}
private fun updateLayout() {
binding.downloadIconBorder.isVisible = state == Download.State.NOT_DOWNLOADED binding.downloadIconBorder.isVisible = state == Download.State.NOT_DOWNLOADED
binding.downloadIcon.isVisible = state == Download.State.NOT_DOWNLOADED || state == Download.State.DOWNLOADING binding.downloadIcon.isVisible = state == Download.State.NOT_DOWNLOADED || state == Download.State.DOWNLOADING

View File

@ -2,13 +2,13 @@ package eu.kanade.tachiyomi.ui.manga.track
import android.annotation.SuppressLint import android.annotation.SuppressLint
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.TrackItemBinding import eu.kanade.tachiyomi.databinding.TrackItemBinding
import eu.kanade.tachiyomi.ui.base.holder.BaseViewHolder
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.text.DateFormat import java.text.DateFormat
class TrackHolder(private val binding: TrackItemBinding, adapter: TrackAdapter) : BaseViewHolder(binding.root) { class TrackHolder(private val binding: TrackItemBinding, adapter: TrackAdapter) : RecyclerView.ViewHolder(binding.root) {
private val preferences: PreferencesHelper by injectLazy() private val preferences: PreferencesHelper by injectLazy()

View File

@ -151,6 +151,6 @@ class MoreController :
} }
companion object { companion object {
private const val URL_HELP = "https://tachiyomi.org/help/" const val URL_HELP = "https://tachiyomi.org/help/"
} }
} }

View File

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.reader
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.annotation.TargetApi import android.annotation.TargetApi
import android.app.ProgressDialog import android.app.ProgressDialog
import android.content.ClipData
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
@ -595,10 +596,11 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
val manga = presenter.manga ?: return val manga = presenter.manga ?: return
val chapter = page.chapter.chapter val chapter = page.chapter.chapter
val stream = file.getUriCompat(this) val uri = file.getUriCompat(this)
val intent = Intent(Intent.ACTION_SEND).apply { val intent = Intent(Intent.ACTION_SEND).apply {
putExtra(Intent.EXTRA_TEXT, getString(R.string.share_page_info, manga.title, chapter.name, page.number)) putExtra(Intent.EXTRA_TEXT, getString(R.string.share_page_info, manga.title, chapter.name, page.number))
putExtra(Intent.EXTRA_STREAM, stream) putExtra(Intent.EXTRA_STREAM, uri)
clipData = ClipData.newRawUri(null, uri)
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
type = "image/*" type = "image/*"
} }

View File

@ -65,6 +65,7 @@ class ReaderSettingsSheet(private val activity: ReaderActivity) : BaseBottomShee
binding.backgroundColor.bindToIntPreference(preferences.readerTheme(), R.array.reader_themes_values) binding.backgroundColor.bindToIntPreference(preferences.readerTheme(), R.array.reader_themes_values)
binding.showPageNumber.bindToPreference(preferences.showPageNumber()) binding.showPageNumber.bindToPreference(preferences.showPageNumber())
binding.fullscreen.bindToPreference(preferences.fullscreen()) binding.fullscreen.bindToPreference(preferences.fullscreen())
binding.dualPageSplit.bindToPreference(preferences.dualPageSplit())
binding.keepscreen.bindToPreference(preferences.keepScreenOn()) binding.keepscreen.bindToPreference(preferences.keepScreenOn())
binding.longTap.bindToPreference(preferences.readWithLongTap()) binding.longTap.bindToPreference(preferences.readWithLongTap())
binding.alwaysShowChapterTransition.bindToPreference(preferences.alwaysShowChapterTransition()) binding.alwaysShowChapterTransition.bindToPreference(preferences.alwaysShowChapterTransition())

View File

@ -0,0 +1,10 @@
package eu.kanade.tachiyomi.ui.reader.model
class InsertPage(val parent: ReaderPage) : ReaderPage(parent.index, parent.url, parent.imageUrl) {
override var chapter: ReaderChapter = parent.chapter
init {
stream = parent.stream
}
}

View File

@ -3,12 +3,12 @@ package eu.kanade.tachiyomi.ui.reader.model
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import java.io.InputStream import java.io.InputStream
class ReaderPage( open class ReaderPage(
index: Int, index: Int,
url: String = "", url: String = "",
imageUrl: String? = null, imageUrl: String? = null,
var stream: (() -> InputStream)? = null var stream: (() -> InputStream)? = null
) : Page(index, url, imageUrl, null) { ) : Page(index, url, imageUrl, null) {
lateinit var chapter: ReaderChapter open lateinit var chapter: ReaderChapter
} }

View File

@ -24,6 +24,7 @@ abstract class ViewerConfig(preferences: PreferencesHelper, private val scope: C
var volumeKeysInverted = false var volumeKeysInverted = false
var trueColor = false var trueColor = false
var alwaysShowChapterTransition = true var alwaysShowChapterTransition = true
var dualPageSplit = false
var navigationMode = 0 var navigationMode = 0
protected set protected set
@ -54,6 +55,9 @@ abstract class ViewerConfig(preferences: PreferencesHelper, private val scope: C
preferences.alwaysShowChapterTransition() preferences.alwaysShowChapterTransition()
.register({ alwaysShowChapterTransition = it }) .register({ alwaysShowChapterTransition = it })
preferences.dualPageSplit()
.register({ dualPageSplit = it }, { imagePropertyChangedListener?.invoke() })
} }
protected abstract fun defaultNavigation(): ViewerNavigation protected abstract fun defaultNavigation(): ViewerNavigation

View File

@ -1,8 +1,7 @@
package eu.kanade.tachiyomi.ui.reader.viewer.pager package eu.kanade.tachiyomi.ui.reader.viewer.navigation
import android.graphics.RectF import android.graphics.RectF
import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.LNavigation
/** /**
* Visualization of default state without any inversion * Visualization of default state without any inversion
@ -14,7 +13,7 @@ import eu.kanade.tachiyomi.ui.reader.viewer.navigation.LNavigation
* | N | M | P | N: Move Left * | N | M | P | N: Move Left
* +---+---+---+ * +---+---+---+
*/ */
class PagerDefaultNavigation : ViewerNavigation() { class RightAndLeftNavigation : ViewerNavigation() {
override var regions: List<Region> = listOf( override var regions: List<Region> = listOf(
Region( Region(
@ -27,5 +26,3 @@ class PagerDefaultNavigation : ViewerNavigation() {
), ),
) )
} }
class VerticalPagerDefaultNavigation : LNavigation()

View File

@ -6,6 +6,7 @@ import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.EdgeNavigation import eu.kanade.tachiyomi.ui.reader.viewer.navigation.EdgeNavigation
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.KindlishNavigation import eu.kanade.tachiyomi.ui.reader.viewer.navigation.KindlishNavigation
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.LNavigation import eu.kanade.tachiyomi.ui.reader.viewer.navigation.LNavigation
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.RightAndLeftNavigation
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -69,8 +70,8 @@ class PagerConfig(
override fun defaultNavigation(): ViewerNavigation { override fun defaultNavigation(): ViewerNavigation {
return when (viewer) { return when (viewer) {
is VerticalPagerViewer -> VerticalPagerDefaultNavigation() is VerticalPagerViewer -> LNavigation()
else -> PagerDefaultNavigation() else -> RightAndLeftNavigation()
} }
} }
@ -80,6 +81,7 @@ class PagerConfig(
1 -> LNavigation() 1 -> LNavigation()
2 -> KindlishNavigation() 2 -> KindlishNavigation()
3 -> EdgeNavigation() 3 -> EdgeNavigation()
4 -> RightAndLeftNavigation()
else -> defaultNavigation() else -> defaultNavigation()
} }
} }

View File

@ -28,6 +28,7 @@ import com.github.chrisbanes.photoview.PhotoView
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import eu.kanade.tachiyomi.ui.reader.model.InsertPage
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressBar import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressBar
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig.ZoomType import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig.ZoomType
@ -241,6 +242,9 @@ class PagerPageHolder(
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.doOnNext { isAnimated -> .doOnNext { isAnimated ->
if (viewer.config.dualPageSplit) {
openStream = processDualPageSplit(openStream!!)
}
if (!isAnimated) { if (!isAnimated) {
initSubsamplingImageView().setImage(ImageSource.inputStream(openStream!!)) initSubsamplingImageView().setImage(ImageSource.inputStream(openStream!!))
} else { } else {
@ -253,6 +257,38 @@ class PagerPageHolder(
.subscribe({}, {}) .subscribe({}, {})
} }
private fun processDualPageSplit(openStream: InputStream): InputStream {
var inputStream = openStream
val (isDoublePage, stream) = when (page) {
is InsertPage -> Pair(true, inputStream)
else -> ImageUtil.isDoublePage(inputStream)
}
inputStream = stream
if (isDoublePage) {
val side = when {
viewer is L2RPagerViewer && page is InsertPage -> ImageUtil.Side.RIGHT
viewer is R2LPagerViewer && page is InsertPage -> ImageUtil.Side.LEFT
viewer is L2RPagerViewer && page !is InsertPage -> ImageUtil.Side.LEFT
viewer is R2LPagerViewer && page !is InsertPage -> ImageUtil.Side.RIGHT
viewer is VerticalPagerViewer && page !is InsertPage -> ImageUtil.Side.RIGHT
viewer is VerticalPagerViewer && page is InsertPage -> ImageUtil.Side.LEFT
else -> error("We should choose a side!")
}
if (page !is InsertPage) {
onPageSplit()
}
inputStream = ImageUtil.splitInHalf(inputStream, side)
}
return inputStream
}
private fun onPageSplit() {
val newPage = InsertPage(page)
viewer.onPageSplit(page, newPage)
}
/** /**
* Called when the page has an error. * Called when the page has an error.
*/ */

View File

@ -12,6 +12,7 @@ import androidx.viewpager.widget.ViewPager
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition
import eu.kanade.tachiyomi.ui.reader.model.InsertPage
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
import eu.kanade.tachiyomi.ui.reader.viewer.BaseViewer import eu.kanade.tachiyomi.ui.reader.viewer.BaseViewer
@ -371,4 +372,8 @@ abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer {
} }
return false return false
} }
fun onPageSplit(currentPage: ReaderPage, newPage: InsertPage) {
adapter.onPageSplit(currentPage, newPage, this::class.java)
}
} }

View File

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.reader.viewer.pager
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition
import eu.kanade.tachiyomi.ui.reader.model.InsertPage
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
@ -18,7 +19,7 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
/** /**
* List of currently set items. * List of currently set items.
*/ */
var items: List<Any> = emptyList() var items: MutableList<Any> = mutableListOf()
private set private set
var nextTransition: ChapterTransition.Next? = null var nextTransition: ChapterTransition.Next? = null
@ -80,6 +81,9 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
} }
} }
// Resets double-page splits, else insert pages get misplaced
items.filterIsInstance<InsertPage>().also { items.removeAll(it) }
if (viewer is R2LPagerViewer) { if (viewer is R2LPagerViewer) {
newItems.reverse() newItems.reverse()
} }
@ -120,4 +124,31 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
} }
return POSITION_NONE return POSITION_NONE
} }
fun onPageSplit(current: Any?, newPage: InsertPage, clazz: Class<out PagerViewer>) {
if (current !is ReaderPage) return
val currentIndex = items.indexOf(current)
val placeAtIndex = when {
clazz.isAssignableFrom(L2RPagerViewer::class.java) -> currentIndex + 1
clazz.isAssignableFrom(VerticalPagerViewer::class.java) -> currentIndex + 1
clazz.isAssignableFrom(R2LPagerViewer::class.java) -> currentIndex
else -> currentIndex
}
// It will enter a endless cycle of insert pages
if (clazz.isAssignableFrom(R2LPagerViewer::class.java) && items[placeAtIndex - 1] is InsertPage) {
return
}
// Same here it will enter a endless cycle of insert pages
if (items[placeAtIndex] is InsertPage) {
return
}
items.add(placeAtIndex, newPage)
notifyDataSetChanged()
}
} }

View File

@ -3,13 +3,13 @@ package eu.kanade.tachiyomi.ui.reader.viewer.webtoon
import android.content.Context import android.content.Context
import android.view.View import android.view.View
import android.view.ViewGroup.LayoutParams import android.view.ViewGroup.LayoutParams
import eu.kanade.tachiyomi.ui.base.holder.BaseViewHolder import androidx.recyclerview.widget.RecyclerView
import rx.Subscription import rx.Subscription
abstract class WebtoonBaseHolder( abstract class WebtoonBaseHolder(
view: View, view: View,
protected val viewer: WebtoonViewer protected val viewer: WebtoonViewer
) : BaseViewHolder(view) { ) : RecyclerView.ViewHolder(view) {
/** /**
* Context getter because it's used often. * Context getter because it's used often.

View File

@ -6,6 +6,7 @@ import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.EdgeNavigation import eu.kanade.tachiyomi.ui.reader.viewer.navigation.EdgeNavigation
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.KindlishNavigation import eu.kanade.tachiyomi.ui.reader.viewer.navigation.KindlishNavigation
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.LNavigation import eu.kanade.tachiyomi.ui.reader.viewer.navigation.LNavigation
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.RightAndLeftNavigation
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -44,7 +45,7 @@ class WebtoonConfig(
} }
override fun defaultNavigation(): ViewerNavigation { override fun defaultNavigation(): ViewerNavigation {
return WebtoonDefaultNavigation() return LNavigation()
} }
override fun updateNavigation(navigationMode: Int) { override fun updateNavigation(navigationMode: Int) {
@ -53,6 +54,7 @@ class WebtoonConfig(
1 -> LNavigation() 1 -> LNavigation()
2 -> KindlishNavigation() 2 -> KindlishNavigation()
3 -> EdgeNavigation() 3 -> EdgeNavigation()
4 -> RightAndLeftNavigation()
else -> defaultNavigation() else -> defaultNavigation()
} }
} }

View File

@ -1,5 +0,0 @@
package eu.kanade.tachiyomi.ui.reader.viewer.webtoon
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.LNavigation
class WebtoonDefaultNavigation : LNavigation()

View File

@ -287,6 +287,14 @@ class WebtoonPageHolder(
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.doOnNext { isAnimated -> .doOnNext { isAnimated ->
if (viewer.config.dualPageSplit) {
val (isDoublePage, stream) = ImageUtil.isDoublePage(openStream!!)
openStream = if (!isDoublePage) {
stream
} else {
ImageUtil.splitAndMerge(stream)
}
}
if (!isAnimated) { if (!isAnimated) {
val subsamplingView = initSubsamplingImageView() val subsamplingView = initSubsamplingImageView()
subsamplingView.isVisible = true subsamplingView.isVisible = true

View File

@ -40,13 +40,22 @@ class UpdatesPresenter(
.subscribeLatestCache(UpdatesController::onNextRecentChapters) .subscribeLatestCache(UpdatesController::onNextRecentChapters)
downloadManager.queue.getStatusObservable() downloadManager.queue.getStatusObservable()
.observeOn(Schedulers.io())
.onBackpressureLatest()
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.doOnNext { onDownloadStatusChange(it) } .subscribeLatestCache(
.subscribeLatestCache(UpdatesController::onChapterDownloadUpdate) { _, error -> { view, it ->
Timber.e(error) onDownloadStatusChange(it)
} view.onChapterDownloadUpdate(it)
},
{ _, error ->
Timber.e(error)
}
)
downloadManager.queue.getProgressObservable() downloadManager.queue.getProgressObservable()
.observeOn(Schedulers.io())
.onBackpressureLatest()
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribeLatestCache(UpdatesController::onChapterDownloadUpdate) { _, error -> .subscribeLatestCache(UpdatesController::onChapterDownloadUpdate) { _, error ->
Timber.e(error) Timber.e(error)

View File

@ -6,11 +6,11 @@ import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.util.TypedValue import android.util.TypedValue
import android.view.ContextThemeWrapper
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.preference.PreferenceController import androidx.preference.PreferenceController
import androidx.preference.PreferenceGroup import androidx.preference.PreferenceGroup

View File

@ -178,6 +178,7 @@ class SettingsGeneralController : SettingsController() {
"cv", "cv",
"de", "de",
"el", "el",
"eo",
"es", "es",
"es-419", "es-419",
"en-US", "en-US",

View File

@ -129,9 +129,10 @@ class SettingsLibraryController : SettingsController() {
R.string.update_6hour, R.string.update_6hour,
R.string.update_12hour, R.string.update_12hour,
R.string.update_24hour, R.string.update_24hour,
R.string.update_48hour R.string.update_48hour,
R.string.update_weekly
) )
entryValues = arrayOf("0", "1", "2", "3", "6", "12", "24", "48") entryValues = arrayOf("0", "1", "2", "3", "6", "12", "24", "48", "168")
defaultValue = "24" defaultValue = "24"
summary = "%s" summary = "%s"

View File

@ -50,6 +50,11 @@ class SettingsReaderController : SettingsController() {
summaryRes = R.string.pref_show_reading_mode_summary summaryRes = R.string.pref_show_reading_mode_summary
defaultValue = true defaultValue = true
} }
switchPreference {
key = Keys.dualPageSplit
titleRes = R.string.pref_dual_page_split
defaultValue = false
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
switchPreference { switchPreference {
key = Keys.trueColor key = Keys.trueColor

View File

@ -44,6 +44,12 @@ class CrashLogUtil(private val context: Context) {
NotificationReceiver.openErrorLogPendingActivity(context, uri) NotificationReceiver.openErrorLogPendingActivity(context, uri)
) )
addAction(
R.drawable.ic_share_24dp,
context.getString(R.string.action_share),
NotificationReceiver.shareCrashLogPendingBroadcast(context, uri, Notifications.ID_CRASH_LOGS)
)
context.notificationManager.notify(Notifications.ID_CRASH_LOGS, build()) context.notificationManager.notify(Notifications.ID_CRASH_LOGS, build())
} }
} }

View File

@ -1,5 +1,11 @@
package eu.kanade.tachiyomi.util.system package eu.kanade.tachiyomi.util.system
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Rect
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.InputStream import java.io.InputStream
import java.net.URLConnection import java.net.URLConnection
@ -68,4 +74,71 @@ object ImageUtil {
GIF("image/gif", "gif"), GIF("image/gif", "gif"),
WEBP("image/webp", "webp") WEBP("image/webp", "webp")
} }
/**
* Check whether the image is a double image (width > height), return the result and original stream
*/
fun isDoublePage(imageStream: InputStream): Pair<Boolean, InputStream> {
val imageBytes = imageStream.readBytes()
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size, options)
return Pair(options.outWidth > options.outHeight, ByteArrayInputStream(imageBytes))
}
/**
* Extract the 'side' part from imageStream and return it as InputStream.
*/
fun splitInHalf(imageStream: InputStream, side: Side): InputStream {
val imageBytes = imageStream.readBytes()
val imageBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
val height = imageBitmap.height
val width = imageBitmap.width
val singlePage = Rect(0, 0, width / 2, height)
val half = Bitmap.createBitmap(width / 2, height, Bitmap.Config.ARGB_8888)
val part = when (side) {
Side.RIGHT -> Rect(width - width / 2, 0, width, height)
Side.LEFT -> Rect(0, 0, width / 2, height)
}
val canvas = Canvas(half)
canvas.drawBitmap(imageBitmap, part, singlePage, null)
val output = ByteArrayOutputStream()
half.compress(Bitmap.CompressFormat.JPEG, 100, output)
return ByteArrayInputStream(output.toByteArray())
}
/**
* Split the image into left and right parts, then merge them into a new image.
*/
fun splitAndMerge(imageStream: InputStream): InputStream {
val imageBytes = imageStream.readBytes()
val imageBitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
val height = imageBitmap.height
val width = imageBitmap.width
val result = Bitmap.createBitmap(width / 2, height * 2, Bitmap.Config.ARGB_8888)
val canvas = Canvas(result)
// right -> upper
val rightPart = Rect(width - width / 2, 0, width, height)
val upperPart = Rect(0, 0, width / 2, height)
canvas.drawBitmap(imageBitmap, rightPart, upperPart, null)
// left -> bottom
val leftPart = Rect(0, 0, width / 2, height)
val bottomPart = Rect(0, height, width / 2, height * 2)
canvas.drawBitmap(imageBitmap, leftPart, bottomPart, null)
val output = ByteArrayOutputStream()
result.compress(Bitmap.CompressFormat.JPEG, 100, output)
return ByteArrayInputStream(output.toByteArray())
}
enum class Side {
RIGHT, LEFT
}
} }

View File

@ -6,33 +6,19 @@ import android.content.pm.PackageManager
import android.webkit.CookieManager import android.webkit.CookieManager
import android.webkit.WebSettings import android.webkit.WebSettings
import android.webkit.WebView import android.webkit.WebView
import eu.kanade.tachiyomi.util.lang.launchUI
import timber.log.Timber import timber.log.Timber
object WebViewUtil { object WebViewUtil {
val WEBVIEW_UA_VERSION_REGEX by lazy {
Regex(""".*Chrome/(\d+)\..*""")
}
var DEFAULT_USER_AGENT: String = "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
private set
const val REQUESTED_WITH = "com.android.browser" const val REQUESTED_WITH = "com.android.browser"
const val MINIMUM_WEBVIEW_VERSION = 87 const val MINIMUM_WEBVIEW_VERSION = 88
fun supportsWebView(context: Context): Boolean { fun supportsWebView(context: Context): Boolean {
try { try {
// May throw android.webkit.WebViewFactory$MissingWebViewPackageException if WebView // May throw android.webkit.WebViewFactory$MissingWebViewPackageException if WebView
// is not installed // is not installed
CookieManager.getInstance() CookieManager.getInstance()
} catch (e: Throwable) {
launchUI {
DEFAULT_USER_AGENT = WebView(context)
.getDefaultUserAgentString()
.replace("; wv", "")
}
} catch (e: Exception) {
Timber.e(e) Timber.e(e)
return false return false
} }
@ -59,7 +45,7 @@ fun WebView.setDefaultSettings() {
} }
private fun WebView.getWebViewMajorVersion(): Int { private fun WebView.getWebViewMajorVersion(): Int {
val uaRegexMatch = WebViewUtil.WEBVIEW_UA_VERSION_REGEX.matchEntire(getDefaultUserAgentString()) val uaRegexMatch = """.*Chrome/(\d+)\..*""".toRegex().matchEntire(getDefaultUserAgentString())
return if (uaRegexMatch != null && uaRegexMatch.groupValues.size > 1) { return if (uaRegexMatch != null && uaRegexMatch.groupValues.size > 1) {
uaRegexMatch.groupValues[1].toInt() uaRegexMatch.groupValues[1].toInt()
} else { } else {

View File

@ -1,24 +1,26 @@
package eu.kanade.tachiyomi.widget package eu.kanade.tachiyomi.widget
import android.content.Context import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.RelativeLayout import android.widget.RelativeLayout
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.appcompat.widget.AppCompatButton import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.google.android.material.button.MaterialButton
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.CommonViewEmptyBinding import eu.kanade.tachiyomi.databinding.CommonViewEmptyBinding
import kotlin.random.Random import kotlin.random.Random
class EmptyView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : class EmptyView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
RelativeLayout(context, attrs) { RelativeLayout(context, attrs) {
private val binding: CommonViewEmptyBinding private val binding: CommonViewEmptyBinding =
CommonViewEmptyBinding.inflate(LayoutInflater.from(context), this, true)
init {
binding = CommonViewEmptyBinding.inflate(LayoutInflater.from(context), this, true)
}
/** /**
* Hide the information view * Hide the information view
@ -40,20 +42,25 @@ class EmptyView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
binding.textLabel.text = message binding.textLabel.text = message
binding.actionsContainer.removeAllViews() binding.actionsContainer.removeAllViews()
if (!actions.isNullOrEmpty()) { actions?.forEach {
actions.forEach { val button = MaterialButton(ContextThemeWrapper(context, R.style.Theme_Widget_Button_Action)).apply {
val button = AppCompatButton(context).apply { layoutParams = LinearLayout.LayoutParams(
layoutParams = LinearLayout.LayoutParams( 0,
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT 1f / actions.size
) )
setText(it.resId) backgroundTintList = ColorStateList.valueOf(Color.TRANSPARENT)
setOnClickListener(it.listener) stateListAnimator = null
} elevation = 0f
binding.actionsContainer.addView(button) setIconResource(it.iconResId)
setText(it.stringResId)
setOnClickListener(it.listener)
} }
binding.actionsContainer.addView(button)
} }
this.isVisible = true this.isVisible = true
@ -75,7 +82,8 @@ class EmptyView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
} }
data class Action( data class Action(
@StringRes val resId: Int, @StringRes val stringResId: Int,
@DrawableRes val iconResId: Int,
val listener: OnClickListener val listener: OnClickListener
) )
} }

View File

@ -1,29 +0,0 @@
package eu.kanade.tachiyomi.widget
import android.content.Context
import android.util.AttributeSet
import androidx.core.view.forEach
import androidx.viewpager.widget.ViewPager
/**
* A [ViewPager] that sets its height to the maximum height of its children.
* This is a way to mimic WRAP_CONTENT for its height.
*/
class MaxHeightViewPager(context: Context, attrs: AttributeSet?) : ViewPager(context, attrs) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
var measuredHeight = heightMeasureSpec
var height = 0
forEach {
it.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED))
val h = it.measuredHeight
if (h > height) height = h
}
if (height != 0) {
measuredHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
}
super.onMeasure(widthMeasureSpec, measuredHeight)
}
}

View File

@ -5,6 +5,6 @@
Ensures visibility on top of the background color. Ensures visibility on top of the background color.
--> -->
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/colorOnBackground" android:state_selected="true"/> <item android:color="?attr/colorAccent" android:state_selected="true"/>
<item android:alpha="0.60" android:color="?attr/colorOnBackground"/> <item android:alpha="0.60" android:color="?attr/colorOnBackground"/>
</selector> </selector>

View File

@ -22,6 +22,7 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:tabTextColor="@color/tabs_selector_background" app:tabTextColor="@color/tabs_selector_background"
app:tabIndicatorColor="?attr/colorAccent"
app:tabGravity="fill" app:tabGravity="fill"
app:tabMode="fixed" /> app:tabMode="fixed" />
@ -38,12 +39,12 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_more_vert_24dp" app:srcCompat="@drawable/ic_more_vert_24dp"
app:tint="?attr/colorOnPrimary" app:tint="?attr/colorOnBackground"
tools:visibility="visible" /> tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<eu.kanade.tachiyomi.widget.MaxHeightViewPager <androidx.viewpager.widget.ViewPager
android:id="@+id/pager" android:id="@+id/pager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />

View File

@ -27,9 +27,8 @@
<LinearLayout <LinearLayout
android:id="@+id/actions_container" android:id="@+id/actions_container"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:orientation="horizontal" />
android:orientation="vertical" />
</LinearLayout> </LinearLayout>

View File

@ -151,6 +151,14 @@
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
app:layout_constraintTop_toBottomOf="@id/show_page_number" /> app:layout_constraintTop_toBottomOf="@id/show_page_number" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/dual_page_split"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/pref_dual_page_split"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintTop_toBottomOf="@id/fullscreen" />
<com.google.android.material.switchmaterial.SwitchMaterial <com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/cutout_short" android:id="@+id/cutout_short"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -158,7 +166,7 @@
android:text="@string/pref_cutout_short" android:text="@string/pref_cutout_short"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
android:visibility="gone" android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/fullscreen" app:layout_constraintTop_toBottomOf="@id/dual_page_split"
tools:visibility="visible" /> tools:visibility="visible" />
<com.google.android.material.switchmaterial.SwitchMaterial <com.google.android.material.switchmaterial.SwitchMaterial

View File

@ -39,7 +39,6 @@
<string name="pref_library_update_restriction_summary">ሁኔታዎቹ ሲሟሉ ብቻ ያዘምኑ</string> <string name="pref_library_update_restriction_summary">ሁኔታዎቹ ሲሟሉ ብቻ ያዘምኑ</string>
<string name="pref_library_update_restriction">የቤተ-መጽሐፍት ዝመና ገደቦች</string> <string name="pref_library_update_restriction">የቤተ-መጽሐፍት ዝመና ገደቦች</string>
<string name="pref_library_update_prioritization">የቤተ-መጽሐፍት ዝመና ትዕዛዝ</string> <string name="pref_library_update_prioritization">የቤተ-መጽሐፍት ዝመና ትዕዛዝ</string>
<string name="update_monthly">ወርሃዊ</string>
<string name="update_weekly">ሳምንታዊ</string> <string name="update_weekly">ሳምንታዊ</string>
<string name="update_48hour">በየ 2 ቀኑ</string> <string name="update_48hour">በየ 2 ቀኑ</string>
<string name="update_24hour">በየቀኑ</string> <string name="update_24hour">በየቀኑ</string>
@ -375,4 +374,4 @@
<string name="backup_restore_missing_trackers">መከታተያዎች አልገቡም:</string> <string name="backup_restore_missing_trackers">መከታተያዎች አልገቡም:</string>
<string name="backup_restore_missing_sources">የጠፋ ምንጮች:</string> <string name="backup_restore_missing_sources">የጠፋ ምንጮች:</string>
<string name="invalid_backup_file_missing_manga">ምትኬ ማንኛውንም ማንጋ አልያዘም ፡፡</string> <string name="invalid_backup_file_missing_manga">ምትኬ ማንኛውንም ማንጋ አልያዘም ፡፡</string>
</resources> </resources>

View File

@ -91,7 +91,6 @@
<string name="update_24hour">يومياً</string> <string name="update_24hour">يومياً</string>
<string name="update_48hour">كل يومان</string> <string name="update_48hour">كل يومان</string>
<string name="update_weekly">أسبوعياً</string> <string name="update_weekly">أسبوعياً</string>
<string name="update_monthly">شهرياً</string>
<string name="pref_library_update_categories">الفئات المتضمنة فى التحديث الشامل</string> <string name="pref_library_update_categories">الفئات المتضمنة فى التحديث الشامل</string>
<string name="all">الكل</string> <string name="all">الكل</string>
<string name="pref_library_update_restriction">قيود تحديثات المكتبة</string> <string name="pref_library_update_restriction">قيود تحديثات المكتبة</string>
@ -692,4 +691,4 @@
<string name="edge_nav">حافة</string> <string name="edge_nav">حافة</string>
<string name="kindlish_nav">كيندل العش</string> <string name="kindlish_nav">كيندل العش</string>
<string name="default_nav">إفتراضي</string> <string name="default_nav">إفتراضي</string>
</resources> </resources>

View File

@ -231,7 +231,6 @@
<string name="pref_library_update_restriction_summary">Actualizar solo cuando se cumplan las condiciones</string> <string name="pref_library_update_restriction_summary">Actualizar solo cuando se cumplan las condiciones</string>
<string name="pref_library_update_restriction">Restricciones de actualización de biblioteca</string> <string name="pref_library_update_restriction">Restricciones de actualización de biblioteca</string>
<string name="pref_library_update_prioritization">Orden de actualización de la biblioteca</string> <string name="pref_library_update_prioritization">Orden de actualización de la biblioteca</string>
<string name="update_monthly">Mensual</string>
<string name="update_weekly">Semanal</string> <string name="update_weekly">Semanal</string>
<string name="update_48hour">Cada 2 días</string> <string name="update_48hour">Cada 2 días</string>
<string name="update_24hour">Diario</string> <string name="update_24hour">Diario</string>
@ -657,4 +656,4 @@
<string name="action_order_by_chapter_number">Por número de capítulo</string> <string name="action_order_by_chapter_number">Por número de capítulo</string>
<string name="action_order_by_upload_date">Por fecha de subida</string> <string name="action_order_by_upload_date">Por fecha de subida</string>
<string name="action_filter_tracked">Rastreado</string> <string name="action_filter_tracked">Rastreado</string>
</resources> </resources>

View File

@ -308,7 +308,6 @@
<string name="app_not_available">Приложението не е достъпно</string> <string name="app_not_available">Приложението не е достъпно</string>
<string name="short_recent_updates">Обновления</string> <string name="short_recent_updates">Обновления</string>
<string name="update_weekly">Ежеседмично</string> <string name="update_weekly">Ежеседмично</string>
<string name="update_monthly">Ежемесечно</string>
<string name="default_category">Категория по подразбиране</string> <string name="default_category">Категория по подразбиране</string>
<string name="default_category_summary">Питай всеки път</string> <string name="default_category_summary">Питай всеки път</string>
<string name="pref_crop_borders">Изрязвай границите</string> <string name="pref_crop_borders">Изрязвай границите</string>

View File

@ -95,7 +95,6 @@
<string name="update_24hour">প্রতিদিন</string> <string name="update_24hour">প্রতিদিন</string>
<string name="update_48hour">প্রতি ২ দিন</string> <string name="update_48hour">প্রতি ২ দিন</string>
<string name="update_weekly">সাপ্তাহিক</string> <string name="update_weekly">সাপ্তাহিক</string>
<string name="update_monthly">মাসিক</string>
<string name="pref_library_update_categories">সার্বজনীন হালনাগাদের জন্য যে বিভাগসমূহ অন্তর্ভুক্ত করবেন</string> <string name="pref_library_update_categories">সার্বজনীন হালনাগাদের জন্য যে বিভাগসমূহ অন্তর্ভুক্ত করবেন</string>
<string name="all">সব</string> <string name="all">সব</string>
<string name="pref_library_update_restriction">সংগ্রহশালা হালনাগেদের সীমাবদ্ধতা</string> <string name="pref_library_update_restriction">সংগ্রহশালা হালনাগেদের সীমাবদ্ধতা</string>
@ -458,7 +457,7 @@
<string name="set_chapter_settings_as_default">প্রকৃত হিসেবে সংরক্ষণ করুন</string> <string name="set_chapter_settings_as_default">প্রকৃত হিসেবে সংরক্ষণ করুন</string>
<string name="confirm_set_chapter_settings">আপনি কি নিশ্চিত সেটিংসগুলো প্রকৃত হিসেবে সংরক্ষণ করবেন\?</string> <string name="confirm_set_chapter_settings">আপনি কি নিশ্চিত সেটিংসগুলো প্রকৃত হিসেবে সংরক্ষণ করবেন\?</string>
<string name="label_data">তথ্য</string> <string name="label_data">তথ্য</string>
<string name="requires_app_restart">কার্যকর করতে অ্যাপ পুনরারম্ভ করতে হব</string> <string name="requires_app_restart">কার্যকর করতে অ্যাপ পুনরারম্ভ করা লাগ</string>
<string name="label_network">নেটওয়ার্ক</string> <string name="label_network">নেটওয়ার্ক</string>
<string name="restoring_backup_canceled">পুনরুদ্ধার বাতিল করা হয়েছে</string> <string name="restoring_backup_canceled">পুনরুদ্ধার বাতিল করা হয়েছে</string>
<string name="restoring_backup_error">ব্যাকআপ পুনরুদ্ধার ব্যর্থ হয়েছে</string> <string name="restoring_backup_error">ব্যাকআপ পুনরুদ্ধার ব্যর্থ হয়েছে</string>
@ -622,13 +621,38 @@
<string name="pref_disable_battery_optimization_summary">পিছেনে লাইব্রেরী হালনাগাদ ও ব্যাকআপে সাহায্য করে</string> <string name="pref_disable_battery_optimization_summary">পিছেনে লাইব্রেরী হালনাগাদ ও ব্যাকআপে সাহায্য করে</string>
<string name="pref_disable_battery_optimization">ব্যাটারি অপ্টিমাইজেশন অক্ষম করুন</string> <string name="pref_disable_battery_optimization">ব্যাটারি অপ্টিমাইজেশন অক্ষম করুন</string>
<string name="tracking_info">ট্র্যাকিং সার্ভিসে অধ্যায়ের অগ্রগতি হালনাগাদের একমুখী পথ। স্বতন্ত্র মাঙ্গার ট্র্যকিং সেটাপের জন্য তাদের নিজস্ব ট্র্যাকিং বাটন ব্যবহার করুন।</string> <string name="tracking_info">ট্র্যাকিং সার্ভিসে অধ্যায়ের অগ্রগতি হালনাগাদের একমুখী পথ। স্বতন্ত্র মাঙ্গার ট্র্যকিং সেটাপের জন্য তাদের নিজস্ব ট্র্যাকিং বাটন ব্যবহার করুন।</string>
<string name="full_restore_online">অনলাইন পুনরুদ্ধার , অনেক ধীর ধীরে হবে কিন্তু আপনাকে আরও আপডেট করা তথ্য এবং অধ্যায় দিবে</string> <string name="full_restore_online">অনলাইন পুনরুদ্ধার, অনেক ধীর কিন্তু আপনাকে আরও আপডেট করা তথ্য এবং অধ্যায় দিবে</string>
<string name="full_restore_mode">নেটওয়ার্ক মোডে</string> <string name="full_restore_mode">নেটওয়ার্ক মোডে</string>
<string name="pref_backup_auto_create_legacy">লিগ্যাসি ব্যাকআপও তৈরি করুন</string> <string name="pref_backup_auto_create_legacy">লিগ্যাসি ব্যাকআপও তৈরি করুন</string>
<string name="pref_create_legacy_backup_summary">"আগের তাছিয়ুমি ভার্এর ব্যবহার করা জন্য"</string> <string name="pref_create_legacy_backup_summary">পুরাতন তাচ্চিওমি ভার্গুলোতে ব্যবহার করা যাবে</string>
<string name="pref_create_legacy_backup">উত্তরাধিকার এর ব্যাকআপ তৈরি করা</string> <string name="pref_create_legacy_backup">উত্তরাধিকার এর ব্যাকআপ তৈরি করা</string>
<string name="pref_label_nsfw_extension">লেবেল এর এক্সটেনশন গুলো তালিকা</string> <string name="pref_label_nsfw_extension">এক্সটেনশন গুলো তালিকায় লেবেল করুন</string>
<string name="pref_show_nsfw_extension">১৮+ এক্সটেনশন গুলো তালিকা</string> <string name="pref_show_nsfw_extension">এক্সটেনশন গুলো তালিকায় প্রদর্শন করুন</string>
<string name="pref_show_nsfw_source">১৮+ মাধ্যম গুলোর তালিকা</string> <string name="pref_show_nsfw_source">উৎস তালিকায় প্রদর্শন করুন</string>
<string name="pref_category_nsfw_content">খারাপ জিনিস (১৮+) মাধ্যম</string> <string name="pref_category_nsfw_content">খারাপ জিনিস (১৮+) উত্স</string>
</resources> <string name="action_filter_tracked">ট্র্যাকড</string>
<string name="pref_viewer_nav">ন্যাভিগেশন নকশা</string>
<string name="action_display_show_number_of_items">আইটেমের সংখ্যা প্রদর্শন করুন</string>
<string name="spen_next_page">পরের পৃষ্ঠা</string>
<string name="spen_previous_page">আগের পৃষ্ঠা</string>
<string name="channel_crash_logs">ক্র্যাশ লগগুলো</string>
<string name="file_picker_error">কোনো ফাইল বাছাইকারী অ্যাপ পাওয়া যায় নি</string>
<string name="migration_help_guide">উৎস স্থানান্তর গাইড</string>
<string name="myanimelist_relogin">দয়া করে MAL এ আবার লগইন করুন</string>
<string name="myanimelist_creds_missing">MAL লগইন পরিচয়পত্র পাওয়া যায় নি</string>
<string name="track_finished_reading_date">পড়া শেষ করার তারিখ</string>
<string name="track_started_reading_date">পড়া শুরু করার তারিখ</string>
<string name="crash_log_saved">ক্র্যাশ লগগুলো সংরক্ষিত হয়েছে</string>
<string name="pref_dump_crash_logs_summary">বিকাশকারীদের সাথে সেয়ার করার জন্য এরর লগগুলো একটি ফাইলে সংরক্ষণ করে</string>
<string name="invalid_backup_file_type">অকার্যকর ব্যাকআপ ফাইল ধরণ: %1$s
\nএটা .proto.gz বা .json দিয়ে শেষ হওয়া উচিত।</string>
<string name="full_restore_offline">অফলাইন পুনরুদ্ধার, দ্রুত শেষ হয় তবে আপনার ব্যাকআপে যা ছিল শুধু তাই থাকে</string>
<string name="right_and_left_nav">ডান ও বাম</string>
<string name="edge_nav">কিনার</string>
<string name="kindlish_nav">কিন্ডেলের মতো</string>
<string name="l_nav">L আকারের</string>
<string name="action_desc">অধোগামী</string>
<string name="action_asc">ঊর্ধ্বগামী</string>
<string name="action_order_by_chapter_number">অধ্যায়ের নম্বর অনুসারে</string>
<string name="action_order_by_upload_date">আপলোডের তারিখ অনুসারে</string>
</resources>

View File

@ -98,7 +98,6 @@
<string name="update_24hour">Cada dia</string> <string name="update_24hour">Cada dia</string>
<string name="update_48hour">Cada 2 dies</string> <string name="update_48hour">Cada 2 dies</string>
<string name="update_weekly">Cada setmana</string> <string name="update_weekly">Cada setmana</string>
<string name="update_monthly">Cada mes</string>
<string name="pref_library_update_categories">Categories a incloure a l\'actualització global</string> <string name="pref_library_update_categories">Categories a incloure a l\'actualització global</string>
<string name="all">Tot</string> <string name="all">Tot</string>
<string name="pref_library_update_restriction">Restriccions d\'actualització de la biblioteca</string> <string name="pref_library_update_restriction">Restriccions d\'actualització de la biblioteca</string>
@ -658,4 +657,6 @@
<string name="action_order_by_upload_date">Per data de pujada</string> <string name="action_order_by_upload_date">Per data de pujada</string>
<string name="action_filter_tracked">En seguiment</string> <string name="action_filter_tracked">En seguiment</string>
<string name="action_display_show_number_of_items">Mostra el nombre d\'elements</string> <string name="action_display_show_number_of_items">Mostra el nombre d\'elements</string>
</resources> <string name="right_and_left_nav">A la dreta i a l\'esquerra</string>
<string name="pref_dual_page_split">Dividit a doble pàgina (ALFA)</string>
</resources>

View File

@ -59,7 +59,6 @@
<string name="update_24hour">Denně</string> <string name="update_24hour">Denně</string>
<string name="update_48hour">Každé 2 dny</string> <string name="update_48hour">Každé 2 dny</string>
<string name="update_weekly">Týdně</string> <string name="update_weekly">Týdně</string>
<string name="update_monthly">Měsíčně</string>
<string name="pref_update_only_non_completed">Aktualizovat pouze vycházející mangy</string> <string name="pref_update_only_non_completed">Aktualizovat pouze vycházející mangy</string>
<string name="pref_auto_update_manga_sync">Synchronizovat kapitoly po přečtení</string> <string name="pref_auto_update_manga_sync">Synchronizovat kapitoly po přečtení</string>
<string name="pref_start_screen">Úvodní obrazovka</string> <string name="pref_start_screen">Úvodní obrazovka</string>
@ -402,4 +401,4 @@
<string name="label_sources">Zdroje</string> <string name="label_sources">Zdroje</string>
<string name="spen_previous_page">Předchozí stránka</string> <string name="spen_previous_page">Předchozí stránka</string>
<string name="spen_next_page">Následující stránka</string> <string name="spen_next_page">Následující stránka</string>
</resources> </resources>

View File

@ -51,7 +51,6 @@
<string name="charging">Тулать</string> <string name="charging">Тулать</string>
<string name="pref_library_update_restriction">Вулавӑша ҫӗнетни чарӑвӗсем</string> <string name="pref_library_update_restriction">Вулавӑша ҫӗнетни чарӑвӗсем</string>
<string name="pref_library_update_prioritization">Вулавӑша ҫӗнетни йӗрки</string> <string name="pref_library_update_prioritization">Вулавӑша ҫӗнетни йӗрки</string>
<string name="update_monthly">Кашни уйӑх</string>
<string name="update_weekly">Кашни эрне</string> <string name="update_weekly">Кашни эрне</string>
<string name="update_48hour">Кашни 2 кун</string> <string name="update_48hour">Кашни 2 кун</string>
<string name="update_24hour">Кашни кун</string> <string name="update_24hour">Кашни кун</string>
@ -652,4 +651,4 @@
<string name="action_order_by_chapter_number">Сыпӑк шучӗпе</string> <string name="action_order_by_chapter_number">Сыпӑк шучӗпе</string>
<string name="action_order_by_upload_date">Тиесе илни вӑхӑчӗпе</string> <string name="action_order_by_upload_date">Тиесе илни вӑхӑчӗпе</string>
<string name="action_filter_tracked">Сӑнавланакан</string> <string name="action_filter_tracked">Сӑнавланакан</string>
</resources> </resources>

View File

@ -89,7 +89,6 @@
<string name="update_24hour">Täglich</string> <string name="update_24hour">Täglich</string>
<string name="update_48hour">Alle 2 Tage</string> <string name="update_48hour">Alle 2 Tage</string>
<string name="update_weekly">Wöchentlich</string> <string name="update_weekly">Wöchentlich</string>
<string name="update_monthly">Monatlich</string>
<string name="pref_library_update_categories">Kategorien für die globale Aktualisierung</string> <string name="pref_library_update_categories">Kategorien für die globale Aktualisierung</string>
<string name="all">Alle</string> <string name="all">Alle</string>
<string name="pref_library_update_restriction">Bibliotheksaktualisierung einschränken</string> <string name="pref_library_update_restriction">Bibliotheksaktualisierung einschränken</string>
@ -480,7 +479,7 @@
<string name="webtoon_side_padding_20">20 %</string> <string name="webtoon_side_padding_20">20 %</string>
<string name="webtoon_side_padding_15">15 %</string> <string name="webtoon_side_padding_15">15 %</string>
<string name="webtoon_side_padding_10">10 %</string> <string name="webtoon_side_padding_10">10 %</string>
<string name="webtoon_side_padding_0">Keinen</string> <string name="webtoon_side_padding_0">Keine</string>
<string name="pref_webtoon_side_padding">Seitenränder</string> <string name="pref_webtoon_side_padding">Seitenränder</string>
<string name="pinned_sources">Angeheftet</string> <string name="pinned_sources">Angeheftet</string>
<string name="action_unpin">Loslösen</string> <string name="action_unpin">Loslösen</string>
@ -492,7 +491,7 @@
<string name="add_to_library">Zur Bibliothek hinzufügen</string> <string name="add_to_library">Zur Bibliothek hinzufügen</string>
<string name="confirm_exit">Zum Beenden nochmal die Zurück-Taste drücken</string> <string name="confirm_exit">Zum Beenden nochmal die Zurück-Taste drücken</string>
<string name="information_webview_required">WebView ist für Tachiyomi erforderlich</string> <string name="information_webview_required">WebView ist für Tachiyomi erforderlich</string>
<string name="licenses">Quelloffene Lizenzen</string> <string name="licenses">Open Source-Lizenzen</string>
<string name="website">Website</string> <string name="website">Website</string>
<string name="pref_confirm_exit">Beenden bestätigen</string> <string name="pref_confirm_exit">Beenden bestätigen</string>
<string name="label_downloaded_only">Nur Heruntergeladenes</string> <string name="label_downloaded_only">Nur Heruntergeladenes</string>
@ -658,4 +657,6 @@
<string name="action_order_by_upload_date">Nach Upload-Datum</string> <string name="action_order_by_upload_date">Nach Upload-Datum</string>
<string name="action_filter_tracked">Getrackt</string> <string name="action_filter_tracked">Getrackt</string>
<string name="action_display_show_number_of_items">Anzahl der Elemente anzeigen</string> <string name="action_display_show_number_of_items">Anzahl der Elemente anzeigen</string>
</resources> <string name="right_and_left_nav">Rechts und Links</string>
<string name="pref_dual_page_split">Doppelseitige Aufteilung (Alpha)</string>
</resources>

View File

@ -20,7 +20,7 @@
<string name="action_filter">Φίλτρο</string> <string name="action_filter">Φίλτρο</string>
<string name="action_filter_downloaded">Λήψεις</string> <string name="action_filter_downloaded">Λήψεις</string>
<string name="action_filter_bookmarked">Στους Σελιδοδείκτες</string> <string name="action_filter_bookmarked">Στους Σελιδοδείκτες</string>
<string name="action_filter_unread">Μη αναγνωσμένα</string> <string name="action_filter_unread">Αδιάβαστα</string>
<string name="action_filter_empty">Αφαίρεση φίλτρου</string> <string name="action_filter_empty">Αφαίρεση φίλτρου</string>
<string name="action_sort_alpha">Αλφαβητικά</string> <string name="action_sort_alpha">Αλφαβητικά</string>
<string name="action_sort_total">Σύνολο κεφαλαίων</string> <string name="action_sort_total">Σύνολο κεφαλαίων</string>
@ -54,19 +54,19 @@
<string name="action_previous_chapter">Προηγούμενο κεφάλαιο</string> <string name="action_previous_chapter">Προηγούμενο κεφάλαιο</string>
<string name="action_next_chapter">Επόμενο κεφάλαιο</string> <string name="action_next_chapter">Επόμενο κεφάλαιο</string>
<string name="action_retry">Επανάληψη</string> <string name="action_retry">Επανάληψη</string>
<string name="action_remove">Διαγραφή</string> <string name="action_remove">Αφαίρεση</string>
<string name="action_resume">Συνέχεια</string> <string name="action_resume">Συνέχεια</string>
<string name="action_move">Μεταφορά</string> <string name="action_move">Μετακίνηση</string>
<string name="action_open_in_browser">Άνοιγμα σε πρόγραμμα περιήγησης</string> <string name="action_open_in_browser">Άνοιγμα σε πρόγραμμα περιήγησης</string>
<string name="action_display_mode">Λειτουργία προβολής</string> <string name="action_display_mode">Λειτουργία προβολής</string>
<string name="action_display">Προβολή</string> <string name="action_display">Προβολή</string>
<string name="action_display_grid">Συμπαγές πλέγμα</string> <string name="action_display_grid">Συμπαγές πλέγμα</string>
<string name="action_display_list">Λίστα</string> <string name="action_display_list">Λίστα</string>
<string name="action_display_download_badge">Σήματα ληφθέντων</string> <string name="action_display_download_badge">Σήματα ληφθέντων</string>
<string name="action_cancel">Ακύρωση</string> <string name="action_cancel">Άκυρο</string>
<string name="action_sort">Ταξινόμηση</string> <string name="action_sort">Ταξινόμηση</string>
<string name="action_install">Εγκατάσταση</string> <string name="action_install">Εγκατάσταση</string>
<string name="action_share">Κοινή χρήση</string> <string name="action_share">Κοινοποίηση</string>
<string name="action_save">Αποθήκευση</string> <string name="action_save">Αποθήκευση</string>
<string name="action_reset">Επαναφορά</string> <string name="action_reset">Επαναφορά</string>
<string name="action_undo">Αναίρεση</string> <string name="action_undo">Αναίρεση</string>
@ -98,7 +98,6 @@
<string name="update_24hour">Καθημερινά</string> <string name="update_24hour">Καθημερινά</string>
<string name="update_48hour">Κάθε 2 ημέρες</string> <string name="update_48hour">Κάθε 2 ημέρες</string>
<string name="update_weekly">Εβδομαδιαία</string> <string name="update_weekly">Εβδομαδιαία</string>
<string name="update_monthly">Μηνιαία</string>
<string name="pref_library_update_categories">Κατηγορίες που περιλαμβάνονται στην ολική ενημέρωση</string> <string name="pref_library_update_categories">Κατηγορίες που περιλαμβάνονται στην ολική ενημέρωση</string>
<string name="all">Όλα</string> <string name="all">Όλα</string>
<string name="pref_library_update_restriction">Περιορισμοί ενημέρωσης βιβλιοθήκης</string> <string name="pref_library_update_restriction">Περιορισμοί ενημέρωσης βιβλιοθήκης</string>
@ -282,9 +281,9 @@
<string name="download_1">Επόμενου κεφαλαίου</string> <string name="download_1">Επόμενου κεφαλαίου</string>
<string name="download_5">Επόμενων 5 κεφαλαίων</string> <string name="download_5">Επόμενων 5 κεφαλαίων</string>
<string name="download_10">Επόμενων 10 κεφαλαίων</string> <string name="download_10">Επόμενων 10 κεφαλαίων</string>
<string name="download_custom">Προσαρμοσμένη</string> <string name="download_custom">Προσαρμοσμένο</string>
<string name="download_all">Όλων</string> <string name="download_all">Όλα</string>
<string name="download_unread">Μη αναγνωσμένων</string> <string name="download_unread">Αδιάβαστα</string>
<string name="confirm_delete_chapters">Είστε βέβαιοι ότι θέλετε να διαγράψετε τα επιλεγμένα κεφάλαια;</string> <string name="confirm_delete_chapters">Είστε βέβαιοι ότι θέλετε να διαγράψετε τα επιλεγμένα κεφάλαια;</string>
<string name="manga_tracking_tab">Παρακολούθηση</string> <string name="manga_tracking_tab">Παρακολούθηση</string>
<string name="reading">Ανάγνωση</string> <string name="reading">Ανάγνωση</string>
@ -437,7 +436,7 @@
<string name="email">Διεύθυνση ηλεκτ. ταχυδρομείου</string> <string name="email">Διεύθυνση ηλεκτ. ταχυδρομείου</string>
<string name="check_for_updates">Έλεγχος για ενημερώσεις</string> <string name="check_for_updates">Έλεγχος για ενημερώσεις</string>
<string name="licenses">Άδειες ανοιχτού κώδικα</string> <string name="licenses">Άδειες ανοιχτού κώδικα</string>
<string name="website">Ιστοσελίδα</string> <string name="website">Ιστότοπος</string>
<string name="battery_optimization_disabled">Η βελτιστοποίηση μπαταρίας είναι ήδη απενεργοποιημένη</string> <string name="battery_optimization_disabled">Η βελτιστοποίηση μπαταρίας είναι ήδη απενεργοποιημένη</string>
<string name="pref_disable_battery_optimization_summary">Βοηθά στις ενημερώσεις βιβλιοθήκης και τα αντίγραφα ασφαλείας στο παρασκήνιο</string> <string name="pref_disable_battery_optimization_summary">Βοηθά στις ενημερώσεις βιβλιοθήκης και τα αντίγραφα ασφαλείας στο παρασκήνιο</string>
<string name="pref_disable_battery_optimization">Απενεργοποίηση βελτιστοποίησης μπαταρίας</string> <string name="pref_disable_battery_optimization">Απενεργοποίηση βελτιστοποίησης μπαταρίας</string>
@ -658,4 +657,6 @@
<string name="action_order_by_upload_date">Κατά ημερομηνία μεταφόρτωσης</string> <string name="action_order_by_upload_date">Κατά ημερομηνία μεταφόρτωσης</string>
<string name="action_filter_tracked">Παρακολουθούνται</string> <string name="action_filter_tracked">Παρακολουθούνται</string>
<string name="action_display_show_number_of_items">Εμφάνιση αριθμού στοιχείων</string> <string name="action_display_show_number_of_items">Εμφάνιση αριθμού στοιχείων</string>
</resources> <string name="right_and_left_nav">Δεξιά και Aριστερά</string>
<string name="pref_dual_page_split">Διπλή διαίρεση σελίδας (ALPHA)</string>
</resources>

View File

@ -273,7 +273,6 @@
<string name="app_not_available">Aplicación no disponible</string> <string name="app_not_available">Aplicación no disponible</string>
<string name="short_recent_updates">Actualizaciones</string> <string name="short_recent_updates">Actualizaciones</string>
<string name="update_weekly">Semanalmente</string> <string name="update_weekly">Semanalmente</string>
<string name="update_monthly">Mensualmente</string>
<string name="pref_library_update_categories">Categorías que incluir en actualización global</string> <string name="pref_library_update_categories">Categorías que incluir en actualización global</string>
<string name="all">Todos</string> <string name="all">Todos</string>
<string name="pref_start_screen">Pantalla inicial</string> <string name="pref_start_screen">Pantalla inicial</string>
@ -688,4 +687,4 @@
<string name="action_order_by_chapter_number">Por número de capítulo</string> <string name="action_order_by_chapter_number">Por número de capítulo</string>
<string name="action_order_by_upload_date">Por fecha de subida</string> <string name="action_order_by_upload_date">Por fecha de subida</string>
<string name="action_filter_tracked">Rastreado</string> <string name="action_filter_tracked">Rastreado</string>
</resources> </resources>

View File

@ -348,7 +348,6 @@
<string name="pref_library_update_restriction_summary">در چه شرایطی کتابخانه آپدیت شود</string> <string name="pref_library_update_restriction_summary">در چه شرایطی کتابخانه آپدیت شود</string>
<string name="pref_library_update_restriction">محدودیت‌های آپدیت کتابخانه</string> <string name="pref_library_update_restriction">محدودیت‌های آپدیت کتابخانه</string>
<string name="pref_library_update_prioritization">ترتیب آپدیت کتابخانه</string> <string name="pref_library_update_prioritization">ترتیب آپدیت کتابخانه</string>
<string name="update_monthly">ماهانه</string>
<string name="update_weekly">هفتگی</string> <string name="update_weekly">هفتگی</string>
<string name="update_48hour">هر ۴۸ ساعت یکبار</string> <string name="update_48hour">هر ۴۸ ساعت یکبار</string>
<string name="update_3hour">هر ۳ ساعت یکبار</string> <string name="update_3hour">هر ۳ ساعت یکبار</string>
@ -639,4 +638,4 @@
<string name="pref_label_nsfw_extension">برچسب دهی در لیست افزونه ها</string> <string name="pref_label_nsfw_extension">برچسب دهی در لیست افزونه ها</string>
<string name="pref_show_nsfw_extension">نمایش در لیست افزونه ها</string> <string name="pref_show_nsfw_extension">نمایش در لیست افزونه ها</string>
<string name="pref_show_nsfw_source">نمایش در لیست منابع</string> <string name="pref_show_nsfw_source">نمایش در لیست منابع</string>
</resources> </resources>

View File

@ -161,7 +161,6 @@
<string name="update_24hour">Päivittäin</string> <string name="update_24hour">Päivittäin</string>
<string name="update_48hour">Kahden päivän välein</string> <string name="update_48hour">Kahden päivän välein</string>
<string name="update_weekly">Viikoittain</string> <string name="update_weekly">Viikoittain</string>
<string name="update_monthly">Kuukausittain</string>
<string name="pref_library_update_categories">Globaalin päivityksen sisältämät kategoriat</string> <string name="pref_library_update_categories">Globaalin päivityksen sisältämät kategoriat</string>
<string name="all">Kaikki</string> <string name="all">Kaikki</string>
<string name="pref_library_update_restriction">Kirjaston päivityksen rajoitukset</string> <string name="pref_library_update_restriction">Kirjaston päivityksen rajoitukset</string>
@ -657,4 +656,6 @@
<string name="action_order_by_chapter_number">Luvunumeron mukaan</string> <string name="action_order_by_chapter_number">Luvunumeron mukaan</string>
<string name="action_order_by_upload_date">Lisäyspäivämäärän mukaan</string> <string name="action_order_by_upload_date">Lisäyspäivämäärän mukaan</string>
<string name="action_filter_tracked">Seuratut</string> <string name="action_filter_tracked">Seuratut</string>
</resources> <string name="action_display_show_number_of_items">Näytä kohteiden määrä</string>
<string name="right_and_left_nav">Oikea ja vasen</string>
</resources>

View File

@ -270,7 +270,6 @@
<string name="pref_library_update_restriction_summary">Isapanahon kung nakamit ang (mga) kondisyon</string> <string name="pref_library_update_restriction_summary">Isapanahon kung nakamit ang (mga) kondisyon</string>
<string name="pref_library_update_restriction">Kondisyon sa pag-update</string> <string name="pref_library_update_restriction">Kondisyon sa pag-update</string>
<string name="pref_library_update_prioritization">Ayos ng pagsapanahon</string> <string name="pref_library_update_prioritization">Ayos ng pagsapanahon</string>
<string name="update_monthly">Buwan-buwan</string>
<string name="update_weekly">Linggo-linggo</string> <string name="update_weekly">Linggo-linggo</string>
<string name="update_48hour">Kada 2 araw</string> <string name="update_48hour">Kada 2 araw</string>
<string name="update_24hour">Araw-araw</string> <string name="update_24hour">Araw-araw</string>
@ -639,4 +638,4 @@
<string name="pref_category_nsfw_content">Mga Pinagkukunang NSFW (pang-18+)</string> <string name="pref_category_nsfw_content">Mga Pinagkukunang NSFW (pang-18+)</string>
<string name="myanimelist_relogin">Mag-login muli po sa MAL</string> <string name="myanimelist_relogin">Mag-login muli po sa MAL</string>
<string name="myanimelist_creds_missing">Di makita ang pag-login sa MAL</string> <string name="myanimelist_creds_missing">Di makita ang pag-login sa MAL</string>
</resources> </resources>

View File

@ -106,8 +106,8 @@
<string name="black_background">Noir</string> <string name="black_background">Noir</string>
<string name="pref_viewer_type">Mode de lecture par défaut</string> <string name="pref_viewer_type">Mode de lecture par défaut</string>
<string name="default_viewer">Par défaut</string> <string name="default_viewer">Par défaut</string>
<string name="left_to_right_viewer">Gauche à droite</string> <string name="left_to_right_viewer">De gauche à droite</string>
<string name="right_to_left_viewer">Droite à gauche</string> <string name="right_to_left_viewer">De droite à gauche</string>
<string name="vertical_viewer">Vertical</string> <string name="vertical_viewer">Vertical</string>
<string name="webtoon_viewer">Webtoon</string> <string name="webtoon_viewer">Webtoon</string>
<string name="pref_image_decoder">Décodeur d\'image</string> <string name="pref_image_decoder">Décodeur d\'image</string>
@ -303,7 +303,6 @@
<string name="action_create">Créer</string> <string name="action_create">Créer</string>
<string name="app_not_available">Application indisponible</string> <string name="app_not_available">Application indisponible</string>
<string name="update_weekly">Chaque semaine</string> <string name="update_weekly">Chaque semaine</string>
<string name="update_monthly">Chaque mois</string>
<string name="default_category">Catégorie par défaut</string> <string name="default_category">Catégorie par défaut</string>
<string name="download_notifier_download_paused">Téléchargement en pause</string> <string name="download_notifier_download_paused">Téléchargement en pause</string>
<string name="local_source">Source locale</string> <string name="local_source">Source locale</string>
@ -403,7 +402,7 @@
<string name="migrate">Déplacer</string> <string name="migrate">Déplacer</string>
<string name="copy">Copier</string> <string name="copy">Copier</string>
<string name="pref_read_with_long_tap">Menu contextuel (appui prolongé)</string> <string name="pref_read_with_long_tap">Menu contextuel (appui prolongé)</string>
<string name="action_open_in_web_view">Ouvrir dans le WebView</string> <string name="action_open_in_web_view">Ouvrir dans WebView</string>
<string name="pref_true_color">Couleurs à 32 bits</string> <string name="pref_true_color">Couleurs à 32 bits</string>
<string name="pref_skip_read_chapters">Passer les chapitres marqués comme lus</string> <string name="pref_skip_read_chapters">Passer les chapitres marqués comme lus</string>
<string name="filter_mode_default">Par défaut</string> <string name="filter_mode_default">Par défaut</string>
@ -691,4 +690,6 @@
<string name="action_order_by_chapter_number">Par numéro de chapitre</string> <string name="action_order_by_chapter_number">Par numéro de chapitre</string>
<string name="action_order_by_upload_date">Par date de téléversement</string> <string name="action_order_by_upload_date">Par date de téléversement</string>
<string name="action_filter_tracked">Suivi</string> <string name="action_filter_tracked">Suivi</string>
</resources> <string name="action_display_show_number_of_items">Afficher le nombre dentrées</string>
<string name="right_and_left_nav">Droite et gauche</string>
</resources>

View File

@ -134,7 +134,6 @@
<string name="pref_library_update_restriction_summary">Actualizar soamente cando as condicións se cumpren</string> <string name="pref_library_update_restriction_summary">Actualizar soamente cando as condicións se cumpren</string>
<string name="pref_library_update_restriction">Restriccións de actualización da biblioteca</string> <string name="pref_library_update_restriction">Restriccións de actualización da biblioteca</string>
<string name="pref_library_update_prioritization">Orden de actualización da biblioteca</string> <string name="pref_library_update_prioritization">Orden de actualización da biblioteca</string>
<string name="update_monthly">Mensualmente</string>
<string name="update_weekly">Semanalmente</string> <string name="update_weekly">Semanalmente</string>
<string name="update_48hour">Cada 2 días</string> <string name="update_48hour">Cada 2 días</string>
<string name="update_24hour">Diariamente</string> <string name="update_24hour">Diariamente</string>
@ -311,4 +310,4 @@
<string name="label_backup">Copia de seguridade</string> <string name="label_backup">Copia de seguridade</string>
<string name="label_categories">Categorías</string> <string name="label_categories">Categorías</string>
<string name="manga">Manga</string> <string name="manga">Manga</string>
</resources> </resources>

View File

@ -200,7 +200,6 @@
<string name="pref_library_update_restriction_summary">עדכן רק כאשר מתקיימים התנאים</string> <string name="pref_library_update_restriction_summary">עדכן רק כאשר מתקיימים התנאים</string>
<string name="pref_library_update_restriction">מגבלות עדכון הספריה</string> <string name="pref_library_update_restriction">מגבלות עדכון הספריה</string>
<string name="pref_library_update_prioritization">סדר עדכון הספריה</string> <string name="pref_library_update_prioritization">סדר עדכון הספריה</string>
<string name="update_monthly">פעם בחודש</string>
<string name="update_weekly">פעם בשבוע</string> <string name="update_weekly">פעם בשבוע</string>
<string name="update_48hour">פעם ביומיים</string> <string name="update_48hour">פעם ביומיים</string>
<string name="update_1hour">פעם בשעה</string> <string name="update_1hour">פעם בשעה</string>

View File

@ -95,7 +95,6 @@
<string name="update_24hour">हर रोज़</string> <string name="update_24hour">हर रोज़</string>
<string name="update_48hour">हर २ दिन</string> <string name="update_48hour">हर २ दिन</string>
<string name="update_weekly">साप्ताहिक</string> <string name="update_weekly">साप्ताहिक</string>
<string name="update_monthly">मासिक</string>
<string name="pref_library_update_categories">वैश्विक श्रेणियाँ जो अद्यतन में शामिल करनी है</string> <string name="pref_library_update_categories">वैश्विक श्रेणियाँ जो अद्यतन में शामिल करनी है</string>
<string name="all">समस्त</string> <string name="all">समस्त</string>
<string name="pref_library_update_restriction">पुस्तकालय अद्यतन प्रतिबंध</string> <string name="pref_library_update_restriction">पुस्तकालय अद्यतन प्रतिबंध</string>
@ -598,9 +597,9 @@
<string name="backup_restore_missing_trackers">ट्रैकर्स में लॉग इन नहीं किया गया:</string> <string name="backup_restore_missing_trackers">ट्रैकर्स में लॉग इन नहीं किया गया:</string>
<string name="pref_remove_bookmarked_chapters">बुकमार्क अध्यायों को हटाएं</string> <string name="pref_remove_bookmarked_chapters">बुकमार्क अध्यायों को हटाएं</string>
<string name="pref_category_delete_chapters">अध्यायों को हटाएं</string> <string name="pref_category_delete_chapters">अध्यायों को हटाएं</string>
<string name="ext_nsfw_warning">18+ सामग्री हो सकती है</string> <string name="ext_nsfw_warning">इसमें NSFW (18+) सामग्री हो सकती है</string>
<string name="ext_nsfw_short">18+</string> <string name="ext_nsfw_short">18+</string>
<string name="parental_controls_info">यह अनौपचारिक या संभावित रूप से फ़्लैग किए गए एक्सटेंशन को ऐप के भीतर 18+ सामग्री के सामने आने से नहीं रोकता है।</string> <string name="parental_controls_info">यह अनौपचारिक या संभावित रूप से फ़्लैग किए गए एक्सटेंशन को ऐप के भीतर NSFW (18+) सामग्री के सामने आने से नहीं रोकता है।</string>
<plurals name="missing_chapters_warning"> <plurals name="missing_chapters_warning">
<item quantity="one">1 लापता अध्याय है</item> <item quantity="one">1 लापता अध्याय है</item>
<item quantity="other">%d लापता अध्याय हैं</item> <item quantity="other">%d लापता अध्याय हैं</item>
@ -622,7 +621,8 @@
<string name="pref_clear_history">इतिहास मिटा दें</string> <string name="pref_clear_history">इतिहास मिटा दें</string>
<string name="clear_history_confirmation">क्या आपको यकीन है\? सारा इतिहास खो जाएगा।</string> <string name="clear_history_confirmation">क्या आपको यकीन है\? सारा इतिहास खो जाएगा।</string>
<string name="clear_history_completed">इतिहास हटाया गया</string> <string name="clear_history_completed">इतिहास हटाया गया</string>
<string name="invalid_backup_file_type">अमान्य बैकअप फ़ाइल: %1$s</string> <string name="invalid_backup_file_type">अमान्य बैकअप फ़ाइल प्रकार: %1$s
\nयह .proto.gz या .json के साथ समाप्त होना चाहिए।</string>
<string name="spen_next_page">अगला पृष्ठ</string> <string name="spen_next_page">अगला पृष्ठ</string>
<string name="spen_previous_page">पिछला पृष्ठ</string> <string name="spen_previous_page">पिछला पृष्ठ</string>
<string name="migration_help_guide">स्रोत माइग्रेशन गाइड</string> <string name="migration_help_guide">स्रोत माइग्रेशन गाइड</string>
@ -635,5 +635,27 @@
<string name="pref_label_nsfw_extension">एक्सटेंशन सूची में लेबल</string> <string name="pref_label_nsfw_extension">एक्सटेंशन सूची में लेबल</string>
<string name="pref_show_nsfw_extension">एक्सटेंशन सूची में दिखाएं</string> <string name="pref_show_nsfw_extension">एक्सटेंशन सूची में दिखाएं</string>
<string name="pref_show_nsfw_source">स्रोत सूची में दिखाएं</string> <string name="pref_show_nsfw_source">स्रोत सूची में दिखाएं</string>
<string name="pref_category_nsfw_content">18+ सामग्री</string> <string name="pref_category_nsfw_content">NSFW (18+)्रोत</string>
</resources> <string name="channel_crash_logs">क्रैश लॉग</string>
<string name="file_picker_error">कोई फ़ाइल पिकर ऐप नहीं मिला</string>
<string name="myanimelist_relogin">कृपया फिर से MAL पर लॉगिन करें</string>
<string name="myanimelist_creds_missing">MAL लॉगिन क्रेडेंशियल नहीं मिला</string>
<string name="track_finished_reading_date">पढ़ना समाप्त की तिथि</string>
<string name="track_started_reading_date">पढ़ना शुरू करने की तारीख</string>
<string name="crash_log_saved">क्रैश लॉग सहेजे गए</string>
<string name="pref_dump_crash_logs_summary">डेवलपर्स के साथ साझा करने के लिए फ़ाइल में त्रुटि लॉग सहेजता है</string>
<string name="pref_dump_crash_logs">डंप क्रैश लॉग</string>
<string name="pref_viewer_nav">नेविगेशन लेआउट</string>
<string name="right_and_left_nav">दाएं और बाएं</string>
<string name="edge_nav">धार</string>
<string name="kindlish_nav">किंडल-ईश</string>
<string name="l_nav">एल आकार</string>
<string name="default_nav">डिफॉल्ट</string>
<string name="network_unmetered">अनमीटर्ड नेटवर्क</string>
<string name="action_desc">अवरोही</string>
<string name="action_asc">आरोही</string>
<string name="action_order_by_chapter_number">अध्याय संख्या से</string>
<string name="action_order_by_upload_date">अपलोड तिथि से</string>
<string name="action_display_show_number_of_items">वस्तुओं की संख्या दिखाएं</string>
<string name="action_filter_tracked">ट्रैक किए गए</string>
</resources>

View File

@ -45,7 +45,6 @@
<string name="pref_library_update_restriction_summary">Ažuriraj samo kad su ispunjeni uvjeti</string> <string name="pref_library_update_restriction_summary">Ažuriraj samo kad su ispunjeni uvjeti</string>
<string name="pref_library_update_restriction">Ograničenja za ažuriranje biblioteke</string> <string name="pref_library_update_restriction">Ograničenja za ažuriranje biblioteke</string>
<string name="pref_library_update_prioritization">Redoslijed ažuriranja biblioteke</string> <string name="pref_library_update_prioritization">Redoslijed ažuriranja biblioteke</string>
<string name="update_monthly">Mjesečno</string>
<string name="update_weekly">Tjedno</string> <string name="update_weekly">Tjedno</string>
<string name="update_48hour">Svaki drugi dan</string> <string name="update_48hour">Svaki drugi dan</string>
<string name="update_24hour">Dnevno</string> <string name="update_24hour">Dnevno</string>
@ -669,4 +668,4 @@
<string name="action_order_by_chapter_number">Po broju poglavlja</string> <string name="action_order_by_chapter_number">Po broju poglavlja</string>
<string name="action_order_by_upload_date">Po datumu preuzimanja</string> <string name="action_order_by_upload_date">Po datumu preuzimanja</string>
<string name="action_filter_tracked">Praćeno</string> <string name="action_filter_tracked">Praćeno</string>
</resources> </resources>

View File

@ -46,7 +46,6 @@
<string name="update_24hour">Naponta</string> <string name="update_24hour">Naponta</string>
<string name="update_48hour">2 naponta</string> <string name="update_48hour">2 naponta</string>
<string name="update_weekly">Hetente</string> <string name="update_weekly">Hetente</string>
<string name="update_monthly">Havonta</string>
<string name="pref_library_update_categories">Globális frissítésben tartalmazott kategóriák</string> <string name="pref_library_update_categories">Globális frissítésben tartalmazott kategóriák</string>
<string name="all">Összes</string> <string name="all">Összes</string>
<string name="pref_library_update_restriction">Könyvtárfrissítési korlátozások</string> <string name="pref_library_update_restriction">Könyvtárfrissítési korlátozások</string>
@ -274,4 +273,4 @@
<string name="ext_version_info">Verzió: %1$s</string> <string name="ext_version_info">Verzió: %1$s</string>
<string name="unofficial_extension_message">Ez a bővítmény nem a hivatalos Tachiyomi bővítménylistából származik.</string> <string name="unofficial_extension_message">Ez a bővítmény nem a hivatalos Tachiyomi bővítménylistából származik.</string>
<string name="obsolete_extension_message">Ez a bővítmény nem elérhető többé.</string> <string name="obsolete_extension_message">Ez a bővítmény nem elérhető többé.</string>
</resources> </resources>

View File

@ -91,7 +91,6 @@
<string name="update_24hour">Tiap hari</string> <string name="update_24hour">Tiap hari</string>
<string name="update_48hour">Tiap 2 hari</string> <string name="update_48hour">Tiap 2 hari</string>
<string name="update_weekly">Tiap minggu</string> <string name="update_weekly">Tiap minggu</string>
<string name="update_monthly">Tiap bulan</string>
<string name="pref_library_update_categories">Kategori untuk disertakan dalam pembaruan global</string> <string name="pref_library_update_categories">Kategori untuk disertakan dalam pembaruan global</string>
<string name="all">Semua</string> <string name="all">Semua</string>
<string name="pref_library_update_restriction">Pembatasan pembaruan perpustakaan</string> <string name="pref_library_update_restriction">Pembatasan pembaruan perpustakaan</string>
@ -501,7 +500,7 @@
<item quantity="other">%1$s lagi</item> <item quantity="other">%1$s lagi</item>
</plurals> </plurals>
<string name="downloaded_only_summary">Filter semua manga di Perpustakaan</string> <string name="downloaded_only_summary">Filter semua manga di Perpustakaan</string>
<string name="check_for_updates">Cek pembaruan</string> <string name="check_for_updates">Periksa pembaruan</string>
<string name="restoring_backup_canceled">Pemulihan dibatalkan</string> <string name="restoring_backup_canceled">Pemulihan dibatalkan</string>
<string name="restore_in_progress">Pemulihan masih dalam proses</string> <string name="restore_in_progress">Pemulihan masih dalam proses</string>
<string name="backup_in_progress">Pencadangan masih dalam proses</string> <string name="backup_in_progress">Pencadangan masih dalam proses</string>
@ -611,7 +610,8 @@
<string name="clear_history_completed">Riwayat telah dihapus</string> <string name="clear_history_completed">Riwayat telah dihapus</string>
<string name="pref_hide_bottom_bar_on_scroll">Sembunyikan bilah bawah saat menggulirkan halaman</string> <string name="pref_hide_bottom_bar_on_scroll">Sembunyikan bilah bawah saat menggulirkan halaman</string>
<string name="pref_category_nsfw_content">Sumber NSFW (18+)</string> <string name="pref_category_nsfw_content">Sumber NSFW (18+)</string>
<string name="invalid_backup_file_type">Cadangan tidak valid: %1$s</string> <string name="invalid_backup_file_type">Tipe file berkas cadangan tidak valid: %1$s
\nHarusnya nama file berakhir dengan .proto.gz atau .json.</string>
<string name="pref_backup_auto_create_legacy">Buat cadangan versi lama juga</string> <string name="pref_backup_auto_create_legacy">Buat cadangan versi lama juga</string>
<string name="pref_create_legacy_backup">Buat cadangan versi</string> <string name="pref_create_legacy_backup">Buat cadangan versi</string>
<string name="spen_next_page">Halaman selanjutnya</string> <string name="spen_next_page">Halaman selanjutnya</string>
@ -635,4 +635,14 @@
<string name="action_order_by_chapter_number">Berdasarkan nomor bab</string> <string name="action_order_by_chapter_number">Berdasarkan nomor bab</string>
<string name="action_order_by_upload_date">Berdasarkan tanggal unggahan</string> <string name="action_order_by_upload_date">Berdasarkan tanggal unggahan</string>
<string name="action_filter_tracked">Dilacak</string> <string name="action_filter_tracked">Dilacak</string>
</resources> <string name="pref_dump_crash_logs">Buang log kerusakan</string>
<string name="channel_crash_logs">Log kerusakan</string>
<string name="track_finished_reading_date">Tanggal selesai membaca</string>
<string name="track_started_reading_date">Tanggal mulai membaca</string>
<string name="crash_log_saved">Log kerusakan disimpan</string>
<string name="pref_dump_crash_logs_summary">Simpan log kesalahan ke sebuah file untuk dibagikan dengan pengembang aplikasi</string>
<string name="pref_viewer_nav">Tata letak navigasi</string>
<string name="right_and_left_nav">Kanan dan Kiri</string>
<string name="network_unmetered">Jaringan yang tidak terukur</string>
<string name="action_display_show_number_of_items">Tampilkan jumlah item</string>
</resources>

View File

@ -299,7 +299,6 @@
<string name="app_not_available">App non disponibile</string> <string name="app_not_available">App non disponibile</string>
<string name="short_recent_updates">Aggiornamenti</string> <string name="short_recent_updates">Aggiornamenti</string>
<string name="update_weekly">Settimanalmente</string> <string name="update_weekly">Settimanalmente</string>
<string name="update_monthly">Mensilmente</string>
<string name="default_category">Categoria predefinita</string> <string name="default_category">Categoria predefinita</string>
<string name="track">Monitoraggio</string> <string name="track">Monitoraggio</string>
<string name="pref_category_tracking">Monitoraggio</string> <string name="pref_category_tracking">Monitoraggio</string>
@ -630,7 +629,7 @@
<string name="no_pinned_sources">Non hai fonti fissate</string> <string name="no_pinned_sources">Non hai fonti fissate</string>
<string name="pref_remove_bookmarked_chapters">Elimina capitoli contrassegnati</string> <string name="pref_remove_bookmarked_chapters">Elimina capitoli contrassegnati</string>
<string name="pref_category_delete_chapters">Elimina capitoli</string> <string name="pref_category_delete_chapters">Elimina capitoli</string>
<string name="ext_nsfw_warning">Potrebbe contenere materiale 18+</string> <string name="ext_nsfw_warning">Potrebbe contenere materiale per adulti</string>
<string name="ext_nsfw_short">18+</string> <string name="ext_nsfw_short">18+</string>
<plurals name="missing_chapters_warning"> <plurals name="missing_chapters_warning">
<item quantity="one">C\'è un capitolo mancante</item> <item quantity="one">C\'è un capitolo mancante</item>
@ -638,7 +637,7 @@
</plurals> </plurals>
<string name="backup_restore_missing_trackers">Trackers non collegati:</string> <string name="backup_restore_missing_trackers">Trackers non collegati:</string>
<string name="no_chapters_error">Nessun capitolo trovato</string> <string name="no_chapters_error">Nessun capitolo trovato</string>
<string name="parental_controls_info">Questo non impedisce a estensioni non ufficiali o potenzialmente segnalate in modo errato di far mostrare contenuti 18+ all\'interno dell\'app.</string> <string name="parental_controls_info">Non impedisce ad estensioni non ufficiali o classificate in modo errato/fuorviante di mostrare contenuti per adulti all\'interno dell\'app.</string>
<string name="chapter_settings_updated">Impostazioni predefinite del capitolo aggiornate</string> <string name="chapter_settings_updated">Impostazioni predefinite del capitolo aggiornate</string>
<string name="share_page_info">%1$s: %2$s, pagina %3$d</string> <string name="share_page_info">%1$s: %2$s, pagina %3$d</string>
<string name="set_chapter_settings_as_default">Imposta come predefinito</string> <string name="set_chapter_settings_as_default">Imposta come predefinito</string>
@ -658,7 +657,8 @@
<string name="spen_next_page">Pagina successiva</string> <string name="spen_next_page">Pagina successiva</string>
<string name="spen_previous_page">Pagina precedente</string> <string name="spen_previous_page">Pagina precedente</string>
<string name="migration_help_guide">Guida alla migrazione di origine</string> <string name="migration_help_guide">Guida alla migrazione di origine</string>
<string name="invalid_backup_file_type">File di backup invalido: %1$s</string> <string name="invalid_backup_file_type">File di backup non valido: %1$s
\nDovrebbe terminare con .proto.gz oppure .json.</string>
<string name="full_restore_offline">Ripristina fuori linea, termina rapidamente ma contiene solo ciò che ha il backup</string> <string name="full_restore_offline">Ripristina fuori linea, termina rapidamente ma contiene solo ciò che ha il backup</string>
<string name="full_restore_online">Ripristina in linea, molto più lentamente ma fornisce informazioni e capitoli più aggiornati</string> <string name="full_restore_online">Ripristina in linea, molto più lentamente ma fornisce informazioni e capitoli più aggiornati</string>
<string name="full_restore_mode">Modalità rete</string> <string name="full_restore_mode">Modalità rete</string>
@ -677,4 +677,18 @@
<string name="edge_nav">Bordo</string> <string name="edge_nav">Bordo</string>
<string name="kindlish_nav">Tipo un Kindle</string> <string name="kindlish_nav">Tipo un Kindle</string>
<string name="l_nav">A forma di L</string> <string name="l_nav">A forma di L</string>
</resources> <string name="channel_crash_logs">Resoconto dei crash</string>
<string name="track_finished_reading_date">Data di fine lettura</string>
<string name="track_started_reading_date">Data di inizio lettura</string>
<string name="crash_log_saved">Resoconto salvato</string>
<string name="pref_dump_crash_logs_summary">Salva un resoconto degli errori su un file per condividerlo con gli sviluppatori</string>
<string name="pref_dump_crash_logs">Salva un resoconto dei crash</string>
<string name="network_unmetered">Connessione illimitata</string>
<string name="action_desc">Decrescente</string>
<string name="action_asc">Crescente</string>
<string name="action_order_by_chapter_number">Per numero di capitolo</string>
<string name="action_order_by_upload_date">Per data di caricamento</string>
<string name="action_filter_tracked">Tracciati</string>
<string name="action_display_show_number_of_items">Elementi visualizzati</string>
<string name="right_and_left_nav">Navigazione destra/sinistra</string>
</resources>

View File

@ -81,7 +81,6 @@
<string name="update_24hour">毎日</string> <string name="update_24hour">毎日</string>
<string name="update_48hour">2日ごと</string> <string name="update_48hour">2日ごと</string>
<string name="update_weekly">毎週</string> <string name="update_weekly">毎週</string>
<string name="update_monthly">毎月</string>
<string name="pref_library_update_categories">グローバル更新に含まれるカテゴリ</string> <string name="pref_library_update_categories">グローバル更新に含まれるカテゴリ</string>
<string name="all">すべて</string> <string name="all">すべて</string>
<string name="pref_library_update_restriction">ライブラリ更新制限</string> <string name="pref_library_update_restriction">ライブラリ更新制限</string>
@ -619,7 +618,8 @@
<string name="pref_create_legacy_backup_summary">古いバージョンのTachiyomiで利用可能</string> <string name="pref_create_legacy_backup_summary">古いバージョンのTachiyomiで利用可能</string>
<string name="pref_create_legacy_backup">レガシー バックアップを作成</string> <string name="pref_create_legacy_backup">レガシー バックアップを作成</string>
<string name="migration_help_guide">ソース移行ガイド</string> <string name="migration_help_guide">ソース移行ガイド</string>
<string name="invalid_backup_file_type">無効なバックアップ ファイル:%1$s</string> <string name="invalid_backup_file_type">無効なバックアップ ファイル:%1$s
\nファイル拡張子は.proto.gzもしくは.jsonであるよう確認してください。</string>
<string name="pref_category_nsfw_content">成人向けのソース</string> <string name="pref_category_nsfw_content">成人向けのソース</string>
<string name="pref_label_nsfw_extension">拡張機能リストでマーク</string> <string name="pref_label_nsfw_extension">拡張機能リストでマーク</string>
<string name="pref_show_nsfw_extension">拡張機能リストに表示</string> <string name="pref_show_nsfw_extension">拡張機能リストに表示</string>
@ -627,4 +627,23 @@
<string name="file_picker_error">ファイルを選択できるアプリが見つかりません</string> <string name="file_picker_error">ファイルを選択できるアプリが見つかりません</string>
<string name="myanimelist_relogin">もう一度MALにログインしてください</string> <string name="myanimelist_relogin">もう一度MALにログインしてください</string>
<string name="myanimelist_creds_missing">MALログイン資格情報が見つかりません</string> <string name="myanimelist_creds_missing">MALログイン資格情報が見つかりません</string>
</resources> <string name="action_display_show_number_of_items">アイテム数を表示する</string>
<string name="channel_crash_logs">クラッシュログ</string>
<string name="track_finished_reading_date">読み終わった日付</string>
<string name="track_started_reading_date">読み始めた日付</string>
<string name="crash_log_saved">クラッシュログが保存されました</string>
<string name="edge_nav"></string>
<string name="default_nav">デフォルト</string>
<string name="action_order_by_chapter_number">章の番号順</string>
<string name="action_order_by_upload_date">アップロードされた日付順</string>
<string name="action_filter_tracked">登録済み</string>
<string name="pref_dump_crash_logs_summary">開発者に渡すよう、エラー ログを保存します</string>
<string name="pref_dump_crash_logs">クラッシュ ログをダンプ</string>
<string name="pref_viewer_nav">ナビゲーションレイアウト</string>
<string name="right_and_left_nav">右と左</string>
<string name="kindlish_nav">Kindleスタイル</string>
<string name="l_nav">L形</string>
<string name="network_unmetered">従量制データではないネットワーク</string>
<string name="action_desc">降順</string>
<string name="action_asc">昇順</string>
</resources>

View File

@ -130,7 +130,6 @@
<string name="update_24hour">ყოველდღე</string> <string name="update_24hour">ყოველდღე</string>
<string name="update_48hour">ყოველ 2 დღეში ერთხელ</string> <string name="update_48hour">ყოველ 2 დღეში ერთხელ</string>
<string name="update_weekly">ყოველ კვირა</string> <string name="update_weekly">ყოველ კვირა</string>
<string name="update_monthly">ყოველთვე</string>
<string name="pref_library_update_prioritization">ბიბლიოთეკის განახლების რიგითობა</string> <string name="pref_library_update_prioritization">ბიბლიოთეკის განახლების რიგითობა</string>
<string name="pref_library_update_restriction">ბიბლიოთეკის განახლების შეზღუდვები</string> <string name="pref_library_update_restriction">ბიბლიოთეკის განახლების შეზღუდვები</string>
<string name="pref_library_update_restriction_summary">განაახლე მხოლოდ როცა პირობები დაკმაყოფილდება</string> <string name="pref_library_update_restriction_summary">განაახლე მხოლოდ როცა პირობები დაკმაყოფილდება</string>
@ -253,12 +252,12 @@
<string name="backup_created">რეზერვი შექმნილია</string> <string name="backup_created">რეზერვი შექმნილია</string>
<string name="invalid_backup_file">არასწორი სარეზერვო ფაილი</string> <string name="invalid_backup_file">არასწორი სარეზერვო ფაილი</string>
<string name="invalid_backup_file_missing_data">ფაილს აკლია მონაცემები.</string> <string name="invalid_backup_file_missing_data">ფაილს აკლია მონაცემები.</string>
<string name="invalid_backup_file_missing_manga">რეზერვი არ შეიცავს არცერთ მანგას</string> <string name="invalid_backup_file_missing_manga">რეზერვი არ შეიცავს არცერთ მანგას.</string>
<string name="backup_restore_missing_sources">დაკარგული წყაროები:</string> <string name="backup_restore_missing_sources">დაკარგული წყაროები:</string>
<string name="restore_completed">აღდგენა შესრულებულია</string> <string name="restore_completed">აღდგენა შესრულებულია</string>
<string name="restore_duration">%02d წუთი, %02d წამი</string> <string name="restore_duration">%02d წუთი, %02d წამი</string>
<string name="backup_in_progress">რეზერვის შემქნა პროგრესშია</string> <string name="backup_in_progress">რეზერვის შემქნა პროგრესშია</string>
<string name="backup_choice">რას გსურს რომ შეუქმნა რეზერვი</string> <string name="backup_choice">რას გსურს რომ შეუქმნა რეზერვი\?</string>
<string name="creating_backup">რეზერვის შექმნა</string> <string name="creating_backup">რეზერვის შექმნა</string>
<string name="creating_backup_error">რეზერვის შექმნა ვერ მოხერხდა</string> <string name="creating_backup_error">რეზერვის შექმნა ვერ მოხერხდა</string>
<string name="restore_in_progress">აღდგენა უკვე მიმდინარეობს</string> <string name="restore_in_progress">აღდგენა უკვე მიმდინარეობს</string>
@ -333,7 +332,7 @@
<string name="manga_removed_library">წაშლილია ბიბლიოთეკიდან</string> <string name="manga_removed_library">წაშლილია ბიბლიოთეკიდან</string>
<string name="manga_info_expand">მეტი ინფორმაციის ჩვენება</string> <string name="manga_info_expand">მეტი ინფორმაციის ჩვენება</string>
<string name="manga_info_collapse">ნაკლები ინფორმაციის ჩვენება</string> <string name="manga_info_collapse">ნაკლები ინფორმაციის ჩვენება</string>
<string name="delete_downloads_for_manga">გადმოწერილი თავების წაშლა</string> <string name="delete_downloads_for_manga">წაიშალოს გადმოწერილი თავები\?</string>
<string name="copied_to_clipboard">დაკოპირებულია ბუფერში: <string name="copied_to_clipboard">დაკოპირებულია ბუფერში:
\n%1$s</string> \n%1$s</string>
<string name="source_not_installed">წყარო არ არის დაინსტალირებული: %1$s</string> <string name="source_not_installed">წყარო არ არის დაინსტალირებული: %1$s</string>
@ -392,7 +391,7 @@
<string name="downloading">გადმოწერა…</string> <string name="downloading">გადმოწერა…</string>
<string name="download_progress">გადმოწერილია %1$d%%</string> <string name="download_progress">გადმოწერილია %1$d%%</string>
<string name="chapter_progress">გვერდი: %1$d</string> <string name="chapter_progress">გვერდი: %1$d</string>
<string name="chapter_subtitle">თავი: %1$d</string> <string name="chapter_subtitle">თავი: %1$s</string>
<string name="no_next_chapter">შემდეგი თავი ვერ მოიძებნა</string> <string name="no_next_chapter">შემდეგი თავი ვერ მოიძებნა</string>
<string name="no_previous_chapter">წინა თავი ვერ მოიძებნა</string> <string name="no_previous_chapter">წინა თავი ვერ მოიძებნა</string>
<string name="decode_image_error">სურათის გადმოტვირთვა ვერ მოხერხდა</string> <string name="decode_image_error">სურათის გადმოტვირთვა ვერ მოხერხდა</string>
@ -408,7 +407,7 @@
<string name="transition_pages_error">გვერდების ჩატვირთვა ვერ მოხერხდა: %1$s</string> <string name="transition_pages_error">გვერდების ჩატვირთვა ვერ მოხერხდა: %1$s</string>
<string name="page_list_empty_error">ვერცერთი გვერდი ვერ მოიძევნა</string> <string name="page_list_empty_error">ვერცერთი გვერდი ვერ მოიძევნა</string>
<string name="updating_library">ბიბლიოთეკის განახლება</string> <string name="updating_library">ბიბლიოთეკის განახლება</string>
<string name="recent_manga_time">თავ. %1$s - %2$</string> <string name="recent_manga_time">თავ. %1$s - %2$s</string>
<string name="migration_info">დააჭირე რომ მონიშნო წყარო საიდანაც გინდა მიგრაცია</string> <string name="migration_info">დააჭირე რომ მონიშნო წყარო საიდანაც გინდა მიგრაცია</string>
<string name="migration_dialog_what_to_include">ამოირჩიე მონაცემები შესაყვანად</string> <string name="migration_dialog_what_to_include">ამოირჩიე მონაცემები შესაყვანად</string>
<string name="migration_selection_prompt">ამოირჩიე წყარო საიდანაც გინდა მიგრაცია</string> <string name="migration_selection_prompt">ამოირჩიე წყარო საიდანაც გინდა მიგრაცია</string>
@ -441,7 +440,7 @@
<string name="description_cover">მანგას ყდა</string> <string name="description_cover">მანგას ყდა</string>
<string name="information_no_downloads">გადმოწერები არ არის</string> <string name="information_no_downloads">გადმოწერები არ არის</string>
<string name="information_no_recent">განახლებები არ არის</string> <string name="information_no_recent">განახლებები არ არის</string>
<string name="information_empty_library">შენი ბიბლიოთეკა ცარიელია, დაამატე სერიები შენს ბიბლიოთეკაში \"დაათვალიერე\"-დან</string> <string name="information_empty_library">შენი ბიბლიოთეკა ცარიელია, დაამატე სერიები შენს ბიბლიოთეკაში \"დაათვალიერე\"-დან.</string>
<string name="download_notifier_text_only_wifi">Wi-Fi კავშირი არ არის ხელმისაწვდომი</string> <string name="download_notifier_text_only_wifi">Wi-Fi კავშირი არ არის ხელმისაწვდომი</string>
<string name="download_notifier_no_network">ინტერნეტთან კავშირი არ არის ხელმისაწვდომი</string> <string name="download_notifier_no_network">ინტერნეტთან კავშირი არ არის ხელმისაწვდომი</string>
<string name="download_notifier_download_paused">გადმოწერა დაპაუზებულია</string> <string name="download_notifier_download_paused">გადმოწერა დაპაუზებულია</string>
@ -472,8 +471,8 @@
<string name="ext_trust">ნდობა</string> <string name="ext_trust">ნდობა</string>
<string name="ext_untrusted">არასანდო</string> <string name="ext_untrusted">არასანდო</string>
<string name="untrusted_extension">არასანდო დამატება</string> <string name="untrusted_extension">არასანდო დამატება</string>
<string name="obsolete_extension_message">დამატება აღარ არის ხელმისაწვდომი</string> <string name="obsolete_extension_message">დამატება აღარ არის ხელმისაწვდომი.</string>
<string name="unofficial_extension_message">დამატება არ არის Tachiyomi-ს ოფიციალური დამატებების სიიდან</string> <string name="unofficial_extension_message">დამატება არ არის Tachiyomi-ს ოფიციალური დამატებების სიიდან.</string>
<string name="pref_double_tap_anim_speed">ორჯერ დაჭერისას ანიმაციის სისწრაფე</string> <string name="pref_double_tap_anim_speed">ორჯერ დაჭერისას ანიმაციის სისწრაფე</string>
<string name="pref_true_color_summary">აუმჯობესებს ხარისხს, თუმცა ამცირებს წარმადობას</string> <string name="pref_true_color_summary">აუმჯობესებს ხარისხს, თუმცა ამცირებს წარმადობას</string>
<string name="pref_custom_brightness">გამოიყენე პერსონალიზებული სიკაშკაშე</string> <string name="pref_custom_brightness">გამოიყენე პერსონალიზებული სიკაშკაშე</string>
@ -497,7 +496,7 @@
<string name="page_downloaded">გვერდი დაკოპირდა %1$s -ში</string> <string name="page_downloaded">გვერდი დაკოპირდა %1$s -ში</string>
<string name="confirm_set_image_as_cover">გამოვიყენოთ ეს სურათი ყდის ნახატად?</string> <string name="confirm_set_image_as_cover">გამოვიყენოთ ეს სურათი ყდის ნახატად?</string>
<string name="information_no_recent_manga">ბოლო ხანებში არაფერი არ არის წაკითხული</string> <string name="information_no_recent_manga">ბოლო ხანებში არაფერი არ არის წაკითხული</string>
<string name="information_empty_category">შენ არ გაქვს კატეგორიები, აირჩიე \"+\" ღილაკი რათა შექმნა ერთი, შენი ბიბლიოთეკის დასაორგანიზებლად</string> <string name="information_empty_category">შენ არ გაქვს კატეგორიები, აირჩიე \"+\" ღილაკი რათა შექმნა ერთი, შენი ბიბლიოთეკის დასაორგანიზებლად.</string>
<string name="information_webview_required">WebView არის აუცილებელი Tachiyomi-ს სამუშაოდ</string> <string name="information_webview_required">WebView არის აუცილებელი Tachiyomi-ს სამუშაოდ</string>
<string name="information_webview_outdated">გთხოვთ განაახლოთ WebView აპლიკაცია უკეთესი თავსებდობისთვის</string> <string name="information_webview_outdated">გთხოვთ განაახლოთ WebView აპლიკაცია უკეთესი თავსებდობისთვის</string>
<string name="download_notifier_downloader_title">გადმომწერი</string> <string name="download_notifier_downloader_title">გადმომწერი</string>
@ -507,15 +506,15 @@
<string name="action_enable_all">ჩართე ყველა</string> <string name="action_enable_all">ჩართე ყველა</string>
<string name="action_disable_all">გამორთე ყველა</string> <string name="action_disable_all">გამორთე ყველა</string>
<string name="secure_screen_summary">აპლიკაციებს შორის გადართვისას შემადგენლობის დამალვა და სკრინშოტების დაბლოკვა</string> <string name="secure_screen_summary">აპლიკაციებს შორის გადართვისას შემადგენლობის დამალვა და სკრინშოტების დაბლოკვა</string>
<string name="untrusted_extension_message">ეს დამატება ხელმოწერილია უცნობი სერთიფიკატის მიერ და არ არის აქტივირებული\\n\\nმავნე დამატებას შეუძლია <string name="untrusted_extension_message">ეს დამატება ხელმოწერილია უცნობი სერთიფიკატის მიერ და არ არის აქტივირებული.
\n \n
\nწაიკითხოს შესვლის მონაცემები შენახული ამ აპლიკაციაში ან გაუშვას თავისი კოდი \\n\\nამ სერტიფიკატის ნდობით თქვენ \nმავნე დამატებას შეუძლია წაიკითხოს შესვლის მონაცემები შენახული ამ აპლიკაციაში ან გაუშვას თავისი კოდი.
\n \n
\nთქვენს თავზე იღებთ რისკებს და პასუხისმგებლობას.</string> \nამ სერტიფიკატის ნდობით თქვენ თქვენს თავზე იღებთ რისკებს და პასუხისმგებლობას.</string>
<string name="tracking_info">ცალმხრივი სინქრონიზაცია თვალყურის სადევნებელ სერვისებში თავების პროგრესის განსაახლებლად. მიადევნე თვალყური ინდივიდუალურ მანგებს მათი ჩანართებიდან</string> <string name="tracking_info">ცალმხრივი სინქრონიზაცია თვალყურის სადევნებელ სერვისებში თავების პროგრესის განსაახლებლად. მიადევნე თვალყური ინდივიდუალურ მანგებს მათი ჩანართებიდან</string>
<string name="backup_restore_content">აღდგენა იყენებს წყაროებს მონაცემების გადმოსაწერად.\\n\\nდარწმუნდი რომ გაქვს დაინსტალირებული ყველა <string name="backup_restore_content">აღდგენა იყენებს წყაროებს მონაცემების გადმოსაწერად.
\n \n
\nსაჭირო დამატება და ხარ დალოგინებული წყაროებში და თვალყურის სადევნებელ სერვისებში ააღდგენამდე.</string> \nდარწმუნდი რომ გაქვს დაინსტალირებული ყველა საჭირო დამატება და ხარ დალოგინებული წყაროებში და თვალყურის სადევნებელ სერვისებში ააღდგენამდე.</string>
<string name="pref_refresh_library_tracking_summary">ანახლებს სტატუსს, შეფასებას და ბოლო თავს წაკითხულს თვალყურის სადევნებელ სერვისებიდან</string> <string name="pref_refresh_library_tracking_summary">ანახლებს სტატუსს, შეფასებას და ბოლო თავს წაკითხულს თვალყურის სადევნებელ სერვისებიდან</string>
<string name="download_notifier_page_ready_error">გვერდი ვერ ჩაიტვირთა</string> <string name="download_notifier_page_ready_error">გვერდი ვერ ჩაიტვირთა</string>
<string name="filter_mode_overlay">გადაფარება</string> <string name="filter_mode_overlay">გადაფარება</string>
@ -584,4 +583,6 @@
<string name="action_start">დაწყება</string> <string name="action_start">დაწყება</string>
<string name="action_sort_date_added">დამატების თარიღი</string> <string name="action_sort_date_added">დამატების თარიღი</string>
<string name="action_download_unread">გადმოიწეროს წაუკითხავი თავები</string> <string name="action_download_unread">გადმოიწეროს წაუკითხავი თავები</string>
</resources> <string name="ext_nsfw_warning">შეიძლება შეიჩაცდეს უცენზურო(18+) კონტენტს</string>
<string name="ext_nsfw_short">18+</string>
</resources>

View File

@ -287,7 +287,6 @@
<string name="pref_library_update_restriction_summary">ಷರತ್ತುಗಳನ್ನು ಪೂರೈಸಿದಾಗ ಮಾತ್ರ ನವೀಕರಿಸಿ</string> <string name="pref_library_update_restriction_summary">ಷರತ್ತುಗಳನ್ನು ಪೂರೈಸಿದಾಗ ಮಾತ್ರ ನವೀಕರಿಸಿ</string>
<string name="pref_library_update_restriction">ಗ್ರಂಥಾಲಯ ನವೀಕರಣ ನಿರ್ಬಂಧಗಳು</string> <string name="pref_library_update_restriction">ಗ್ರಂಥಾಲಯ ನವೀಕರಣ ನಿರ್ಬಂಧಗಳು</string>
<string name="pref_library_update_prioritization">ಗ್ರಂಥಾಲಯ ನವೀಕರಣ ಪಾಳಿ</string> <string name="pref_library_update_prioritization">ಗ್ರಂಥಾಲಯ ನವೀಕರಣ ಪಾಳಿ</string>
<string name="update_monthly">ತಿಂಗಳಿಗೊಮ್ಮೆ</string>
<string name="update_weekly">ವಾರಕ್ಕೊಮ್ಮೆ</string> <string name="update_weekly">ವಾರಕ್ಕೊಮ್ಮೆ</string>
<string name="update_48hour">ಪ್ರತಿ 2 ದಿನಗಳಿಗೊಮ್ಮೆ</string> <string name="update_48hour">ಪ್ರತಿ 2 ದಿನಗಳಿಗೊಮ್ಮೆ</string>
<string name="update_24hour">ಪ್ರತಿದಿನ</string> <string name="update_24hour">ಪ್ರತಿದಿನ</string>
@ -639,4 +638,4 @@
<string name="pref_show_nsfw_extension">ವಿಸ್ತರಣೆಗಳ ಪಟ್ಟಿಯಲ್ಲಿ ತೋರಿಸಿ</string> <string name="pref_show_nsfw_extension">ವಿಸ್ತರಣೆಗಳ ಪಟ್ಟಿಯಲ್ಲಿ ತೋರಿಸಿ</string>
<string name="pref_show_nsfw_source">ಮೂಲಗಳ ಪಟ್ಟಿಯಲ್ಲಿ ತೋರಿಸಿ</string> <string name="pref_show_nsfw_source">ಮೂಲಗಳ ಪಟ್ಟಿಯಲ್ಲಿ ತೋರಿಸಿ</string>
<string name="pref_category_nsfw_content">ವಯಸ್ಕ (18+) ಮೂಲಗಳು</string> <string name="pref_category_nsfw_content">ವಯಸ್ಕ (18+) ಮೂಲಗಳು</string>
</resources> </resources>

View File

@ -95,7 +95,6 @@
<string name="update_24hour">1일</string> <string name="update_24hour">1일</string>
<string name="update_48hour">2일</string> <string name="update_48hour">2일</string>
<string name="update_weekly">1주</string> <string name="update_weekly">1주</string>
<string name="update_monthly">1달</string>
<string name="pref_library_update_categories">전역 업데이트에 포함할 카테고리</string> <string name="pref_library_update_categories">전역 업데이트에 포함할 카테고리</string>
<string name="all">전부</string> <string name="all">전부</string>
<string name="pref_library_update_restriction">서재 업데이트 제한</string> <string name="pref_library_update_restriction">서재 업데이트 제한</string>
@ -379,4 +378,4 @@
<string name="filter_mode_darken">번 / 어둡게하기</string> <string name="filter_mode_darken">번 / 어둡게하기</string>
<string name="label_help">도움</string> <string name="label_help">도움</string>
<string name="email">이메일 주소</string> <string name="email">이메일 주소</string>
</resources> </resources>

View File

@ -185,7 +185,6 @@
<string name="pref_library_update_restriction_summary">अटी पूर्ण झाल्यावरच अध्यातन करावे</string> <string name="pref_library_update_restriction_summary">अटी पूर्ण झाल्यावरच अध्यातन करावे</string>
<string name="pref_library_update_restriction">लायब्ररी अद्ययावत प्रतिबंधी</string> <string name="pref_library_update_restriction">लायब्ररी अद्ययावत प्रतिबंधी</string>
<string name="pref_library_update_prioritization">लायब्ररी अद्ययावत क्रम</string> <string name="pref_library_update_prioritization">लायब्ररी अद्ययावत क्रम</string>
<string name="update_monthly">मासिक</string>
<string name="double_tap_anim_speed_0">अनिमेशन नाही</string> <string name="double_tap_anim_speed_0">अनिमेशन नाही</string>
<string name="zoom_start_center">सेंटर</string> <string name="zoom_start_center">सेंटर</string>
<string name="zoom_start_right">उजवी बाजू</string> <string name="zoom_start_right">उजवी बाजू</string>

View File

@ -94,7 +94,6 @@
<string name="update_24hour">Setiap hari</string> <string name="update_24hour">Setiap hari</string>
<string name="update_48hour">Setiap 2 hari</string> <string name="update_48hour">Setiap 2 hari</string>
<string name="update_weekly">Setiap minggu</string> <string name="update_weekly">Setiap minggu</string>
<string name="update_monthly">Setiap bulan</string>
<string name="pref_library_update_categories">Kategori bagi kemas kini keseluruhan</string> <string name="pref_library_update_categories">Kategori bagi kemas kini keseluruhan</string>
<string name="all">Semua</string> <string name="all">Semua</string>
<string name="pref_library_update_restriction">Batasan kemas kini pustaka</string> <string name="pref_library_update_restriction">Batasan kemas kini pustaka</string>
@ -645,4 +644,6 @@
<string name="track_started_reading_date">Tarikh mula membaca</string> <string name="track_started_reading_date">Tarikh mula membaca</string>
<string name="track_finished_reading_date">Tarikh selesai membaca</string> <string name="track_finished_reading_date">Tarikh selesai membaca</string>
<string name="action_filter_tracked">Dijejaki</string> <string name="action_filter_tracked">Dijejaki</string>
</resources> <string name="action_display_show_number_of_items">Tunjuk jumlah bilangan</string>
<string name="right_and_left_nav">Kanan dan kiri</string>
</resources>

View File

@ -90,7 +90,6 @@
<string name="update_24hour">Daglig</string> <string name="update_24hour">Daglig</string>
<string name="update_48hour">Annenhver dag</string> <string name="update_48hour">Annenhver dag</string>
<string name="update_weekly">Ukentlig</string> <string name="update_weekly">Ukentlig</string>
<string name="update_monthly">Månedlig</string>
<string name="all">Alle</string> <string name="all">Alle</string>
<string name="charging">Lading</string> <string name="charging">Lading</string>
<string name="pref_start_screen">Startskjerm</string> <string name="pref_start_screen">Startskjerm</string>
@ -612,4 +611,4 @@
<string name="tracking_info">Enveissynkronisering for å oppdatere kapittelfremdrift til sporingstjenester. Sett opp sporing for individuelle mangaoppføringer fra deres sporingsknapp.</string> <string name="tracking_info">Enveissynkronisering for å oppdatere kapittelfremdrift til sporingstjenester. Sett opp sporing for individuelle mangaoppføringer fra deres sporingsknapp.</string>
<string name="pref_true_color_summary">Reduserer sjattering, men koster ytelse</string> <string name="pref_true_color_summary">Reduserer sjattering, men koster ytelse</string>
<string name="pref_webtoon_side_padding">Sidefyll</string> <string name="pref_webtoon_side_padding">Sidefyll</string>
</resources> </resources>

View File

@ -81,7 +81,6 @@
<string name="update_24hour">Dagelijks</string> <string name="update_24hour">Dagelijks</string>
<string name="update_48hour">Tweedagelijks</string> <string name="update_48hour">Tweedagelijks</string>
<string name="update_weekly">Wekelijks</string> <string name="update_weekly">Wekelijks</string>
<string name="update_monthly">Maandelijks</string>
<string name="all">Alles</string> <string name="all">Alles</string>
<string name="pref_start_screen">Beginscherm</string> <string name="pref_start_screen">Beginscherm</string>
<string name="pref_language">Taal</string> <string name="pref_language">Taal</string>
@ -645,4 +644,17 @@
<string name="kindlish_nav">Kindle-achtig</string> <string name="kindlish_nav">Kindle-achtig</string>
<string name="l_nav">L-vormig</string> <string name="l_nav">L-vormig</string>
<string name="default_nav">Standaard</string> <string name="default_nav">Standaard</string>
</resources> <string name="channel_crash_logs">Crashlogboeken</string>
<string name="track_finished_reading_date">Datum klaar met lezen</string>
<string name="track_started_reading_date">Datum begonnen met lezen</string>
<string name="crash_log_saved">Crashlogboeken opgeslagen</string>
<string name="pref_dump_crash_logs_summary">Slaat foutmeldingslogboeken op in een bestand om te delen met de ontwikkelaars</string>
<string name="pref_dump_crash_logs">Crashlogboeken opslaan</string>
<string name="network_unmetered">Onbeperkt netwerk</string>
<string name="action_desc">Aflopend</string>
<string name="action_asc">Oplopend</string>
<string name="action_order_by_chapter_number">Op hoofdstuknummer</string>
<string name="action_order_by_upload_date">Op uploaddatum</string>
<string name="action_display_show_number_of_items">Aantal items tonen</string>
<string name="action_filter_tracked">Getracked</string>
</resources>

View File

@ -161,7 +161,6 @@
<string name="update_24hour">Co 1 dzień</string> <string name="update_24hour">Co 1 dzień</string>
<string name="update_48hour">Co 2 dni</string> <string name="update_48hour">Co 2 dni</string>
<string name="update_weekly">Co tydzień</string> <string name="update_weekly">Co tydzień</string>
<string name="update_monthly">Co miesiąc</string>
<string name="pref_library_update_categories">Kategorie zawarte w globalnej aktualizacji</string> <string name="pref_library_update_categories">Kategorie zawarte w globalnej aktualizacji</string>
<string name="all">Wszystko</string> <string name="all">Wszystko</string>
<string name="pref_library_update_restriction">Warunki aktualizacji biblioteki</string> <string name="pref_library_update_restriction">Warunki aktualizacji biblioteki</string>
@ -680,4 +679,5 @@
<string name="action_order_by_chapter_number">Według numeru rozdziału</string> <string name="action_order_by_chapter_number">Według numeru rozdziału</string>
<string name="action_order_by_upload_date">Po dacie dodania</string> <string name="action_order_by_upload_date">Po dacie dodania</string>
<string name="action_filter_tracked">Śledzone</string> <string name="action_filter_tracked">Śledzone</string>
</resources> <string name="action_display_show_number_of_items">Pokaż liczbę elementów</string>
</resources>

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