Merge branch 'devel' into m3
This commit is contained in:
@@ -11,6 +11,7 @@ import org.koin.core.component.get
|
||||
import org.koitharu.kotatsu.BuildConfig
|
||||
import org.koitharu.kotatsu.core.model.MangaPage
|
||||
import org.koitharu.kotatsu.core.network.CommonHeaders
|
||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
import org.koitharu.kotatsu.utils.CacheUtils
|
||||
import org.koitharu.kotatsu.utils.ext.await
|
||||
import org.koitharu.kotatsu.utils.ext.medianOrNull
|
||||
@@ -28,7 +29,7 @@ object MangaUtils : KoinComponent {
|
||||
suspend fun determineMangaIsWebtoon(pages: List<MangaPage>): Boolean? {
|
||||
try {
|
||||
val page = pages.medianOrNull() ?: return null
|
||||
val url = page.source.repository.getPageUrl(page)
|
||||
val url = MangaRepository(page.source).getPageUrl(page)
|
||||
val uri = Uri.parse(url)
|
||||
val size = if (uri.scheme == "cbz") {
|
||||
val zip = ZipFile(uri.schemeSpecificPart)
|
||||
|
||||
@@ -2,51 +2,38 @@ package org.koitharu.kotatsu.core.model
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.koin.core.context.GlobalContext
|
||||
import org.koin.core.error.NoBeanDefFoundException
|
||||
import org.koin.core.qualifier.named
|
||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
import org.koitharu.kotatsu.core.parser.site.*
|
||||
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
@Parcelize
|
||||
enum class MangaSource(
|
||||
val title: String,
|
||||
val locale: String?,
|
||||
val cls: Class<out MangaRepository>,
|
||||
) : Parcelable {
|
||||
LOCAL("Local", null, LocalMangaRepository::class.java),
|
||||
READMANGA_RU("ReadManga", "ru", ReadmangaRepository::class.java),
|
||||
MINTMANGA("MintManga", "ru", MintMangaRepository::class.java),
|
||||
SELFMANGA("SelfManga", "ru", SelfMangaRepository::class.java),
|
||||
MANGACHAN("Манга-тян", "ru", MangaChanRepository::class.java),
|
||||
DESUME("Desu.me", "ru", DesuMeRepository::class.java),
|
||||
HENCHAN("Хентай-тян", "ru", HenChanRepository::class.java),
|
||||
YAOICHAN("Яой-тян", "ru", YaoiChanRepository::class.java),
|
||||
MANGATOWN("MangaTown", "en", MangaTownRepository::class.java),
|
||||
MANGALIB("MangaLib", "ru", MangaLibRepository::class.java),
|
||||
LOCAL("Local", null),
|
||||
READMANGA_RU("ReadManga", "ru"),
|
||||
MINTMANGA("MintManga", "ru"),
|
||||
SELFMANGA("SelfManga", "ru"),
|
||||
MANGACHAN("Манга-тян", "ru"),
|
||||
DESUME("Desu.me", "ru"),
|
||||
HENCHAN("Хентай-тян", "ru"),
|
||||
YAOICHAN("Яой-тян", "ru"),
|
||||
MANGATOWN("MangaTown", "en"),
|
||||
MANGALIB("MangaLib", "ru"),
|
||||
|
||||
// NUDEMOON("Nude-Moon", "ru", NudeMoonRepository::class.java),
|
||||
MANGAREAD("MangaRead", "en", MangareadRepository::class.java),
|
||||
REMANGA("Remanga", "ru", RemangaRepository::class.java),
|
||||
HENTAILIB("HentaiLib", "ru", HentaiLibRepository::class.java),
|
||||
ANIBEL("Anibel", "be", AnibelRepository::class.java),
|
||||
NINEMANGA_EN("NineManga English", "en", NineMangaRepository.English::class.java),
|
||||
NINEMANGA_ES("NineManga Español", "es", NineMangaRepository.Spanish::class.java),
|
||||
NINEMANGA_RU("NineManga Русский", "ru", NineMangaRepository.Russian::class.java),
|
||||
NINEMANGA_DE("NineManga Deutsch", "de", NineMangaRepository.Deutsch::class.java),
|
||||
NINEMANGA_IT("NineManga Italiano", "it", NineMangaRepository.Italiano::class.java),
|
||||
NINEMANGA_BR("NineManga Brasil", "pt", NineMangaRepository.Brazil::class.java),
|
||||
NINEMANGA_FR("NineManga Français", "fr", NineMangaRepository.Francais::class.java),
|
||||
EXHENTAI("ExHentai", null, ExHentaiRepository::class.java),
|
||||
MANGAOWL("MangaOwl", "en", MangaOwlRepository::class.java),
|
||||
MANGADEX("MangaDex", null, MangaDexRepository::class.java),
|
||||
MANGAREAD("MangaRead", "en"),
|
||||
REMANGA("Remanga", "ru"),
|
||||
HENTAILIB("HentaiLib", "ru"),
|
||||
ANIBEL("Anibel", "be"),
|
||||
NINEMANGA_EN("NineManga English", "en"),
|
||||
NINEMANGA_ES("NineManga Español", "es"),
|
||||
NINEMANGA_RU("NineManga Русский", "ru"),
|
||||
NINEMANGA_DE("NineManga Deutsch", "de"),
|
||||
NINEMANGA_IT("NineManga Italiano", "it"),
|
||||
NINEMANGA_BR("NineManga Brasil", "pt"),
|
||||
NINEMANGA_FR("NineManga Français", "fr"),
|
||||
EXHENTAI("ExHentai", null),
|
||||
MANGAOWL("MangaOwl", "en"),
|
||||
MANGADEX("MangaDex", null),
|
||||
;
|
||||
|
||||
@get:Throws(NoBeanDefFoundException::class)
|
||||
@Deprecated("", ReplaceWith("MangaRepository(this)",
|
||||
"org.koitharu.kotatsu.core.parser.MangaRepository"))
|
||||
val repository: MangaRepository
|
||||
get() = GlobalContext.get().get(named(this))
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import org.koin.core.qualifier.named
|
||||
import org.koin.dsl.bind
|
||||
import org.koin.dsl.module
|
||||
import org.koitharu.kotatsu.BuildConfig
|
||||
import org.koitharu.kotatsu.base.domain.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.utils.CacheUtils
|
||||
import org.koitharu.kotatsu.utils.DownloadManagerHelper
|
||||
import java.util.concurrent.TimeUnit
|
||||
@@ -30,4 +31,5 @@ val networkModule
|
||||
}.build()
|
||||
}
|
||||
factory { DownloadManagerHelper(get(), get()) }
|
||||
single { MangaLoaderContext(get(), get()) }
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.koitharu.kotatsu.core.parser
|
||||
|
||||
import android.net.Uri
|
||||
import coil.map.Mapper
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
|
||||
class FaviconMapper() : Mapper<Uri, HttpUrl> {
|
||||
|
||||
override fun map(data: Uri): HttpUrl {
|
||||
val mangaSource = MangaSource.valueOf(data.schemeSpecificPart)
|
||||
val repo = MangaRepository(mangaSource) as RemoteMangaRepository
|
||||
return repo.getFaviconUrl().toHttpUrl()
|
||||
}
|
||||
|
||||
override fun handles(data: Uri) = data.scheme == "favicon"
|
||||
}
|
||||
@@ -7,6 +7,8 @@ import org.koitharu.kotatsu.core.model.*
|
||||
|
||||
interface MangaRepository {
|
||||
|
||||
val source: MangaSource
|
||||
|
||||
val sortOrders: Set<SortOrder>
|
||||
|
||||
suspend fun getList2(
|
||||
|
||||
@@ -2,15 +2,12 @@ package org.koitharu.kotatsu.core.parser
|
||||
|
||||
import org.koin.core.qualifier.named
|
||||
import org.koin.dsl.module
|
||||
import org.koitharu.kotatsu.base.domain.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.core.parser.site.*
|
||||
|
||||
val parserModule
|
||||
get() = module {
|
||||
|
||||
single { MangaLoaderContext(get(), get()) }
|
||||
|
||||
factory<MangaRepository>(named(MangaSource.READMANGA_RU)) { ReadmangaRepository(get()) }
|
||||
factory<MangaRepository>(named(MangaSource.MINTMANGA)) { MintMangaRepository(get()) }
|
||||
factory<MangaRepository>(named(MangaSource.SELFMANGA)) { SelfMangaRepository(get()) }
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.koitharu.kotatsu.core.parser
|
||||
import org.koitharu.kotatsu.base.domain.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.core.exceptions.ParseException
|
||||
import org.koitharu.kotatsu.core.model.MangaPage
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.core.model.MangaTag
|
||||
import org.koitharu.kotatsu.core.model.SortOrder
|
||||
import org.koitharu.kotatsu.core.prefs.SourceSettings
|
||||
@@ -12,8 +11,6 @@ abstract class RemoteMangaRepository(
|
||||
protected val loaderContext: MangaLoaderContext
|
||||
) : MangaRepository {
|
||||
|
||||
protected abstract val source: MangaSource
|
||||
|
||||
protected abstract val defaultDomain: String
|
||||
|
||||
private val conf by lazy {
|
||||
@@ -29,6 +26,8 @@ abstract class RemoteMangaRepository(
|
||||
|
||||
override suspend fun getTags(): Set<MangaTag> = emptySet()
|
||||
|
||||
open fun getFaviconUrl() = "https://${getDomain()}/favicon.ico"
|
||||
|
||||
open fun onCreatePreferences(map: MutableMap<String, Any>) {
|
||||
map[SourceSettings.KEY_DOMAIN] = defaultDomain
|
||||
}
|
||||
@@ -53,8 +52,10 @@ abstract class RemoteMangaRepository(
|
||||
if (subdomain != null) {
|
||||
append(subdomain)
|
||||
append('.')
|
||||
append(conf.getDomain(defaultDomain).removePrefix("www."))
|
||||
} else {
|
||||
append(conf.getDomain(defaultDomain))
|
||||
}
|
||||
append(conf.getDomain(defaultDomain))
|
||||
append(this@withDomain)
|
||||
}
|
||||
else -> this
|
||||
|
||||
@@ -21,6 +21,10 @@ class AnibelRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
|
||||
SortOrder.NEWEST
|
||||
)
|
||||
|
||||
override fun getFaviconUrl(): String {
|
||||
return "https://cdn.${getDomain()}/favicons/favicon.png"
|
||||
}
|
||||
|
||||
override suspend fun getList2(
|
||||
offset: Int,
|
||||
query: String?,
|
||||
|
||||
@@ -128,7 +128,7 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
|
||||
scanlator = null,
|
||||
branch = null,
|
||||
)
|
||||
}
|
||||
} ?: bypassLicensedChapters(manga)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -191,6 +191,32 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) :
|
||||
map[SourceSettings.KEY_USE_SSL] = true
|
||||
}
|
||||
|
||||
private suspend fun bypassLicensedChapters(manga: Manga): List<MangaChapter> {
|
||||
val doc = loaderContext.httpGet(manga.url.withDomain("m")).parseHtml()
|
||||
val list = doc.body().selectFirst("ul.detail-ch-list") ?: return emptyList()
|
||||
val dateFormat = SimpleDateFormat("MMM dd,yyyy", Locale.US)
|
||||
return list.select("li").asReversed().mapIndexedNotNull { i, li ->
|
||||
val a = li.selectFirst("a") ?: return@mapIndexedNotNull null
|
||||
val href = a.relUrl("href")
|
||||
val name = a.selectFirst("span.vol")?.text().orEmpty().ifEmpty {
|
||||
a.ownText()
|
||||
}
|
||||
MangaChapter(
|
||||
id = generateUid(href),
|
||||
url = href,
|
||||
source = MangaSource.MANGATOWN,
|
||||
number = i + 1,
|
||||
uploadDate = parseChapterDate(
|
||||
dateFormat,
|
||||
li.selectFirst("span.time")?.text()
|
||||
),
|
||||
name = name.ifEmpty { "${manga.title} - ${i + 1}" },
|
||||
scanlator = null,
|
||||
branch = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.parseTagKey() = split('/').findLast { TAG_REGEX matches it }
|
||||
|
||||
private companion object {
|
||||
|
||||
@@ -151,8 +151,10 @@ class MangareadRepository(
|
||||
?.selectFirst("div.reading-content")
|
||||
?: throw ParseException("Root not found")
|
||||
return root.select("div.page-break").map { div ->
|
||||
val img = div.selectFirst("img")
|
||||
val url = img?.relUrl("src") ?: parseFailed("Page image not found")
|
||||
val img = div.selectFirst("img") ?: parseFailed("Page image not found")
|
||||
val url = img.relUrl("data-src").ifEmpty {
|
||||
img.relUrl("src")
|
||||
}
|
||||
MangaPage(
|
||||
id = generateUid(url),
|
||||
url = url,
|
||||
|
||||
@@ -5,6 +5,7 @@ import coil.ImageLoader
|
||||
import okhttp3.OkHttpClient
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.dsl.module
|
||||
import org.koitharu.kotatsu.core.parser.FaviconMapper
|
||||
import org.koitharu.kotatsu.local.data.CbzFetcher
|
||||
|
||||
val uiModule
|
||||
@@ -15,6 +16,7 @@ val uiModule
|
||||
.componentRegistry(
|
||||
ComponentRegistry.Builder()
|
||||
.add(CbzFetcher())
|
||||
.add(FaviconMapper())
|
||||
.build()
|
||||
).build()
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ class DetailsViewModel(
|
||||
var manga = mangaDataRepository.resolveIntent(intent)
|
||||
?: throw MangaNotFoundException("Cannot find manga")
|
||||
mangaData.value = manga
|
||||
manga = manga.source.repository.getDetails(manga)
|
||||
manga = MangaRepository(manga.source).getDetails(manga)
|
||||
// find default branch
|
||||
val hist = historyRepository.getOne(manga)
|
||||
selectedBranch.value = if (hist != null) {
|
||||
|
||||
@@ -145,7 +145,7 @@ class DownloadManager(
|
||||
while (true) {
|
||||
try {
|
||||
val response = call.clone().await()
|
||||
withContext(Dispatchers.IO) {
|
||||
runInterruptible(Dispatchers.IO) {
|
||||
file.outputStream().use { out ->
|
||||
checkNotNull(response.body).byteStream().copyTo(out)
|
||||
}
|
||||
|
||||
@@ -9,23 +9,24 @@ import coil.fetch.FetchResult
|
||||
import coil.fetch.Fetcher
|
||||
import coil.fetch.SourceResult
|
||||
import coil.size.Size
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runInterruptible
|
||||
import okio.buffer
|
||||
import okio.source
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
class CbzFetcher : Fetcher<Uri> {
|
||||
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
override suspend fun fetch(
|
||||
pool: BitmapPool,
|
||||
data: Uri,
|
||||
size: Size,
|
||||
options: Options,
|
||||
): FetchResult {
|
||||
): FetchResult = runInterruptible(Dispatchers.IO) {
|
||||
val zip = ZipFile(data.schemeSpecificPart)
|
||||
val entry = zip.getEntry(data.fragment)
|
||||
val ext = MimeTypeMap.getFileExtensionFromUrl(entry.name)
|
||||
return SourceResult(
|
||||
SourceResult(
|
||||
source = ExtraCloseableBufferedSource(
|
||||
zip.getInputStream(entry).source().buffer(),
|
||||
zip,
|
||||
|
||||
@@ -31,7 +31,7 @@ class MangaZip(val file: File) {
|
||||
return writableCbz.flush()
|
||||
}
|
||||
|
||||
fun addCover(file: File, ext: String) {
|
||||
suspend fun addCover(file: File, ext: String) {
|
||||
val name = buildString {
|
||||
append(FILENAME_PATTERN.format(0, 0))
|
||||
if (ext.isNotEmpty() && ext.length <= 4) {
|
||||
@@ -39,11 +39,11 @@ class MangaZip(val file: File) {
|
||||
append(ext)
|
||||
}
|
||||
}
|
||||
writableCbz[name] = file
|
||||
writableCbz.put(name, file)
|
||||
index.setCoverEntry(name)
|
||||
}
|
||||
|
||||
fun addPage(chapter: MangaChapter, file: File, pageNumber: Int, ext: String) {
|
||||
suspend fun addPage(chapter: MangaChapter, file: File, pageNumber: Int, ext: String) {
|
||||
val name = buildString {
|
||||
append(FILENAME_PATTERN.format(chapter.number, pageNumber))
|
||||
if (ext.isNotEmpty() && ext.length <= 4) {
|
||||
@@ -51,7 +51,7 @@ class MangaZip(val file: File) {
|
||||
append(ext)
|
||||
}
|
||||
}
|
||||
writableCbz[name] = file
|
||||
writableCbz.put(name, file)
|
||||
index.addChapter(chapter)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package org.koitharu.kotatsu.local.data
|
||||
|
||||
import androidx.annotation.CheckResult
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.*
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
@@ -27,11 +26,13 @@ class WritableCbzFile(private val file: File) {
|
||||
}
|
||||
ZipInputStream(FileInputStream(file)).use { zip ->
|
||||
var entry = zip.nextEntry
|
||||
while (entry != null) {
|
||||
while (entry != null && currentCoroutineContext().isActive) {
|
||||
val target = File(dir.path + File.separator + entry.name)
|
||||
target.parentFile?.mkdirs()
|
||||
target.outputStream().use { out ->
|
||||
zip.copyTo(out)
|
||||
runInterruptible {
|
||||
target.parentFile?.mkdirs()
|
||||
target.outputStream().use { out ->
|
||||
zip.copyTo(out)
|
||||
}
|
||||
}
|
||||
zip.closeEntry()
|
||||
entry = zip.nextEntry
|
||||
@@ -51,11 +52,13 @@ class WritableCbzFile(private val file: File) {
|
||||
tempFile.delete()
|
||||
}
|
||||
try {
|
||||
ZipOutputStream(FileOutputStream(tempFile)).use { zip ->
|
||||
dir.listFiles()?.forEach {
|
||||
zipFile(it, it.name, zip)
|
||||
runInterruptible {
|
||||
ZipOutputStream(FileOutputStream(tempFile)).use { zip ->
|
||||
dir.listFiles()?.forEach {
|
||||
zipFile(it, it.name, zip)
|
||||
}
|
||||
zip.flush()
|
||||
}
|
||||
zip.flush()
|
||||
}
|
||||
tempFile.renameTo(file)
|
||||
} finally {
|
||||
@@ -67,29 +70,26 @@ class WritableCbzFile(private val file: File) {
|
||||
|
||||
operator fun get(name: String) = File(dir, name)
|
||||
|
||||
operator fun set(name: String, file: File) {
|
||||
suspend fun put(name: String, file: File) = runInterruptible(Dispatchers.IO) {
|
||||
file.copyTo(this[name], overwrite = true)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private fun zipFile(fileToZip: File, fileName: String, zipOut: ZipOutputStream) {
|
||||
if (fileToZip.isDirectory) {
|
||||
if (fileName.endsWith("/")) {
|
||||
zipOut.putNextEntry(ZipEntry(fileName))
|
||||
} else {
|
||||
zipOut.putNextEntry(ZipEntry("$fileName/"))
|
||||
}
|
||||
zipOut.closeEntry()
|
||||
fileToZip.listFiles()?.forEach { childFile ->
|
||||
zipFile(childFile, "$fileName/${childFile.name}", zipOut)
|
||||
}
|
||||
private fun zipFile(fileToZip: File, fileName: String, zipOut: ZipOutputStream) {
|
||||
if (fileToZip.isDirectory) {
|
||||
if (fileName.endsWith("/")) {
|
||||
zipOut.putNextEntry(ZipEntry(fileName))
|
||||
} else {
|
||||
FileInputStream(fileToZip).use { fis ->
|
||||
val zipEntry = ZipEntry(fileName)
|
||||
zipOut.putNextEntry(zipEntry)
|
||||
fis.copyTo(zipOut)
|
||||
}
|
||||
zipOut.putNextEntry(ZipEntry("$fileName/"))
|
||||
}
|
||||
zipOut.closeEntry()
|
||||
fileToZip.listFiles()?.forEach { childFile ->
|
||||
zipFile(childFile, "$fileName/${childFile.name}", zipOut)
|
||||
}
|
||||
} else {
|
||||
FileInputStream(fileToZip).use { fis ->
|
||||
val zipEntry = ZipEntry(fileName)
|
||||
zipOut.putNextEntry(zipEntry)
|
||||
fis.copyTo(zipOut)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.util.zip.ZipFile
|
||||
|
||||
class LocalMangaRepository(private val context: Context) : MangaRepository {
|
||||
|
||||
override val source = MangaSource.LOCAL
|
||||
private val filenameFilter = CbzFilter()
|
||||
|
||||
override suspend fun getList2(
|
||||
|
||||
@@ -50,7 +50,7 @@ class PageLoader(
|
||||
|
||||
private fun loadAsync(page: MangaPage): Deferred<File> {
|
||||
var repo = repository
|
||||
if (repo?.javaClass != page.source.cls) {
|
||||
if (repo?.source != page.source) {
|
||||
repo = mangaRepositoryOf(page.source)
|
||||
repository = repo
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.koitharu.kotatsu.reader.ui
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.net.Uri
|
||||
import android.util.LongSparseArray
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
@@ -77,7 +76,7 @@ class ReaderViewModel(
|
||||
var manga = dataRepository.resolveIntent(intent)
|
||||
?: throw MangaNotFoundException("Cannot find manga")
|
||||
mangaData.value = manga
|
||||
val repo = manga.source.repository
|
||||
val repo = MangaRepository(manga.source)
|
||||
manga = repo.getDetails(manga)
|
||||
manga.chapters?.forEach {
|
||||
chapters.put(it.id, it)
|
||||
@@ -206,7 +205,7 @@ class ReaderViewModel(
|
||||
private suspend fun loadChapter(chapterId: Long): List<ReaderPage> {
|
||||
val manga = checkNotNull(mangaData.value) { "Manga is null" }
|
||||
val chapter = checkNotNull(chapters[chapterId]) { "Requested chapter not found" }
|
||||
val repo = manga.source.repository
|
||||
val repo = MangaRepository(manga.source)
|
||||
return repo.getPages(chapter).mapIndexed { index, page ->
|
||||
ReaderPage.from(page, index, chapterId)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.core.model.SortOrder
|
||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.search.ui.MangaSuggestionsProvider
|
||||
import org.koitharu.kotatsu.utils.ext.levenshteinDistance
|
||||
@@ -29,7 +30,7 @@ class MangaSearchRepository(
|
||||
MangaProviderFactory.getSources(settings, includeHidden = false).asFlow()
|
||||
.flatMapMerge(concurrency) { source ->
|
||||
runCatching {
|
||||
source.repository.getList2(
|
||||
MangaRepository(source).getList2(
|
||||
offset = 0,
|
||||
query = query,
|
||||
sortOrder = SortOrder.POPULARITY
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package org.koitharu.kotatsu.settings.sources
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.*
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.koin.android.ext.android.get
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.BaseFragment
|
||||
@@ -19,7 +19,7 @@ import org.koitharu.kotatsu.settings.sources.adapter.SourceConfigListener
|
||||
import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem
|
||||
|
||||
class SourcesSettingsFragment : BaseFragment<FragmentSettingsSourcesBinding>(),
|
||||
SourceConfigListener {
|
||||
SourceConfigListener, SearchView.OnQueryTextListener, MenuItem.OnActionExpandListener {
|
||||
|
||||
private lateinit var reorderHelper: ItemTouchHelper
|
||||
private val viewModel by viewModel<SourcesSettingsViewModel>()
|
||||
@@ -42,7 +42,7 @@ class SourcesSettingsFragment : BaseFragment<FragmentSettingsSourcesBinding>(),
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val sourcesAdapter = SourceConfigAdapter(this)
|
||||
val sourcesAdapter = SourceConfigAdapter(this, get(), viewLifecycleOwner)
|
||||
with(binding.recyclerView) {
|
||||
setHasFixedSize(true)
|
||||
addItemDecoration(SourceConfigItemDecoration(view.context))
|
||||
@@ -59,6 +59,17 @@ class SourcesSettingsFragment : BaseFragment<FragmentSettingsSourcesBinding>(),
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
inflater.inflate(R.menu.opt_sources, menu)
|
||||
val searchMenuItem = menu.findItem(R.id.action_search)
|
||||
searchMenuItem.setOnActionExpandListener(this)
|
||||
val searchView = searchMenuItem.actionView as SearchView
|
||||
searchView.setOnQueryTextListener(this)
|
||||
searchView.setIconifiedByDefault(false)
|
||||
searchView.queryHint = searchMenuItem.title
|
||||
}
|
||||
|
||||
override fun onWindowInsetsChanged(insets: Insets) {
|
||||
binding.recyclerView.updatePadding(
|
||||
bottom = insets.bottom,
|
||||
@@ -83,6 +94,20 @@ class SourcesSettingsFragment : BaseFragment<FragmentSettingsSourcesBinding>(),
|
||||
viewModel.expandOrCollapse(header.localeId)
|
||||
}
|
||||
|
||||
override fun onQueryTextSubmit(query: String?): Boolean = false
|
||||
|
||||
override fun onQueryTextChange(newText: String?): Boolean {
|
||||
viewModel.performSearch(newText)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onMenuItemActionExpand(item: MenuItem?): Boolean = true
|
||||
|
||||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
||||
(item.actionView as SearchView).setQuery("", false)
|
||||
return true
|
||||
}
|
||||
|
||||
private inner class SourcesReorderCallback : ItemTouchHelper.SimpleCallback(
|
||||
ItemTouchHelper.DOWN or ItemTouchHelper.UP,
|
||||
0,
|
||||
|
||||
@@ -21,6 +21,7 @@ class SourcesSettingsViewModel(
|
||||
|
||||
val items = MutableLiveData<List<SourceConfigItem>>(emptyList())
|
||||
private val expandedGroups = HashSet<String?>()
|
||||
private var searchQuery: String? = null
|
||||
|
||||
init {
|
||||
buildList()
|
||||
@@ -63,9 +64,30 @@ class SourcesSettingsViewModel(
|
||||
buildList()
|
||||
}
|
||||
|
||||
fun performSearch(query: String?) {
|
||||
searchQuery = query?.trim()
|
||||
buildList()
|
||||
}
|
||||
|
||||
private fun buildList() {
|
||||
val sources = MangaProviderFactory.getSources(settings, includeHidden = true)
|
||||
val hiddenSources = settings.hiddenSources
|
||||
val query = searchQuery
|
||||
if (!query.isNullOrEmpty()) {
|
||||
items.value = sources.mapNotNull {
|
||||
if (!it.title.contains(query, ignoreCase = true)) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
SourceConfigItem.SourceItem(
|
||||
source = it,
|
||||
isEnabled = it.name !in hiddenSources,
|
||||
isDraggable = false,
|
||||
)
|
||||
}.ifEmpty {
|
||||
listOf(SourceConfigItem.EmptySearchResult)
|
||||
}
|
||||
return
|
||||
}
|
||||
val map = sources.groupByTo(TreeMap(LocaleKeyComparator())) {
|
||||
if (it.name !in hiddenSources) {
|
||||
KEY_ENABLED
|
||||
@@ -81,6 +103,7 @@ class SourcesSettingsViewModel(
|
||||
SourceConfigItem.SourceItem(
|
||||
source = it,
|
||||
isEnabled = true,
|
||||
isDraggable = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -102,6 +125,7 @@ class SourcesSettingsViewModel(
|
||||
SourceConfigItem.SourceItem(
|
||||
source = it,
|
||||
isEnabled = false,
|
||||
isDraggable = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
package org.koitharu.kotatsu.settings.sources.adapter
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import coil.ImageLoader
|
||||
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
||||
import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem
|
||||
|
||||
class SourceConfigAdapter(
|
||||
listener: SourceConfigListener,
|
||||
coil: ImageLoader,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
) : AsyncListDifferDelegationAdapter<SourceConfigItem>(
|
||||
SourceConfigDiffCallback(),
|
||||
sourceConfigHeaderDelegate(),
|
||||
sourceConfigGroupDelegate(listener),
|
||||
sourceConfigItemDelegate(listener),
|
||||
sourceConfigItemDelegate(listener, coil, lifecycleOwner),
|
||||
sourceConfigDraggableItemDelegate(listener),
|
||||
sourceConfigEmptySearchDelegate(),
|
||||
)
|
||||
@@ -4,14 +4,19 @@ import android.annotation.SuppressLint
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.widget.CompoundButton
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updatePaddingRelative
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import coil.ImageLoader
|
||||
import coil.request.Disposable
|
||||
import coil.request.ImageRequest
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegate
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.databinding.ItemExpandableBinding
|
||||
import org.koitharu.kotatsu.databinding.ItemFilterHeaderBinding
|
||||
import org.koitharu.kotatsu.databinding.ItemSourceConfigBinding
|
||||
import org.koitharu.kotatsu.databinding.ItemSourceConfigDraggableBinding
|
||||
import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem
|
||||
import org.koitharu.kotatsu.utils.ext.enqueueWith
|
||||
|
||||
fun sourceConfigHeaderDelegate() = adapterDelegateViewBinding<SourceConfigItem.Header, SourceConfigItem, ItemFilterHeaderBinding>(
|
||||
{ layoutInflater, parent -> ItemFilterHeaderBinding.inflate(layoutInflater, parent, false) }
|
||||
@@ -38,11 +43,44 @@ fun sourceConfigGroupDelegate(
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
fun sourceConfigItemDelegate(
|
||||
listener: SourceConfigListener,
|
||||
coil: ImageLoader,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
) = adapterDelegateViewBinding<SourceConfigItem.SourceItem, SourceConfigItem, ItemSourceConfigBinding>(
|
||||
{ layoutInflater, parent -> ItemSourceConfigBinding.inflate(layoutInflater, parent, false) }
|
||||
{ layoutInflater, parent -> ItemSourceConfigBinding.inflate(layoutInflater, parent, false) },
|
||||
on = { item, _, _ -> item is SourceConfigItem.SourceItem && !item.isDraggable }
|
||||
) {
|
||||
|
||||
var imageRequest: Disposable? = null
|
||||
|
||||
binding.switchToggle.setOnCheckedChangeListener { _, isChecked ->
|
||||
listener.onItemEnabledChanged(item, isChecked)
|
||||
}
|
||||
|
||||
bind {
|
||||
binding.textViewTitle.text = item.source.title
|
||||
binding.switchToggle.isChecked = item.isEnabled
|
||||
imageRequest = ImageRequest.Builder(context)
|
||||
.data(item.faviconUrl)
|
||||
.error(R.drawable.ic_favicon_fallback)
|
||||
.target(binding.imageViewIcon)
|
||||
.lifecycle(lifecycleOwner)
|
||||
.enqueueWith(coil)
|
||||
}
|
||||
|
||||
onViewRecycled {
|
||||
imageRequest?.dispose()
|
||||
imageRequest = null
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
fun sourceConfigDraggableItemDelegate(
|
||||
listener: SourceConfigListener,
|
||||
) = adapterDelegateViewBinding<SourceConfigItem.SourceItem, SourceConfigItem, ItemSourceConfigDraggableBinding>(
|
||||
{ layoutInflater, parent -> ItemSourceConfigDraggableBinding.inflate(layoutInflater, parent, false) },
|
||||
on = { item, _, _ -> item is SourceConfigItem.SourceItem && item.isDraggable }
|
||||
) {
|
||||
|
||||
val eventListener = object : View.OnClickListener, View.OnTouchListener,
|
||||
@@ -70,11 +108,9 @@ fun sourceConfigItemDelegate(
|
||||
bind {
|
||||
binding.textViewTitle.text = item.source.title
|
||||
binding.switchToggle.isChecked = item.isEnabled
|
||||
binding.imageViewHandle.isVisible = item.isEnabled
|
||||
binding.imageViewConfig.isVisible = item.isEnabled
|
||||
binding.root.updatePaddingRelative(
|
||||
start = if (item.isEnabled) 0 else binding.imageViewHandle.paddingStart * 2,
|
||||
end = if (item.isEnabled) 0 else binding.imageViewConfig.paddingEnd,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun sourceConfigEmptySearchDelegate() = adapterDelegate<SourceConfigItem.EmptySearchResult, SourceConfigItem>(
|
||||
R.layout.item_sources_empty
|
||||
) { }
|
||||
@@ -2,21 +2,25 @@ package org.koitharu.kotatsu.settings.sources.adapter
|
||||
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem
|
||||
import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem.*
|
||||
|
||||
class SourceConfigDiffCallback : DiffUtil.ItemCallback<SourceConfigItem>() {
|
||||
|
||||
override fun areItemsTheSame(oldItem: SourceConfigItem, newItem: SourceConfigItem): Boolean {
|
||||
return when {
|
||||
oldItem.javaClass != newItem.javaClass -> false
|
||||
oldItem is SourceConfigItem.LocaleGroup && newItem is SourceConfigItem.LocaleGroup -> {
|
||||
oldItem is LocaleGroup && newItem is LocaleGroup -> {
|
||||
oldItem.localeId == newItem.localeId
|
||||
}
|
||||
oldItem is SourceConfigItem.SourceItem && newItem is SourceConfigItem.SourceItem -> {
|
||||
oldItem is SourceItem && newItem is SourceItem -> {
|
||||
oldItem.source == newItem.source
|
||||
}
|
||||
oldItem is SourceConfigItem.Header && newItem is SourceConfigItem.Header -> {
|
||||
oldItem is Header && newItem is Header -> {
|
||||
oldItem.titleResId == newItem.titleResId
|
||||
}
|
||||
oldItem == EmptySearchResult && newItem == EmptySearchResult -> {
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.koitharu.kotatsu.settings.sources.model
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.annotation.StringRes
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
|
||||
@@ -49,8 +50,12 @@ sealed interface SourceConfigItem {
|
||||
class SourceItem(
|
||||
val source: MangaSource,
|
||||
val isEnabled: Boolean,
|
||||
val isDraggable: Boolean,
|
||||
) : SourceConfigItem {
|
||||
|
||||
val faviconUrl: Uri
|
||||
get() = Uri.fromParts("favicon", source.name, null)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
@@ -59,6 +64,7 @@ sealed interface SourceConfigItem {
|
||||
|
||||
if (source != other.source) return false
|
||||
if (isEnabled != other.isEnabled) return false
|
||||
if (isDraggable != other.isDraggable) return false
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -66,7 +72,10 @@ sealed interface SourceConfigItem {
|
||||
override fun hashCode(): Int {
|
||||
var result = source.hashCode()
|
||||
result = 31 * result + isEnabled.hashCode()
|
||||
result = 31 * result + isDraggable.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
object EmptySearchResult : SourceConfigItem
|
||||
}
|
||||
@@ -10,4 +10,10 @@ object PendingIntentCompat {
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
val FLAG_MUTABLE = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
PendingIntent.FLAG_MUTABLE
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ class RecentWidgetProvider : AppWidgetProvider() {
|
||||
context,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_MUTABLE
|
||||
)
|
||||
)
|
||||
views.setEmptyView(R.id.stackView, R.id.textView_holder)
|
||||
|
||||
@@ -31,7 +31,7 @@ class ShelfWidgetProvider : AppWidgetProvider() {
|
||||
context,
|
||||
0,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_IMMUTABLE
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntentCompat.FLAG_MUTABLE
|
||||
)
|
||||
)
|
||||
views.setEmptyView(R.id.gridView, R.id.textView_holder)
|
||||
|
||||
16
app/src/main/res/drawable/ic_favicon_fallback.xml
Normal file
16
app/src/main/res/drawable/ic_favicon_fallback.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="4dp" />
|
||||
<solid android:color="?colorControlLight" />
|
||||
</shape>
|
||||
</item>
|
||||
<item
|
||||
android:bottom="4dp"
|
||||
android:drawable="@drawable/ic_web"
|
||||
android:left="4dp"
|
||||
android:right="4dp"
|
||||
android:top="4dp" />
|
||||
</layer-list>
|
||||
@@ -4,17 +4,18 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:listPreferredItemHeightSmall"
|
||||
android:background="?android:windowBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView_handle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingHorizontal="?listPreferredItemPaddingStart"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/ic_reorder_handle" />
|
||||
android:id="@+id/imageView_icon"
|
||||
android:layout_width="?android:listPreferredItemHeightSmall"
|
||||
android:layout_height="?android:listPreferredItemHeightSmall"
|
||||
android:layout_marginHorizontal="?listPreferredItemPaddingStart"
|
||||
android:labelFor="@id/textView_title"
|
||||
android:padding="8dp"
|
||||
android:scaleType="fitCenter"
|
||||
tools:src="@tools:sample/avatars" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_title"
|
||||
@@ -30,16 +31,7 @@
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
android:id="@+id/switch_toggle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView_config"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/settings"
|
||||
android:paddingHorizontal="?listPreferredItemPaddingEnd"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/ic_settings" />
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="?listPreferredItemPaddingEnd" />
|
||||
|
||||
</LinearLayout>
|
||||
46
app/src/main/res/layout/item_source_config_draggable.xml
Normal file
46
app/src/main/res/layout/item_source_config_draggable.xml
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:listPreferredItemHeightSmall"
|
||||
android:background="?android:windowBackground"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView_handle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingHorizontal="?listPreferredItemPaddingStart"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/ic_reorder_handle" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="marquee"
|
||||
android:fadingEdge="horizontal"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
tools:text="@tools:sample/lorem[1]" />
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
android:id="@+id/switch_toggle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView_config"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/settings"
|
||||
android:paddingHorizontal="?listPreferredItemPaddingEnd"
|
||||
android:scaleType="center"
|
||||
android:src="@drawable/ic_settings" />
|
||||
|
||||
</LinearLayout>
|
||||
9
app/src/main/res/layout/item_sources_empty.xml
Normal file
9
app/src/main/res/layout/item_sources_empty.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
android:text="@string/nothing_found"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:textColor="?android:textColorSecondary" />
|
||||
@@ -4,9 +4,10 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_languages"
|
||||
android:icon="@drawable/ic_locale"
|
||||
android:title="@string/languages"
|
||||
app:showAsAction="ifRoom" />
|
||||
android:id="@+id/action_search"
|
||||
android:icon="@drawable/ic_search"
|
||||
android:title="@string/search"
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
||||
app:showAsAction="ifRoom|collapseActionView" />
|
||||
|
||||
</menu>
|
||||
@@ -246,4 +246,7 @@
|
||||
<string name="system_default">Па змаўчанні</string>
|
||||
<string name="exclude_nsfw_from_history">Не паказваць NSFW мангу з гісторыі</string>
|
||||
<string name="error_empty_name">Імя не можа быць пустым</string>
|
||||
<string name="show_pages_numbers">Паказваць нумары старонак</string>
|
||||
<string name="enabled_sources">Уключаныя крыніцы</string>
|
||||
<string name="available_sources">Даступныя крыніцы</string>
|
||||
</resources>
|
||||
@@ -242,8 +242,11 @@
|
||||
<string name="date_format">Formato de la fecha</string>
|
||||
<string name="system_default">Por defecto</string>
|
||||
<string name="tracker_warning">Algunos fabricantes pueden cambiar el comportamiento del sistema, lo que podría interrumpir las tareas en segundo plano.</string>
|
||||
<string name="error_empty_name">Nombre no debe estar vacío</string>
|
||||
<string name="error_empty_name">El nombre no debe estar vacío</string>
|
||||
<string name="auth_not_supported_by">Autorización en %s no es compatible</string>
|
||||
<string name="text_clear_cookies_prompt">Se cerrará la sesión de todas las fuentes en las que esté autorizado</string>
|
||||
<string name="exclude_nsfw_from_history">Excluye manga NSFW del historial</string>
|
||||
<string name="show_pages_numbers">Mostrar los números de páginas</string>
|
||||
<string name="enabled_sources">Fuentes activadas</string>
|
||||
<string name="available_sources">Fuentes disponibles</string>
|
||||
</resources>
|
||||
10
app/src/main/res/values-fa/strings.xml
Normal file
10
app/src/main/res/values-fa/strings.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="close_menu">بستن منو</string>
|
||||
<string name="open_menu">بازکردن منو</string>
|
||||
<string name="local_storage">محل ذخیره سازی</string>
|
||||
<string name="favourites">موارد دلخواه</string>
|
||||
<string name="history">تاریخچه</string>
|
||||
<string name="error_occurred">خطایی رخ داده است</string>
|
||||
<string name="network_error">خطای اتصال به شبکه</string>
|
||||
</resources>
|
||||
@@ -1,2 +1,252 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="open_menu">Abrir menu</string>
|
||||
<string name="local_storage">Armazenamento local</string>
|
||||
<string name="favourites">Favoritos</string>
|
||||
<string name="error_occurred">Ocorreu um erro</string>
|
||||
<string name="network_error">Erro de conexão de rede</string>
|
||||
<string name="details">Detalhes</string>
|
||||
<string name="list">Lista</string>
|
||||
<string name="detailed_list">Lista detalhada</string>
|
||||
<string name="grid">Grade</string>
|
||||
<string name="list_mode">Modo lista</string>
|
||||
<string name="settings">Configurações</string>
|
||||
<string name="loading_">Carregando…</string>
|
||||
<string name="chapter_d_of_d">Capítulo %1$d de %2$d</string>
|
||||
<string name="try_again">Tente novamente</string>
|
||||
<string name="clear_history">Limpar histórico</string>
|
||||
<string name="nothing_found">Nada encontrado</string>
|
||||
<string name="history_is_empty">Histórico vazio</string>
|
||||
<string name="add_bookmark">Add marca páginas</string>
|
||||
<string name="you_have_not_favourites_yet">Você não tem favoritos ainda</string>
|
||||
<string name="add_to_favourites">Adicionar aos favoritos</string>
|
||||
<string name="add">Add</string>
|
||||
<string name="enter_category_name">Nomeie a categoria</string>
|
||||
<string name="save">Salvar</string>
|
||||
<string name="share">Compartilhar</string>
|
||||
<string name="create_shortcut">Criar atalho…</string>
|
||||
<string name="share_s">Compartilhar %s</string>
|
||||
<string name="search">Pesquisar</string>
|
||||
<string name="search_manga">Pesquisar mangá</string>
|
||||
<string name="manga_downloading_">Baixando mangá…</string>
|
||||
<string name="download_complete">Download completo</string>
|
||||
<string name="downloads">Downloads</string>
|
||||
<string name="by_name">Por nome</string>
|
||||
<string name="popular">Populares</string>
|
||||
<string name="by_rating">Por avaliação</string>
|
||||
<string name="all">Todos</string>
|
||||
<string name="sort_order">Ordem de classificação</string>
|
||||
<string name="genre">Gênero</string>
|
||||
<string name="filter">Filtro</string>
|
||||
<string name="dark">Escuro</string>
|
||||
<string name="automatic">Automático</string>
|
||||
<string name="pages">Páginas</string>
|
||||
<string name="clear">Limpar</string>
|
||||
<string name="text_clear_history_prompt">Você realmente quer limpar todo o seu histórico de leitura\? Essa ação não pode ser desfeita.</string>
|
||||
<string name="remove">Remover</string>
|
||||
<string name="_s_removed_from_history">\"%s\" removido do histórico</string>
|
||||
<string name="_s_deleted_from_local_storage">\"%s\" deletado do armazenamento local</string>
|
||||
<string name="wait_for_loading_finish">Aguarde o carregamento para finalizar</string>
|
||||
<string name="save_page">Salvar página</string>
|
||||
<string name="page_saved">Página salva com sucesso</string>
|
||||
<string name="share_image">Compartilhar imagem</string>
|
||||
<string name="_import">Importar</string>
|
||||
<string name="updated">Utualizado</string>
|
||||
<string name="delete">Deletar</string>
|
||||
<string name="operation_not_supported">Essa operação não é suportada</string>
|
||||
<string name="history_and_cache">Histórico e cache</string>
|
||||
<string name="clear_pages_cache">Limpar cache de páginas</string>
|
||||
<string name="cache">Cache</string>
|
||||
<string name="text_file_sizes">B|kB|MB|GB|TB</string>
|
||||
<string name="standard">Padrão</string>
|
||||
<string name="webtoon">Webtoon</string>
|
||||
<string name="read_mode">Modo leitura</string>
|
||||
<string name="grid_size">Tamanho de grade</string>
|
||||
<string name="search_on_s">Pesquisar em %s</string>
|
||||
<string name="delete_manga">Deletar mangá</string>
|
||||
<string name="reader_settings">Configurações de leitura</string>
|
||||
<string name="switch_pages">Mudar páginas</string>
|
||||
<string name="taps_on_edges">Cliques na borda</string>
|
||||
<string name="volume_buttons">Botões de volume</string>
|
||||
<string name="network_consumption_warning">Essa operação pode consumir muito tráfego de rede</string>
|
||||
<string name="dont_ask_again">Não pergunte novamente</string>
|
||||
<string name="cancelling_">Cancelando…</string>
|
||||
<string name="error">Erro</string>
|
||||
<string name="clear_thumbs_cache">Limpar cache de thumbnails</string>
|
||||
<string name="search_history_cleared">Histórico de pesquisa limpo</string>
|
||||
<string name="gestures_only">Apenas gestos</string>
|
||||
<string name="internal_storage">Armazenamento interno</string>
|
||||
<string name="external_storage">Armazenamento externo</string>
|
||||
<string name="domain">Domínio</string>
|
||||
<string name="application_update">Verificar automaticamente se há actualizações</string>
|
||||
<string name="app_update_available">Actualização da aplicação está disponível</string>
|
||||
<string name="show_notification_app_update">Mostrar notificação se a actualização estiver disponível</string>
|
||||
<string name="open_in_browser">Aberto no navegador</string>
|
||||
<string name="large_manga_save_confirm">Esta manga tem %s. Quer salvar tudo isto\?</string>
|
||||
<string name="save_manga">Salvar mangá</string>
|
||||
<string name="notifications">Notificações</string>
|
||||
<string name="new_chapters">Novos capítulos</string>
|
||||
<string name="show_notification_new_chapters">Notifique sobre atualizações do mangá que está lendo</string>
|
||||
<string name="download">Download</string>
|
||||
<string name="read_from_start">Ler desde o início</string>
|
||||
<string name="restart">Reiniciar</string>
|
||||
<string name="notifications_settings">Configurações das notificações</string>
|
||||
<string name="light_indicator">Indicador luminoso</string>
|
||||
<string name="close_menu">Fechar menu</string>
|
||||
<string name="remote_sources">Fontes remotas</string>
|
||||
<string name="close">Fechar</string>
|
||||
<string name="light">Brilho</string>
|
||||
<string name="history">Histórico</string>
|
||||
<string name="read">Ler</string>
|
||||
<string name="processing_">Processando…</string>
|
||||
<string name="newest">Novos</string>
|
||||
<string name="theme">Tema</string>
|
||||
<string name="no_description">Sem descrição</string>
|
||||
<string name="_continue">Continuar</string>
|
||||
<string name="chapters">Capítulos</string>
|
||||
<string name="add_new_category">Add nova categoria</string>
|
||||
<string name="warning">Aviso</string>
|
||||
<string name="text_delete_local_manga">Você realmente quer deletar \"%s\" do armazenamento local de seu celular\?
|
||||
\nEssa operação não pode ser desfeita.</string>
|
||||
<string name="text_file_not_supported">Arquivo inválido. Apenas ZIP e CBZ são suportados.</string>
|
||||
<string name="clear_search_history">Limpar histórico de pesquisa</string>
|
||||
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">Activado %1$d de %2$d</string>
|
||||
<string name="notification_sound">Som de notificação</string>
|
||||
<string name="show_pages_numbers">Mostrar números de páginas</string>
|
||||
<string name="state_finished">Concluído</string>
|
||||
<string name="state_ongoing">em andamento</string>
|
||||
<string name="categories_">Categorias…</string>
|
||||
<string name="rename">Renomear</string>
|
||||
<string name="remove_category">Remover categoria</string>
|
||||
<string name="text_empty_holder_primary">Está meio vazio aqui…</string>
|
||||
<string name="manga_shelf">Prateleira de manga</string>
|
||||
<string name="done">Feito</string>
|
||||
<string name="related">Relacionado</string>
|
||||
<string name="zoom_mode_keep_start">Manter no início</string>
|
||||
<string name="clear_updates_feed">Limpar feed de atualizações</string>
|
||||
<string name="updates_feed_cleared">Feed de atualizações limpo</string>
|
||||
<string name="update">Atualizar</string>
|
||||
<string name="feed_will_update_soon">A atualização do feed começará em breve</string>
|
||||
<string name="track_sources">Confira as atualizações do mangá</string>
|
||||
<string name="dont_check">Não verifique</string>
|
||||
<string name="enter_password">Digite a senha</string>
|
||||
<string name="wrong_password">Senha incorreta</string>
|
||||
<string name="repeat_password">Repita a senha</string>
|
||||
<string name="passwords_mismatch">As senhas não coincidem</string>
|
||||
<string name="about">Cerca de</string>
|
||||
<string name="app_version">Versão %s</string>
|
||||
<string name="check_for_updates">Verifique se há atualizações</string>
|
||||
<string name="checking_for_updates">Verificando atualizações…</string>
|
||||
<string name="no_update_available">Nenhuma atualização disponível</string>
|
||||
<string name="right_to_left">Direita para esquerda</string>
|
||||
<string name="create_category">Nova categoria</string>
|
||||
<string name="report_github">Criar problema no GitHub</string>
|
||||
<string name="scale_mode">Modo de escala</string>
|
||||
<string name="zoom_mode_fit_center">Centro de ajuste</string>
|
||||
<string name="zoom_mode_fit_width">Ajustar à largura</string>
|
||||
<string name="restart_required">É necessário reiniciar</string>
|
||||
<string name="backup_restore">Restauração de backup</string>
|
||||
<string name="create_backup">Criar backup de dados</string>
|
||||
<string name="restore_backup">Restaurar do backup</string>
|
||||
<string name="data_restored">Dados restaurados</string>
|
||||
<string name="preparing_">Preparando…</string>
|
||||
<string name="file_not_found">Arquivo não encontrado</string>
|
||||
<string name="data_restored_success">Todos os dados restaurados com sucesso</string>
|
||||
<string name="data_restored_with_errors">Os dados foram restaurados, mas há erros</string>
|
||||
<string name="just_now">Agora mesmo</string>
|
||||
<string name="yesterday">Ontem</string>
|
||||
<string name="long_ago">Muito tempo atrás</string>
|
||||
<string name="group">Grupo</string>
|
||||
<string name="today">Hoje</string>
|
||||
<string name="tap_to_try_again">Toque para tentar novamente</string>
|
||||
<string name="silent">Silencioso</string>
|
||||
<string name="captcha_required">O CAPTCHA é obrigatório</string>
|
||||
<string name="captcha_solve">Resolver</string>
|
||||
<string name="cookies_cleared">Todos os cookies foram removidos</string>
|
||||
<string name="chapters_checking_progress">Verificando novos capítulos: %1$d de %2$d</string>
|
||||
<string name="clear_feed">Limpar feed</string>
|
||||
<string name="text_clear_updates_feed_prompt">Todo o histórico de atualizações será apagado e esta ação não poderá ser desfeita. Tem certeza\?</string>
|
||||
<string name="new_chapters_checking">Verificação de novos capítulos</string>
|
||||
<string name="reverse">Reverter</string>
|
||||
<string name="sign_in">Entrar</string>
|
||||
<string name="default_s">Padrão: %s</string>
|
||||
<string name="_and_x_more">…e %1$d mais</string>
|
||||
<string name="next">Próximo</string>
|
||||
<string name="protect_application_subtitle">Digite a senha que será necessária quando o aplicativo for iniciado</string>
|
||||
<string name="confirm">Confirme</string>
|
||||
<string name="password_length_hint">A senha deve ter pelo menos 4 caracteres</string>
|
||||
<string name="description">Descrição</string>
|
||||
<string name="backup_saved">Cópias de segurança salvas com sucesso</string>
|
||||
<string name="tracker_warning">Alguns fabricantes podem alterar o comportamento do sistema, o que pode quebrar as tarefas de fundo.</string>
|
||||
<string name="read_more">Leia mais</string>
|
||||
<string name="hide_toolbar">Ocultar a barra de ferramentas ao rolar</string>
|
||||
<string name="search_only_on_s">Pesquise apenas em %s</string>
|
||||
<string name="other">Outros</string>
|
||||
<string name="languages">Línguas</string>
|
||||
<string name="welcome">Bem vindo</string>
|
||||
<string name="available_sources">Fontes disponíveis</string>
|
||||
<string name="enabled_sources">Fontes ativadas</string>
|
||||
<string name="queued">Enfileirado</string>
|
||||
<string name="text_downloads_holder">No momento, não há downloads ativos</string>
|
||||
<string name="error_empty_name">O nome não deve estar vazio</string>
|
||||
<string name="about_app_translation_summary">Traduzir esta aplicação</string>
|
||||
<string name="about_author">Autor</string>
|
||||
<string name="about_feedback">Comentar</string>
|
||||
<string name="about_feedback_4pda">Tópico no 4PDA</string>
|
||||
<string name="about_support_developer">Apoiar o desenvolvedor</string>
|
||||
<string name="about_support_developer_summary">Se gostar desta aplicação, pode ajudar financeiramente através de Yoomoney (ex. Yandex.Money)</string>
|
||||
<string name="about_gratitudes">agradecimento</string>
|
||||
<string name="about_gratitudes_summary">Estas pessoas fazem o Kotatsu tornar-se melhor!</string>
|
||||
<string name="about_copyright_and_licenses">Direitos de autor e licenças</string>
|
||||
<string name="about_license">Licença</string>
|
||||
<string name="chapter_is_missing_text">Este capítulo está em falta no seu dispositivo. Descarregue ou leia-o online.</string>
|
||||
<string name="chapter_is_missing">Falta um capítulo</string>
|
||||
<string name="auth_complete">Autorização completa</string>
|
||||
<string name="auth_not_supported_by">A autorização em %s não é suportada</string>
|
||||
<string name="genres">Géneros</string>
|
||||
<string name="about_app_translation">Tradução</string>
|
||||
<string name="text_clear_cookies_prompt">Será desconectado de todas as fontes em que estiver autorizado</string>
|
||||
<string name="vibration">Vibração</string>
|
||||
<string name="cannot_find_available_storage">Não é possível encontrar nenhum armazenamento disponível</string>
|
||||
<string name="favourites_categories">Categorias favoritas</string>
|
||||
<string name="category_delete_confirm">Quer realmente remover a categoria \"%s\" dos seus favoritos\?
|
||||
\nSerá perdido todos os mangas contidos.</string>
|
||||
<string name="text_history_holder_secondary">Pode encontrar o que ler no menu lateral.</string>
|
||||
<string name="text_local_holder_secondary">Pode salvá-lo a partir de fontes online ou importá-lo a partir de ficheiro.</string>
|
||||
<string name="recent_manga">Manga recente</string>
|
||||
<string name="other_storage">Outro armazenamento</string>
|
||||
<string name="text_search_holder_secondary">Tente reformular a consulta.</string>
|
||||
<string name="prefer_rtl_reader">Prefira o leitor da direita para a esquerda</string>
|
||||
<string name="not_available">Não disponível</string>
|
||||
<string name="size_s">Tamanho: %s</string>
|
||||
<string name="text_history_holder_primary">Mangá que está a ler será afixada aqui</string>
|
||||
<string name="text_local_holder_primary">Ainda não tem nenhuma mangá salvo</string>
|
||||
<string name="pages_animation">Animação de páginas</string>
|
||||
<string name="favourites_category_empty">Esta categoria está vazia</string>
|
||||
<string name="read_later">Leia mais tarde</string>
|
||||
<string name="updates">atualizações</string>
|
||||
<string name="all_favourites">Todos os favoritos</string>
|
||||
<string name="waiting_for_network">À espera de rede…</string>
|
||||
<string name="use_ssl">Utilizar ligação segura (HTTPS)</string>
|
||||
<string name="search_results">Resultados da pesquisa</string>
|
||||
<string name="text_feed_holder">Aqui verá os novos capítulos do mangá que está a ler</string>
|
||||
<string name="new_version_s">Nova versão: %s</string>
|
||||
<string name="rotate_screen">Girar a tela</string>
|
||||
<string name="update_check_failed">Falha na verificação de atualização</string>
|
||||
<string name="protect_application">Proteger aplicativo</string>
|
||||
<string name="protect_application_summary">Pedir senha no início do aplicativo</string>
|
||||
<string name="zoom_mode_fit_height">Ajustar à altura</string>
|
||||
<string name="black_dark_theme">Tema Black dark</string>
|
||||
<string name="black_dark_theme_summary">Útil para telas AMOLED</string>
|
||||
<string name="reader_mode_hint">A configuração escolhida será lembrada para este mangá</string>
|
||||
<string name="backup_information">Você pode criar backup de seu histórico e favoritos e restaurá-lo</string>
|
||||
<string name="clear_cookies">Limpar cookies</string>
|
||||
<string name="text_clear_search_history_prompt">Você realmente deseja remover todas as consultas de pesquisa recentes\? Essa ação não pode ser desfeita.</string>
|
||||
<string name="auth_required">Você deve autorizar a visualização deste conteúdo</string>
|
||||
<string name="text_categories_holder">Pode usar categorias para organizar seu mangá favorito. Pressione <<+>> para criar uma categoria</string>
|
||||
<string name="prefer_rtl_reader_summary">Você pode configurar o modo de leitura para cada mangá separadamente</string>
|
||||
<string name="manga_save_location">Local onde serão armazenados os mangás baixados</string>
|
||||
<string name="exclude_nsfw_from_history">Excluir manga NSFW da história</string>
|
||||
<string name="date_format">Formato da data</string>
|
||||
<string name="system_default">Padrão</string>
|
||||
</resources>
|
||||
@@ -7,8 +7,8 @@
|
||||
<string name="local_storage">Local storage</string>
|
||||
<string name="favourites">Favourites</string>
|
||||
<string name="history">History</string>
|
||||
<string name="error_occurred">An error has occurred</string>
|
||||
<string name="network_error">Network connection error</string>
|
||||
<string name="error_occurred">An error occurred</string>
|
||||
<string name="network_error">Could not connect to the Internet</string>
|
||||
<string name="details">Details</string>
|
||||
<string name="chapters">Chapters</string>
|
||||
<string name="list">List</string>
|
||||
@@ -23,12 +23,12 @@
|
||||
<string name="try_again">Try again</string>
|
||||
<string name="clear_history">Clear history</string>
|
||||
<string name="nothing_found">Nothing found</string>
|
||||
<string name="history_is_empty">History is empty</string>
|
||||
<string name="history_is_empty">No history yet</string>
|
||||
<string name="read">Read</string>
|
||||
<string name="add_bookmark">Add bookmark</string>
|
||||
<string name="you_have_not_favourites_yet">You have not favourites yet</string>
|
||||
<string name="add_to_favourites">Add to favourites</string>
|
||||
<string name="add_new_category">Add new category</string>
|
||||
<string name="you_have_not_favourites_yet">No favourites yet</string>
|
||||
<string name="add_to_favourites">Favourite this</string>
|
||||
<string name="add_new_category">New category</string>
|
||||
<string name="add">Add</string>
|
||||
<string name="enter_category_name">Enter category name</string>
|
||||
<string name="save">Save</string>
|
||||
@@ -37,40 +37,40 @@
|
||||
<string name="share_s">Share %s</string>
|
||||
<string name="search">Search</string>
|
||||
<string name="search_manga">Search manga</string>
|
||||
<string name="manga_downloading_">Manga downloading…</string>
|
||||
<string name="manga_downloading_">Downloading…</string>
|
||||
<string name="processing_">Processing…</string>
|
||||
<string name="download_complete">Download complete</string>
|
||||
<string name="download_complete">Downloaded</string>
|
||||
<string name="downloads">Downloads</string>
|
||||
<string name="by_name">By name</string>
|
||||
<string name="by_name">Name</string>
|
||||
<string name="popular">Popular</string>
|
||||
<string name="updated">Updated</string>
|
||||
<string name="newest">Newest</string>
|
||||
<string name="by_rating">By rating</string>
|
||||
<string name="by_rating">Rating</string>
|
||||
<string name="all">All</string>
|
||||
<string name="sort_order">Sort order</string>
|
||||
<string name="sort_order">Sorting order</string>
|
||||
<string name="genre">Genre</string>
|
||||
<string name="filter">Filter</string>
|
||||
<string name="theme">Theme</string>
|
||||
<string name="light">Light</string>
|
||||
<string name="dark">Dark</string>
|
||||
<string name="automatic">Automatic</string>
|
||||
<string name="automatic">Follow system</string>
|
||||
<string name="pages">Pages</string>
|
||||
<string name="clear">Clear</string>
|
||||
<string name="text_clear_history_prompt">Do you really want to clear all your reading history? This action cannot be undone.</string>
|
||||
<string name="text_clear_history_prompt">Clear all reading history permanently?</string>
|
||||
<string name="remove">Remove</string>
|
||||
<string name="_s_removed_from_history">\"%s\" removed from history</string>
|
||||
<string name="_s_deleted_from_local_storage">\"%s\" deleted from local storage</string>
|
||||
<string name="wait_for_loading_finish">Wait for the load to finish</string>
|
||||
<string name="wait_for_loading_finish">Wait for loading to finish…</string>
|
||||
<string name="save_page">Save page</string>
|
||||
<string name="page_saved">Page saved successful</string>
|
||||
<string name="page_saved">Saved</string>
|
||||
<string name="share_image">Share image</string>
|
||||
<string name="_import">Import</string>
|
||||
<string name="delete">Delete</string>
|
||||
<string name="operation_not_supported">This operation is not supported</string>
|
||||
<string name="text_file_not_supported">Invalid file. Only ZIP and CBZ are supported.</string>
|
||||
<string name="text_file_not_supported">Either pick a ZIP or CBZ file.</string>
|
||||
<string name="no_description">No description</string>
|
||||
<string name="history_and_cache">History and cache</string>
|
||||
<string name="clear_pages_cache">Clear pages cache</string>
|
||||
<string name="clear_pages_cache">Clear page cache</string>
|
||||
<string name="cache">Cache</string>
|
||||
<string name="text_file_sizes">B|kB|MB|GB|TB</string>
|
||||
<string name="standard">Standard</string>
|
||||
@@ -79,32 +79,32 @@
|
||||
<string name="grid_size">Grid size</string>
|
||||
<string name="search_on_s">Search on %s</string>
|
||||
<string name="delete_manga">Delete manga</string>
|
||||
<string name="text_delete_local_manga">Do you really want to delete \"%s\" from your phone\'s local storage? \nThis operation cannot be undone.</string>
|
||||
<string name="text_delete_local_manga">Delete \"%s\" from device permanently?</string>
|
||||
<string name="reader_settings">Reader settings</string>
|
||||
<string name="switch_pages">Switch pages</string>
|
||||
<string name="taps_on_edges">Taps on edges</string>
|
||||
<string name="taps_on_edges">Edge taps</string>
|
||||
<string name="volume_buttons">Volume buttons</string>
|
||||
<string name="_continue">Continue</string>
|
||||
<string name="warning">Warning</string>
|
||||
<string name="network_consumption_warning">This operation may consume a lot of network traffic</string>
|
||||
<string name="network_consumption_warning">This may transfer a lot of data</string>
|
||||
<string name="dont_ask_again">Don\'t ask again</string>
|
||||
<string name="cancelling_">Cancelling…</string>
|
||||
<string name="error">Error</string>
|
||||
<string name="clear_thumbs_cache">Clear thumbnails cache</string>
|
||||
<string name="clear_search_history">Clear search history</string>
|
||||
<string name="search_history_cleared">Search history cleared</string>
|
||||
<string name="search_history_cleared">Cleared</string>
|
||||
<string name="gestures_only">Gestures only</string>
|
||||
<string name="internal_storage">Internal storage</string>
|
||||
<string name="external_storage">External storage</string>
|
||||
<string name="domain">Domain</string>
|
||||
<string name="application_update">Check for updates automatically</string>
|
||||
<string name="app_update_available">Application update is available</string>
|
||||
<string name="show_notification_app_update">Show notification if update is available</string>
|
||||
<string name="open_in_browser">Open in browser</string>
|
||||
<string name="large_manga_save_confirm">This manga has %s. Do you want to save all of it?</string>
|
||||
<string name="save_manga">Save manga</string>
|
||||
<string name="application_update">Check for new versions of the app</string>
|
||||
<string name="app_update_available">A new version of the app is available</string>
|
||||
<string name="show_notification_app_update">Show notification if a new version is available</string>
|
||||
<string name="open_in_browser">Open in web browser</string>
|
||||
<string name="large_manga_save_confirm">This manga has %s. Save all of it?</string>
|
||||
<string name="save_manga">Save</string>
|
||||
<string name="notifications">Notifications</string>
|
||||
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">Enabled %1$d of %2$d</string>
|
||||
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">Turned on %1$d of %2$d</string>
|
||||
<string name="new_chapters">New chapters</string>
|
||||
<string name="show_notification_new_chapters">Notify about updates of manga you are reading</string>
|
||||
<string name="download">Download</string>
|
||||
@@ -112,61 +112,61 @@
|
||||
<string name="restart">Restart</string>
|
||||
<string name="notifications_settings">Notifications settings</string>
|
||||
<string name="notification_sound">Notification sound</string>
|
||||
<string name="light_indicator">Light indicator</string>
|
||||
<string name="light_indicator">LED indicator</string>
|
||||
<string name="vibration">Vibration</string>
|
||||
<string name="favourites_categories">Favourites categories</string>
|
||||
<string name="favourites_categories">Favourite categories</string>
|
||||
<string name="categories_">Categories…</string>
|
||||
<string name="rename">Rename</string>
|
||||
<string name="category_delete_confirm">Do you really want to remove category \"%s\" from your favourites? \nAll containing manga will be lost.</string>
|
||||
<string name="remove_category">Remove category</string>
|
||||
<string name="category_delete_confirm">Remove the \"%s\" category from your favourites? \nAll manga in it will be lost.</string>
|
||||
<string name="remove_category">Remove</string>
|
||||
<string name="text_empty_holder_primary">It\'s kind of empty here…</string>
|
||||
<string name="text_categories_holder">You can use categories to organize your favourite manga. Press «+» to create a category</string>
|
||||
<string name="text_categories_holder">You can use categories to organize your favourites. Press «+» to create a category</string>
|
||||
<string name="text_search_holder_secondary">Try to reformulate the query.</string>
|
||||
<string name="text_history_holder_primary">Manga you are reading will be displayed here</string>
|
||||
<string name="text_history_holder_secondary">You can find what to read in side menu.</string>
|
||||
<string name="text_local_holder_primary">You have not any saved manga yet</string>
|
||||
<string name="text_local_holder_secondary">You can save it from online sources or import from file.</string>
|
||||
<string name="manga_shelf">Manga shelf</string>
|
||||
<string name="recent_manga">Recent manga</string>
|
||||
<string name="pages_animation">Pages animation</string>
|
||||
<string name="manga_save_location">Manga download location</string>
|
||||
<string name="text_history_holder_primary">What you read will be displayed here</string>
|
||||
<string name="text_history_holder_secondary">Find what to read in side menu.</string>
|
||||
<string name="text_local_holder_primary">Save something first</string>
|
||||
<string name="text_local_holder_secondary">Save it from online sources or import files.</string>
|
||||
<string name="manga_shelf">Shelf</string>
|
||||
<string name="recent_manga">Recent</string>
|
||||
<string name="pages_animation">Page animation</string>
|
||||
<string name="manga_save_location">Folder for downloads</string>
|
||||
<string name="not_available">Not available</string>
|
||||
<string name="cannot_find_available_storage">Cannot find any available storage</string>
|
||||
<string name="cannot_find_available_storage">No available storage</string>
|
||||
<string name="other_storage">Other storage</string>
|
||||
<string name="use_ssl">Use secure connection (HTTPS)</string>
|
||||
<string name="use_ssl">Use secure (HTTPS) connection</string>
|
||||
<string name="done">Done</string>
|
||||
<string name="all_favourites">All favourites</string>
|
||||
<string name="favourites_category_empty">This category is empty</string>
|
||||
<string name="favourites_category_empty">Empty category</string>
|
||||
<string name="read_later">Read later</string>
|
||||
<string name="updates">Updates</string>
|
||||
<string name="text_feed_holder">Here you will see the new chapters of the manga you are reading</string>
|
||||
<string name="text_feed_holder">New chapters of what you are reading is shown here</string>
|
||||
<string name="search_results">Search results</string>
|
||||
<string name="related">Related</string>
|
||||
<string name="new_version_s">New version: %s</string>
|
||||
<string name="size_s">Size: %s</string>
|
||||
<string name="waiting_for_network">Waiting for network…</string>
|
||||
<string name="clear_updates_feed">Clear updates feed</string>
|
||||
<string name="updates_feed_cleared">Updates feed cleared</string>
|
||||
<string name="updates_feed_cleared">Cleared</string>
|
||||
<string name="rotate_screen">Rotate screen</string>
|
||||
<string name="update">Update</string>
|
||||
<string name="feed_will_update_soon">Feed update will start soon</string>
|
||||
<string name="track_sources">Check updates for manga</string>
|
||||
<string name="dont_check">Don`t check</string>
|
||||
<string name="track_sources">Look for updates</string>
|
||||
<string name="dont_check">Don\'t check</string>
|
||||
<string name="enter_password">Enter password</string>
|
||||
<string name="wrong_password">Wrong password</string>
|
||||
<string name="protect_application">Protect application</string>
|
||||
<string name="protect_application_summary">Ask for password on application start</string>
|
||||
<string name="repeat_password">Repeat password</string>
|
||||
<string name="passwords_mismatch">Passwords do not match</string>
|
||||
<string name="protect_application">Protect the app</string>
|
||||
<string name="protect_application_summary">Ask for password when starting Kotatsu</string>
|
||||
<string name="repeat_password">Repeat the password</string>
|
||||
<string name="passwords_mismatch">Mismatching passwords</string>
|
||||
<string name="about">About</string>
|
||||
<string name="app_version">Version %s</string>
|
||||
<string name="check_for_updates">Check for updates</string>
|
||||
<string name="checking_for_updates">Checking for updates…</string>
|
||||
<string name="update_check_failed">Update check failed</string>
|
||||
<string name="update_check_failed">Could not look for updates</string>
|
||||
<string name="no_update_available">No updates available</string>
|
||||
<string name="right_to_left">Right to left</string>
|
||||
<string name="prefer_rtl_reader">Prefer Right to left reader</string>
|
||||
<string name="prefer_rtl_reader_summary">You can set up the reading mode for each manga separately</string>
|
||||
<string name="right_to_left">Right-to-left (←)</string>
|
||||
<string name="prefer_rtl_reader">Prefer right-to-left (→) reader</string>
|
||||
<string name="prefer_rtl_reader_summary">Reading mode can be set up separately for each series</string>
|
||||
<string name="create_category">New category</string>
|
||||
<string name="report_github">Create issue on GitHub</string>
|
||||
<string name="scale_mode">Scale mode</string>
|
||||
@@ -174,17 +174,17 @@
|
||||
<string name="zoom_mode_fit_height">Fit to height</string>
|
||||
<string name="zoom_mode_fit_width">Fit to width</string>
|
||||
<string name="zoom_mode_keep_start">Keep at start</string>
|
||||
<string name="black_dark_theme">Black dark theme</string>
|
||||
<string name="black_dark_theme_summary">Useful for AMOLED screens</string>
|
||||
<string name="black_dark_theme">Black</string>
|
||||
<string name="black_dark_theme_summary">Uses less power on AMOLED screens</string>
|
||||
<string name="restart_required">Restart required</string>
|
||||
<string name="backup_restore"><![CDATA[Backup & Restore]]></string>
|
||||
<string name="backup_restore"><![CDATA[Backup and restore]]></string>
|
||||
<string name="create_backup">Create data backup</string>
|
||||
<string name="restore_backup">Restore from backup</string>
|
||||
<string name="data_restored">Data restored</string>
|
||||
<string name="data_restored">Restored</string>
|
||||
<string name="preparing_">Preparing…</string>
|
||||
<string name="file_not_found">File not found</string>
|
||||
<string name="data_restored_success">All data restored successfully</string>
|
||||
<string name="data_restored_with_errors">The data restored, but there are errors</string>
|
||||
<string name="data_restored_success">All data was restored</string>
|
||||
<string name="data_restored_with_errors">The data was restored, but there are errors</string>
|
||||
<string name="backup_information">You can create backup of your history and favourites and restore it</string>
|
||||
<string name="just_now">Just now</string>
|
||||
<string name="yesterday">Yesterday</string>
|
||||
@@ -192,61 +192,62 @@
|
||||
<string name="group">Group</string>
|
||||
<string name="today">Today</string>
|
||||
<string name="tap_to_try_again">Tap to try again</string>
|
||||
<string name="reader_mode_hint">Chosen configuration will be remembered for this manga</string>
|
||||
<string name="reader_mode_hint">The chosen configuration will be remembered for this manga</string>
|
||||
<string name="silent">Silent</string>
|
||||
<string name="captcha_required">CAPTCHA is required</string>
|
||||
<string name="captcha_required">CAPTCHA required</string>
|
||||
<string name="captcha_solve">Solve</string>
|
||||
<string name="clear_cookies">Clear cookies</string>
|
||||
<string name="cookies_cleared">All cookies was removed</string>
|
||||
<string name="cookies_cleared">All cookies were removed</string>
|
||||
<string name="chapters_checking_progress">Checking for new chapters: %1$d of %2$d</string>
|
||||
<string name="clear_feed">Clear feed</string>
|
||||
<string name="text_clear_updates_feed_prompt">All updates history will be cleared and this action cannot be undone. Are you sure?</string>
|
||||
<string name="new_chapters_checking">New chapters checking</string>
|
||||
<string name="text_clear_updates_feed_prompt">Clear all update history permanently?</string>
|
||||
<string name="new_chapters_checking">Looking for new chapters…</string>
|
||||
<string name="reverse">Reverse</string>
|
||||
<string name="sign_in">Sign in</string>
|
||||
<string name="auth_required">You should authorize to view this content</string>
|
||||
<string name="auth_required">Sign in to view this content</string>
|
||||
<string name="default_s">Default: %s</string>
|
||||
<string name="_and_x_more">…and %1$d more</string>
|
||||
<string name="next">Next</string>
|
||||
<string name="protect_application_subtitle">Enter password that will be required when the application starts</string>
|
||||
<string name="protect_application_subtitle">Enter a password to start the app with</string>
|
||||
<string name="confirm">Confirm</string>
|
||||
<string name="password_length_hint">Password must be at least 4 characters</string>
|
||||
<string name="password_length_hint">The password must be 4 characters or more</string>
|
||||
<string name="hide_toolbar">Hide toolbar when scrolling</string>
|
||||
<string name="search_only_on_s">Search only on %s</string>
|
||||
<string name="text_clear_search_history_prompt">Do you really want to remove all recent search queries? This action cannot be undone.</string>
|
||||
<string name="text_clear_search_history_prompt">Remove all recent search queries permanently?</string>
|
||||
<string name="other">Other</string>
|
||||
<string name="languages">Languages</string>
|
||||
<string name="welcome">Welcome</string>
|
||||
<string name="backup_saved">Backup saved successfully</string>
|
||||
<string name="tracker_warning">Some manufacturers can change the system behavior, which may breaks background tasks.</string>
|
||||
<string name="description">Description</string>
|
||||
<string name="backup_saved">Backup saved</string>
|
||||
<string name="tracker_warning">Some devices have different system behavior, which may break background tasks.</string>
|
||||
<string name="read_more">Read more</string>
|
||||
<string name="queued">Queued</string>
|
||||
<string name="text_downloads_holder">There are currently no active downloads</string>
|
||||
<string name="chapter_is_missing_text">This chapter is missing on your device. Download or read it online.</string>
|
||||
<string name="chapter_is_missing">Chapter is missing</string>
|
||||
<string name="text_downloads_holder">No active downloads</string>
|
||||
<string name="chapter_is_missing_text">Download or read this missing chapter online.</string>
|
||||
<string name="chapter_is_missing">The chapter is missing</string>
|
||||
<string name="about_app_translation_summary">Translate this app</string>
|
||||
<string name="about_app_translation">Translation</string>
|
||||
<string name="about_author">Author</string>
|
||||
<string name="about_feedback">Feedback</string>
|
||||
<string name="about_feedback_4pda">Topic on 4PDA</string>
|
||||
<string name="about_support_developer">Support the developer</string>
|
||||
<string name="about_support_developer_summary">If you like this app, you can help financially through Yoomoney (ex. Yandex.Money)</string>
|
||||
<string name="about_support_developer_summary">If you like this app, you can send money through Yoomoney (ex. Yandex.Money)</string>
|
||||
<string name="about_gratitudes">Gratitudes</string>
|
||||
<string name="about_gratitudes_summary">These people make Kotatsu become better!</string>
|
||||
<string name="about_gratitudes_summary">These people all made Kotatsu better</string>
|
||||
<string name="about_copyright_and_licenses">Copyright and Licenses</string>
|
||||
<string name="about_license">License</string>
|
||||
<string name="auth_complete">Authorization complete</string>
|
||||
<string name="auth_not_supported_by">Authorization on %s is not supported</string>
|
||||
<string name="text_clear_cookies_prompt">You will be logged out from all sources that you are authorized in</string>
|
||||
<string name="auth_complete">Authorized</string>
|
||||
<string name="auth_not_supported_by">Logging in on %s is not supported</string>
|
||||
<string name="text_clear_cookies_prompt">You will be logged out from all sources</string>
|
||||
<string name="genres">Genres</string>
|
||||
<string name="state_finished">Finished</string>
|
||||
<string name="state_ongoing">Ongoing</string>
|
||||
<string name="date_format">Date format</string>
|
||||
<string name="system_default">Default</string>
|
||||
<string name="exclude_nsfw_from_history">Exclude NSFW manga from history</string>
|
||||
<string name="error_empty_name">Name should not be empty</string>
|
||||
<string name="show_pages_numbers">Show pages numbers</string>
|
||||
<string name="enabled_sources">Enabled sources</string>
|
||||
<string name="error_empty_name">You must enter a name</string>
|
||||
<string name="show_pages_numbers">Numbered pages</string>
|
||||
<string name="enabled_sources">Used sources</string>
|
||||
<string name="available_sources">Available sources</string>
|
||||
<string name="dynamic_theme">Dynamic theme</string>
|
||||
<string name="dynamic_theme_summary">Applies a theme created on the color scheme of your wallpaper</string>
|
||||
|
||||
@@ -31,7 +31,7 @@ class RemoteMangaRepositoryTest(private val source: MangaSource) : KoinTest {
|
||||
@get:Rule
|
||||
val koinTestRule = KoinTestRule.create {
|
||||
printLogger(Level.ERROR)
|
||||
modules(repositoryTestModule)
|
||||
modules(repositoryTestModule, parserModule)
|
||||
}
|
||||
|
||||
@get:Rule
|
||||
|
||||
@@ -7,7 +7,6 @@ import org.koitharu.kotatsu.base.domain.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.core.network.TestCookieJar
|
||||
import org.koitharu.kotatsu.core.network.UserAgentInterceptor
|
||||
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
||||
import org.koitharu.kotatsu.core.parser.SourceSettingsStub
|
||||
import org.koitharu.kotatsu.core.prefs.SourceSettings
|
||||
import java.util.concurrent.TimeUnit
|
||||
@@ -31,12 +30,4 @@ val repositoryTestModule
|
||||
}
|
||||
}
|
||||
}
|
||||
factory { (source: MangaSource) ->
|
||||
runCatching {
|
||||
source.cls.getDeclaredConstructor(MangaLoaderContext::class.java)
|
||||
.newInstance(get<MangaLoaderContext>())
|
||||
}.recoverCatching {
|
||||
source.cls.newInstance()
|
||||
}.getOrThrow() as RemoteMangaRepository
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user