Merge branch 'devel' into feature/mal
# Conflicts: # app/src/main/java/org/koitharu/kotatsu/settings/HistorySettingsFragment.kt # app/src/main/res/values/strings.xml
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:largeHeap="true"
|
||||
android:localeConfig="@xml/locales"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
@@ -247,6 +248,9 @@
|
||||
<meta-data
|
||||
android:name="android.webkit.WebView.MetricsOptOut"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="com.samsung.android.icon_container.has_icon_container"
|
||||
android:value="@bool/com_samsung_android_icon_container_has_icon_container"/>
|
||||
|
||||
</application>
|
||||
|
||||
|
||||
@@ -31,6 +31,9 @@ abstract class CoroutineIntentService : BaseService() {
|
||||
processIntent(startId, intent)
|
||||
}
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTraceDebug()
|
||||
onError(startId, e)
|
||||
} finally {
|
||||
stopSelf(startId)
|
||||
}
|
||||
|
||||
@@ -2,20 +2,20 @@ package org.koitharu.kotatsu.base.ui.list
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import android.util.ArrayMap
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.collection.ArrayMap
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.savedstate.SavedStateRegistry
|
||||
import androidx.savedstate.SavedStateRegistryOwner
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import org.koitharu.kotatsu.base.ui.list.decor.AbstractSelectionItemDecoration
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
||||
private const val PROVIDER_NAME = "selection_decoration_sectioned"
|
||||
|
||||
|
||||
@@ -516,6 +516,6 @@ class FastScroller @JvmOverloads constructor(
|
||||
|
||||
interface SectionIndexer {
|
||||
|
||||
fun getSectionText(context: Context, position: Int): CharSequence
|
||||
fun getSectionText(context: Context, position: Int): CharSequence?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,12 @@ import android.graphics.Bitmap
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import org.koitharu.kotatsu.core.network.AndroidCookieJar
|
||||
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
|
||||
|
||||
private const val CF_CLEARANCE = "cf_clearance"
|
||||
|
||||
class CloudFlareClient(
|
||||
private val cookieJar: AndroidCookieJar,
|
||||
private val cookieJar: MutableCookieJar,
|
||||
private val callback: CloudFlareCallback,
|
||||
private val targetUrl: String,
|
||||
) : WebViewClient() {
|
||||
@@ -42,4 +42,4 @@ class CloudFlareClient(
|
||||
return cookieJar.loadForRequest(targetUrl.toHttpUrl())
|
||||
.find { it.name == CF_CLEARANCE }?.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,13 +12,13 @@ import androidx.core.view.isInvisible
|
||||
import androidx.fragment.app.setFragmentResult
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import org.koitharu.kotatsu.base.ui.AlertDialogFragment
|
||||
import org.koitharu.kotatsu.core.network.AndroidCookieJar
|
||||
import org.koitharu.kotatsu.core.network.UserAgentInterceptor
|
||||
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
|
||||
import org.koitharu.kotatsu.databinding.FragmentCloudflareBinding
|
||||
import org.koitharu.kotatsu.utils.ext.stringArgument
|
||||
import org.koitharu.kotatsu.utils.ext.withArgs
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class CloudFlareDialog : AlertDialogFragment<FragmentCloudflareBinding>(), CloudFlareCallback {
|
||||
@@ -27,7 +27,7 @@ class CloudFlareDialog : AlertDialogFragment<FragmentCloudflareBinding>(), Cloud
|
||||
private val pendingResult = Bundle(1)
|
||||
|
||||
@Inject
|
||||
lateinit var cookieJar: AndroidCookieJar
|
||||
lateinit var cookieJar: MutableCookieJar
|
||||
|
||||
override fun onInflateView(
|
||||
inflater: LayoutInflater,
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.app.Application
|
||||
import android.content.Context
|
||||
import android.provider.SearchRecentSuggestions
|
||||
import android.text.Html
|
||||
import android.util.AndroidRuntimeException
|
||||
import androidx.collection.arraySetOf
|
||||
import androidx.room.InvalidationTracker
|
||||
import coil.ComponentRegistry
|
||||
@@ -25,6 +26,10 @@ import org.koitharu.kotatsu.BuildConfig
|
||||
import org.koitharu.kotatsu.base.ui.util.ActivityRecreationHandle
|
||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
import org.koitharu.kotatsu.core.network.*
|
||||
import org.koitharu.kotatsu.core.network.cookies.AndroidCookieJar
|
||||
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
|
||||
import org.koitharu.kotatsu.core.network.cookies.PreferencesCookieJar
|
||||
import org.koitharu.kotatsu.core.os.NetworkState
|
||||
import org.koitharu.kotatsu.core.os.ShortcutsUpdater
|
||||
import org.koitharu.kotatsu.core.parser.MangaLoaderContextImpl
|
||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
@@ -39,6 +44,7 @@ import org.koitharu.kotatsu.search.ui.MangaSuggestionsProvider
|
||||
import org.koitharu.kotatsu.settings.backup.BackupObserver
|
||||
import org.koitharu.kotatsu.sync.domain.SyncController
|
||||
import org.koitharu.kotatsu.utils.IncognitoModeIndicator
|
||||
import org.koitharu.kotatsu.utils.ext.connectivityManager
|
||||
import org.koitharu.kotatsu.utils.ext.isLowRamDevice
|
||||
import org.koitharu.kotatsu.utils.image.CoilImageGetter
|
||||
import org.koitharu.kotatsu.widget.WidgetUpdater
|
||||
@@ -50,7 +56,7 @@ import javax.inject.Singleton
|
||||
interface AppModule {
|
||||
|
||||
@Binds
|
||||
fun bindCookieJar(androidCookieJar: AndroidCookieJar): CookieJar
|
||||
fun bindCookieJar(androidCookieJar: MutableCookieJar): CookieJar
|
||||
|
||||
@Binds
|
||||
fun bindMangaLoaderContext(mangaLoaderContextImpl: MangaLoaderContextImpl): MangaLoaderContext
|
||||
@@ -60,6 +66,17 @@ interface AppModule {
|
||||
|
||||
companion object {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCookieJar(
|
||||
@ApplicationContext context: Context
|
||||
): MutableCookieJar = try {
|
||||
AndroidCookieJar()
|
||||
} catch (e: AndroidRuntimeException) {
|
||||
// WebView is not available
|
||||
PreferencesCookieJar(context)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideOkHttpClient(
|
||||
@@ -81,6 +98,12 @@ interface AppModule {
|
||||
}.build()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideNetworkState(
|
||||
@ApplicationContext context: Context
|
||||
) = NetworkState(context.connectivityManager)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideMangaDatabase(
|
||||
|
||||
@@ -14,11 +14,11 @@ abstract class MangaDao {
|
||||
abstract suspend fun find(id: Long): MangaWithTags?
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM manga WHERE title LIKE :query OR alt_title LIKE :query LIMIT :limit")
|
||||
@Query("SELECT * FROM manga WHERE (title LIKE :query OR alt_title LIKE :query) AND manga_id IN (SELECT manga_id FROM favourites UNION SELECT manga_id FROM history) LIMIT :limit")
|
||||
abstract suspend fun searchByTitle(query: String, limit: Int): List<MangaWithTags>
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM manga WHERE (title LIKE :query OR alt_title LIKE :query) AND source = :source LIMIT :limit")
|
||||
@Query("SELECT * FROM manga WHERE (title LIKE :query OR alt_title LIKE :query) AND source = :source AND manga_id IN (SELECT manga_id FROM favourites UNION SELECT manga_id FROM history) LIMIT :limit")
|
||||
abstract suspend fun searchByTitle(query: String, source: String, limit: Int): List<MangaWithTags>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
@@ -47,4 +47,4 @@ abstract class MangaDao {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package org.koitharu.kotatsu.core.exceptions.resolve
|
||||
|
||||
import android.util.ArrayMap
|
||||
import androidx.activity.result.ActivityResultCallback
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.collection.ArrayMap
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
package org.koitharu.kotatsu.core.network
|
||||
package org.koitharu.kotatsu.core.network.cookies
|
||||
|
||||
import android.webkit.CookieManager
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import androidx.annotation.WorkerThread
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.HttpUrl
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.CookieJar
|
||||
import okhttp3.HttpUrl
|
||||
|
||||
@Singleton
|
||||
class AndroidCookieJar @Inject constructor() : CookieJar {
|
||||
class AndroidCookieJar : MutableCookieJar {
|
||||
|
||||
private val cookieManager = CookieManager.getInstance()
|
||||
|
||||
@WorkerThread
|
||||
override fun loadForRequest(url: HttpUrl): List<Cookie> {
|
||||
val rawCookie = cookieManager.getCookie(url.toString()) ?: return emptyList()
|
||||
return rawCookie.split(';').mapNotNull {
|
||||
@@ -21,6 +19,7 @@ class AndroidCookieJar @Inject constructor() : CookieJar {
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
||||
if (cookies.isEmpty()) {
|
||||
return
|
||||
@@ -31,7 +30,7 @@ class AndroidCookieJar @Inject constructor() : CookieJar {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun clear() = suspendCoroutine<Boolean> { continuation ->
|
||||
override suspend fun clear() = suspendCoroutine<Boolean> { continuation ->
|
||||
cookieManager.removeAllCookies(continuation::resume)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package org.koitharu.kotatsu.core.network.cookies
|
||||
|
||||
import android.util.Base64
|
||||
import okhttp3.Cookie
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.ObjectInputStream
|
||||
import java.io.ObjectOutputStream
|
||||
|
||||
|
||||
class CookieWrapper(
|
||||
val cookie: Cookie,
|
||||
) {
|
||||
|
||||
constructor(encodedString: String) : this(
|
||||
ObjectInputStream(ByteArrayInputStream(Base64.decode(encodedString, Base64.NO_WRAP))).use {
|
||||
val name = it.readUTF()
|
||||
val value = it.readUTF()
|
||||
val expiresAt = it.readLong()
|
||||
val domain = it.readUTF()
|
||||
val path = it.readUTF()
|
||||
val secure = it.readBoolean()
|
||||
val httpOnly = it.readBoolean()
|
||||
val persistent = it.readBoolean()
|
||||
val hostOnly = it.readBoolean()
|
||||
Cookie.Builder().also { c ->
|
||||
c.name(name)
|
||||
c.value(value)
|
||||
if (persistent) {
|
||||
c.expiresAt(expiresAt)
|
||||
}
|
||||
if (hostOnly) {
|
||||
c.hostOnlyDomain(domain)
|
||||
} else {
|
||||
c.domain(domain)
|
||||
}
|
||||
c.path(path)
|
||||
if (secure) {
|
||||
c.secure()
|
||||
}
|
||||
if (httpOnly) {
|
||||
c.httpOnly()
|
||||
}
|
||||
}.build()
|
||||
},
|
||||
)
|
||||
|
||||
fun encode(): String {
|
||||
val output = ByteArrayOutputStream()
|
||||
ObjectOutputStream(output).use {
|
||||
it.writeUTF(cookie.name)
|
||||
it.writeUTF(cookie.value)
|
||||
it.writeLong(cookie.expiresAt)
|
||||
it.writeUTF(cookie.domain)
|
||||
it.writeUTF(cookie.path)
|
||||
it.writeBoolean(cookie.secure)
|
||||
it.writeBoolean(cookie.httpOnly)
|
||||
it.writeBoolean(cookie.persistent)
|
||||
it.writeBoolean(cookie.hostOnly)
|
||||
}
|
||||
return Base64.encodeToString(output.toByteArray(), Base64.NO_WRAP)
|
||||
}
|
||||
|
||||
fun isExpired() = cookie.expiresAt < System.currentTimeMillis()
|
||||
|
||||
fun key(): String {
|
||||
return (if (cookie.secure) "https" else "http") + "://" + cookie.domain + cookie.path + "|" + cookie.name
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as CookieWrapper
|
||||
|
||||
if (cookie != other.cookie) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return cookie.hashCode()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package org.koitharu.kotatsu.core.network.cookies
|
||||
|
||||
import androidx.annotation.WorkerThread
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.CookieJar
|
||||
import okhttp3.HttpUrl
|
||||
|
||||
interface MutableCookieJar : CookieJar {
|
||||
|
||||
@WorkerThread
|
||||
override fun loadForRequest(url: HttpUrl): List<Cookie>
|
||||
|
||||
@WorkerThread
|
||||
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>)
|
||||
|
||||
suspend fun clear(): Boolean
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package org.koitharu.kotatsu.core.network.cookies
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.collection.ArrayMap
|
||||
import androidx.core.content.edit
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.HttpUrl
|
||||
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
||||
|
||||
private const val PREFS_NAME = "cookies"
|
||||
|
||||
class PreferencesCookieJar(
|
||||
context: Context,
|
||||
) : MutableCookieJar {
|
||||
|
||||
private val cache = ArrayMap<String, CookieWrapper>()
|
||||
private val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
private var isLoaded = false
|
||||
|
||||
@WorkerThread
|
||||
override fun loadForRequest(url: HttpUrl): List<Cookie> {
|
||||
loadPersistent()
|
||||
val expired = HashSet<String>()
|
||||
val result = ArrayList<Cookie>()
|
||||
for ((key, cookie) in cache) {
|
||||
if (cookie.isExpired()) {
|
||||
expired += key
|
||||
} else if (cookie.cookie.matches(url)) {
|
||||
result += cookie.cookie
|
||||
}
|
||||
}
|
||||
if (expired.isNotEmpty()) {
|
||||
cache.removeAll(expired)
|
||||
removePersistent(expired)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
|
||||
val wrapped = cookies.map { CookieWrapper(it) }
|
||||
prefs.edit(commit = true) {
|
||||
for (cookie in wrapped) {
|
||||
val key = cookie.key()
|
||||
cache[key] = cookie
|
||||
if (cookie.cookie.persistent) {
|
||||
putString(key, cookie.encode())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun clear(): Boolean {
|
||||
cache.clear()
|
||||
withContext(Dispatchers.IO) {
|
||||
prefs.edit(commit = true) { clear() }
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private fun loadPersistent() {
|
||||
if (!isLoaded) {
|
||||
val map = prefs.all
|
||||
cache.ensureCapacity(map.size)
|
||||
for ((k, v) in map) {
|
||||
val cookie = try {
|
||||
CookieWrapper(v as String)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTraceDebug()
|
||||
continue
|
||||
}
|
||||
cache[k] = cookie
|
||||
}
|
||||
isLoaded = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun removePersistent(keys: Collection<String>) {
|
||||
prefs.edit(commit = true) {
|
||||
for (key in keys) {
|
||||
remove(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package org.koitharu.kotatsu.core.os
|
||||
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.ConnectivityManager.NetworkCallback
|
||||
import android.net.Network
|
||||
import android.net.NetworkRequest
|
||||
import kotlinx.coroutines.flow.first
|
||||
import org.koitharu.kotatsu.utils.MediatorStateFlow
|
||||
import org.koitharu.kotatsu.utils.ext.isNetworkAvailable
|
||||
|
||||
class NetworkState(
|
||||
private val connectivityManager: ConnectivityManager,
|
||||
) : MediatorStateFlow<Boolean>(connectivityManager.isNetworkAvailable) {
|
||||
|
||||
private val callback = NetworkCallbackImpl()
|
||||
|
||||
override fun onActive() {
|
||||
invalidate()
|
||||
val request = NetworkRequest.Builder().build()
|
||||
connectivityManager.registerNetworkCallback(request, callback)
|
||||
}
|
||||
|
||||
override fun onInactive() {
|
||||
connectivityManager.unregisterNetworkCallback(callback)
|
||||
}
|
||||
|
||||
suspend fun awaitForConnection() {
|
||||
if (value) {
|
||||
return
|
||||
}
|
||||
first { it }
|
||||
}
|
||||
|
||||
private fun invalidate() {
|
||||
publishValue(connectivityManager.isNetworkAvailable)
|
||||
}
|
||||
|
||||
private inner class NetworkCallbackImpl : NetworkCallback() {
|
||||
|
||||
override fun onAvailable(network: Network) = invalidate()
|
||||
|
||||
override fun onLost(network: Network) = invalidate()
|
||||
|
||||
override fun onUnavailable() = invalidate()
|
||||
}
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package org.koitharu.kotatsu.core.os
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager.NetworkCallback
|
||||
import android.net.Network
|
||||
import android.net.NetworkRequest
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.channels.ProducerScope
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.channels.onSuccess
|
||||
import kotlinx.coroutines.channels.trySendBlocking
|
||||
import kotlinx.coroutines.flow.FlowCollector
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import org.koitharu.kotatsu.utils.ext.connectivityManager
|
||||
import org.koitharu.kotatsu.utils.ext.isNetworkAvailable
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class NetworkStateObserver @Inject constructor(
|
||||
@ApplicationContext context: Context,
|
||||
) : StateFlow<Boolean> {
|
||||
|
||||
private val connectivityManager = context.connectivityManager
|
||||
|
||||
override val replayCache: List<Boolean>
|
||||
get() = listOf(value)
|
||||
|
||||
override val value: Boolean
|
||||
get() = connectivityManager.isNetworkAvailable
|
||||
|
||||
override suspend fun collect(collector: FlowCollector<Boolean>): Nothing {
|
||||
collector.emit(value)
|
||||
while (true) {
|
||||
observeImpl().collect(collector)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun awaitForConnection(): Unit {
|
||||
if (value) {
|
||||
return
|
||||
}
|
||||
first { it }
|
||||
}
|
||||
|
||||
private fun observeImpl() = callbackFlow<Boolean> {
|
||||
val request = NetworkRequest.Builder().build()
|
||||
val callback = FlowNetworkCallback(this)
|
||||
connectivityManager.registerNetworkCallback(request, callback)
|
||||
awaitClose {
|
||||
connectivityManager.unregisterNetworkCallback(callback)
|
||||
}
|
||||
}
|
||||
|
||||
private inner class FlowNetworkCallback(
|
||||
private val producerScope: ProducerScope<Boolean>,
|
||||
) : NetworkCallback() {
|
||||
|
||||
private var prevValue = value
|
||||
|
||||
override fun onAvailable(network: Network) = update()
|
||||
|
||||
override fun onLost(network: Network) = update()
|
||||
|
||||
override fun onUnavailable() = update()
|
||||
|
||||
private fun update() {
|
||||
val newValue = connectivityManager.isNetworkAvailable
|
||||
if (newValue != prevValue) {
|
||||
producerScope.trySendBlocking(newValue).onSuccess {
|
||||
prevValue = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,25 +6,25 @@ import android.util.Base64
|
||||
import android.webkit.WebView
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import okhttp3.OkHttpClient
|
||||
import org.koitharu.kotatsu.core.network.AndroidCookieJar
|
||||
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
|
||||
import org.koitharu.kotatsu.core.prefs.SourceSettings
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.config.MangaSourceConfig
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.utils.ext.toList
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
@Singleton
|
||||
class MangaLoaderContextImpl @Inject constructor(
|
||||
override val httpClient: OkHttpClient,
|
||||
override val cookieJar: AndroidCookieJar,
|
||||
override val cookieJar: MutableCookieJar,
|
||||
@ApplicationContext private val androidContext: Context,
|
||||
) : MangaLoaderContext() {
|
||||
|
||||
|
||||
@@ -52,10 +52,13 @@ class ZipOutput(
|
||||
return if (entryNames.add(entry.name)) {
|
||||
val zipEntry = ZipEntry(entry.name)
|
||||
output.putNextEntry(zipEntry)
|
||||
other.getInputStream(entry).use { input ->
|
||||
input.copyTo(output)
|
||||
try {
|
||||
other.getInputStream(entry).use { input ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
} finally {
|
||||
output.closeEntry()
|
||||
}
|
||||
output.closeEntry()
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
||||
@@ -228,18 +228,18 @@ class DetailsActivity :
|
||||
}
|
||||
}
|
||||
|
||||
private fun onHistoryChanged(info: HistoryInfo?) {
|
||||
private fun onHistoryChanged(info: HistoryInfo) {
|
||||
with(binding.buttonRead) {
|
||||
if (info?.history != null) {
|
||||
if (info.history != null) {
|
||||
setText(R.string._continue)
|
||||
setIconResource(R.drawable.ic_play)
|
||||
setIconResource(if (info.isIncognitoMode) R.drawable.ic_incognito else R.drawable.ic_play)
|
||||
} else {
|
||||
setText(R.string.read)
|
||||
setIconResource(R.drawable.ic_read)
|
||||
setIconResource(if (info.isIncognitoMode) R.drawable.ic_incognito else R.drawable.ic_play)
|
||||
}
|
||||
}
|
||||
val text = when {
|
||||
info == null -> getString(R.string.loading_)
|
||||
!info.isValid -> getString(R.string.loading_)
|
||||
info.currentChapter >= 0 -> getString(R.string.chapter_d_of_d, info.currentChapter + 1, info.totalChapters)
|
||||
info.totalChapters == 0 -> getString(R.string.no_chapters)
|
||||
else -> resources.getQuantityString(R.plurals.chapters, info.totalChapters, info.totalChapters)
|
||||
|
||||
@@ -18,7 +18,6 @@ import coil.request.ImageRequest
|
||||
import coil.util.CoilUtils
|
||||
import com.google.android.material.chip.Chip
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.BaseFragment
|
||||
@@ -27,9 +26,9 @@ import org.koitharu.kotatsu.base.ui.list.decor.SpacingItemDecoration
|
||||
import org.koitharu.kotatsu.base.ui.widgets.ChipsView
|
||||
import org.koitharu.kotatsu.bookmarks.domain.Bookmark
|
||||
import org.koitharu.kotatsu.bookmarks.ui.adapter.BookmarksAdapter
|
||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||
import org.koitharu.kotatsu.databinding.FragmentDetailsBinding
|
||||
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
|
||||
import org.koitharu.kotatsu.details.ui.model.HistoryInfo
|
||||
import org.koitharu.kotatsu.details.ui.scrobbling.ScrobblingItemDecoration
|
||||
import org.koitharu.kotatsu.details.ui.scrobbling.ScrollingInfoAdapter
|
||||
import org.koitharu.kotatsu.history.domain.PROGRESS_NONE
|
||||
@@ -45,8 +44,20 @@ import org.koitharu.kotatsu.scrobbling.domain.model.ScrobblingInfo
|
||||
import org.koitharu.kotatsu.search.ui.MangaListActivity
|
||||
import org.koitharu.kotatsu.search.ui.SearchActivity
|
||||
import org.koitharu.kotatsu.utils.FileSize
|
||||
import org.koitharu.kotatsu.utils.ext.*
|
||||
import org.koitharu.kotatsu.utils.ext.computeSize
|
||||
import org.koitharu.kotatsu.utils.ext.crossfade
|
||||
import org.koitharu.kotatsu.utils.ext.drawableTop
|
||||
import org.koitharu.kotatsu.utils.ext.enqueueWith
|
||||
import org.koitharu.kotatsu.utils.ext.ifNullOrEmpty
|
||||
import org.koitharu.kotatsu.utils.ext.measureHeight
|
||||
import org.koitharu.kotatsu.utils.ext.referer
|
||||
import org.koitharu.kotatsu.utils.ext.resolveDp
|
||||
import org.koitharu.kotatsu.utils.ext.scaleUpActivityOptionsOf
|
||||
import org.koitharu.kotatsu.utils.ext.textAndVisible
|
||||
import org.koitharu.kotatsu.utils.ext.toFileOrNull
|
||||
import org.koitharu.kotatsu.utils.ext.viewLifecycleScope
|
||||
import org.koitharu.kotatsu.utils.image.CoverSizeResolver
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class DetailsFragment :
|
||||
@@ -75,7 +86,7 @@ class DetailsFragment :
|
||||
binding.chipsTags.onChipClickListener = this
|
||||
viewModel.manga.observe(viewLifecycleOwner, ::onMangaUpdated)
|
||||
viewModel.isLoading.observe(viewLifecycleOwner, ::onLoadingStateChanged)
|
||||
viewModel.readingHistory.observe(viewLifecycleOwner, ::onHistoryChanged)
|
||||
viewModel.historyInfo.observe(viewLifecycleOwner, ::onHistoryChanged)
|
||||
viewModel.bookmarks.observe(viewLifecycleOwner, ::onBookmarksChanged)
|
||||
viewModel.scrobblingInfo.observe(viewLifecycleOwner, ::onScrobblingInfoChanged)
|
||||
viewModel.description.observe(viewLifecycleOwner, ::onDescriptionChanged)
|
||||
@@ -123,12 +134,14 @@ class DetailsFragment :
|
||||
drawableTop = ContextCompat.getDrawable(context, R.drawable.ic_state_finished)
|
||||
}
|
||||
}
|
||||
|
||||
MangaState.ONGOING -> {
|
||||
infoLayout.textViewState.apply {
|
||||
textAndVisible = resources.getString(R.string.state_ongoing)
|
||||
drawableTop = ContextCompat.getDrawable(context, R.drawable.ic_state_ongoing)
|
||||
}
|
||||
}
|
||||
|
||||
else -> infoLayout.textViewState.isVisible = false
|
||||
}
|
||||
if (manga.source == MangaSource.LOCAL) {
|
||||
@@ -178,8 +191,8 @@ class DetailsFragment :
|
||||
}
|
||||
}
|
||||
|
||||
private fun onHistoryChanged(history: MangaHistory?) {
|
||||
binding.progressView.setPercent(history?.percent ?: PROGRESS_NONE, animate = true)
|
||||
private fun onHistoryChanged(history: HistoryInfo) {
|
||||
binding.progressView.setPercent(history.history?.percent ?: PROGRESS_NONE, animate = true)
|
||||
}
|
||||
|
||||
private fun onLoadingStateChanged(isLoading: Boolean) {
|
||||
@@ -229,6 +242,7 @@ class DetailsFragment :
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
R.id.textView_source -> {
|
||||
startActivity(
|
||||
MangaListActivity.newIntent(
|
||||
@@ -237,6 +251,7 @@ class DetailsFragment :
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
R.id.imageView_cover -> {
|
||||
startActivity(
|
||||
ImageActivity.newIntent(v.context, manga.largeCoverUrl.ifNullOrEmpty { manga.coverUrl }),
|
||||
@@ -249,7 +264,7 @@ class DetailsFragment :
|
||||
override fun onLongClick(v: View): Boolean {
|
||||
when (v.id) {
|
||||
R.id.button_read -> {
|
||||
if (viewModel.readingHistory.value == null) {
|
||||
if (viewModel.historyInfo.value?.history == null) {
|
||||
return false
|
||||
}
|
||||
val menu = PopupMenu(v.context, v)
|
||||
@@ -271,12 +286,14 @@ class DetailsFragment :
|
||||
)
|
||||
true
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
menu.show()
|
||||
return true
|
||||
}
|
||||
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,18 @@ import androidx.lifecycle.viewModelScope
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import java.io.IOException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChangedBy
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.transformLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.plus
|
||||
import org.koitharu.kotatsu.R
|
||||
@@ -46,6 +54,7 @@ import org.koitharu.kotatsu.utils.asFlowLiveData
|
||||
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
|
||||
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
||||
import org.koitharu.kotatsu.utils.ext.runCatchingCancellable
|
||||
import java.io.IOException
|
||||
|
||||
class DetailsViewModel @AssistedInject constructor(
|
||||
@Assisted intent: MangaIntent,
|
||||
@@ -91,17 +100,18 @@ class DetailsViewModel @AssistedInject constructor(
|
||||
val manga = delegate.manga.filterNotNull().asLiveData(viewModelScope.coroutineContext)
|
||||
val favouriteCategories = favourite.asLiveData(viewModelScope.coroutineContext)
|
||||
val newChaptersCount = newChapters.asLiveData(viewModelScope.coroutineContext)
|
||||
|
||||
@Deprecated("")
|
||||
val readingHistory = history.asLiveData(viewModelScope.coroutineContext)
|
||||
val isChaptersReversed = chaptersReversed.asLiveData(viewModelScope.coroutineContext)
|
||||
|
||||
val historyInfo = combine(
|
||||
val historyInfo: LiveData<HistoryInfo> = combine(
|
||||
delegate.manga,
|
||||
history,
|
||||
) { m, h ->
|
||||
HistoryInfo(m, h)
|
||||
}.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, null)
|
||||
settings.observeAsFlow(AppSettings.KEY_INCOGNITO_MODE) { isIncognitoModeEnabled },
|
||||
) { m, h, im ->
|
||||
HistoryInfo(m, h, im)
|
||||
}.asFlowLiveData(
|
||||
context = viewModelScope.coroutineContext + Dispatchers.Default,
|
||||
defaultValue = HistoryInfo(null, null, false),
|
||||
)
|
||||
|
||||
val bookmarks = delegate.manga.flatMapLatest {
|
||||
if (it != null) bookmarksRepository.observeBookmarks(it) else flowOf(emptyList())
|
||||
|
||||
@@ -42,7 +42,8 @@ class ChaptersAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSectionText(context: Context, position: Int): CharSequence {
|
||||
return items[position].chapter.number.toString()
|
||||
override fun getSectionText(context: Context, position: Int): CharSequence? {
|
||||
val item = items.getOrNull(position) ?: return null
|
||||
return item.chapter.number.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,12 @@ class HistoryInfo(
|
||||
val totalChapters: Int,
|
||||
val currentChapter: Int,
|
||||
val history: MangaHistory?,
|
||||
val isIncognitoMode: Boolean,
|
||||
) {
|
||||
|
||||
val isValid: Boolean
|
||||
get() = totalChapters >= 0
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
@@ -18,6 +22,7 @@ class HistoryInfo(
|
||||
if (totalChapters != other.totalChapters) return false
|
||||
if (currentChapter != other.currentChapter) return false
|
||||
if (history != other.history) return false
|
||||
if (isIncognitoMode != other.isIncognitoMode) return false
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -26,20 +31,21 @@ class HistoryInfo(
|
||||
var result = totalChapters
|
||||
result = 31 * result + currentChapter
|
||||
result = 31 * result + (history?.hashCode() ?: 0)
|
||||
result = 31 * result + isIncognitoMode.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
fun HistoryInfo(manga: Manga?, history: MangaHistory?): HistoryInfo? {
|
||||
val chapters = manga?.chapters ?: return null
|
||||
fun HistoryInfo(manga: Manga?, history: MangaHistory?, isIncognitoMode: Boolean): HistoryInfo {
|
||||
val chapters = manga?.chapters
|
||||
return HistoryInfo(
|
||||
totalChapters = chapters.size,
|
||||
currentChapter = if (history != null) {
|
||||
totalChapters = chapters?.size ?: -1,
|
||||
currentChapter = if (history != null && !chapters.isNullOrEmpty()) {
|
||||
chapters.indexOfFirst { it.id == history.chapterId }
|
||||
} else {
|
||||
-1
|
||||
},
|
||||
history = history,
|
||||
isIncognitoMode = isIncognitoMode,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@ package org.koitharu.kotatsu.explore.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.viewModels
|
||||
@@ -11,11 +13,12 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.ImageLoader
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.domain.reverseAsync
|
||||
import org.koitharu.kotatsu.base.ui.BaseFragment
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.base.ui.util.RecyclerViewOwner
|
||||
import org.koitharu.kotatsu.base.ui.util.ReversibleAction
|
||||
import org.koitharu.kotatsu.bookmarks.ui.BookmarksActivity
|
||||
import org.koitharu.kotatsu.databinding.FragmentExploreBinding
|
||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
@@ -31,6 +34,7 @@ import org.koitharu.kotatsu.search.ui.MangaListActivity
|
||||
import org.koitharu.kotatsu.settings.SettingsActivity
|
||||
import org.koitharu.kotatsu.suggestions.ui.SuggestionsActivity
|
||||
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ExploreFragment :
|
||||
@@ -67,6 +71,7 @@ class ExploreFragment :
|
||||
}
|
||||
viewModel.onError.observe(viewLifecycleOwner, ::onError)
|
||||
viewModel.onOpenManga.observe(viewLifecycleOwner, ::onOpenManga)
|
||||
viewModel.onActionDone.observe(viewLifecycleOwner, ::onActionDone)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
@@ -95,6 +100,7 @@ class ExploreFragment :
|
||||
viewModel.openRandom()
|
||||
return
|
||||
}
|
||||
|
||||
else -> return
|
||||
}
|
||||
startActivity(intent)
|
||||
@@ -105,6 +111,14 @@ class ExploreFragment :
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
override fun onItemLongClick(item: ExploreItem.Source, view: View): Boolean {
|
||||
val menu = PopupMenu(view.context, view)
|
||||
menu.inflate(R.menu.popup_source)
|
||||
menu.setOnMenuItemClickListener(SourceMenuListener(item))
|
||||
menu.show()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onRetryClick(error: Throwable) = Unit
|
||||
|
||||
override fun onEmptyActionClick() = onManageClick(requireView())
|
||||
@@ -124,6 +138,37 @@ class ExploreFragment :
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
private fun onActionDone(action: ReversibleAction) {
|
||||
val handle = action.handle
|
||||
val length = if (handle == null) Snackbar.LENGTH_SHORT else Snackbar.LENGTH_LONG
|
||||
val snackbar = Snackbar.make(binding.recyclerView, action.stringResId, length)
|
||||
if (handle != null) {
|
||||
snackbar.setAction(R.string.undo) { handle.reverseAsync() }
|
||||
}
|
||||
snackbar.anchorView = (activity as? BottomNavOwner)?.bottomNav
|
||||
snackbar.show()
|
||||
}
|
||||
|
||||
private inner class SourceMenuListener(
|
||||
private val sourceItem: ExploreItem.Source,
|
||||
) : PopupMenu.OnMenuItemClickListener {
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_settings -> {
|
||||
startActivity(SettingsActivity.newSourceSettingsIntent(requireContext(), sourceItem.source))
|
||||
}
|
||||
|
||||
R.id.action_hide -> {
|
||||
viewModel.hideSource(sourceItem.source)
|
||||
}
|
||||
|
||||
else -> return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun newInstance() = ExploreFragment()
|
||||
|
||||
@@ -4,11 +4,17 @@ import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.asFlow
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.domain.ReversibleHandle
|
||||
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.base.ui.util.ReversibleAction
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.explore.domain.ExploreRepository
|
||||
import org.koitharu.kotatsu.explore.ui.model.ExploreItem
|
||||
@@ -16,6 +22,7 @@ import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.utils.SingleLiveEvent
|
||||
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class ExploreViewModel @Inject constructor(
|
||||
@@ -24,6 +31,7 @@ class ExploreViewModel @Inject constructor(
|
||||
) : BaseViewModel() {
|
||||
|
||||
val onOpenManga = SingleLiveEvent<Manga>()
|
||||
val onActionDone = SingleLiveEvent<ReversibleAction>()
|
||||
|
||||
val content: LiveData<List<ExploreItem>> = isLoading.asFlow().flatMapLatest { loading ->
|
||||
if (loading) {
|
||||
@@ -40,6 +48,16 @@ class ExploreViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun hideSource(source: MangaSource) {
|
||||
launchJob(Dispatchers.Default) {
|
||||
settings.hiddenSources += source.name
|
||||
val rollback = ReversibleHandle {
|
||||
settings.hiddenSources -= source.name
|
||||
}
|
||||
onActionDone.postCall(ReversibleAction(R.string.source_disabled, rollback))
|
||||
}
|
||||
}
|
||||
|
||||
private fun createContentFlow() = settings.observe()
|
||||
.filter {
|
||||
it == AppSettings.KEY_SOURCES_HIDDEN ||
|
||||
|
||||
@@ -14,14 +14,14 @@ class HistoryListAdapter(
|
||||
listener: MangaListListener
|
||||
) : MangaListAdapter(coil, lifecycleOwner, listener), FastScroller.SectionIndexer {
|
||||
|
||||
override fun getSectionText(context: Context, position: Int): CharSequence {
|
||||
override fun getSectionText(context: Context, position: Int): CharSequence? {
|
||||
val list = items
|
||||
for (i in (0..position).reversed()) {
|
||||
val item = list[i]
|
||||
val item = list.getOrNull(i) ?: continue
|
||||
if (item is DateTimeAgo) {
|
||||
return item.format(context.resources)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.koitharu.kotatsu.utils.ext.copyToSuspending
|
||||
import org.koitharu.kotatsu.utils.ext.longHashCode
|
||||
import org.koitharu.kotatsu.utils.ext.subdir
|
||||
import org.koitharu.kotatsu.utils.ext.takeIfReadable
|
||||
import org.koitharu.kotatsu.utils.ext.takeIfWriteable
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import javax.inject.Inject
|
||||
@@ -18,9 +19,14 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class PagesCache @Inject constructor(@ApplicationContext context: Context) {
|
||||
|
||||
private val cacheDir = context.externalCacheDir ?: context.cacheDir
|
||||
private val cacheDir = checkNotNull(findSuitableDir(context)) {
|
||||
val dirs = (context.externalCacheDirs + context.cacheDir).joinToString(";") {
|
||||
it.absolutePath
|
||||
}
|
||||
"Cannot find any suitable directory for PagesCache: [$dirs]"
|
||||
}
|
||||
private val lruCache = createDiskLruCacheSafe(
|
||||
dir = cacheDir.subdir(CacheDir.PAGES.dir),
|
||||
dir = cacheDir,
|
||||
size = FileSize.MEGABYTES.convert(200, FileSize.BYTES),
|
||||
)
|
||||
|
||||
@@ -29,7 +35,7 @@ class PagesCache @Inject constructor(@ApplicationContext context: Context) {
|
||||
}
|
||||
|
||||
suspend fun put(url: String, inputStream: InputStream): File = withContext(Dispatchers.IO) {
|
||||
val file = File(cacheDir, url.longHashCode().toString())
|
||||
val file = File(cacheDir.parentFile, url.longHashCode().toString())
|
||||
try {
|
||||
file.outputStream().use { out ->
|
||||
inputStream.copyToSuspending(out)
|
||||
@@ -50,3 +56,10 @@ private fun createDiskLruCacheSafe(dir: File, size: Long): DiskLruCache {
|
||||
DiskLruCache.create(dir, size)
|
||||
}
|
||||
}
|
||||
|
||||
private fun findSuitableDir(context: Context): File? {
|
||||
val dirs = context.externalCacheDirs + context.cacheDir
|
||||
return dirs.firstNotNullOfOrNull {
|
||||
it.subdir(CacheDir.PAGES.dir).takeIfWriteable()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.model.RATING_UNKNOWN
|
||||
import org.koitharu.kotatsu.utils.AlphanumComparator
|
||||
import org.koitharu.kotatsu.utils.ext.copyToSuspending
|
||||
import org.koitharu.kotatsu.utils.ext.deleteAwait
|
||||
import org.koitharu.kotatsu.utils.ext.longOf
|
||||
@@ -58,7 +59,7 @@ class DirMangaImporter(
|
||||
|
||||
private suspend fun addPages(output: CbzMangaOutput, root: DocumentFile, path: String, state: State) {
|
||||
var number = 0
|
||||
for (file in root.listFiles()) {
|
||||
for (file in root.listFiles().sortedWith(compareBy(AlphanumComparator()) { it.name.orEmpty() })) {
|
||||
when {
|
||||
file.isDirectory -> {
|
||||
addPages(output, file, path + "/" + file.name, state)
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException
|
||||
import org.koitharu.kotatsu.core.github.AppUpdateRepository
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsFlow
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsLiveData
|
||||
import org.koitharu.kotatsu.history.domain.HistoryRepository
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
@@ -34,9 +35,12 @@ class MainViewModel @Inject constructor(
|
||||
|
||||
val onOpenReader = SingleLiveEvent<Manga>()
|
||||
|
||||
val isResumeEnabled = historyRepository
|
||||
.observeHasItems()
|
||||
.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, false)
|
||||
val isResumeEnabled = combine(
|
||||
historyRepository.observeHasItems(),
|
||||
settings.observeAsFlow(AppSettings.KEY_INCOGNITO_MODE) { isIncognitoModeEnabled },
|
||||
) { hasItems, incognito ->
|
||||
hasItems && !incognito
|
||||
}.asFlowLiveData(viewModelScope.coroutineContext + Dispatchers.Default, false)
|
||||
|
||||
val isFeedAvailable = settings.observeAsLiveData(
|
||||
context = viewModelScope.coroutineContext + Dispatchers.Default,
|
||||
|
||||
@@ -313,7 +313,7 @@ class ReaderViewModel @AssistedInject constructor(
|
||||
} ?: ReaderState(manga, preselectedBranch)
|
||||
}
|
||||
|
||||
val branch = chapters[currentState.value?.chapterId ?: 0L].branch
|
||||
val branch = chapters[currentState.value?.chapterId ?: 0L]?.branch
|
||||
mangaData.value = manga.filterChapters(branch)
|
||||
readerMode.postValue(mode)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import androidx.annotation.CallSuper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||
import org.koitharu.kotatsu.core.os.NetworkStateObserver
|
||||
import org.koitharu.kotatsu.core.os.NetworkState
|
||||
import org.koitharu.kotatsu.databinding.LayoutPageInfoBinding
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
|
||||
@@ -14,12 +14,12 @@ abstract class BasePageHolder<B : ViewBinding>(
|
||||
protected val binding: B,
|
||||
loader: PageLoader,
|
||||
settings: ReaderSettings,
|
||||
networkStateObserver: NetworkStateObserver,
|
||||
networkState: NetworkState,
|
||||
exceptionResolver: ExceptionResolver,
|
||||
) : RecyclerView.ViewHolder(binding.root), PageHolderDelegate.Callback {
|
||||
|
||||
@Suppress("LeakingThis")
|
||||
protected val delegate = PageHolderDelegate(loader, settings, this, networkStateObserver, exceptionResolver)
|
||||
protected val delegate = PageHolderDelegate(loader, settings, this, networkState, exceptionResolver)
|
||||
protected val bindingInfo = LayoutPageInfoBinding.bind(binding.root)
|
||||
|
||||
val context: Context
|
||||
|
||||
@@ -5,7 +5,7 @@ import androidx.recyclerview.widget.AsyncListDiffer
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||
import org.koitharu.kotatsu.core.os.NetworkStateObserver
|
||||
import org.koitharu.kotatsu.core.os.NetworkState
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
|
||||
import org.koitharu.kotatsu.utils.ext.resetTransformations
|
||||
@@ -16,7 +16,7 @@ import kotlin.coroutines.suspendCoroutine
|
||||
abstract class BaseReaderAdapter<H : BasePageHolder<*>>(
|
||||
private val loader: PageLoader,
|
||||
private val readerSettings: ReaderSettings,
|
||||
private val networkState: NetworkStateObserver,
|
||||
private val networkState: NetworkState,
|
||||
private val exceptionResolver: ExceptionResolver,
|
||||
) : RecyclerView.Adapter<H>() {
|
||||
|
||||
@@ -70,7 +70,7 @@ abstract class BaseReaderAdapter<H : BasePageHolder<*>>(
|
||||
parent: ViewGroup,
|
||||
loader: PageLoader,
|
||||
settings: ReaderSettings,
|
||||
networkState: NetworkStateObserver,
|
||||
networkState: NetworkState,
|
||||
exceptionResolver: ExceptionResolver,
|
||||
): H
|
||||
|
||||
|
||||
@@ -17,10 +17,11 @@ import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.plus
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||
import org.koitharu.kotatsu.core.os.NetworkStateObserver
|
||||
import org.koitharu.kotatsu.core.os.NetworkState
|
||||
import org.koitharu.kotatsu.parsers.model.MangaPage
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
|
||||
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
@@ -28,7 +29,7 @@ class PageHolderDelegate(
|
||||
private val loader: PageLoader,
|
||||
private val readerSettings: ReaderSettings,
|
||||
private val callback: Callback,
|
||||
private val networkState: NetworkStateObserver,
|
||||
private val networkState: NetworkState,
|
||||
private val exceptionResolver: ExceptionResolver,
|
||||
) : DefaultOnImageEventListener, Observer<ReaderSettings> {
|
||||
|
||||
@@ -138,6 +139,7 @@ class PageHolderDelegate(
|
||||
} catch (e: CancellationException) {
|
||||
throw e
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTraceDebug()
|
||||
state = State.ERROR
|
||||
error = e
|
||||
callback.onError(e)
|
||||
|
||||
@@ -3,22 +3,24 @@ package org.koitharu.kotatsu.reader.ui.pager.reversed
|
||||
import android.graphics.PointF
|
||||
import android.view.Gravity
|
||||
import android.widget.FrameLayout
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||
import org.koitharu.kotatsu.core.model.ZoomMode
|
||||
import org.koitharu.kotatsu.core.os.NetworkStateObserver
|
||||
import org.koitharu.kotatsu.core.os.NetworkState
|
||||
import org.koitharu.kotatsu.databinding.ItemPageBinding
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
|
||||
import org.koitharu.kotatsu.reader.ui.pager.standard.PageHolder
|
||||
|
||||
class ReversedPageHolder(
|
||||
owner: LifecycleOwner,
|
||||
binding: ItemPageBinding,
|
||||
loader: PageLoader,
|
||||
settings: ReaderSettings,
|
||||
networkState: NetworkStateObserver,
|
||||
networkState: NetworkState,
|
||||
exceptionResolver: ExceptionResolver,
|
||||
) : PageHolder(binding, loader, settings, networkState, exceptionResolver) {
|
||||
) : PageHolder(owner, binding, loader, settings, networkState, exceptionResolver) {
|
||||
|
||||
init {
|
||||
(binding.textViewNumber.layoutParams as FrameLayout.LayoutParams)
|
||||
|
||||
@@ -2,17 +2,19 @@ package org.koitharu.kotatsu.reader.ui.pager.reversed
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||
import org.koitharu.kotatsu.core.os.NetworkStateObserver
|
||||
import org.koitharu.kotatsu.core.os.NetworkState
|
||||
import org.koitharu.kotatsu.databinding.ItemPageBinding
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
|
||||
|
||||
class ReversedPagesAdapter(
|
||||
private val lifecycleOwner: LifecycleOwner,
|
||||
loader: PageLoader,
|
||||
settings: ReaderSettings,
|
||||
networkState: NetworkStateObserver,
|
||||
networkState: NetworkState,
|
||||
exceptionResolver: ExceptionResolver,
|
||||
) : BaseReaderAdapter<ReversedPageHolder>(loader, settings, networkState, exceptionResolver) {
|
||||
|
||||
@@ -20,9 +22,10 @@ class ReversedPagesAdapter(
|
||||
parent: ViewGroup,
|
||||
loader: PageLoader,
|
||||
settings: ReaderSettings,
|
||||
networkState: NetworkStateObserver,
|
||||
networkState: NetworkState,
|
||||
exceptionResolver: ExceptionResolver,
|
||||
) = ReversedPageHolder(
|
||||
owner = lifecycleOwner,
|
||||
binding = ItemPageBinding.inflate(LayoutInflater.from(parent.context), parent, false),
|
||||
loader = loader,
|
||||
settings = settings,
|
||||
|
||||
@@ -8,7 +8,7 @@ import android.view.ViewGroup
|
||||
import androidx.core.view.children
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.async
|
||||
import org.koitharu.kotatsu.core.os.NetworkStateObserver
|
||||
import org.koitharu.kotatsu.core.os.NetworkState
|
||||
import org.koitharu.kotatsu.databinding.FragmentReaderStandardBinding
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderState
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReader
|
||||
@@ -26,7 +26,7 @@ import kotlin.math.absoluteValue
|
||||
class ReversedReaderFragment : BaseReader<FragmentReaderStandardBinding>() {
|
||||
|
||||
@Inject
|
||||
lateinit var networkStateObserver: NetworkStateObserver
|
||||
lateinit var networkState: NetworkState
|
||||
|
||||
private var pagerAdapter: ReversedPagesAdapter? = null
|
||||
|
||||
@@ -39,10 +39,11 @@ class ReversedReaderFragment : BaseReader<FragmentReaderStandardBinding>() {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
pagerAdapter = ReversedPagesAdapter(
|
||||
viewModel.pageLoader,
|
||||
viewModel.readerSettings,
|
||||
networkStateObserver,
|
||||
exceptionResolver,
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
loader = viewModel.pageLoader,
|
||||
settings = viewModel.readerSettings,
|
||||
networkState = networkState,
|
||||
exceptionResolver = exceptionResolver,
|
||||
)
|
||||
with(binding.pager) {
|
||||
adapter = pagerAdapter
|
||||
|
||||
@@ -5,12 +5,13 @@ import android.graphics.PointF
|
||||
import android.net.Uri
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.davemorrissey.labs.subscaleview.ImageSource
|
||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||
import org.koitharu.kotatsu.core.model.ZoomMode
|
||||
import org.koitharu.kotatsu.core.os.NetworkStateObserver
|
||||
import org.koitharu.kotatsu.core.os.NetworkState
|
||||
import org.koitharu.kotatsu.databinding.ItemPageBinding
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
|
||||
@@ -19,15 +20,17 @@ import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
import org.koitharu.kotatsu.utils.ext.*
|
||||
|
||||
open class PageHolder(
|
||||
owner: LifecycleOwner,
|
||||
binding: ItemPageBinding,
|
||||
loader: PageLoader,
|
||||
settings: ReaderSettings,
|
||||
networkState: NetworkStateObserver,
|
||||
networkState: NetworkState,
|
||||
exceptionResolver: ExceptionResolver,
|
||||
) : BasePageHolder<ItemPageBinding>(binding, loader, settings, networkState, exceptionResolver),
|
||||
View.OnClickListener {
|
||||
|
||||
init {
|
||||
binding.ssiv.bindToLifecycle(owner)
|
||||
binding.ssiv.isEagerLoadingEnabled = !isLowRamDevice(context)
|
||||
binding.ssiv.addOnImageEventListener(delegate)
|
||||
@Suppress("LeakingThis")
|
||||
|
||||
@@ -8,7 +8,7 @@ import android.view.ViewGroup
|
||||
import androidx.core.view.children
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.async
|
||||
import org.koitharu.kotatsu.core.os.NetworkStateObserver
|
||||
import org.koitharu.kotatsu.core.os.NetworkState
|
||||
import org.koitharu.kotatsu.databinding.FragmentReaderStandardBinding
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderState
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReader
|
||||
@@ -25,7 +25,7 @@ import kotlin.math.absoluteValue
|
||||
class PagerReaderFragment : BaseReader<FragmentReaderStandardBinding>() {
|
||||
|
||||
@Inject
|
||||
lateinit var networkStateObserver: NetworkStateObserver
|
||||
lateinit var networkState: NetworkState
|
||||
|
||||
private var pagesAdapter: PagesAdapter? = null
|
||||
|
||||
@@ -38,10 +38,11 @@ class PagerReaderFragment : BaseReader<FragmentReaderStandardBinding>() {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
pagesAdapter = PagesAdapter(
|
||||
viewModel.pageLoader,
|
||||
viewModel.readerSettings,
|
||||
networkStateObserver,
|
||||
exceptionResolver,
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
loader = viewModel.pageLoader,
|
||||
settings = viewModel.readerSettings,
|
||||
networkState = networkState,
|
||||
exceptionResolver = exceptionResolver,
|
||||
)
|
||||
with(binding.pager) {
|
||||
adapter = pagesAdapter
|
||||
|
||||
@@ -2,27 +2,30 @@ package org.koitharu.kotatsu.reader.ui.pager.standard
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||
import org.koitharu.kotatsu.core.os.NetworkStateObserver
|
||||
import org.koitharu.kotatsu.core.os.NetworkState
|
||||
import org.koitharu.kotatsu.databinding.ItemPageBinding
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
|
||||
|
||||
class PagesAdapter(
|
||||
private val lifecycleOwner: LifecycleOwner,
|
||||
loader: PageLoader,
|
||||
settings: ReaderSettings,
|
||||
networkStateObserver: NetworkStateObserver,
|
||||
networkState: NetworkState,
|
||||
exceptionResolver: ExceptionResolver,
|
||||
) : BaseReaderAdapter<PageHolder>(loader, settings, networkStateObserver, exceptionResolver) {
|
||||
) : BaseReaderAdapter<PageHolder>(loader, settings, networkState, exceptionResolver) {
|
||||
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
loader: PageLoader,
|
||||
settings: ReaderSettings,
|
||||
networkState: NetworkStateObserver,
|
||||
networkState: NetworkState,
|
||||
exceptionResolver: ExceptionResolver,
|
||||
) = PageHolder(
|
||||
owner = lifecycleOwner,
|
||||
binding = ItemPageBinding.inflate(LayoutInflater.from(parent.context), parent, false),
|
||||
loader = loader,
|
||||
settings = settings,
|
||||
|
||||
@@ -2,17 +2,19 @@ package org.koitharu.kotatsu.reader.ui.pager.webtoon
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||
import org.koitharu.kotatsu.core.os.NetworkStateObserver
|
||||
import org.koitharu.kotatsu.core.os.NetworkState
|
||||
import org.koitharu.kotatsu.databinding.ItemPageWebtoonBinding
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
|
||||
|
||||
class WebtoonAdapter(
|
||||
private val lifecycleOwner: LifecycleOwner,
|
||||
loader: PageLoader,
|
||||
settings: ReaderSettings,
|
||||
networkState: NetworkStateObserver,
|
||||
networkState: NetworkState,
|
||||
exceptionResolver: ExceptionResolver,
|
||||
) : BaseReaderAdapter<WebtoonHolder>(loader, settings, networkState, exceptionResolver) {
|
||||
|
||||
@@ -20,9 +22,10 @@ class WebtoonAdapter(
|
||||
parent: ViewGroup,
|
||||
loader: PageLoader,
|
||||
settings: ReaderSettings,
|
||||
networkState: NetworkStateObserver,
|
||||
networkState: NetworkState,
|
||||
exceptionResolver: ExceptionResolver,
|
||||
) = WebtoonHolder(
|
||||
owner = lifecycleOwner,
|
||||
binding = ItemPageWebtoonBinding.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
parent,
|
||||
|
||||
@@ -3,12 +3,13 @@ package org.koitharu.kotatsu.reader.ui.pager.webtoon
|
||||
import android.net.Uri
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.davemorrissey.labs.subscaleview.ImageSource
|
||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||
import com.davemorrissey.labs.subscaleview.decoder.SkiaPooledImageRegionDecoder
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver
|
||||
import org.koitharu.kotatsu.core.os.NetworkStateObserver
|
||||
import org.koitharu.kotatsu.core.os.NetworkState
|
||||
import org.koitharu.kotatsu.databinding.ItemPageWebtoonBinding
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.reader.ui.config.ReaderSettings
|
||||
@@ -22,10 +23,11 @@ import org.koitharu.kotatsu.utils.ext.setProgressCompat
|
||||
import org.koitharu.kotatsu.utils.ext.showCompat
|
||||
|
||||
class WebtoonHolder(
|
||||
owner: LifecycleOwner,
|
||||
binding: ItemPageWebtoonBinding,
|
||||
loader: PageLoader,
|
||||
settings: ReaderSettings,
|
||||
networkState: NetworkStateObserver,
|
||||
networkState: NetworkState,
|
||||
exceptionResolver: ExceptionResolver,
|
||||
) : BasePageHolder<ItemPageWebtoonBinding>(binding, loader, settings, networkState, exceptionResolver),
|
||||
View.OnClickListener {
|
||||
@@ -34,6 +36,7 @@ class WebtoonHolder(
|
||||
private val goneOnInvisibleListener = GoneOnInvisibleListener(bindingInfo.progressBar)
|
||||
|
||||
init {
|
||||
binding.ssiv.bindToLifecycle(owner)
|
||||
binding.ssiv.regionDecoderFactory = SkiaPooledImageRegionDecoder.Factory()
|
||||
binding.ssiv.addOnImageEventListener(delegate)
|
||||
bindingInfo.buttonRetry.setOnClickListener(this)
|
||||
|
||||
@@ -7,7 +7,7 @@ import android.view.ViewGroup
|
||||
import android.view.animation.AccelerateDecelerateInterpolator
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.async
|
||||
import org.koitharu.kotatsu.core.os.NetworkStateObserver
|
||||
import org.koitharu.kotatsu.core.os.NetworkState
|
||||
import org.koitharu.kotatsu.databinding.FragmentReaderWebtoonBinding
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderState
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReader
|
||||
@@ -22,7 +22,7 @@ import javax.inject.Inject
|
||||
class WebtoonReaderFragment : BaseReader<FragmentReaderWebtoonBinding>() {
|
||||
|
||||
@Inject
|
||||
lateinit var networkStateObserver: NetworkStateObserver
|
||||
lateinit var networkState: NetworkState
|
||||
|
||||
private val scrollInterpolator = AccelerateDecelerateInterpolator()
|
||||
private var webtoonAdapter: WebtoonAdapter? = null
|
||||
@@ -35,10 +35,11 @@ class WebtoonReaderFragment : BaseReader<FragmentReaderWebtoonBinding>() {
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
webtoonAdapter = WebtoonAdapter(
|
||||
viewModel.pageLoader,
|
||||
viewModel.readerSettings,
|
||||
networkStateObserver,
|
||||
exceptionResolver,
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
loader = viewModel.pageLoader,
|
||||
settings = viewModel.readerSettings,
|
||||
networkState = networkState,
|
||||
exceptionResolver = exceptionResolver,
|
||||
)
|
||||
with(binding.recyclerView) {
|
||||
setHasFixedSize(true)
|
||||
|
||||
@@ -12,7 +12,7 @@ import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.BasePreferenceFragment
|
||||
import org.koitharu.kotatsu.core.network.AndroidCookieJar
|
||||
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
|
||||
import org.koitharu.kotatsu.core.os.ShortcutsUpdater
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.local.data.CacheDir
|
||||
@@ -45,7 +45,7 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach
|
||||
lateinit var malRepository: MALRepository
|
||||
|
||||
@Inject
|
||||
lateinit var cookieJar: AndroidCookieJar
|
||||
lateinit var cookieJar: MutableCookieJar
|
||||
|
||||
@Inject
|
||||
lateinit var shortcutsUpdater: ShortcutsUpdater
|
||||
|
||||
@@ -86,7 +86,6 @@ class SourceSettingsFragment : BasePreferenceFragment(0) {
|
||||
}.onSuccess { username ->
|
||||
preference.title = getString(R.string.logged_in_as, username)
|
||||
}.onFailure { error ->
|
||||
preference.isEnabled = error is AuthRequiredException
|
||||
when {
|
||||
error is AuthRequiredException -> Unit
|
||||
ExceptionResolver.canResolve(error) -> {
|
||||
|
||||
@@ -12,7 +12,7 @@ import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.base.ui.util.ReversibleAction
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.core.os.NetworkStateObserver
|
||||
import org.koitharu.kotatsu.core.os.NetworkState
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.ListMode
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsFlow
|
||||
@@ -46,14 +46,14 @@ class ShelfViewModel @Inject constructor(
|
||||
private val favouritesRepository: FavouritesRepository,
|
||||
private val trackingRepository: TrackingRepository,
|
||||
private val settings: AppSettings,
|
||||
networkStateObserver: NetworkStateObserver,
|
||||
networkState: NetworkState,
|
||||
) : BaseViewModel(), ListExtraProvider {
|
||||
|
||||
val onActionDone = SingleLiveEvent<ReversibleAction>()
|
||||
|
||||
val content: LiveData<List<ListModel>> = combine(
|
||||
settings.observeAsFlow(AppSettings.KEY_SHELF_SECTIONS) { shelfSections },
|
||||
networkStateObserver,
|
||||
networkState,
|
||||
repository.observeShelfContent(),
|
||||
) { sections, isConnected, content ->
|
||||
mapList(content, sections, isConnected)
|
||||
|
||||
@@ -46,9 +46,9 @@ class ShelfAdapter(
|
||||
.addDelegate(errorStateListAD(listener))
|
||||
}
|
||||
|
||||
override fun getSectionText(context: Context, position: Int): CharSequence {
|
||||
val item = items.getOrNull(position) as? ShelfSectionModel
|
||||
return item?.getTitle(context.resources) ?: ""
|
||||
override fun getSectionText(context: Context, position: Int): CharSequence? {
|
||||
val item = items.getOrNull(position) as? ShelfSectionModel ?: return null
|
||||
return item.getTitle(context.resources)
|
||||
}
|
||||
|
||||
private class DiffCallback : DiffUtil.ItemCallback<ListModel>() {
|
||||
|
||||
@@ -5,16 +5,17 @@ import android.accounts.AccountManager
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.util.ArrayMap
|
||||
import androidx.collection.ArrayMap
|
||||
import androidx.room.InvalidationTracker
|
||||
import androidx.room.withTransaction
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koitharu.kotatsu.BuildConfig
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
@@ -22,6 +23,9 @@ import org.koitharu.kotatsu.core.db.TABLE_FAVOURITES
|
||||
import org.koitharu.kotatsu.core.db.TABLE_FAVOURITE_CATEGORIES
|
||||
import org.koitharu.kotatsu.core.db.TABLE_HISTORY
|
||||
import org.koitharu.kotatsu.utils.ext.processLifecycleScope
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class SyncController @Inject constructor(
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package org.koitharu.kotatsu.utils
|
||||
|
||||
import android.util.ArrayMap
|
||||
import java.util.*
|
||||
import kotlin.coroutines.coroutineContext
|
||||
import kotlin.coroutines.resume
|
||||
import androidx.collection.ArrayMap
|
||||
import kotlinx.coroutines.CancellableContinuation
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import java.util.LinkedList
|
||||
import kotlin.coroutines.coroutineContext
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
class CompositeMutex<T : Any> : Set<T> {
|
||||
|
||||
@@ -27,7 +27,7 @@ class CompositeMutex<T : Any> : Set<T> {
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return data.isEmpty()
|
||||
return data.isEmpty
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<T> {
|
||||
@@ -59,7 +59,7 @@ class CompositeMutex<T : Any> : Set<T> {
|
||||
|
||||
private suspend fun waitForRemoval(element: T) {
|
||||
val list = data[element] ?: return
|
||||
suspendCancellableCoroutine<Unit> { continuation ->
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
list.add(continuation)
|
||||
continuation.invokeOnCancellation {
|
||||
list.remove(continuation)
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.koitharu.kotatsu.utils
|
||||
|
||||
import kotlinx.coroutines.flow.FlowCollector
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
abstract class MediatorStateFlow<T>(initialValue: T) : StateFlow<T> {
|
||||
|
||||
private val delegate = MutableStateFlow(initialValue)
|
||||
private val collectors = AtomicInteger(0)
|
||||
|
||||
final override val replayCache: List<T>
|
||||
get() = delegate.replayCache
|
||||
|
||||
final override val value: T
|
||||
get() = delegate.value
|
||||
|
||||
final override suspend fun collect(collector: FlowCollector<T>): Nothing {
|
||||
try {
|
||||
if (collectors.getAndIncrement() == 0) {
|
||||
onActive()
|
||||
}
|
||||
delegate.collect(collector)
|
||||
} finally {
|
||||
if (collectors.decrementAndGet() == 0) {
|
||||
onInactive()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun publishValue(v: T) {
|
||||
delegate.value = v
|
||||
}
|
||||
|
||||
abstract fun onActive()
|
||||
|
||||
abstract fun onInactive()
|
||||
}
|
||||
@@ -5,12 +5,16 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.speech.RecognizerIntent
|
||||
import androidx.activity.result.contract.ActivityResultContract
|
||||
import androidx.core.os.ConfigurationCompat
|
||||
import java.util.Locale
|
||||
|
||||
class VoiceInputContract : ActivityResultContract<String?, String?>() {
|
||||
|
||||
override fun createIntent(context: Context, input: String?): Intent {
|
||||
val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
|
||||
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
|
||||
val locale = ConfigurationCompat.getLocales(context.resources.configuration).get(0) ?: Locale.getDefault()
|
||||
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, locale.toLanguageTag())
|
||||
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, input)
|
||||
return intent
|
||||
}
|
||||
@@ -23,4 +27,4 @@ class VoiceInputContract : ActivityResultContract<String?, String?>() {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ fun File.subdir(name: String) = File(this, name).also {
|
||||
|
||||
fun File.takeIfReadable() = takeIf { it.exists() && it.canRead() }
|
||||
|
||||
fun File.takeIfWriteable() = takeIf { it.exists() && it.canWrite() }
|
||||
|
||||
fun ZipFile.readText(entry: ZipEntry) = getInputStream(entry).bufferedReader().use {
|
||||
it.readText()
|
||||
}
|
||||
@@ -74,4 +76,4 @@ private fun computeSizeInternal(file: File): Long {
|
||||
} else {
|
||||
return file.length()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
13
app/src/main/res/menu/popup_source.xml
Normal file
13
app/src/main/res/menu/popup_source.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
android:title="@string/settings" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_hide"
|
||||
android:title="@string/hide" />
|
||||
|
||||
</menu>
|
||||
@@ -70,7 +70,7 @@
|
||||
<string name="list">Liste</string>
|
||||
<string name="chapters">Kapitel</string>
|
||||
<string name="details">Einzelheiten</string>
|
||||
<string name="network_error">Keine Verbindung zum Internet möglich</string>
|
||||
<string name="network_error">Netzwerkfehler</string>
|
||||
<string name="error_occurred">Es ist ein Fehler aufgetreten</string>
|
||||
<string name="history">Verlauf</string>
|
||||
<string name="favourites">Favoriten</string>
|
||||
@@ -372,4 +372,23 @@
|
||||
<string name="import_completed">Import abgeschlossen</string>
|
||||
<string name="import_completed_hint">Du kannst die Originaldatei aus dem Speicher löschen, um Platz zu sparen</string>
|
||||
<string name="import_will_start_soon">Import wird bald beginnen</string>
|
||||
<string name="server_error">Serverseitiger Fehler (%1$d). Bitte versuchen Sie es später noch einmal</string>
|
||||
<string name="compact">Kompakt</string>
|
||||
<string name="contrast">Kontrast</string>
|
||||
<string name="network_unavailable_hint">Schalten Sie Wi-Fi oder ein mobiles Netzwerk ein, um Manga online zu lesen</string>
|
||||
<string name="clear_new_chapters_counters">Auch klare Informationen über neue Kapitel</string>
|
||||
<string name="text_unsaved_changes_prompt">Ungespeicherte Änderungen speichern oder verwerfen\?</string>
|
||||
<string name="discard">Verwerfen</string>
|
||||
<string name="reset">Zurücksetzen</string>
|
||||
<string name="brightness">Helligkeit</string>
|
||||
<string name="color_correction_hint">Die gewählten Farbeinstellungen werden für diesen Manga in Erinnerung bleiben</string>
|
||||
<string name="color_correction">Farbkorrektur</string>
|
||||
<string name="error_no_space_left">Kein Platz mehr auf dem Gerät</string>
|
||||
<string name="different_languages">Verschiedene Sprachen</string>
|
||||
<string name="network_unavailable">Netzwerk ist nicht verfügbar</string>
|
||||
<string name="webtoon_zoom_summary">Vergrößerungs-/Verkleinerungsgesten im Webtoon-Modus zulassen (beta)</string>
|
||||
<string name="reader_control_ltr">Ergonomische Leserkontrolle</string>
|
||||
<string name="reader_control_ltr_summary">Tippe auf den rechten Rand oder drücke die rechte Taste, um immer zur nächsten Seite zu wechseln</string>
|
||||
<string name="reader_slider">Seitenwechsel-Schieberegler anzeigen</string>
|
||||
<string name="source_disabled">Quelle deaktiviert</string>
|
||||
</resources>
|
||||
35
app/src/main/res/values-el/plurals.xml
Normal file
35
app/src/main/res/values-el/plurals.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<plurals name="days_ago">
|
||||
<item quantity="one">%1$d μέρα πριν</item>
|
||||
<item quantity="other">%1$d μέρες πριν</item>
|
||||
</plurals>
|
||||
<plurals name="items">
|
||||
<item quantity="one">%1$dστοιχείο</item>
|
||||
<item quantity="other">%1$dστοιχεία</item>
|
||||
</plurals>
|
||||
<plurals name="new_chapters">
|
||||
<item quantity="one">%1$dνέο κεφάλαιο</item>
|
||||
<item quantity="other">%1$dνέα κεφάλαια</item>
|
||||
</plurals>
|
||||
<plurals name="chapters_from_x">
|
||||
<item quantity="one">%1$dκεφάλαιο%2$d</item>
|
||||
<item quantity="other">%1$dκεφάλαια%2$d</item>
|
||||
</plurals>
|
||||
<plurals name="hours_ago">
|
||||
<item quantity="one">%1$dώρα πριν</item>
|
||||
<item quantity="other">%1$dώρες πριν</item>
|
||||
</plurals>
|
||||
<plurals name="chapters">
|
||||
<item quantity="one">%1$dκεφάλαιο</item>
|
||||
<item quantity="other">%1$dκεφάλαια</item>
|
||||
</plurals>
|
||||
<plurals name="pages">
|
||||
<item quantity="one">Σύνολο%1$dσελίδα</item>
|
||||
<item quantity="other">Σύνολο%1$dσελίδες</item>
|
||||
</plurals>
|
||||
<plurals name="minutes_ago">
|
||||
<item quantity="one">%1$dλεπτό πριν</item>
|
||||
<item quantity="other">%1$d λεπτά πριν</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
81
app/src/main/res/values-el/strings.xml
Normal file
81
app/src/main/res/values-el/strings.xml
Normal file
@@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="local_storage">Εσωτερικός χώρος</string>
|
||||
<string name="close_menu">Κλείσιμο μενού</string>
|
||||
<string name="open_menu">Άνοιγμα μενού</string>
|
||||
<string name="favourites">Αγαπημένα</string>
|
||||
<string name="history">Ιστορικό</string>
|
||||
<string name="error_occurred">Προέκυψε σφάλμα</string>
|
||||
<string name="try_again">Επανάληψη</string>
|
||||
<string name="grid">Πλέγμα</string>
|
||||
<string name="list_mode">Εμφάνιση ως λίστα</string>
|
||||
<string name="settings">Ρυθμίσεις</string>
|
||||
<string name="remote_sources">Απομακρυσμένες πηγές</string>
|
||||
<string name="computing_">Επεξεργασία…</string>
|
||||
<string name="close">Κλείσιμο</string>
|
||||
<string name="clear_history">Εκκαθάριση ιστορικού</string>
|
||||
<string name="nothing_found">Δεν βρέθηκε τίποτα</string>
|
||||
<string name="history_is_empty">Κενό ιστορικό</string>
|
||||
<string name="read">Διάβασε</string>
|
||||
<string name="add_to_favourites">Προσθήκη στα αγαπημένα</string>
|
||||
<string name="add_new_category">Νέα κατηγορία</string>
|
||||
<string name="save">Αποθήκευση</string>
|
||||
<string name="share">Κοινοποιήση</string>
|
||||
<string name="create_shortcut">Δημιουργία συντόμευσης…</string>
|
||||
<string name="share_s">Κοινοποίηση %s</string>
|
||||
<string name="search">Αναζήτηση</string>
|
||||
<string name="search_manga">Αναζήτηση μάνγκα</string>
|
||||
<string name="manga_downloading_">Λήψη…</string>
|
||||
<string name="download_complete">Κατεβασμένο</string>
|
||||
<string name="downloads">Λήψεις</string>
|
||||
<string name="updated">Ενημερωμένο</string>
|
||||
<string name="newest">Νεότερο</string>
|
||||
<string name="by_rating">Βαθμολογία</string>
|
||||
<string name="filter">Φίλτρο</string>
|
||||
<string name="dark">Σκοτεινό</string>
|
||||
<string name="automatic">Όπως στο σύστημα</string>
|
||||
<string name="clear">Εκκαθάριση</string>
|
||||
<string name="text_clear_history_prompt">Να διαγράψετε μόνιμα όλο το ιστορικό ανάγνωσης;</string>
|
||||
<string name="remove">Διαγραφή</string>
|
||||
<string name="save_page">Αποθήκευση σελίδας</string>
|
||||
<string name="page_saved">Αποθηκευμένα</string>
|
||||
<string name="share_image">Κοινή χρήση εικόνας</string>
|
||||
<string name="_import">Εισαγωγή</string>
|
||||
<string name="delete">Διαγραφή</string>
|
||||
<string name="text_file_not_supported">Επιλέξτε ένα αρχείο ZIP ή CBZ.</string>
|
||||
<string name="no_description">Χωρίς περιγραφή</string>
|
||||
<string name="history_and_cache">Ιστορικό και μνήμη cache</string>
|
||||
<string name="clear_pages_cache">Εκκαθάριση μνήμης cache της σελίδας</string>
|
||||
<string name="cache">Προσωρινή Μνήμη</string>
|
||||
<string name="text_file_sizes">B|kB|MB|GB|TB</string>
|
||||
<string name="standard">Τυπικό</string>
|
||||
<string name="webtoon">Μάνχγουα</string>
|
||||
<string name="search_on_s">Αναζήτηση στο %s</string>
|
||||
<string name="delete_manga">Διαγραφή μάνγκα</string>
|
||||
<string name="text_delete_local_manga">Μόνιμη διαγραφή του \"%s\" από τη συσκευή;</string>
|
||||
<string name="reader_settings">Ρυθμίσεις λειτουργίας ανάγνωσης</string>
|
||||
<string name="switch_pages">Αλλαγή σελίδων</string>
|
||||
<string name="network_error">Αδυναμία σύνδεσης στο ίντερνετ</string>
|
||||
<string name="chapters">Κεφάλαια</string>
|
||||
<string name="details">Πληροφορίες</string>
|
||||
<string name="list">Λίστα</string>
|
||||
<string name="detailed_list">Λεπτομερής λίστα</string>
|
||||
<string name="loading_">Φόρτωση…</string>
|
||||
<string name="chapter_d_of_d">Κεφάλαιο%1$d από %2$d</string>
|
||||
<string name="you_have_not_favourites_yet">Δεν υπάρχουν αγαπημένα</string>
|
||||
<string name="add">Προσθήκη</string>
|
||||
<string name="enter_category_name">Εισαγωγή ονόματος κατηγορίας</string>
|
||||
<string name="processing_">Επεξεργασία…</string>
|
||||
<string name="by_name">Όνομα</string>
|
||||
<string name="popular">Δημοφιλή</string>
|
||||
<string name="sort_order">Τρόπος Ταξινόμησης</string>
|
||||
<string name="_s_removed_from_history">Το \"%s\" αφαιρέθηκε από το ιστορικό</string>
|
||||
<string name="theme">Θέμα</string>
|
||||
<string name="light">Φωτεινό</string>
|
||||
<string name="pages">Σελίδες</string>
|
||||
<string name="wait_for_loading_finish">Περιμένετε να ολοκληρωθεί η φόρτωση…</string>
|
||||
<string name="_s_deleted_from_local_storage">Το \"%s\" διαγράφηκε από τον τοπικό χώρο αποθήκευσης</string>
|
||||
<string name="operation_not_supported">Αυτή η λειτουργία δεν υποστηρίζεται</string>
|
||||
<string name="read_mode">Λειτουργία ανάγνωσης</string>
|
||||
<string name="grid_size">Μέγεθος πλέγματος</string>
|
||||
</resources>
|
||||
@@ -6,7 +6,7 @@
|
||||
<string name="favourites">Favoritos</string>
|
||||
<string name="history">Historial</string>
|
||||
<string name="error_occurred">Ocurrió un error</string>
|
||||
<string name="network_error">No se pudo conectar a Internet</string>
|
||||
<string name="network_error">Error en la red</string>
|
||||
<string name="details">Detalles</string>
|
||||
<string name="chapters">Capítulos</string>
|
||||
<string name="list">Lista</string>
|
||||
@@ -266,7 +266,7 @@
|
||||
<string name="suggestions_excluded_genres">Excluir géneros</string>
|
||||
<string name="suggestions_excluded_genres_summary">Especifica los géneros que no quieres ver en las sugerencias</string>
|
||||
<string name="removal_completed">Remoción completada</string>
|
||||
<string name="batch_manga_save_confirm">¿Estás seguro de que quieres descargar todos los manga seleccionados con todos sus capítulos\? Esta acción puede consumir mucho tráfico y almacenamiento</string>
|
||||
<string name="batch_manga_save_confirm">¿Descargar todos los mangas seleccionados y sus capítulos\? Esto puede consumir mucho tráfico y almacenamiento.</string>
|
||||
<string name="text_delete_local_manga_batch">¿Eliminar elementos seleccionados del dispositivo de forma permanente\?</string>
|
||||
<string name="hide">Ocultar</string>
|
||||
<string name="download_slowdown">Ralentización de la descarga</string>
|
||||
@@ -375,7 +375,7 @@
|
||||
<string name="manga_error_description_pattern">Detalles del error:<br><tt>%1$s</tt><br><br>1. Intenta <a href=%2$s>abrir el manga en un navegador web</a> para asegurarte de que está disponible en tu fuente<br>2. Si está disponible, envía un informe de error a los desarrolladores.</string>
|
||||
<string name="history_shortcuts_summary">Hacer que los mangas recientes estén disponibles mediante una pulsación larga en el icono de la aplicación</string>
|
||||
<string name="color_correction_hint">Los ajustes de color elegidos serán recordados para este manga</string>
|
||||
<string name="feed">Feed</string>
|
||||
<string name="feed">Fuente</string>
|
||||
<string name="downloading_manga">Descargando manga</string>
|
||||
<string name="history_shortcuts">Mostrar los accesos directos a los mangas recientes</string>
|
||||
<string name="reader_control_ltr_summary">Tocando el borde derecho o pulsando la tecla derecha se pasa siempre a la página siguiente</string>
|
||||
@@ -384,10 +384,17 @@
|
||||
<string name="brightness">Brillo</string>
|
||||
<string name="contrast">Contraste</string>
|
||||
<string name="reset">Restablecer</string>
|
||||
<string name="text_unsaved_changes_prompt">Tienes cambios sin guardar. ¿Quieres guardarlos o descartarlos\?</string>
|
||||
<string name="text_unsaved_changes_prompt">¿Guardar o descartar los cambios no guardados\?</string>
|
||||
<string name="discard">Descartar</string>
|
||||
<string name="error_no_space_left">Sin espacio en dispositivo</string>
|
||||
<string name="webtoon_zoom">Zoom de webtoon</string>
|
||||
<string name="webtoon_zoom_summary">Permitir el gesto de acercamiento/alejamiento en modo webtoon (beta)</string>
|
||||
<string name="reader_slider">Mostrar el deslizador de cambio de página</string>
|
||||
<string name="server_error">Error del servidor (%1$d). Vuelva a intentarlo más tarde</string>
|
||||
<string name="clear_new_chapters_counters">Información clara sobre los nuevos capítulos</string>
|
||||
<string name="different_languages">Diferentes idiomas</string>
|
||||
<string name="network_unavailable">La red no está disponible</string>
|
||||
<string name="compact">Compacta</string>
|
||||
<string name="network_unavailable_hint">Enciende la Wi-Fi o la red móvil para leer los mangas en línea</string>
|
||||
<string name="source_disabled">Fuente desactivada</string>
|
||||
</resources>
|
||||
@@ -202,7 +202,7 @@
|
||||
<string name="list">Liste</string>
|
||||
<string name="chapters">Chapitres</string>
|
||||
<string name="details">Détails</string>
|
||||
<string name="network_error">Impossible de se connecter à Internet</string>
|
||||
<string name="network_error">Erreur réseau</string>
|
||||
<string name="error_occurred">Une erreur s\'est produite</string>
|
||||
<string name="history">Historique</string>
|
||||
<string name="favourites">Favoris</string>
|
||||
@@ -395,4 +395,5 @@
|
||||
<string name="compact">Compact</string>
|
||||
<string name="server_error">Erreur côté serveur (%1$d). Veuillez réessayer plus tard</string>
|
||||
<string name="clear_new_chapters_counters">Effacer aussi les informations sur les nouveaux chapitres</string>
|
||||
<string name="source_disabled">Source désactivée</string>
|
||||
</resources>
|
||||
@@ -102,15 +102,15 @@
|
||||
<string name="notification_sound">Suara pemberitahuan</string>
|
||||
<string name="categories_">Kategori…</string>
|
||||
<string name="rename">Ubah Nama</string>
|
||||
<string name="category_delete_confirm">"Hapus kategori \"%s\" dari favorit Anda\?
|
||||
\nSemua manga disana akan hilang."</string>
|
||||
<string name="category_delete_confirm">Hapus kategori \"%s\" dari favorit Anda\?
|
||||
\nSemua manga di situ akan hilang.</string>
|
||||
<string name="text_empty_holder_primary">Sepi juga di sini…</string>
|
||||
<string name="text_categories_holder">Anda bisa menggunakan kategori untuk mengelola favorit Anda. Tekan «+» untuk membuat kategori</string>
|
||||
<string name="text_history_holder_primary">Apa yang Anda baca akan ditampilkan di sini</string>
|
||||
<string name="text_history_holder_secondary">Cari apa untuk di baca di bilah samping.</string>
|
||||
<string name="text_local_holder_primary">Simpan sesuatu dulu</string>
|
||||
<string name="manga_shelf">Rak</string>
|
||||
<string name="recent_manga">Terbaru</string>
|
||||
<string name="recent_manga">Baru-baru ini</string>
|
||||
<string name="pages_animation">Animasi halaman</string>
|
||||
<string name="manga_save_location">Folder untuk unduhan</string>
|
||||
<string name="not_available">Tidak tersedia</string>
|
||||
@@ -298,7 +298,7 @@
|
||||
<string name="status_completed">Selesai</string>
|
||||
<string name="canceled">Dibatalkan</string>
|
||||
<string name="sync_title">Sinkronisasi data Anda</string>
|
||||
<string name="enter_email_text">Masukkan email Anda untuk melanjutkan</string>
|
||||
<string name="enter_email_text">Masukkan surel Anda untuk melanjutkan</string>
|
||||
<string name="tracking">Pelacakan</string>
|
||||
<string name="logout">Keluar</string>
|
||||
<string name="sync">Sinkronisasi</string>
|
||||
@@ -329,7 +329,7 @@
|
||||
<string name="exit_confirmation_summary">Tekan Kembali dua kali untuk keluar dari aplikasi</string>
|
||||
<string name="exit_confirmation">Konfirmasi keluar</string>
|
||||
<string name="pages_cache">Tembolok halaman</string>
|
||||
<string name="other_cache">Cache lainnya</string>
|
||||
<string name="other_cache">Tembolok lainnya</string>
|
||||
<string name="storage_usage">Penggunaan penyimpanan</string>
|
||||
<string name="available">Tersedia</string>
|
||||
<string name="incognito_mode">Mode Incognito</string>
|
||||
@@ -356,14 +356,14 @@
|
||||
<string name="crash_text">Ada sesuatu yang salah. Mohon untuk mengirim laporan kutu (bug) ke pengembang untuk membantu kami memperbaikinya.</string>
|
||||
<string name="report">Lapor</string>
|
||||
<string name="exclude_nsfw_from_history_summary">Manga yang ditandai sebagai NSFW tidak akan ditambahkan ke riwayat dan progres Anda tidak akan disimpan</string>
|
||||
<string name="clear_cookies_summary">Bisa membantu dalam beberapa masalah. Seluruh otorisasi akan menjadi tidak valid.</string>
|
||||
<string name="clear_cookies_summary">Bisa membantu dalam beberapa masalah. Seluruh otorisasi akan menjadi tidak valid</string>
|
||||
<string name="manage">Kelola</string>
|
||||
<string name="no_manga_sources_text">Aktifkan sumber manga untuk membaca manga daring</string>
|
||||
<string name="categories_delete_confirm">Apakah Anda yakin ingin menghapus kategori favorit yang dipilih\?
|
||||
\n Semua manga di sana akan hilang dan ini tidak bisa diurungkan.</string>
|
||||
<string name="reorder">Atur Ulang</string>
|
||||
<string name="saved_manga">Manga tersimpan</string>
|
||||
<string name="confirm_exit">Tekan lagi untuk keluar</string>
|
||||
<string name="confirm_exit">Tekan Kembali lagi untuk keluar</string>
|
||||
<string name="no_chapters">Tidak ada bab</string>
|
||||
<string name="history_shortcuts">Tampilkan pintasan manga baru-baru ini</string>
|
||||
<string name="history_shortcuts_summary">Buat manga baru-baru ini tersedia dengan menekan panjang pada ikon aplikasi</string>
|
||||
@@ -372,4 +372,5 @@
|
||||
<string name="gestures_only">Hanya gestur</string>
|
||||
<string name="dns_over_https">DNS melalui HTTPS</string>
|
||||
<string name="status_dropped">Istirahat</string>
|
||||
<string name="off_short">Mati</string>
|
||||
</resources>
|
||||
@@ -320,4 +320,6 @@
|
||||
<string name="invalid_domain_message">Dominio non valido</string>
|
||||
<string name="select_range">Seleziona l\'intervallo</string>
|
||||
<string name="not_found_404">Contenuto non trovato o rimosso</string>
|
||||
<string name="compact">Compatto</string>
|
||||
<string name="source_disabled">Fonte disabilitata</string>
|
||||
</resources>
|
||||
@@ -63,7 +63,7 @@
|
||||
<string name="dark">ダークテーマ</string>
|
||||
<string name="pages">ページ</string>
|
||||
<string name="theme">テーマ</string>
|
||||
<string name="network_error">インターネットに接続出来ませんでした</string>
|
||||
<string name="network_error">ネットワークエラー</string>
|
||||
<string name="enter_category_name">カテゴリー名を入力してください</string>
|
||||
<string name="updated">アップデート</string>
|
||||
<string name="cache">キャッシュ</string>
|
||||
@@ -395,4 +395,5 @@
|
||||
<string name="network_unavailable_hint">Wi-Fiまたはモバイルネットワークをオンにして、オンラインでマンガを読むことができます</string>
|
||||
<string name="webtoon_zoom">Webtoonズーム</string>
|
||||
<string name="webtoon_zoom_summary">ウェブトゥーンモードでズームイン/ズームアウトのジェスチャーを可能にする(ベータ版)</string>
|
||||
<string name="compact">コンパクト</string>
|
||||
</resources>
|
||||
2
app/src/main/res/values-ko/plurals.xml
Normal file
2
app/src/main/res/values-ko/plurals.xml
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
108
app/src/main/res/values-ko/strings.xml
Normal file
108
app/src/main/res/values-ko/strings.xml
Normal file
@@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="sort_order">정렬 기준</string>
|
||||
<string name="_import">불러오기</string>
|
||||
<string name="network_error">네트워크 오류</string>
|
||||
<string name="list">목록</string>
|
||||
<string name="save">저장</string>
|
||||
<string name="share">공유하기</string>
|
||||
<string name="share_s">%s 공유</string>
|
||||
<string name="search">검색하기</string>
|
||||
<string name="warning">경고</string>
|
||||
<string name="internal_storage">내부 저장소</string>
|
||||
<string name="external_storage">외부 저장소</string>
|
||||
<string name="domain">도메인</string>
|
||||
<string name="app_update_available">새 버전이 존재합니다</string>
|
||||
<string name="open_in_browser">웹 브라우저에서 열기</string>
|
||||
<string name="save_manga">저장</string>
|
||||
<string name="notifications">알림</string>
|
||||
<string name="read_from_start">처음부터 읽기</string>
|
||||
<string name="light_indicator">LED 표시</string>
|
||||
<string name="vibration">진동</string>
|
||||
<string name="rename">이름 바꾸기</string>
|
||||
<string name="category_delete_confirm">즐겨찾기에서 \"%s\" 카테고리를 제거하시겠습니까\?
|
||||
\n포함된 모든 만화가 지워집니다.</string>
|
||||
<string name="remove_category">지우기</string>
|
||||
<string name="text_search_holder_secondary">쿼리를 재구성하십시오.</string>
|
||||
<string name="text_history_holder_secondary">사이드 메뉴에서 만화를 탐색해보세요.</string>
|
||||
<string name="text_shelf_holder_primary">만화가 여기에 표시됩니다</string>
|
||||
<string name="text_shelf_holder_secondary">«탐색» 섹션에서 만화를 탐색해보세요</string>
|
||||
<string name="pages_animation">페이지 전환 효과</string>
|
||||
<string name="cannot_find_available_storage">사용 가능한 저장소 없음</string>
|
||||
<string name="done">완료</string>
|
||||
<string name="favourites_category_empty">빈 카테고리</string>
|
||||
<string name="updates">업데이트</string>
|
||||
<string name="new_version_s">새 버전: %s</string>
|
||||
<string name="waiting_for_network">네트워크 연결을 기다리는 중…</string>
|
||||
<string name="clear_updates_feed">업데이트 피드 지우기</string>
|
||||
<string name="close_menu">메뉴 닫기</string>
|
||||
<string name="open_menu">메뉴 열기</string>
|
||||
<string name="local_storage">내장 메모리</string>
|
||||
<string name="favourites">즐겨찾기</string>
|
||||
<string name="remove">지우기</string>
|
||||
<string name="settings">설정</string>
|
||||
<string name="loading_">불러오는 중…</string>
|
||||
<string name="close">닫기</string>
|
||||
<string name="try_again">다시 시도</string>
|
||||
<string name="you_have_not_favourites_yet">즐겨찾기가 비어있음</string>
|
||||
<string name="filter">필터링</string>
|
||||
<string name="light">밝게</string>
|
||||
<string name="dark">어둡게</string>
|
||||
<string name="pages">페이지</string>
|
||||
<string name="read">지금 읽기</string>
|
||||
<string name="by_name">이름 순</string>
|
||||
<string name="popular">인기 순</string>
|
||||
<string name="chapter_d_of_d">%2$d화 중 %1$d화</string>
|
||||
<string name="downloads">다운로드</string>
|
||||
<string name="by_rating">평점 순</string>
|
||||
<string name="save_page">페이지 저장</string>
|
||||
<string name="page_saved">저장됨</string>
|
||||
<string name="share_image">이미지 공유하기</string>
|
||||
<string name="text_file_not_supported">ZIP 혹은 CBZ 파일을 선택하세요.</string>
|
||||
<string name="history_and_cache">기록 및 캐시</string>
|
||||
<string name="cache">캐시</string>
|
||||
<string name="delete_manga">만화 제거</string>
|
||||
<string name="volume_buttons">볼륨 키</string>
|
||||
<string name="nothing_found">결과 없음</string>
|
||||
<string name="add_to_favourites">즐겨찾기 추가</string>
|
||||
<string name="download_complete">다운로드 완료</string>
|
||||
<string name="add_new_category">새 카테고리</string>
|
||||
<string name="search_manga">만화를 검색하세요</string>
|
||||
<string name="manga_downloading_">다운로드 중…</string>
|
||||
<string name="processing_">처리중…</string>
|
||||
<string name="updated">최근 업데이트 순</string>
|
||||
<string name="newest">최근 발간 순</string>
|
||||
<string name="automatic">시스템 설정</string>
|
||||
<string name="delete">지우기</string>
|
||||
<string name="wait_for_loading_finish">잠시만 기다려주세요…</string>
|
||||
<string name="text_file_sizes">바이트|kB|MB|GB|TB</string>
|
||||
<string name="clear_pages_cache">페이지 캐시 지우기</string>
|
||||
<string name="read_mode">읽기 모드</string>
|
||||
<string name="grid_size">격자 크기</string>
|
||||
<string name="search_on_s">%s에서 검색</string>
|
||||
<string name="text_delete_local_manga">장치에서 \"%s\"를 영구적으로 삭제하시겠습니까\?</string>
|
||||
<string name="switch_pages">페이지 전환</string>
|
||||
<string name="taps_on_edges">가장자리 탭</string>
|
||||
<string name="webtoon">웹툰</string>
|
||||
<string name="clear_search_history">검색 기록 지우기</string>
|
||||
<string name="reader_settings">읽기 모드</string>
|
||||
<string name="network_consumption_warning">이 동작은 많은 데이터 사용을</string>
|
||||
<string name="clear_thumbs_cache">썸네일 캐시 지우기</string>
|
||||
<string name="dont_ask_again">다시 묻지 않음</string>
|
||||
<string name="cancelling_">취소 중…</string>
|
||||
<string name="error">오류</string>
|
||||
<string name="application_update">업데이트 확인</string>
|
||||
<string name="show_notification_app_update">업데이트 가능 시 알림 설정</string>
|
||||
<string name="large_manga_save_confirm">이 만화에는 %s가 있습니다. 모두 저장하시겠습니까\?</string>
|
||||
<string name="favourites_categories">즐겨찾기 카테고리</string>
|
||||
<string name="download">다운로드</string>
|
||||
<string name="notifications_settings">알림 설정</string>
|
||||
<string name="notification_sound">알림음</string>
|
||||
<string name="categories_">카테고리…</string>
|
||||
<string name="text_history_holder_primary">읽은 내용이 여기에 표시됩니다</string>
|
||||
<string name="not_available">사용할 수 없음</string>
|
||||
<string name="all_favourites">모든 즐겨찾기</string>
|
||||
<string name="read_later">나중에 읽기</string>
|
||||
<string name="search_results">검색 결과</string>
|
||||
<string name="size_s">크기: %s</string>
|
||||
</resources>
|
||||
43
app/src/main/res/values-pl/plurals.xml
Normal file
43
app/src/main/res/values-pl/plurals.xml
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<plurals name="new_chapters">
|
||||
<item quantity="one">%1$d nowy rozdział</item>
|
||||
<item quantity="few">%1$d nowe rozdziały</item>
|
||||
<item quantity="many">%1$d nowych rozdziałów</item>
|
||||
</plurals>
|
||||
<plurals name="minutes_ago">
|
||||
<item quantity="one">%1$d minutę temu</item>
|
||||
<item quantity="few">%1$d minuty temu</item>
|
||||
<item quantity="many">%1$d minut temu</item>
|
||||
</plurals>
|
||||
<plurals name="pages">
|
||||
<item quantity="one">Łącznie %1$d strona</item>
|
||||
<item quantity="few">Łącznie %1$d strony</item>
|
||||
<item quantity="many">Łącznie %1$d stron</item>
|
||||
</plurals>
|
||||
<plurals name="hours_ago">
|
||||
<item quantity="one">%1$d godzinę temu</item>
|
||||
<item quantity="few">%1$d godziny temu</item>
|
||||
<item quantity="many">%1$d godzin temu</item>
|
||||
</plurals>
|
||||
<plurals name="days_ago">
|
||||
<item quantity="one">%1$d dzień temu</item>
|
||||
<item quantity="few">%1$d dni temu</item>
|
||||
<item quantity="many">%1$d dni temu</item>
|
||||
</plurals>
|
||||
<plurals name="items">
|
||||
<item quantity="one">%1$d przedmiot</item>
|
||||
<item quantity="few">%1$d przedmioty</item>
|
||||
<item quantity="many">%1$d przedmiotów</item>
|
||||
</plurals>
|
||||
<plurals name="chapters_from_x">
|
||||
<item quantity="one">%1$d rozdział z %2$d</item>
|
||||
<item quantity="few">%1$d rozdziały z %2$d</item>
|
||||
<item quantity="many">%1$d rozdziałów z %2$d</item>
|
||||
</plurals>
|
||||
<plurals name="chapters">
|
||||
<item quantity="one">%1$d rozdział</item>
|
||||
<item quantity="few">%1$d rozdziały</item>
|
||||
<item quantity="many">%1$d rozdziałów</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
397
app/src/main/res/values-pl/strings.xml
Normal file
397
app/src/main/res/values-pl/strings.xml
Normal file
@@ -0,0 +1,397 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="favourites">Ulubione</string>
|
||||
<string name="history">Historia</string>
|
||||
<string name="error_occurred">Napotkano błąd</string>
|
||||
<string name="details">Szczegółowy</string>
|
||||
<string name="chapters">Rozdziały</string>
|
||||
<string name="list">Lista</string>
|
||||
<string name="detailed_list">Lista szczegółowa</string>
|
||||
<string name="grid">Siatka</string>
|
||||
<string name="list_mode">Tryb listy</string>
|
||||
<string name="settings">Ustawienia</string>
|
||||
<string name="loading_">Ładowanie…</string>
|
||||
<string name="chapter_d_of_d">Rozdział %1$d z %2$d</string>
|
||||
<string name="close">Zamknij</string>
|
||||
<string name="clear_history">Wyczyść historię</string>
|
||||
<string name="add">Dodaj</string>
|
||||
<string name="save">Zapisz</string>
|
||||
<string name="share">Udostępnij</string>
|
||||
<string name="search">Szukaj</string>
|
||||
<string name="search_manga">Szukaj mang</string>
|
||||
<string name="manga_downloading_">Pobieranie…</string>
|
||||
<string name="download_complete">Pobrano</string>
|
||||
<string name="downloads">Pobrane</string>
|
||||
<string name="by_name">Nazwa</string>
|
||||
<string name="popular">Popularność</string>
|
||||
<string name="newest">Najnowsze</string>
|
||||
<string name="by_rating">Ocena</string>
|
||||
<string name="filter">Filtry</string>
|
||||
<string name="light">Jasny</string>
|
||||
<string name="dark">Ciemny</string>
|
||||
<string name="pages">Strony</string>
|
||||
<string name="clear">Wyczyść</string>
|
||||
<string name="remove">Usuń</string>
|
||||
<string name="share_image">Udostępnij zdjęcie</string>
|
||||
<string name="delete">Usuń</string>
|
||||
<string name="no_description">Brak opisu</string>
|
||||
<string name="read_mode">Tryb czytania</string>
|
||||
<string name="network_error">Błąd sieci</string>
|
||||
<string name="computing_">Obliczanie…</string>
|
||||
<string name="try_again">Spróbuj ponownie</string>
|
||||
<string name="nothing_found">Nic nie znaleziono</string>
|
||||
<string name="history_is_empty">Brak historii</string>
|
||||
<string name="read">Czytaj</string>
|
||||
<string name="you_have_not_favourites_yet">Brak ulubionych</string>
|
||||
<string name="add_to_favourites">Dodaj do ulubionych</string>
|
||||
<string name="add_new_category">Nowa kategoria</string>
|
||||
<string name="create_shortcut">Stwórz skrót</string>
|
||||
<string name="share_s">Udostępnij %s</string>
|
||||
<string name="processing_">Przetwarzanie…</string>
|
||||
<string name="updated">Zaktualizowane</string>
|
||||
<string name="_s_removed_from_history">„%s” usunięte z historii</string>
|
||||
<string name="save_page">Zapisz stronę</string>
|
||||
<string name="page_saved">Zapisano</string>
|
||||
<string name="vibration">Wibracje</string>
|
||||
<string name="manga_shelf">Biblioteka</string>
|
||||
<string name="recent_manga">Ostatnie</string>
|
||||
<string name="black_dark_theme">Tryb czarny</string>
|
||||
<string name="preparing_">Przygotowywanie…</string>
|
||||
<string name="file_not_found">Plik nieznaleziony</string>
|
||||
<string name="yesterday">Wczoraj</string>
|
||||
<string name="long_ago">Dawno temu</string>
|
||||
<string name="group">Grupa</string>
|
||||
<string name="today">Dzisiaj</string>
|
||||
<string name="sign_in">Zaloguj</string>
|
||||
<string name="next">Dalej</string>
|
||||
<string name="confirm">Potwierdź</string>
|
||||
<string name="welcome">Witaj</string>
|
||||
<string name="state_finished">Skończone</string>
|
||||
<string name="state_ongoing">W trakcie</string>
|
||||
<string name="screenshots_allow">Zezwól</string>
|
||||
<string name="suggestions">Proponowane</string>
|
||||
<string name="suggestions_enable">Włącz propozycje</string>
|
||||
<string name="enabled">Włączone</string>
|
||||
<string name="disabled">Wyłączone</string>
|
||||
<string name="never">Nigdy</string>
|
||||
<string name="always">Zawsze</string>
|
||||
<string name="search_chapters">Znajdź rozdział</string>
|
||||
<string name="percent_string_pattern">%1$s%%</string>
|
||||
<string name="appearance">Wygląd</string>
|
||||
<string name="hide">Schowaj</string>
|
||||
<string name="sync">Synchronizacja</string>
|
||||
<string name="sync_title">Synchronizuj swoje dane</string>
|
||||
<string name="name">Nazwa</string>
|
||||
<string name="edit">Edytuj</string>
|
||||
<string name="logout">Wyloguj</string>
|
||||
<string name="undo">Cofnij</string>
|
||||
<string name="send">Wyślij</string>
|
||||
<string name="status_planned">Planowane</string>
|
||||
<string name="status_reading">Czytane</string>
|
||||
<string name="status_re_reading">Czytane ponownie</string>
|
||||
<string name="status_completed">Skończone</string>
|
||||
<string name="show_all">Pokaż wszystkie</string>
|
||||
<string name="select_range">Wybierz zakres</string>
|
||||
<string name="clear_all_history">Wyczyść całą historię</string>
|
||||
<string name="last_2_hours">Ostatnie 2 godziny</string>
|
||||
<string name="history_cleared">Historia wyczyszczona</string>
|
||||
<string name="manage">Zarządzaj</string>
|
||||
<string name="random">Losowe</string>
|
||||
<string name="empty">Puste</string>
|
||||
<string name="changelog">Lista zmian</string>
|
||||
<string name="explore">Przeglądaj</string>
|
||||
<string name="available">Dostępne</string>
|
||||
<string name="options">Ustawienia</string>
|
||||
<string name="source_disabled">Źródło wyłączone</string>
|
||||
<string name="compact">Kompaktowy</string>
|
||||
<string name="server_error">Błąd po stronie serwera (%1$d). Sprónuj ponownie później</string>
|
||||
<string name="network_unavailable">Sieć niedostępna</string>
|
||||
<string name="different_languages">Inne języki</string>
|
||||
<string name="discard">Odrzuć</string>
|
||||
<string name="brightness">Jasność</string>
|
||||
<string name="contrast">Kontrast</string>
|
||||
<string name="color_correction">Korekcja kolorów</string>
|
||||
<string name="seconds_pattern">%ss</string>
|
||||
<string name="off_short">Wyłącz</string>
|
||||
<string name="automatic_scroll">Automatyczne przewijanie</string>
|
||||
<string name="no_chapters">Brak rozdziałów</string>
|
||||
<string name="incognito_mode">Tryb incognito</string>
|
||||
<string name="downloading_manga">Pobieranie mangi</string>
|
||||
<string name="removed_from_favourites">Usunięto z ulubionych</string>
|
||||
<string name="enter_email_text">Wprowadź swój email aby kontynuować</string>
|
||||
<string name="storage_usage">Wykorzystana pamięć</string>
|
||||
<string name="saved_manga">Zapisane mangi</string>
|
||||
<string name="no_bookmarks_yet">Brak zakładek</string>
|
||||
<string name="no_bookmarks_summary">Możesz tworzyć zakładki w trakcie czytania mangi</string>
|
||||
<string name="bookmarks_removed">Zakładki usunięte</string>
|
||||
<string name="appwidget_recent_description">Twoje ostatnio czytane mangi</string>
|
||||
<string name="disable_all">Wyłącz wszystkie</string>
|
||||
<string name="disable_battery_optimization">Wyłącz optymalizację baterii</string>
|
||||
<string name="detect_reader_mode">Autowykrywanie trybu czytania</string>
|
||||
<string name="removed_from_history">Usunięte z historii</string>
|
||||
<string name="bookmark_added">Dodano zakładkę</string>
|
||||
<string name="bookmark_removed">Usunięto zakładkę</string>
|
||||
<string name="bookmarks">Zakładki</string>
|
||||
<string name="bookmark_remove">Usuń zakładkę</string>
|
||||
<string name="bookmark_add">Dodaj zakładkę</string>
|
||||
<string name="empty_favourite_categories">Brak ulubionych kategorii</string>
|
||||
<string name="edit_category">Edytuj kategorię</string>
|
||||
<string name="notifications_enable">Włącz powiadomienia</string>
|
||||
<string name="back">Wróć</string>
|
||||
<string name="account_already_exists">Konto już istnieje</string>
|
||||
<string name="canceled">Anulowano</string>
|
||||
<string name="download_slowdown">Zwolnienie pobierania</string>
|
||||
<string name="chapters_empty">Brak rozdziałów w tej mandze</string>
|
||||
<string name="various_languages">Różne języki</string>
|
||||
<string name="only_using_wifi">Tylko na Wi-Fi</string>
|
||||
<string name="screenshots_block_all">Zawsze blokuj</string>
|
||||
<string name="date_format">Format daty</string>
|
||||
<string name="genres">Gatunki</string>
|
||||
<string name="find_genre">Znajdź gatunek</string>
|
||||
<string name="read_more">Czytaj więcej</string>
|
||||
<string name="other">Inne</string>
|
||||
<string name="captcha_solve">Rozwiąż</string>
|
||||
<string name="captcha_required">Wymagane CAPTCHA</string>
|
||||
<string name="silent">Cichy</string>
|
||||
<string name="tap_to_try_again">Dotknij aby spróbować ponownie</string>
|
||||
<string name="just_now">Teraz</string>
|
||||
<string name="data_restored">Przywrócone</string>
|
||||
<string name="zoom_mode_fit_width">Dopasuj do szerokości</string>
|
||||
<string name="zoom_mode_fit_height">Dopasuj do wysokości</string>
|
||||
<string name="zoom_mode_fit_center">Dopasuj do środka</string>
|
||||
<string name="create_category">Nowa kategoria</string>
|
||||
<string name="no_update_available">Brak nowych aktualizacji</string>
|
||||
<string name="check_for_updates">Sprawdź dostępność aktualizacji</string>
|
||||
<string name="checking_for_updates">Sprawdzanie aktualizacji…</string>
|
||||
<string name="app_version">Wersja %s</string>
|
||||
<string name="about">O aplikacji</string>
|
||||
<string name="categories_">Kategorie…</string>
|
||||
<string name="rename">Zmień nazwę</string>
|
||||
<string name="remove_category">Usuń</string>
|
||||
<string name="text_empty_holder_primary">Jest tu dosyć pusto…</string>
|
||||
<string name="favourites_categories">Ulubione kategorie</string>
|
||||
<string name="light_indicator">Powiadomienie LED</string>
|
||||
<string name="new_chapters">Nowe rozdziały</string>
|
||||
<string name="close_menu">Zamknij kartę</string>
|
||||
<string name="open_menu">Otwórz kartę</string>
|
||||
<string name="local_storage">Pamięć wewnętrzna</string>
|
||||
<string name="text_shelf_holder_primary">Tutaj będą wyświetlane Twoje mangi</string>
|
||||
<string name="text_shelf_holder_secondary">Znajdź materiały do czytania w zakładce „Przeglądaj”</string>
|
||||
<string name="text_feed_holder">W tym miejscu pojawią się powiadomienia o nowych rozdziałach z mang które czytasz</string>
|
||||
<string name="pages_cache">Strony w pamięci podręcznej</string>
|
||||
<string name="pages_animation">Animacja przewracania strony</string>
|
||||
<string name="other_cache">Inne rzeczy w pamięci podręcznej</string>
|
||||
<string name="open_in_browser">Otwórz w przeglądarce</string>
|
||||
<string name="show_pages_numbers">Numerowane strony</string>
|
||||
<string name="notifications">Powiadomienia</string>
|
||||
<string name="notification_sound">Dźwięk powiadomień</string>
|
||||
<string name="notifications_settings">Ustawienia powiadomień</string>
|
||||
<string name="remote_sources">Zewnętrzne źródła</string>
|
||||
<string name="theme">Motyw</string>
|
||||
<string name="automatic">Systemowy</string>
|
||||
<string name="history_and_cache">Historia i pamięć podręczna</string>
|
||||
<string name="clear_pages_cache">Wyczyść pamięć podręczną stron</string>
|
||||
<string name="cache">Pamięć podręczna</string>
|
||||
<string name="text_file_sizes">B|kB|MB|GB|TB</string>
|
||||
<string name="grid_size">Wielkość siatki</string>
|
||||
<string name="search_on_s">Szukaj na %s</string>
|
||||
<string name="delete_manga">Usuń mangę</string>
|
||||
<string name="_continue">Dalej</string>
|
||||
<string name="dont_ask_again">Nie pytaj ponownie</string>
|
||||
<string name="cancelling_">Anulowanie…</string>
|
||||
<string name="error">Błąd</string>
|
||||
<string name="search_history_cleared">Wyczyszczone</string>
|
||||
<string name="internal_storage">Pamięć wewnętrzna</string>
|
||||
<string name="external_storage">Pamięć zewnętrzna</string>
|
||||
<string name="domain">Domena</string>
|
||||
<string name="application_update">Sprawdź dostępność nowej wersji aplikacji</string>
|
||||
<string name="app_update_available">Nowa wersja aplikacji jest dostępna</string>
|
||||
<string name="show_notification_app_update">Pokaż powiadomienie gdy nowa wersja jest dostępna</string>
|
||||
<string name="large_manga_save_confirm">Ta manga ma %s. Zapisać wszystko?</string>
|
||||
<string name="save_manga">Zapisz</string>
|
||||
<string name="download">Pobierz</string>
|
||||
<string name="read_from_start">Czytaj od początku</string>
|
||||
<string name="category_delete_confirm">Usunąć kategorię „%s” z Twoich ulubionych? Wszystkie mangi w niej będą z niej usunięte.</string>
|
||||
<string name="text_categories_holder">Możesz użyć kategorii do organizowania swoich ulubionych. Kliknij «+» aby stworzyć kategorię</string>
|
||||
<string name="text_local_holder_primary">Najpierw coś zapisz</string>
|
||||
<string name="not_available">Niedostępne</string>
|
||||
<string name="done">Zapisz</string>
|
||||
<string name="all_favourites">Wszystkie ulubione</string>
|
||||
<string name="favourites_category_empty">Pusta kategoria</string>
|
||||
<string name="read_later">Czytaj później</string>
|
||||
<string name="updates">Aktualizacje</string>
|
||||
<string name="new_version_s">Nowa wersja: %s</string>
|
||||
<string name="size_s">Wielkość: %s</string>
|
||||
<string name="waiting_for_network">Czekanie na sieć…</string>
|
||||
<string name="rotate_screen">Obróć ekran</string>
|
||||
<string name="update">Odśwież</string>
|
||||
<string name="track_sources">Szukaj aktualizacji</string>
|
||||
<string name="dont_check">Nie sprawdzaj</string>
|
||||
<string name="enter_password">Wprowadź hasło</string>
|
||||
<string name="wrong_password">Złe hasło</string>
|
||||
<string name="protect_application">Chroń aplikację</string>
|
||||
<string name="protect_application_summary">Pytaj o hasło przy starcie Kotatsu</string>
|
||||
<string name="repeat_password">Wprowadź ponownie hasło</string>
|
||||
<string name="black_dark_theme_summary">Zużywa mniej prądu na ekranach AMOLED</string>
|
||||
<string name="backup_restore">Kopia zapasowa i przywracanie</string>
|
||||
<string name="create_backup">Utwórz kopię zapasową danych</string>
|
||||
<string name="restore_backup">Przywróć z kopii zapasowej</string>
|
||||
<string name="nsfw">18+</string>
|
||||
<string name="enabled_d_of_d">%1$d na %2$d włączone</string>
|
||||
<string name="enter_category_name">Wprowadź nazwę kategorii</string>
|
||||
<string name="standard">Standardowy</string>
|
||||
<string name="webtoon">Webtoon</string>
|
||||
<string name="reader_settings">Ustawienia czytnika</string>
|
||||
<string name="switch_pages">Zmiana strony</string>
|
||||
<string name="volume_buttons">Przyciski głośności</string>
|
||||
<string name="warning">Uwaga</string>
|
||||
<string name="taps_on_edges">Dotknięcie krawędzi</string>
|
||||
<string name="updates_feed_cleared">Wyczyszczone</string>
|
||||
<string name="scale_mode">Tryb skalowania</string>
|
||||
<string name="clear_cookies">Wyczyść ciasteczka</string>
|
||||
<string name="cookies_cleared">Wszystkie ciasteczka wyczyszczone</string>
|
||||
<string name="search_only_on_s">Szukaj tylko na %s</string>
|
||||
<string name="about_app_translation_summary">Przetłumacz tą aplikację</string>
|
||||
<string name="about_app_translation">Tłumaczenie</string>
|
||||
<string name="error_empty_name">Musisz wpisać nazwę</string>
|
||||
<string name="available_sources">Dostępne źródła</string>
|
||||
<string name="dynamic_theme">Motyw dynamiczny</string>
|
||||
<string name="gestures_only">Tylko gesty</string>
|
||||
<string name="cannot_find_available_storage">Brak dostępnej pamięci</string>
|
||||
<string name="other_storage">Inny</string>
|
||||
<string name="search_results">Wyniki wyszukiwania</string>
|
||||
<string name="related">Szukaj podobnych</string>
|
||||
<string name="data_restored_success">Wszystkie dane zostały przywrócone</string>
|
||||
<string name="data_restored_with_errors">Dane zostały przywrócone, ale z błędami</string>
|
||||
<string name="reverse">Od tyłu</string>
|
||||
<string name="text_downloads_holder">Brak aktywnych pobrań</string>
|
||||
<string name="system_default">Domyślny</string>
|
||||
<string name="screenshots_policy">Polityka zrzutów ekranu</string>
|
||||
<string name="suggestions_excluded_genres">Wyklucz gatunki</string>
|
||||
<string name="suggestions_excluded_genres_summary">Określ gatunki, których nie chcesz widzieć w sugestiach</string>
|
||||
<string name="logged_in_as">Zalogowano jako %s</string>
|
||||
<string name="onboard_text">Wybierz języki, w których chcesz czytać mangi. Możesz zmienić to później w ustawieniach.</string>
|
||||
<string name="report">Zgłoś</string>
|
||||
<string name="data_deletion">Usuwanie danych</string>
|
||||
<string name="invalid_domain_message">Nieważna domena</string>
|
||||
<string name="reorder">Zmień kolejność</string>
|
||||
<string name="exit_confirmation">Potwierdzenie wyjścia</string>
|
||||
<string name="memory_usage_pattern">%s - %s</string>
|
||||
<string name="reader_info_pattern">Rozdz. %1$d/%2$d Str. %3$d/%4$d</string>
|
||||
<string name="network_unavailable_hint">Włącz Wi-Fi lub sieć komórkową, aby czytać mangę online</string>
|
||||
<string name="_import">Importuj</string>
|
||||
<string name="text_file_not_supported">Wybierz plik ZIP lub CBZ.</string>
|
||||
<string name="restart">Uruchom ponownie</string>
|
||||
<string name="clear_search_history">Wyczyść historię wyszukiwania</string>
|
||||
<string name="operation_not_supported">Ta operacja nie jest obsługiwana</string>
|
||||
<string name="wait_for_loading_finish">Poczekaj na zakończenie ładowania…</string>
|
||||
<string name="sort_order">Tryb sortowania</string>
|
||||
<string name="content">Treści</string>
|
||||
<string name="filter_load_error">Nie można załadować listy gatunków</string>
|
||||
<string name="status_on_hold">Wstrzymane</string>
|
||||
<string name="status_dropped">Porzucone</string>
|
||||
<string name="use_fingerprint">Użyj odcisku palca, jeśli jest dostępny</string>
|
||||
<string name="appwidget_shelf_description">Mangi z Twoich ulubionych</string>
|
||||
<string name="show_reading_indicators">Pokaż wskaźniki postępu czytania</string>
|
||||
<string name="show_reading_indicators_summary">Pokaż procent przeczytania w historii i ulubionych</string>
|
||||
<string name="exclude_nsfw_from_history_summary">Manga oznaczona jako NSFW nigdy nie zostanie dodana do historii, a Twoje postępy nie zostaną zapisane</string>
|
||||
<string name="dns_over_https">DNS przez HTTPS</string>
|
||||
<string name="default_mode">Tryb domyślny</string>
|
||||
<string name="text_clear_history_prompt">Trwale wyczyścić całą historię czytania?</string>
|
||||
<string name="_s_deleted_from_local_storage">„%s” usunięte z pamięci lokalnej</string>
|
||||
<string name="clear_updates_feed">Wyczyść tablicę aktualizacji</string>
|
||||
<string name="feed">Tablica</string>
|
||||
<string name="text_delete_local_manga">Usunąć trwale „%s” z urządzenia?</string>
|
||||
<string name="network_consumption_warning">Może to spowodować przeniesienie dużej ilości danych</string>
|
||||
<string name="clear_thumbs_cache">Wyczyść pamięć podręczną miniatur</string>
|
||||
<string name="text_search_holder_secondary">Spróbuj przeformułować zapytanie.</string>
|
||||
<string name="text_history_holder_primary">To co czytasz będzie wyświetlane tutaj</string>
|
||||
<string name="text_history_holder_secondary">Znajdź to, co warto przeczytać, w menu bocznym.</string>
|
||||
<string name="text_local_holder_secondary">Zapisz ze źródeł online lub zaimportuj pliki.</string>
|
||||
<string name="manga_save_location">Folder pobranych</string>
|
||||
<string name="feed_will_update_soon">Aktualizacja tablicy rozpocznie się wkrótce</string>
|
||||
<string name="passwords_mismatch">Niezgodne hasła</string>
|
||||
<string name="update_check_failed">Nie można wyszukać aktualizacji</string>
|
||||
<string name="right_to_left">Od prawej do lewej</string>
|
||||
<string name="zoom_mode_keep_start">Trzymaj na starcie</string>
|
||||
<string name="report_github">Utwórz problem na GitHubie</string>
|
||||
<string name="backup_information">Możesz utworzyć kopię zapasową swojej historii i ulubionych oraz przywrócić ją</string>
|
||||
<string name="reader_mode_hint">Wybrana konfiguracja zostanie zapamiętana dla tej mangi</string>
|
||||
<string name="chapters_checking_progress">Sprawdzanie nowych rozdziałów: %1$d z %2$d</string>
|
||||
<string name="clear_feed">Wyczyść tablicę</string>
|
||||
<string name="text_clear_updates_feed_prompt">Wyczyścić trwale całą historię aktualizacji?</string>
|
||||
<string name="check_for_new_chapters">Szukanie nowych rozdziałów</string>
|
||||
<string name="auth_required">Zaloguj się, aby wyświetlić tę zawartość</string>
|
||||
<string name="default_s">Domyślnie: %s</string>
|
||||
<string name="_and_x_more">…i jeszcze %1$d</string>
|
||||
<string name="protect_application_subtitle">Wprowadź hasło, aby uruchomić aplikację</string>
|
||||
<string name="password_length_hint">Hasło musi mieć co najmniej 4 znaki</string>
|
||||
<string name="text_clear_search_history_prompt">Trwale usunąć wszystkie ostatnie zapytania wyszukiwania?</string>
|
||||
<string name="backup_saved">Zapisano kopię zapasową</string>
|
||||
<string name="tracker_warning">Systemy niektórych urządzeń inaczej się zachowują. Może to zakłócać wykonywanie zadań w tle.</string>
|
||||
<string name="queued">W kolejce</string>
|
||||
<string name="chapter_is_missing_text">Pobierz lub przeczytaj ten brakujący rozdział online.</string>
|
||||
<string name="chapter_is_missing">Brak rozdziału</string>
|
||||
<string name="about_feedback">Komentarz</string>
|
||||
<string name="about_feedback_4pda">Temat na 4PDA</string>
|
||||
<string name="auth_complete">Uprawniony</string>
|
||||
<string name="auth_not_supported_by">Logowanie na %s nie jest obsługiwane</string>
|
||||
<string name="text_clear_cookies_prompt">Zostaniesz wylogowany ze wszystkich źródeł</string>
|
||||
<string name="exclude_nsfw_from_history">Wyklucz mangi NSFW z historii</string>
|
||||
<string name="enabled_sources">Wykorzystane źródła</string>
|
||||
<string name="dynamic_theme_summary">Stosuje motyw utworzony na podstawie schematu kolorów Twojej tapety</string>
|
||||
<string name="importing_progress">Importowanie mangi: %1$d z %2$d</string>
|
||||
<string name="screenshots_block_nsfw">Zablokuj na NSFW</string>
|
||||
<string name="suggestions_summary">Proponuj mangi na podstawie Twoich preferencji</string>
|
||||
<string name="suggestions_info">Wszystkie dane są analizowane lokalnie na tym urządzeniu. Twoje dane osobowe nie są przekazywane do żadnych usług</string>
|
||||
<string name="text_suggestion_holder">Zacznij czytać mangę, a otrzymasz spersonalizowane sugestie</string>
|
||||
<string name="exclude_nsfw_from_suggestions">Nie proponuj mang NSFW</string>
|
||||
<string name="reset_filter">Zresetuj filtr</string>
|
||||
<string name="preload_pages">Ładuj wstępnie strony</string>
|
||||
<string name="suggestions_updating">Aktualizowanie sugestii</string>
|
||||
<string name="text_delete_local_manga_batch">Trwale usunąć wybrane elementy z urządzenia?</string>
|
||||
<string name="removal_completed">Usuwanie zakończone</string>
|
||||
<string name="batch_manga_save_confirm">Pobrać wszystkie wybrane mangi i ich rozdziały? Może to zużyć dużo danych i pamięci.</string>
|
||||
<string name="parallel_downloads">Pobieranie równoległe</string>
|
||||
<string name="download_slowdown_summary">Pomaga uniknąć blokowania Twojego adresu IP</string>
|
||||
<string name="local_manga_processing">Przetwarzanie zapisanej mangi</string>
|
||||
<string name="chapters_will_removed_background">Rozdziały zostaną usunięte w tle. Może to zająć trochę czasu</string>
|
||||
<string name="email_enter_hint">Wpisz swój adres e-mail, aby kontynuować</string>
|
||||
<string name="new_sources_text">Dostępne są nowe źródła mang</string>
|
||||
<string name="check_new_chapters_title">Sprawdzaj dostępność nowych rozdziałów i informuj o nich</string>
|
||||
<string name="show_notification_new_chapters_on">Będziesz otrzymywać powiadomienia o aktualizacjach mang, które czytasz</string>
|
||||
<string name="show_notification_new_chapters_off">Nie będziesz otrzymywać powiadomień, ale nowe rozdziały będą podświetlane na listach</string>
|
||||
<string name="tracking">Śledzenie</string>
|
||||
<string name="detect_reader_mode_summary">Automatycznie wykryj, czy manga to webtoon</string>
|
||||
<string name="disable_battery_optimization_summary">Pomaga w sprawdzaniu aktualizacji w tle</string>
|
||||
<string name="crash_text">Coś poszło nie tak. Zgłoś błąd programistom, aby pomóc nam go naprawić.</string>
|
||||
<string name="clear_cookies_summary">Może pomóc w przypadku niektórych problemów. Wszystkie autoryzacje zostaną unieważnione</string>
|
||||
<string name="no_manga_sources">Brak źródeł mang</string>
|
||||
<string name="no_manga_sources_text">Włącz źródła mang do czytania mang online</string>
|
||||
<string name="categories_delete_confirm">Czy na pewno chcesz usunąć wybrane ulubione kategorie? Wszystkie w nich mangi zostaną usunięte i nie będzie można tego cofnąć.</string>
|
||||
<string name="confirm_exit">Naciśnij ponownie Wstecz, aby wyjść</string>
|
||||
<string name="exit_confirmation_summary">Naciśnij dwukrotnie przycisk Wstecz, aby wyjść z aplikacji</string>
|
||||
<string name="removed_from_s">Usunięto z „%s”</string>
|
||||
<string name="not_found_404">Treść nie została znaleziona lub została usunięta</string>
|
||||
<string name="app_update_available_s">Dostępna aktualizacja aplikacji: %s</string>
|
||||
<string name="reader_info_bar">Pokaż pasek informacji w czytniku</string>
|
||||
<string name="comics_archive">Archiwum komiksów</string>
|
||||
<string name="folder_with_images">Folder z obrazami</string>
|
||||
<string name="importing_manga">Importowanie mangi</string>
|
||||
<string name="import_completed">Importowanie zakończone</string>
|
||||
<string name="import_completed_hint">Możesz usunąć oryginalny plik z pamięci, aby zaoszczędzić miejsce</string>
|
||||
<string name="import_will_start_soon">Import rozpocznie się wkrótce</string>
|
||||
<string name="color_correction_hint">Wybrane ustawienia kolorów zostaną zapamiętane dla tej mangi</string>
|
||||
<string name="history_shortcuts">Pokaż ostatnie skróty do mang</string>
|
||||
<string name="history_shortcuts_summary">Pokaż ostatnie mangi po długim naciśnięciu ikony aplikacji</string>
|
||||
<string name="reader_control_ltr_summary">Stuknięcie w prawą krawędź lub naciśnięcie prawego klawisza zawsze powoduje przejście do następnej strony</string>
|
||||
<string name="reader_control_ltr">Ergonomiczne sterowanie czytnikiem</string>
|
||||
<string name="text_unsaved_changes_prompt">Zapisać czy odrzucić niezapisane zmiany?</string>
|
||||
<string name="error_no_space_left">Brak miejsca w urządzeniu</string>
|
||||
<string name="reader_slider">Pokaż suwak przełączania stron</string>
|
||||
<string name="webtoon_zoom">Powiększanie webtoon</string>
|
||||
<string name="webtoon_zoom_summary">Zezwalaj na gest powiększania/pomniejszania w trybie webtoon (beta)</string>
|
||||
<string name="clear_new_chapters_counters">Wyczyść też informacje o nowych rozdziałach</string>
|
||||
<string name="reset">Resetuj</string>
|
||||
<string name="manga_error_description_pattern">Szczegóły błędu:<br><tt>%1$s</tt><br><br>1. Spróbuj <a href=%2$s>otworzyć mangę w przeglądarce internetowej</a> aby upewnić się, że jest dostępna w źródle<br>2. Jeśli jest dostępna, wyślij raport o błędzie do programistów.</string>
|
||||
</resources>
|
||||
@@ -1,44 +1,44 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<plurals name="pages">
|
||||
<item quantity="one">Всего %1$d страница</item>
|
||||
<item quantity="few">Всего %1$d страницы</item>
|
||||
<item quantity="many">Всего %1$d страниц</item>
|
||||
</plurals>
|
||||
<plurals name="items">
|
||||
<item quantity="one">%1$d элемент</item>
|
||||
<item quantity="few">%1$d элемента</item>
|
||||
<item quantity="many">%1$d элементов</item>
|
||||
</plurals>
|
||||
<plurals name="new_chapters">
|
||||
<item quantity="one">%1$d новая глава</item>
|
||||
<item quantity="few">%1$d новых главы</item>
|
||||
<item quantity="many">%1$d новых глав</item>
|
||||
</plurals>
|
||||
<plurals name="chapters">
|
||||
<item quantity="one">%1$d глава</item>
|
||||
<item quantity="few">%1$d главы</item>
|
||||
<item quantity="many">%1$d глав</item>
|
||||
</plurals>
|
||||
<plurals name="chapters_from_x">
|
||||
<item quantity="one">%1$d глава из %2$d</item>
|
||||
<item quantity="few">%1$d главы из %2$d</item>
|
||||
<item quantity="many">%1$d глав из %2$d</item>
|
||||
</plurals>
|
||||
|
||||
<plurals name="minutes_ago">
|
||||
<item quantity="one">%1$d минуту назад</item>
|
||||
<item quantity="few">%1$d минуты назад</item>
|
||||
<item quantity="many">%1$d минут назад</item>
|
||||
</plurals>
|
||||
<plurals name="hours_ago">
|
||||
<item quantity="one">%1$d час назад</item>
|
||||
<item quantity="few">%1$d часа назад</item>
|
||||
<item quantity="many">%1$d часов назад</item>
|
||||
</plurals>
|
||||
<plurals name="days_ago">
|
||||
<item quantity="one">%1$d день назад</item>
|
||||
<item quantity="few">%1$d дня назад</item>
|
||||
<item quantity="many">%1$d дней назад</item>
|
||||
</plurals>
|
||||
<plurals name="pages">
|
||||
<item quantity="one">Всего %1$d страница</item>
|
||||
<item quantity="few">Всего %1$d страницы</item>
|
||||
<item quantity="many">Всего %1$d страниц</item>
|
||||
</plurals>
|
||||
<plurals name="items">
|
||||
<item quantity="one">%1$d элемент</item>
|
||||
<item quantity="few">%1$d элемента</item>
|
||||
<item quantity="many">%1$d элементов</item>
|
||||
</plurals>
|
||||
<plurals name="new_chapters">
|
||||
<item quantity="one">%1$d новая глава</item>
|
||||
<item quantity="few">%1$d новые главы</item>
|
||||
<item quantity="many">%1$d новых глав</item>
|
||||
<item quantity="other">%1$d новых глав</item>
|
||||
</plurals>
|
||||
<plurals name="chapters">
|
||||
<item quantity="one">%1$d глава</item>
|
||||
<item quantity="few">%1$d главы</item>
|
||||
<item quantity="many">%1$d глав</item>
|
||||
</plurals>
|
||||
<plurals name="chapters_from_x">
|
||||
<item quantity="one">%1$d глава из %2$d</item>
|
||||
<item quantity="few">%1$d главы из %2$d</item>
|
||||
<item quantity="many">%1$d глав из %2$d</item>
|
||||
</plurals>
|
||||
<plurals name="minutes_ago">
|
||||
<item quantity="one">%1$d минуту назад</item>
|
||||
<item quantity="few">%1$d минуты назад</item>
|
||||
<item quantity="many">%1$d минут назад</item>
|
||||
</plurals>
|
||||
<plurals name="hours_ago">
|
||||
<item quantity="one">%1$d час назад</item>
|
||||
<item quantity="few">%1$d часа назад</item>
|
||||
<item quantity="many">%1$d часов назад</item>
|
||||
</plurals>
|
||||
<plurals name="days_ago">
|
||||
<item quantity="one">%1$d день назад</item>
|
||||
<item quantity="few">%1$d дня назад</item>
|
||||
<item quantity="many">%1$d дней назад</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
@@ -6,7 +6,7 @@
|
||||
<string name="favourites">Избранное</string>
|
||||
<string name="history">История</string>
|
||||
<string name="error_occurred">Произошла ошибка</string>
|
||||
<string name="network_error">Не удалось подключиться к интернету</string>
|
||||
<string name="network_error">Ошибка сети</string>
|
||||
<string name="details">Подробности</string>
|
||||
<string name="chapters">Главы</string>
|
||||
<string name="list">Список</string>
|
||||
@@ -396,4 +396,5 @@
|
||||
<string name="clear_new_chapters_counters">Также очистить информацию о новых главах</string>
|
||||
<string name="server_error">Внутренняя ошибка сервера (%1$d). Повторите попытку позже</string>
|
||||
<string name="compact">Компактно</string>
|
||||
<string name="source_disabled">Источник отключен</string>
|
||||
</resources>
|
||||
43
app/src/main/res/values-sr/plurals.xml
Normal file
43
app/src/main/res/values-sr/plurals.xml
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<plurals name="pages">
|
||||
<item quantity="one">Тотално %1$d странa</item>
|
||||
<item quantity="few">Тотално %1$d странице</item>
|
||||
<item quantity="other">Тотално %1$d странице</item>
|
||||
</plurals>
|
||||
<plurals name="items">
|
||||
<item quantity="one">%1$d ставке</item>
|
||||
<item quantity="few">%1$d ставки</item>
|
||||
<item quantity="other">%1$d ставка</item>
|
||||
</plurals>
|
||||
<plurals name="chapters_from_x">
|
||||
<item quantity="one">%1$d поглавља од %2$d</item>
|
||||
<item quantity="few">%1$d поглавља од %2$d</item>
|
||||
<item quantity="other">%1$d поглавља од %2$d</item>
|
||||
</plurals>
|
||||
<plurals name="minutes_ago">
|
||||
<item quantity="one">пре %1$d минута</item>
|
||||
<item quantity="few">пре %1$d минута</item>
|
||||
<item quantity="other">пре %1$d минута</item>
|
||||
</plurals>
|
||||
<plurals name="hours_ago">
|
||||
<item quantity="one">пре %1$d сата</item>
|
||||
<item quantity="few">пре %1$d сата</item>
|
||||
<item quantity="other">пре %1$d сата</item>
|
||||
</plurals>
|
||||
<plurals name="days_ago">
|
||||
<item quantity="one">пре %1$d дана</item>
|
||||
<item quantity="few">пре %1$d дана</item>
|
||||
<item quantity="other">пре %1$d дана</item>
|
||||
</plurals>
|
||||
<plurals name="new_chapters">
|
||||
<item quantity="one">%1$d нова поглавља</item>
|
||||
<item quantity="few">%1$d нових поглавља</item>
|
||||
<item quantity="other">%1$d нових поглавља</item>
|
||||
</plurals>
|
||||
<plurals name="chapters">
|
||||
<item quantity="one">%1$d поглављe</item>
|
||||
<item quantity="few">%1$d поглавља</item>
|
||||
<item quantity="other">%1$d поглавља</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
15
app/src/main/res/values-sr/strings.xml
Normal file
15
app/src/main/res/values-sr/strings.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="local_storage">Локално складиште</string>
|
||||
<string name="close_menu">Затвори мени</string>
|
||||
<string name="error_occurred">Грешка се појавила</string>
|
||||
<string name="open_menu">Отвори мени</string>
|
||||
<string name="favourites">Фаворити</string>
|
||||
<string name="history">Историја</string>
|
||||
<string name="network_error">Неуспешно повезивање са интернетом</string>
|
||||
<string name="details">Детаљи</string>
|
||||
<string name="chapters">Поглавља</string>
|
||||
<string name="list">Листа</string>
|
||||
<string name="detailed_list">Детаљна листа</string>
|
||||
<string name="grid">Табла</string>
|
||||
</resources>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="network_error">İnternete bağlı olduğunuzdan emin olunuz</string>
|
||||
<string name="network_error">Ağ hatası</string>
|
||||
<string name="close_menu">Menüyü kapat</string>
|
||||
<string name="open_menu">Menüyü aç</string>
|
||||
<string name="local_storage">Dahili Depolama</string>
|
||||
@@ -395,4 +395,5 @@
|
||||
<string name="saved_manga">Kaydedilen mangalar</string>
|
||||
<string name="history_shortcuts_summary">Uygulama simgesine uzun basarak son mangaları kullanılabilir hale getirin</string>
|
||||
<string name="reader_control_ltr_summary">Sağ kenara dokunulduğunda veya sağ tuşa basıldığında her zaman bir sonraki sayfaya geçilir</string>
|
||||
<string name="source_disabled">Kaynak devre dışı</string>
|
||||
</resources>
|
||||
4
app/src/main/res/values-v33/bools.xml
Normal file
4
app/src/main/res/values-v33/bools.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<bool name="com_samsung_android_icon_container_has_icon_container">false</bool>
|
||||
</resources>
|
||||
@@ -15,4 +15,10 @@
|
||||
<plurals name="days_ago">
|
||||
<item quantity="other">%1$d ngày trước</item>
|
||||
</plurals>
|
||||
<plurals name="chapters_from_x">
|
||||
<item quantity="other">%1$d chương từ %2$d</item>
|
||||
</plurals>
|
||||
<plurals name="pages">
|
||||
<item quantity="other">Tổng %1$d trang</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
@@ -5,7 +5,7 @@
|
||||
<string name="favourites">喜欢</string>
|
||||
<string name="history">历史</string>
|
||||
<string name="error_occurred">发生了一个错误</string>
|
||||
<string name="network_error">未能连接到互联网</string>
|
||||
<string name="network_error">网络错误</string>
|
||||
<string name="chapters">章节</string>
|
||||
<string name="list">列表</string>
|
||||
<string name="data_restored_with_errors">数据被恢复了,但有错误</string>
|
||||
@@ -31,7 +31,7 @@
|
||||
<string name="clear_cookies">清除cookies</string>
|
||||
<string name="chapters_checking_progress">检查新的章节: %1$d/%2$d</string>
|
||||
<string name="error_empty_name">你必须输入一个名称</string>
|
||||
<string name="new_sources_text">新的漫画来源</string>
|
||||
<string name="new_sources_text">有新的漫画源可用</string>
|
||||
<string name="suggestions_summary">根据你的喜好推荐漫画</string>
|
||||
<string name="suggestions_info">所有的数据都在这个设备上进行本地分析. 您的个人数据不会被转移到任何服务机构</string>
|
||||
<string name="never">从不</string>
|
||||
@@ -48,7 +48,7 @@
|
||||
<string name="detailed_list">详细列表</string>
|
||||
<string name="grid">网格</string>
|
||||
<string name="list_mode">列表模式</string>
|
||||
<string name="remote_sources">远程资源</string>
|
||||
<string name="remote_sources">远程源</string>
|
||||
<string name="loading_">加载中…</string>
|
||||
<string name="computing_">计算中…</string>
|
||||
<string name="chapter_d_of_d">%1$d/%2$d章节</string>
|
||||
@@ -230,7 +230,7 @@
|
||||
<string name="about_feedback_4pda">关于4PDA主题</string>
|
||||
<string name="auth_complete">授权</string>
|
||||
<string name="auth_not_supported_by">不支持在%s上登录</string>
|
||||
<string name="text_clear_cookies_prompt">你将被从所有来源中注销</string>
|
||||
<string name="text_clear_cookies_prompt">你将退出登录所有来源</string>
|
||||
<string name="genres">类型</string>
|
||||
<string name="state_ongoing">连载中</string>
|
||||
<string name="state_finished">已完结</string>
|
||||
@@ -395,4 +395,5 @@
|
||||
<string name="clear_new_chapters_counters">同样清除新章节信息</string>
|
||||
<string name="server_error">服务器端错误 (%1$d)。请稍后再试</string>
|
||||
<string name="compact">紧凑</string>
|
||||
<string name="source_disabled">已禁用图源</string>
|
||||
</resources>
|
||||
@@ -3,4 +3,5 @@
|
||||
<bool name="is_tablet">false</bool>
|
||||
<bool name="light_status_bar">true</bool>
|
||||
<bool name="light_navigation_bar">false</bool>
|
||||
</resources>
|
||||
<bool name="com_samsung_android_icon_container_has_icon_container">true</bool>
|
||||
</resources>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<string name="favourites">Favourites</string>
|
||||
<string name="history">History</string>
|
||||
<string name="error_occurred">An error occurred</string>
|
||||
<string name="network_error">Could not connect to the Internet</string>
|
||||
<string name="network_error">Network error</string>
|
||||
<string name="details">Details</string>
|
||||
<string name="chapters">Chapters</string>
|
||||
<string name="list">List</string>
|
||||
@@ -399,4 +399,5 @@
|
||||
<string name="clear_new_chapters_counters">Also clear information about new chapters</string>
|
||||
<string name="compact">Compact</string>
|
||||
<string name="mal" translatable="false">MyAnimeList</string>
|
||||
<string name="source_disabled">Source disabled</string>
|
||||
</resources>
|
||||
|
||||
29
app/src/main/res/xml/locales.xml
Normal file
29
app/src/main/res/xml/locales.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<locale-config
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<locale android:name="en" />
|
||||
<locale android:name="ar" />
|
||||
<locale android:name="be" />
|
||||
<locale android:name="bn" />
|
||||
<locale android:name="de" />
|
||||
<locale android:name="el" />
|
||||
<locale android:name="es" />
|
||||
<locale android:name="fa" />
|
||||
<locale android:name="fi" />
|
||||
<locale android:name="fr" />
|
||||
<locale android:name="in" />
|
||||
<locale android:name="it" />
|
||||
<locale android:name="ja" />
|
||||
<locale android:name="nb-rNO" />
|
||||
<locale android:name="pl" />
|
||||
<locale android:name="pt" />
|
||||
<locale android:name="pt-rBR" />
|
||||
<locale android:name="ru" />
|
||||
<locale android:name="si" />
|
||||
<locale android:name="sr" />
|
||||
<locale android:name="sv" />
|
||||
<locale android:name="tr" />
|
||||
<locale android:name="uk" />
|
||||
<locale android:name="zh-rCN" />
|
||||
<locale android:name="zh-rTW" />
|
||||
</locale-config>
|
||||
Reference in New Issue
Block a user