Merge branch 'devel' of github.com:nv95/Kotatsu into feature/suggestions

This commit is contained in:
Koitharu
2022-02-27 16:32:32 +02:00
105 changed files with 1387 additions and 1142 deletions

View File

@@ -5,29 +5,29 @@ import android.net.Uri
import android.os.Bundle
import org.koitharu.kotatsu.core.model.Manga
class MangaIntent(
class MangaIntent private constructor(
val manga: Manga?,
val mangaId: Long,
val uri: Uri?
val uri: Uri?,
) {
constructor(intent: Intent?) : this(
manga = intent?.getParcelableExtra(KEY_MANGA),
mangaId = intent?.getLongExtra(KEY_ID, ID_NONE) ?: ID_NONE,
uri = intent?.data
)
constructor(args: Bundle?) : this(
manga = args?.getParcelable(KEY_MANGA),
mangaId = args?.getLong(KEY_ID, ID_NONE) ?: ID_NONE,
uri = null
)
companion object {
fun from(intent: Intent?) = MangaIntent(
manga = intent?.getParcelableExtra(KEY_MANGA),
mangaId = intent?.getLongExtra(KEY_ID, ID_NONE) ?: ID_NONE,
uri = intent?.data
)
fun from(args: Bundle?) = MangaIntent(
manga = args?.getParcelable(KEY_MANGA),
mangaId = args?.getLong(KEY_ID, ID_NONE) ?: ID_NONE,
uri = null
)
const val ID_NONE = 0L
const val KEY_MANGA = "manga"
const val KEY_ID = "id"
}
}
}

View File

@@ -1,24 +0,0 @@
package org.koitharu.kotatsu.base.domain
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.prefs.AppSettings
object MangaProviderFactory {
fun getSources(settings: AppSettings, includeHidden: Boolean): List<MangaSource> {
val list = MangaSource.values().toList() - MangaSource.LOCAL
val order = settings.sourcesOrder
val sorted = list.sortedBy { x ->
val e = order.indexOf(x.ordinal)
if (e == -1) order.size + x.ordinal else e
}
return if (includeHidden) {
sorted
} else {
val hidden = settings.hiddenSources
sorted.filterNot { x ->
x.name in hidden
}
}
}
}

View File

@@ -5,7 +5,6 @@ import android.net.Uri
import android.util.Size
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runInterruptible
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import org.koin.core.component.KoinComponent
@@ -14,7 +13,6 @@ import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.core.model.MangaPage
import org.koitharu.kotatsu.core.network.CommonHeaders
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.utils.CacheUtils
import org.koitharu.kotatsu.utils.ext.await
import org.koitharu.kotatsu.utils.ext.medianOrNull
import java.io.InputStream
@@ -40,15 +38,14 @@ object MangaUtils : KoinComponent {
}
}
} else {
val client = get<OkHttpClient>()
val request = Request.Builder()
.url(url)
.get()
.header(CommonHeaders.REFERER, page.referer)
.cacheControl(CacheUtils.CONTROL_DISABLED)
.cacheControl(CommonHeaders.CACHE_CONTROL_DISABLED)
.build()
client.newCall(request).await().use {
withContext(Dispatchers.IO) {
get<OkHttpClient>().newCall(request).await().use {
runInterruptible(Dispatchers.IO) {
getBitmapSize(it.body?.byteStream())
}
}
@@ -66,10 +63,10 @@ object MangaUtils : KoinComponent {
val options = BitmapFactory.Options().apply {
inJustDecodeBounds = true
}
BitmapFactory.decodeStream(input, null, options)
BitmapFactory.decodeStream(input, null, options)?.recycle()
val imageHeight: Int = options.outHeight
val imageWidth: Int = options.outWidth
check(imageHeight > 0 && imageWidth > 0)
return Size(imageWidth, imageHeight)
}
}
}

View File

@@ -17,8 +17,7 @@ abstract class AlertDialogFragment<B : ViewBinding> : DialogFragment() {
get() = checkNotNull(viewBinding)
final override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val inflater = activity?.layoutInflater ?: LayoutInflater.from(requireContext())
val binding = onInflateView(inflater, null)
val binding = onInflateView(layoutInflater, null)
viewBinding = binding
return MaterialAlertDialogBuilder(requireContext(), theme)
.setView(binding.root)
@@ -43,4 +42,4 @@ abstract class AlertDialogFragment<B : ViewBinding> : DialogFragment() {
protected fun bindingOrNull(): B? = viewBinding
protected abstract fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): B
}
}

View File

@@ -35,9 +35,10 @@ abstract class BaseActivity<B : ViewBinding> : AppCompatActivity(), OnApplyWindo
private var lastInsets: Insets = Insets.NONE
override fun onCreate(savedInstanceState: Bundle?) {
val settings = get<AppSettings>()
when {
get<AppSettings>().isAmoledTheme -> setTheme(R.style.ThemeOverlay_Kotatsu_AMOLED)
get<AppSettings>().isDynamicTheme -> setTheme(R.style.Theme_Kotatsu_Monet)
settings.isAmoledTheme -> setTheme(R.style.ThemeOverlay_Kotatsu_AMOLED)
settings.isDynamicTheme -> setTheme(R.style.Theme_Kotatsu_Monet)
}
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
@@ -130,4 +131,4 @@ abstract class BaseActivity<B : ViewBinding> : AppCompatActivity(), OnApplyWindo
super.onBackPressed()
}
}
}
}

View File

@@ -10,8 +10,7 @@ import androidx.viewbinding.ViewBinding
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import org.koitharu.kotatsu.R
abstract class BaseBottomSheet<B : ViewBinding> :
BottomSheetDialogFragment() {
abstract class BaseBottomSheet<B : ViewBinding> : BottomSheetDialogFragment() {
private var viewBinding: B? = null
@@ -40,4 +39,4 @@ abstract class BaseBottomSheet<B : ViewBinding> :
}
protected abstract fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): B
}
}

View File

@@ -11,8 +11,8 @@ import androidx.preference.PreferenceFragmentCompat
import org.koin.android.ext.android.inject
import org.koitharu.kotatsu.core.prefs.AppSettings
abstract class BasePreferenceFragment(@StringRes private val titleId: Int) :
PreferenceFragmentCompat(), OnApplyWindowInsetsListener {
abstract class BasePreferenceFragment(@StringRes private val titleId: Int) : PreferenceFragmentCompat(),
OnApplyWindowInsetsListener {
protected val settings by inject<AppSettings>(mode = LazyThreadSafetyMode.NONE)
@@ -36,4 +36,4 @@ abstract class BasePreferenceFragment(@StringRes private val titleId: Int) :
)
return insets
}
}
}

View File

@@ -8,10 +8,10 @@ import android.widget.BaseAdapter
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.runBlocking
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.databinding.ItemStorageBinding
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
import org.koitharu.kotatsu.utils.ext.getStorageName
import org.koitharu.kotatsu.local.data.LocalStorageManager
import org.koitharu.kotatsu.utils.ext.inflate
import java.io.File
@@ -20,15 +20,18 @@ class StorageSelectDialog private constructor(private val delegate: AlertDialog)
fun show() = delegate.show()
class Builder(context: Context, defaultValue: File?, listener: OnStorageSelectListener) {
class Builder(context: Context, storageManager: LocalStorageManager, listener: OnStorageSelectListener) {
private val adapter = VolumesAdapter(context)
private val adapter = VolumesAdapter(storageManager)
private val delegate = MaterialAlertDialogBuilder(context)
init {
if (adapter.isEmpty) {
delegate.setMessage(R.string.cannot_find_available_storage)
} else {
val defaultValue = runBlocking {
storageManager.getDefaultWriteableDir()
}
adapter.selectedItemPosition = adapter.volumes.indexOfFirst {
it.first.canonicalPath == defaultValue?.canonicalPath
}
@@ -57,10 +60,10 @@ class StorageSelectDialog private constructor(private val delegate: AlertDialog)
fun create() = StorageSelectDialog(delegate.create())
}
private class VolumesAdapter(context: Context) : BaseAdapter() {
private class VolumesAdapter(storageManager: LocalStorageManager) : BaseAdapter() {
var selectedItemPosition: Int = -1
val volumes = getAvailableVolumes(context)
val volumes = getAvailableVolumes(storageManager)
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = convertView ?: parent.inflate(R.layout.item_storage)
@@ -82,9 +85,11 @@ class StorageSelectDialog private constructor(private val delegate: AlertDialog)
override fun hasStableIds() = true
private fun getAvailableVolumes(context: Context): List<Pair<File, String>> {
return LocalMangaRepository.getAvailableStorageDirs(context).map {
it to it.getStorageName(context)
private fun getAvailableVolumes(storageManager: LocalStorageManager): List<Pair<File, String>> {
return runBlocking {
storageManager.getWriteableDirs().map {
it to storageManager.getStorageDisplayName(it)
}
}
}
}

View File

@@ -1,5 +1,7 @@
package org.koitharu.kotatsu.core.network
import okhttp3.CacheControl
object CommonHeaders {
const val REFERER = "Referer"
@@ -7,4 +9,7 @@ object CommonHeaders {
const val ACCEPT = "Accept"
const val CONTENT_DISPOSITION = "Content-Disposition"
const val COOKIE = "Cookie"
}
val CACHE_CONTROL_DISABLED: CacheControl
get() = CacheControl.Builder().noStore().build()
}

View File

@@ -2,27 +2,24 @@ package org.koitharu.kotatsu.core.network
import okhttp3.CookieJar
import okhttp3.OkHttpClient
import org.koin.android.ext.koin.androidContext
import org.koin.core.qualifier.named
import org.koin.dsl.bind
import org.koin.dsl.module
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.base.domain.MangaLoaderContext
import org.koitharu.kotatsu.utils.CacheUtils
import org.koitharu.kotatsu.local.data.LocalStorageManager
import org.koitharu.kotatsu.utils.DownloadManagerHelper
import java.util.concurrent.TimeUnit
val networkModule
get() = module {
single { AndroidCookieJar() } bind CookieJar::class
single(named(CacheUtils.QUALIFIER_HTTP)) { CacheUtils.createHttpCache(androidContext()) }
single {
OkHttpClient.Builder().apply {
connectTimeout(20, TimeUnit.SECONDS)
readTimeout(60, TimeUnit.SECONDS)
writeTimeout(20, TimeUnit.SECONDS)
cookieJar(get())
cache(get(named(CacheUtils.QUALIFIER_HTTP)))
cache(get<LocalStorageManager>().createHttpCache())
addInterceptor(UserAgentInterceptor())
addInterceptor(CloudFlareInterceptor())
if (BuildConfig.DEBUG) {
@@ -32,4 +29,4 @@ val networkModule
}
factory { DownloadManagerHelper(get(), get()) }
single { MangaLoaderContext(get(), get()) }
}
}

View File

@@ -64,10 +64,10 @@ abstract class RemoteMangaRepository(
protected fun generateUid(url: String): Long {
var h = 1125899906842597L
source.name.forEach { c ->
h = 31 * h + c.toLong()
h = 31 * h + c.code
}
url.forEach { c ->
h = 31 * h + c.toLong()
h = 31 * h + c.code
}
return h
}
@@ -75,7 +75,7 @@ abstract class RemoteMangaRepository(
protected fun generateUid(id: Long): Long {
var h = 1125899906842597L
source.name.forEach { c ->
h = 31 * h + c.toLong()
h = 31 * h + c.code
}
h = 31 * h + id
return h
@@ -84,4 +84,4 @@ abstract class RemoteMangaRepository(
protected fun parseFailed(message: String? = null): Nothing {
throw ParseException(message)
}
}
}

View File

@@ -9,6 +9,7 @@ import org.koitharu.kotatsu.core.model.*
import org.koitharu.kotatsu.core.parser.MangaRepositoryAuthProvider
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
import org.koitharu.kotatsu.utils.ext.*
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.*
@@ -32,7 +33,7 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
offset: Int,
query: String?,
tags: Set<MangaTag>?,
sortOrder: SortOrder?
sortOrder: SortOrder?,
): List<Manga> {
copyCookies()
val domain = getDomain()
@@ -97,9 +98,7 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
}
val branchId = content.getJSONArray("branches").optJSONObject(0)
?.getLong("id") ?: throw ParseException("No branches found")
val chapters = loaderContext.httpGet(
url = "https://api.$domain/api/titles/chapters/?branch_id=$branchId"
).parseJson().getJSONArray("content")
val chapters = grabChapters(domain, branchId)
val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US)
return manga.copy(
description = content.getString("description"),
@@ -118,11 +117,11 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
chapters = chapters.mapIndexed { i, jo ->
val id = jo.getLong("id")
val name = jo.getString("name").toTitleCase(Locale.ROOT)
val publishers = jo.getJSONArray("publishers")
val publishers = jo.optJSONArray("publishers")
MangaChapter(
id = generateUid(id),
url = "/api/titles/chapters/$id/",
number = chapters.length() - i,
number = chapters.size - i,
name = buildString {
append("Том ")
append(jo.optString("tome", "0"))
@@ -135,7 +134,7 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
}
},
uploadDate = dateFormat.tryParse(jo.getString("upload_date")),
scanlator = publishers.optJSONObject(0)?.getStringOrNull("name"),
scanlator = publishers?.optJSONObject(0)?.getStringOrNull("name"),
source = MangaSource.REMANGA,
branch = null,
)
@@ -146,16 +145,28 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
val referer = "https://${getDomain()}/"
val content = loaderContext.httpGet(chapter.url.withDomain(subdomain = "api")).parseJson()
.getJSONObject("content").getJSONArray("pages")
val pages = ArrayList<MangaPage>(content.length())
for (i in 0 until content.length()) {
when (val item = content.get(i)) {
is JSONObject -> pages += parsePage(item, referer)
is JSONArray -> item.mapTo(pages) { parsePage(it, referer) }
.getJSONObject("content")
val pages = content.optJSONArray("pages")
if (pages == null) {
val pubDate = content.getStringOrNull("pub_date")?.let {
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US).tryParse(it)
}
if (pubDate != null && pubDate > System.currentTimeMillis()) {
val at = SimpleDateFormat.getDateInstance(DateFormat.LONG).format(Date(pubDate))
parseFailed("Глава станет доступной $at")
} else {
parseFailed("Глава недоступна")
}
}
val result = ArrayList<MangaPage>(pages.length())
for (i in 0 until pages.length()) {
when (val item = pages.get(i)) {
is JSONObject -> result += parsePage(item, referer)
is JSONArray -> item.mapTo(result) { parsePage(it, referer) }
else -> throw ParseException("Unknown json item $item")
}
}
return pages
return result
}
override suspend fun getTags(): Set<MangaTag> {
@@ -198,6 +209,26 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito
source = source,
)
private suspend fun grabChapters(domain: String, branchId: Long): List<JSONObject> {
val result = ArrayList<JSONObject>(100)
var page = 1
while (true) {
val content = loaderContext.httpGet(
"https://api.$domain/api/titles/chapters/?branch_id=$branchId&page=$page&count=100"
).parseJson().getJSONArray("content")
val len = content.length()
if (len == 0) {
break
}
result.ensureCapacity(result.size + len)
for (i in 0 until len) {
result.add(content.getJSONObject(i))
}
page++
}
return result
}
private companion object {
const val PAGE_SIZE = 30

View File

@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.core.prefs
import android.content.Context
import android.content.SharedPreferences
import android.net.Uri
import android.os.Build
import android.provider.Settings
import androidx.appcompat.app.AppCompatDelegate
@@ -12,133 +13,149 @@ import com.google.android.material.color.DynamicColors
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.flow.callbackFlow
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.model.ZoomMode
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
import org.koitharu.kotatsu.utils.delegates.prefs.*
import org.koitharu.kotatsu.utils.ext.toUriOrNull
import java.io.File
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.*
class AppSettings private constructor(private val prefs: SharedPreferences) :
SharedPreferences by prefs {
class AppSettings(context: Context) {
constructor(context: Context) : this(
PreferenceManager.getDefaultSharedPreferences(context)
)
private val prefs = PreferenceManager.getDefaultSharedPreferences(context)
var listMode by EnumPreferenceDelegate(
ListMode::class.java,
KEY_LIST_MODE,
ListMode.DETAILED_LIST
)
var listMode: ListMode
get() = prefs.getString(KEY_LIST_MODE, null)?.findEnumValue(ListMode.values()) ?: ListMode.DETAILED_LIST
set(value) = prefs.edit { putString(KEY_LIST_MODE, value.name) }
var defaultSection by IntEnumPreferenceDelegate(
AppSection::class.java,
KEY_APP_SECTION,
AppSection.HISTORY
)
var defaultSection: AppSection
get() = prefs.getString(KEY_APP_SECTION, null)?.findEnumValue(AppSection.values()) ?: AppSection.HISTORY
set(value) = prefs.edit { putString(KEY_APP_SECTION, value.name) }
val theme by StringIntPreferenceDelegate(
KEY_THEME,
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
)
val theme: Int
get() = prefs.getString(KEY_THEME, null)?.toIntOrNull() ?: AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
val isDynamicTheme by BoolPreferenceDelegate(KEY_DYNAMIC_THEME, defaultValue = false)
val isDynamicTheme: Boolean
get() = prefs.getBoolean(KEY_DYNAMIC_THEME, false)
val isAmoledTheme by BoolPreferenceDelegate(KEY_THEME_AMOLED, defaultValue = false)
val isAmoledTheme: Boolean
get() = prefs.getBoolean(KEY_THEME_AMOLED, false)
val isToolbarHideWhenScrolling by BoolPreferenceDelegate(KEY_HIDE_TOOLBAR, defaultValue = true)
val isToolbarHideWhenScrolling: Boolean
get() = prefs.getBoolean(KEY_HIDE_TOOLBAR, true)
var gridSize by IntPreferenceDelegate(KEY_GRID_SIZE, defaultValue = 100)
var gridSize: Int
get() = prefs.getInt(KEY_GRID_SIZE, 100)
set(value) = prefs.edit { putInt(KEY_GRID_SIZE, value) }
val readerPageSwitch by StringSetPreferenceDelegate(
KEY_READER_SWITCHERS,
arraySetOf(PAGE_SWITCH_TAPS)
)
val readerPageSwitch: Set<String>
get() = prefs.getStringSet(KEY_READER_SWITCHERS, null) ?: setOf(PAGE_SWITCH_TAPS)
var isTrafficWarningEnabled by BoolPreferenceDelegate(KEY_TRAFFIC_WARNING, defaultValue = true)
var isTrafficWarningEnabled: Boolean
get() = prefs.getBoolean(KEY_TRAFFIC_WARNING, true)
set(value) = prefs.edit { putBoolean(KEY_TRAFFIC_WARNING, value) }
val appUpdateAuto by BoolPreferenceDelegate(KEY_APP_UPDATE_AUTO, defaultValue = true)
val appUpdateAuto: Boolean
get() = prefs.getBoolean(KEY_APP_UPDATE_AUTO, true)
var appUpdate by LongPreferenceDelegate(KEY_APP_UPDATE, defaultValue = 0L)
var appUpdate: Long
get() = prefs.getLong(KEY_APP_UPDATE, 0L)
set(value) = prefs.edit { putLong(KEY_APP_UPDATE, value) }
val trackerNotifications by BoolPreferenceDelegate(
KEY_TRACKER_NOTIFICATIONS,
defaultValue = true
)
val trackerNotifications: Boolean
get() = prefs.getBoolean(KEY_TRACKER_NOTIFICATIONS, true)
var notificationSound by StringPreferenceDelegate(
KEY_NOTIFICATIONS_SOUND,
Settings.System.DEFAULT_NOTIFICATION_URI.toString()
)
var notificationSound: Uri
get() = prefs.getString(KEY_NOTIFICATIONS_SOUND, null)?.toUriOrNull()
?: Settings.System.DEFAULT_NOTIFICATION_URI
set(value) = prefs.edit { putString(KEY_NOTIFICATIONS_SOUND, value.toString()) }
val notificationVibrate by BoolPreferenceDelegate(KEY_NOTIFICATIONS_VIBRATE, false)
val notificationVibrate: Boolean
get() = prefs.getBoolean(KEY_NOTIFICATIONS_VIBRATE, false)
val notificationLight by BoolPreferenceDelegate(KEY_NOTIFICATIONS_LIGHT, true)
val notificationLight: Boolean
get() = prefs.getBoolean(KEY_NOTIFICATIONS_LIGHT, true)
val readerAnimation by BoolPreferenceDelegate(KEY_READER_ANIMATION, false)
val readerAnimation: Boolean
get() = prefs.getBoolean(KEY_READER_ANIMATION, false)
val isPreferRtlReader by BoolPreferenceDelegate(KEY_READER_PREFER_RTL, false)
val isPreferRtlReader: Boolean
get() = prefs.getBoolean(KEY_READER_PREFER_RTL, false)
var historyGrouping by BoolPreferenceDelegate(KEY_HISTORY_GROUPING, true)
var historyGrouping: Boolean
get() = prefs.getBoolean(KEY_HISTORY_GROUPING, true)
set(value) = prefs.edit { putBoolean(KEY_HISTORY_GROUPING, value) }
var isHistoryExcludeNsfw by BoolPreferenceDelegate(KEY_HISTORY_EXCLUDE_NSFW, false)
val isHistoryExcludeNsfw: Boolean
get() = prefs.getBoolean(KEY_HISTORY_EXCLUDE_NSFW, false)
var chaptersReverse by BoolPreferenceDelegate(KEY_REVERSE_CHAPTERS, false)
var chaptersReverse: Boolean
get() = prefs.getBoolean(KEY_REVERSE_CHAPTERS, false)
set(value) = prefs.edit { putBoolean(KEY_REVERSE_CHAPTERS, value) }
val zoomMode by EnumPreferenceDelegate(
ZoomMode::class.java,
KEY_ZOOM_MODE,
ZoomMode.FIT_CENTER
)
val zoomMode: ZoomMode
get() = prefs.getString(KEY_ZOOM_MODE, null)?.findEnumValue(ZoomMode.values()) ?: ZoomMode.FIT_CENTER
val trackSources by StringSetPreferenceDelegate(
KEY_TRACK_SOURCES,
arraySetOf(TRACK_FAVOURITES, TRACK_HISTORY)
)
val trackSources: Set<String>
get() = prefs.getStringSet(KEY_TRACK_SOURCES, null) ?: arraySetOf(TRACK_FAVOURITES, TRACK_HISTORY)
var appPassword by NullableStringPreferenceDelegate(KEY_APP_PASSWORD)
private var sourcesOrderStr by NullableStringPreferenceDelegate(KEY_SOURCES_ORDER)
var appPassword: String?
get() = prefs.getString(KEY_APP_PASSWORD, null)
set(value) = prefs.edit { if (value != null) putString(KEY_APP_PASSWORD, value) else remove(KEY_APP_PASSWORD) }
var sourcesOrder: List<Int>
get() = sourcesOrderStr?.split('|')?.mapNotNull(String::toIntOrNull).orEmpty()
set(value) {
sourcesOrderStr = value.joinToString("|")
get() = prefs.getString(KEY_SOURCES_ORDER, null)
?.split('|')
?.mapNotNull(String::toIntOrNull)
.orEmpty()
set(value) = prefs.edit {
putString(KEY_SOURCES_ORDER, value.joinToString("|"))
}
var hiddenSources by StringSetPreferenceDelegate(KEY_SOURCES_HIDDEN)
var hiddenSources: Set<String>
get() = prefs.getStringSet(KEY_SOURCES_HIDDEN, null) ?: emptySet()
set(value) = prefs.edit { putStringSet(KEY_SOURCES_HIDDEN, value) }
val isSourcesSelected: Boolean
get() = KEY_SOURCES_HIDDEN in prefs
val isPagesNumbersEnabled by BoolPreferenceDelegate(KEY_PAGES_NUMBERS, false)
val isPagesNumbersEnabled: Boolean
get() = prefs.getBoolean(KEY_PAGES_NUMBERS, false)
fun getStorageDir(context: Context): File? {
val value = prefs.getString(KEY_LOCAL_STORAGE, null)?.let {
var mangaStorageDir: File?
get() = prefs.getString(KEY_LOCAL_STORAGE, null)?.let {
File(it)
}?.takeIf { it.exists() && it.canWrite() }
return value ?: LocalMangaRepository.getFallbackStorageDir(context)
}
fun setStorageDir(context: Context, file: File?) {
prefs.edit {
if (file == null) {
}?.takeIf { it.exists() }
set(value) = prefs.edit {
if (value == null) {
remove(KEY_LOCAL_STORAGE)
} else {
putString(KEY_LOCAL_STORAGE, file.path)
putString(KEY_LOCAL_STORAGE, value.path)
}
}
}
fun dateFormat(format: String? = prefs.getString(KEY_DATE_FORMAT, "")): DateFormat =
fun getDateFormat(format: String = prefs.getString(KEY_DATE_FORMAT, "").orEmpty()): DateFormat =
when (format) {
"" -> DateFormat.getDateInstance(DateFormat.SHORT)
else -> SimpleDateFormat(format, Locale.getDefault())
}
@Deprecated("Use observe()")
fun getMangaSources(includeHidden: Boolean): List<MangaSource> {
val list = MangaSource.values().toMutableList()
list.remove(MangaSource.LOCAL)
val order = sourcesOrder
list.sortBy { x ->
val e = order.indexOf(x.ordinal)
if (e == -1) order.size + x.ordinal else e
}
if (!includeHidden) {
val hidden = hiddenSources
list.removeAll { x -> x.name in hidden }
}
return list
}
fun subscribe(listener: SharedPreferences.OnSharedPreferenceChangeListener) {
prefs.registerOnSharedPreferenceChangeListener(listener)
}
@@ -157,6 +174,10 @@ class AppSettings private constructor(private val prefs: SharedPreferences) :
}
}
private fun <E : Enum<E>> String.findEnumValue(values: Array<E>): E? {
return values.find { it.name == this }
}
companion object {
const val PAGE_SWITCH_TAPS = "taps"
@@ -166,7 +187,7 @@ class AppSettings private constructor(private val prefs: SharedPreferences) :
const val TRACK_FAVOURITES = "favourites"
const val KEY_LIST_MODE = "list_mode_2"
const val KEY_APP_SECTION = "app_section"
const val KEY_APP_SECTION = "app_section_2"
const val KEY_THEME = "theme"
const val KEY_DYNAMIC_THEME = "dynamic_theme"
const val KEY_THEME_AMOLED = "amoled_theme"
@@ -210,14 +231,15 @@ class AppSettings private constructor(private val prefs: SharedPreferences) :
const val KEY_APP_TRANSLATION = "about_app_translation"
const val KEY_APP_GRATITUDES = "about_gratitudes"
const val KEY_FEEDBACK_4PDA = "about_feedback_4pda"
const val KEY_FEEDBACK_DISCORD = "about_feedback_discord"
const val KEY_FEEDBACK_GITHUB = "about_feedback_github"
const val KEY_SUPPORT_DEVELOPER = "about_support_developer"
val isDynamicColorAvailable: Boolean
get() = DynamicColors.isDynamicColorAvailable() ||
(isSamsung && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
(isSamsung && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
private val isSamsung
get() = Build.MANUFACTURER.equals("samsung", ignoreCase = true)
}
}
}

View File

@@ -1,26 +1,15 @@
package org.koitharu.kotatsu.core.prefs
import android.content.Context
import android.content.SharedPreferences
import org.koitharu.kotatsu.utils.delegates.prefs.LongPreferenceDelegate
import androidx.core.content.edit
class AppWidgetConfig private constructor(
private val prefs: SharedPreferences,
val widgetId: Int
) : SharedPreferences by prefs {
private const val CATEGORY_ID = "cat_id"
var categoryId by LongPreferenceDelegate(CATEGORY_ID, 0L)
class AppWidgetConfig(context: Context, val widgetId: Int) {
companion object {
private val prefs = context.getSharedPreferences("appwidget_$widgetId", Context.MODE_PRIVATE)
private const val CATEGORY_ID = "cat_id"
fun getInstance(context: Context, widgetId: Int) = AppWidgetConfig(
context.getSharedPreferences(
"appwidget_$widgetId",
Context.MODE_PRIVATE
), widgetId
)
}
}
var categoryId: Long
get() = prefs.getLong(CATEGORY_ID, 0L)
set(value) = prefs.edit { putLong(CATEGORY_ID, value) }
}

View File

@@ -10,4 +10,4 @@ enum class ReaderMode(val id: Int) {
fun valueOf(id: Int) = values().firstOrNull { it.id == id }
}
}
}

View File

@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.core.ui
import coil.ComponentRegistry
import coil.ImageLoader
import coil.util.CoilUtils
import okhttp3.OkHttpClient
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module
@@ -11,8 +12,11 @@ import org.koitharu.kotatsu.local.data.CbzFetcher
val uiModule
get() = module {
single {
val httpClient = get<OkHttpClient>().newBuilder()
.cache(CoilUtils.createDefaultCache(androidContext()))
.build()
ImageLoader.Builder(androidContext())
.okHttpClient(get<OkHttpClient>())
.okHttpClient(httpClient)
.componentRegistry(
ComponentRegistry.Builder()
.add(CbzFetcher())

View File

@@ -47,7 +47,7 @@ class DetailsActivity : BaseActivity<ActivityDetailsBinding>(),
TabLayoutMediator.TabConfigurationStrategy {
private val viewModel by viewModel<DetailsViewModel> {
parametersOf(MangaIntent.from(intent))
parametersOf(MangaIntent(intent))
}
override fun onCreate(savedInstanceState: Bundle?) {
@@ -280,4 +280,4 @@ class DetailsActivity : BaseActivity<ActivityDetailsBinding>(),
.putExtra(MangaIntent.KEY_ID, mangaId)
}
}
}
}

View File

@@ -15,9 +15,7 @@ import androidx.core.view.updatePadding
import coil.ImageLoader
import coil.request.ImageRequest
import coil.util.CoilUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
import org.koitharu.kotatsu.R
@@ -33,7 +31,7 @@ import org.koitharu.kotatsu.image.ui.ImageActivity
import org.koitharu.kotatsu.reader.ui.ReaderActivity
import org.koitharu.kotatsu.reader.ui.ReaderState
import org.koitharu.kotatsu.search.ui.SearchActivity
import org.koitharu.kotatsu.utils.FileSizeUtils
import org.koitharu.kotatsu.utils.FileSize
import org.koitharu.kotatsu.utils.ext.*
class DetailsFragment : BaseFragment<FragmentDetailsBinding>(), View.OnClickListener,
@@ -114,10 +112,8 @@ class DetailsFragment : BaseFragment<FragmentDetailsBinding>(), View.OnClickList
val file = manga.url.toUri().toFileOrNull()
if (file != null) {
viewLifecycleScope.launch {
val size = withContext(Dispatchers.IO) {
file.length()
}
textViewSize.text = FileSizeUtils.formatBytes(requireContext(), size)
val size = file.computeSize()
textViewSize.text = FileSize.BYTES.format(requireContext(), size)
}
sizeContainer.isVisible = true
} else {
@@ -270,4 +266,4 @@ class DetailsFragment : BaseFragment<FragmentDetailsBinding>(), View.OnClickList
.lifecycle(viewLifecycleOwner)
.enqueueWith(coil)
}
}
}

View File

@@ -164,7 +164,7 @@ class DetailsViewModel(
branch: String?,
): List<ChapterListItem> {
val result = ArrayList<ChapterListItem>(chapters.size)
val dateFormat = settings.dateFormat()
val dateFormat = settings.getDateFormat()
val currentIndex = chapters.indexOfFirst { it.id == currentId }
val firstNewIndex = chapters.size - newCount
val downloadedIds = downloadedChapters?.mapToSet { it.id }
@@ -196,7 +196,7 @@ class DetailsViewModel(
val result = ArrayList<ChapterListItem>(sourceChapters.size)
val currentIndex = sourceChapters.indexOfFirst { it.id == currentId }
val firstNewIndex = sourceChapters.size - newCount
val dateFormat = settings.dateFormat()
val dateFormat = settings.getDateFormat()
for (i in sourceChapters.indices) {
val chapter = sourceChapters[i]
if (chapter.branch != branch) {
@@ -253,4 +253,4 @@ class DetailsViewModel(
}
return groups.maxByOrNull { it.value.size }?.key
}
}
}

View File

@@ -18,11 +18,9 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.network.CommonHeaders
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.local.data.MangaZip
import org.koitharu.kotatsu.local.data.PagesCache
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
import org.koitharu.kotatsu.utils.CacheUtils
import org.koitharu.kotatsu.utils.ext.await
import org.koitharu.kotatsu.utils.ext.deleteAwait
import org.koitharu.kotatsu.utils.ext.waitForNetwork
@@ -30,7 +28,6 @@ import java.io.File
class DownloadManager(
private val context: Context,
private val settings: AppSettings,
private val imageLoader: ImageLoader,
private val okHttp: OkHttpClient,
private val cache: PagesCache,
@@ -50,7 +47,7 @@ class DownloadManager(
fun downloadManga(manga: Manga, chaptersIds: Set<Long>?, startId: Int) = flow<State> {
emit(State.Preparing(startId, manga, null))
var cover: Drawable? = null
val destination = settings.getStorageDir(context)
val destination = localMangaRepository.getOutputDir()
checkNotNull(destination) { context.getString(R.string.cannot_find_available_storage) }
var output: MangaZip? = null
try {
@@ -136,7 +133,7 @@ class DownloadManager(
val request = Request.Builder()
.url(url)
.header(CommonHeaders.REFERER, referer)
.cacheControl(CacheUtils.CONTROL_DISABLED)
.cacheControl(CommonHeaders.CACHE_CONTROL_DISABLED)
.get()
.build()
val call = okHttp.newCall(request)
@@ -236,4 +233,4 @@ class DownloadManager(
private const val DOWNLOAD_ERROR_DELAY = 500L
private const val TEMP_PAGE_FILE = "page.tmp"
}
}
}

View File

@@ -53,7 +53,7 @@ class DownloadService : BaseService() {
notificationManager = NotificationManagerCompat.from(this)
wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager)
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "kotatsu:downloading")
downloadManager = DownloadManager(this, get(), get(), get(), get(), get())
downloadManager = DownloadManager(this, get(), get(), get(), get())
DownloadNotification.createChannel(this)
registerReceiver(controlReceiver, IntentFilter(ACTION_DOWNLOAD_CANCEL))
}

View File

@@ -6,13 +6,15 @@ import org.koin.core.qualifier.named
import org.koin.dsl.module
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.local.data.LocalStorageManager
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
import org.koitharu.kotatsu.local.ui.LocalListViewModel
val localModule
get() = module {
single { LocalMangaRepository(androidContext()) }
single { LocalStorageManager(androidContext(), get()) }
single { LocalMangaRepository(get()) }
factory<MangaRepository>(named(MangaSource.LOCAL)) { get<LocalMangaRepository>() }
viewModel { LocalListViewModel(get(), get(), get(), get()) }

View File

@@ -1,7 +1,7 @@
package org.koitharu.kotatsu.local.data
enum class Cache(val dir: String) {
enum class CacheDir(val dir: String) {
THUMBS("image_cache"),
PAGES("pages");
}
}

View File

@@ -0,0 +1,113 @@
package org.koitharu.kotatsu.local.data
import android.content.ContentResolver
import android.content.Context
import android.os.StatFs
import androidx.annotation.WorkerThread
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runInterruptible
import kotlinx.coroutines.withContext
import okhttp3.Cache
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.utils.ext.computeSize
import org.koitharu.kotatsu.utils.ext.getStorageName
import java.io.File
private const val DIR_NAME = "manga"
private const val CACHE_DISK_PERCENTAGE = 0.02
private const val CACHE_SIZE_MIN: Long = 10 * 1024 * 1024 // 10MB
private const val CACHE_SIZE_MAX: Long = 250 * 1024 * 1024 // 250MB
class LocalStorageManager(
private val context: Context,
private val settings: AppSettings,
) {
val contentResolver: ContentResolver
get() = context.contentResolver
fun createHttpCache(): Cache {
val directory = File(context.externalCacheDir ?: context.cacheDir, "http")
directory.mkdirs()
val maxSize = calculateDiskCacheSize(directory)
return Cache(directory, maxSize)
}
suspend fun computeCacheSize(cache: CacheDir) = withContext(Dispatchers.IO) {
getCacheDirs(cache.dir).sumOf { it.computeSize() }
}
suspend fun clearCache(cache: CacheDir) = runInterruptible(Dispatchers.IO) {
getCacheDirs(cache.dir).forEach { it.deleteRecursively() }
}
suspend fun getReadableDirs(): List<File> = runInterruptible(Dispatchers.IO) {
getConfiguredStorageDirs()
.filter { it.isReadable() }
}
suspend fun getWriteableDirs(): List<File> = runInterruptible(Dispatchers.IO) {
getConfiguredStorageDirs()
.filter { it.isWriteable() }
}
suspend fun getDefaultWriteableDir(): File? = runInterruptible(Dispatchers.IO) {
val preferredDir = settings.mangaStorageDir?.takeIf { it.isWriteable() }
preferredDir ?: getFallbackStorageDir()?.takeIf { it.isWriteable() }
}
fun getStorageDisplayName(file: File) = file.getStorageName(context)
@WorkerThread
private fun getConfiguredStorageDirs(): MutableSet<File> {
val set = getAvailableStorageDirs()
settings.mangaStorageDir?.let {
set.add(it)
}
return set
}
@WorkerThread
private fun getAvailableStorageDirs(): MutableSet<File> {
val result = LinkedHashSet<File>()
result += File(context.filesDir, DIR_NAME)
result += context.getExternalFilesDirs(DIR_NAME)
result.retainAll { it.exists() || it.mkdirs() }
return result
}
@WorkerThread
private fun getFallbackStorageDir(): File? {
return context.getExternalFilesDir(DIR_NAME) ?: File(context.filesDir, DIR_NAME).takeIf {
it.exists() || it.mkdirs()
}
}
@WorkerThread
private fun getCacheDirs(subDir: String): MutableSet<File> {
val result = LinkedHashSet<File>()
result += File(context.cacheDir, subDir)
context.externalCacheDirs.mapTo(result) {
File(it, subDir)
}
return result
}
private fun calculateDiskCacheSize(cacheDirectory: File): Long {
return try {
val cacheDir = StatFs(cacheDirectory.absolutePath)
val size = CACHE_DISK_PERCENTAGE * cacheDir.blockCountLong * cacheDir.blockSizeLong
return size.toLong().coerceIn(CACHE_SIZE_MIN, CACHE_SIZE_MAX)
} catch (_: Exception) {
CACHE_SIZE_MIN
}
}
private fun File.isReadable() = runCatching {
canRead()
}.getOrDefault(false)
private fun File.isWriteable() = runCatching {
canWrite()
}.getOrDefault(false)
}

View File

@@ -16,7 +16,7 @@ class MangaZip(val file: File) {
private var index = MangaIndex(null)
suspend fun prepare(manga: Manga) {
writableCbz.prepare()
writableCbz.prepare(overwrite = true)
index = MangaIndex(writableCbz[INDEX_ENTRY].takeIfReadable()?.readText())
index.setMangaInfo(manga, append = true)
}

View File

@@ -2,35 +2,25 @@ package org.koitharu.kotatsu.local.data
import android.content.Context
import com.tomclaw.cache.DiskLruCache
import org.koitharu.kotatsu.utils.FileSizeUtils
import org.koitharu.kotatsu.utils.FileSize
import org.koitharu.kotatsu.utils.ext.longHashCode
import org.koitharu.kotatsu.utils.ext.subdir
import org.koitharu.kotatsu.utils.ext.takeIfReadable
import java.io.File
import java.io.InputStream
import java.io.OutputStream
class PagesCache(context: Context) {
private val cacheDir = context.externalCacheDir ?: context.cacheDir
private val lruCache = DiskLruCache.create(
cacheDir.subdir(Cache.PAGES.dir),
FileSizeUtils.mbToBytes(200)
cacheDir.subdir(CacheDir.PAGES.dir),
FileSize.MEGABYTES.convert(200, FileSize.BYTES),
)
operator fun get(url: String): File? {
return lruCache.get(url)?.takeIfReadable()
}
@Deprecated("Useless lambda")
fun put(url: String, writer: (OutputStream) -> Unit): File {
val file = File(cacheDir, url.longHashCode().toString())
file.outputStream().use(writer)
val res = lruCache.put(url, file)
file.delete()
return res
}
fun put(url: String, inputStream: InputStream): File {
val file = File(cacheDir, url.longHashCode().toString())
file.outputStream().use { out ->
@@ -40,4 +30,4 @@ class PagesCache(context: Context) {
file.delete()
return res
}
}
}

View File

@@ -14,9 +14,13 @@ class WritableCbzFile(private val file: File) {
private val dir = File(file.parentFile, file.nameWithoutExtension)
suspend fun prepare() = withContext(Dispatchers.IO) {
check(dir.list().isNullOrEmpty()) {
"Dir ${dir.name} is not empty"
suspend fun prepare(overwrite: Boolean) = withContext(Dispatchers.IO) {
if (!dir.list().isNullOrEmpty()) {
if (overwrite) {
dir.deleteRecursively()
} else {
throw IllegalStateException("Dir ${dir.name} is not empty")
}
}
if (!dir.exists()) {
dir.mkdir()

View File

@@ -1,7 +1,6 @@
package org.koitharu.kotatsu.local.domain
import android.annotation.SuppressLint
import android.content.Context
import android.net.Uri
import android.webkit.MimeTypeMap
import androidx.collection.ArraySet
@@ -10,19 +9,22 @@ import androidx.core.net.toUri
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runInterruptible
import kotlinx.coroutines.withContext
import org.koitharu.kotatsu.core.exceptions.UnsupportedFileException
import org.koitharu.kotatsu.core.model.*
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.local.data.CbzFilter
import org.koitharu.kotatsu.local.data.LocalStorageManager
import org.koitharu.kotatsu.local.data.MangaIndex
import org.koitharu.kotatsu.local.data.MangaZip
import org.koitharu.kotatsu.utils.AlphanumComparator
import org.koitharu.kotatsu.utils.ext.*
import java.io.File
import java.io.IOException
import java.util.*
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
class LocalMangaRepository(private val context: Context) : MangaRepository {
class LocalMangaRepository(private val storageManager: LocalStorageManager) : MangaRepository {
override val source = MangaSource.LOCAL
private val filenameFilter = CbzFilter()
@@ -149,24 +151,26 @@ class LocalMangaRepository(private val context: Context) : MangaRepository {
}
}
suspend fun findSavedManga(remoteManga: Manga): Manga? = runInterruptible(Dispatchers.IO) {
suspend fun findSavedManga(remoteManga: Manga): Manga? {
val files = getAllFiles()
for (file in files) {
val index = ZipFile(file).use { zip ->
val entry = zip.getEntry(MangaZip.INDEX_ENTRY)
entry?.let(zip::readText)?.let(::MangaIndex)
} ?: continue
val info = index.getMangaInfo() ?: continue
if (info.id == remoteManga.id) {
val fileUri = file.toUri().toString()
return@runInterruptible info.copy(
source = MangaSource.LOCAL,
url = fileUri,
chapters = info.chapters?.map { c -> c.copy(url = fileUri) }
)
return runInterruptible(Dispatchers.IO) {
for (file in files) {
val index = ZipFile(file).use { zip ->
val entry = zip.getEntry(MangaZip.INDEX_ENTRY)
entry?.let(zip::readText)?.let(::MangaIndex)
} ?: continue
val info = index.getMangaInfo() ?: continue
if (info.id == remoteManga.id) {
val fileUri = file.toUri().toString()
return@runInterruptible info.copy(
source = MangaSource.LOCAL,
url = fileUri,
chapters = info.chapters?.map { c -> c.copy(url = fileUri) }
)
}
}
null
}
null
}
private fun zipUri(file: File, entryName: String) =
@@ -193,32 +197,38 @@ class LocalMangaRepository(private val context: Context) : MangaRepository {
override suspend fun getTags() = emptySet<MangaTag>()
private fun getAllFiles() = getAvailableStorageDirs(context).flatMap { dir ->
suspend fun import(uri: Uri) {
val contentResolver = storageManager.contentResolver
withContext(Dispatchers.IO) {
val name = contentResolver.resolveName(uri)
?: throw IOException("Cannot fetch name from uri: $uri")
if (!isFileSupported(name)) {
throw UnsupportedFileException("Unsupported file on $uri")
}
val dest = File(
getOutputDir() ?: throw IOException("External files dir unavailable"),
name,
)
runInterruptible {
contentResolver.openInputStream(uri)?.use { source ->
dest.outputStream().use { output ->
source.copyTo(output)
}
}
} ?: throw IOException("Cannot open input stream: $uri")
}
}
fun isFileSupported(name: String): Boolean {
val ext = name.substringAfterLast('.').lowercase(Locale.ROOT)
return ext == "cbz" || ext == "zip"
}
suspend fun getOutputDir(): File? {
return storageManager.getDefaultWriteableDir()
}
private suspend fun getAllFiles() = storageManager.getReadableDirs().flatMap { dir ->
dir.listFiles(filenameFilter)?.toList().orEmpty()
}
companion object {
private const val DIR_NAME = "manga"
fun isFileSupported(name: String): Boolean {
val ext = name.substringAfterLast('.').lowercase(Locale.ROOT)
return ext == "cbz" || ext == "zip"
}
fun getAvailableStorageDirs(context: Context): List<File> {
val result = ArrayList<File?>(5)
result += File(context.filesDir, DIR_NAME)
result += context.getExternalFilesDirs(DIR_NAME)
return result.filterNotNull()
.distinctBy { it.canonicalPath }
.filter { it.exists() || it.mkdir() }
}
fun getFallbackStorageDir(context: Context): File? {
return context.getExternalFilesDir(DIR_NAME) ?: context.filesDir.sub(DIR_NAME).takeIf {
(it.exists() || it.mkdir()) && it.canWrite()
}
}
}
}

View File

@@ -18,14 +18,16 @@ import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.download.ui.service.DownloadService
import org.koitharu.kotatsu.list.ui.MangaListFragment
import org.koitharu.kotatsu.utils.ext.ellipsize
import org.koitharu.kotatsu.utils.progress.Progress
class LocalListFragment : MangaListFragment(), ActivityResultCallback<Uri?> {
class LocalListFragment : MangaListFragment(), ActivityResultCallback<List<@JvmSuppressWildcards Uri>> {
override val viewModel by viewModel<LocalListViewModel>()
private val importCall = registerForActivityResult(
ActivityResultContracts.OpenDocument(),
ActivityResultContracts.OpenMultipleDocuments(),
this
)
private var importSnackbar: Snackbar? = null
private val downloadReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == DownloadService.ACTION_DOWNLOAD_COMPLETE) {
@@ -45,6 +47,12 @@ class LocalListFragment : MangaListFragment(), ActivityResultCallback<Uri?> {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.onMangaRemoved.observe(viewLifecycleOwner, ::onItemRemoved)
viewModel.importProgress.observe(viewLifecycleOwner, ::onImportProgressChanged)
}
override fun onDestroyView() {
importSnackbar = null
super.onDestroyView()
}
override fun onDetach() {
@@ -84,10 +92,9 @@ class LocalListFragment : MangaListFragment(), ActivityResultCallback<Uri?> {
return context?.getString(R.string.local_storage)
}
override fun onActivityResult(result: Uri?) {
if (result != null) {
viewModel.importFile(context?.applicationContext ?: return, result)
}
override fun onActivityResult(result: List<@JvmSuppressWildcards Uri>) {
if (result.isEmpty()) return
viewModel.importFiles(result)
}
override fun onCreatePopupMenu(inflater: MenuInflater, menu: Menu, data: Manga) {
@@ -121,6 +128,25 @@ class LocalListFragment : MangaListFragment(), ActivityResultCallback<Uri?> {
).show()
}
private fun onImportProgressChanged(progress: Progress?) {
if (progress == null) {
importSnackbar?.dismiss()
importSnackbar = null
return
}
val summaryText = getString(
R.string.importing_progress,
progress.value + 1,
progress.total,
)
importSnackbar?.setText(summaryText) ?: run {
val snackbar =
Snackbar.make(binding.recyclerView, summaryText, Snackbar.LENGTH_INDEFINITE)
importSnackbar = snackbar
snackbar.show()
}
}
companion object {
fun newInstance() = LocalListFragment()

View File

@@ -1,15 +1,14 @@
package org.koitharu.kotatsu.local.ui
import android.content.Context
import android.net.Uri
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.runInterruptible
import kotlinx.coroutines.withContext
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.UnsupportedFileException
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.os.ShortcutsRepository
import org.koitharu.kotatsu.core.prefs.AppSettings
@@ -19,21 +18,22 @@ import org.koitharu.kotatsu.list.ui.model.*
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
import org.koitharu.kotatsu.utils.SingleLiveEvent
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
import org.koitharu.kotatsu.utils.ext.resolveName
import java.io.File
import org.koitharu.kotatsu.utils.progress.Progress
import java.io.IOException
class LocalListViewModel(
private val repository: LocalMangaRepository,
private val historyRepository: HistoryRepository,
private val settings: AppSettings,
settings: AppSettings,
private val shortcutsRepository: ShortcutsRepository,
) : MangaListViewModel(settings) {
val onMangaRemoved = SingleLiveEvent<Manga>()
val importProgress = MutableLiveData<Progress?>(null)
private val listError = MutableStateFlow<Throwable?>(null)
private val mangaList = MutableStateFlow<List<Manga>?>(null)
private val headerModel = ListHeader(null, R.string.local_storage)
private var importJob: Job? = null
override val content = combine(
mangaList,
@@ -60,37 +60,23 @@ class LocalListViewModel(
override fun onRefresh() {
launchLoadingJob(Dispatchers.Default) {
try {
listError.value = null
mangaList.value = repository.getList2(0)
} catch (e: Throwable) {
listError.value = e
}
doRefresh()
}
}
override fun onRetry() = onRefresh()
fun importFile(context: Context, uri: Uri) {
launchLoadingJob {
val contentResolver = context.contentResolver
withContext(Dispatchers.IO) {
val name = contentResolver.resolveName(uri)
?: throw IOException("Cannot fetch name from uri: $uri")
if (!LocalMangaRepository.isFileSupported(name)) {
throw UnsupportedFileException("Unsupported file on $uri")
}
val dest = settings.getStorageDir(context)?.let { File(it, name) }
?: throw IOException("External files dir unavailable")
runInterruptible {
contentResolver.openInputStream(uri)?.use { source ->
dest.outputStream().use { output ->
source.copyTo(output)
}
}
} ?: throw IOException("Cannot open input stream: $uri")
fun importFiles(uris: List<Uri>) {
val previousJob = importJob
importJob = launchJob(Dispatchers.Default) {
previousJob?.join()
importProgress.postValue(Progress(0, uris.size))
for ((i, uri) in uris.withIndex()) {
repository.import(uri)
importProgress.postValue(Progress(i + 1, uris.size))
doRefresh()
}
onRefresh()
importProgress.postValue(null)
}
}
@@ -108,4 +94,13 @@ class LocalListViewModel(
onMangaRemoved.call(manga)
}
}
private suspend fun doRefresh() {
try {
listError.value = null
mangaList.value = repository.getList2(0)
} catch (e: Throwable) {
listError.value = e
}
}
}

View File

@@ -5,7 +5,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import org.koitharu.kotatsu.base.domain.MangaProviderFactory
import org.koitharu.kotatsu.base.ui.BaseViewModel
import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException
import org.koitharu.kotatsu.core.model.Manga
@@ -25,7 +24,7 @@ class MainViewModel(
val remoteSources = settings.observe()
.filter { it == AppSettings.KEY_SOURCES_ORDER || it == AppSettings.KEY_SOURCES_HIDDEN }
.onStart { emit("") }
.map { MangaProviderFactory.getSources(settings, includeHidden = false) }
.map { settings.getMangaSources(includeHidden = false) }
.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default)
fun openLastReader() {
@@ -35,4 +34,4 @@ class MainViewModel(
onOpenReader.call(manga)
}
}
}
}

View File

@@ -15,7 +15,6 @@ import org.koitharu.kotatsu.core.model.MangaPage
import org.koitharu.kotatsu.core.network.CommonHeaders
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.local.data.PagesCache
import org.koitharu.kotatsu.utils.CacheUtils
import org.koitharu.kotatsu.utils.ext.await
import org.koitharu.kotatsu.utils.ext.mangaRepositoryOf
import java.io.File
@@ -70,7 +69,7 @@ class PageLoader(
.get()
.header(CommonHeaders.REFERER, page.referer)
.header(CommonHeaders.ACCEPT, "image/webp,image/png;q=0.9,image/jpeg,*/*;q=0.8")
.cacheControl(CacheUtils.CONTROL_DISABLED)
.cacheControl(CommonHeaders.CACHE_CONTROL_DISABLED)
.build()
okHttp.newCall(request).await().use { response ->
check(response.isSuccessful) {
@@ -103,4 +102,4 @@ class PageLoader(
}
private companion object Lock
}
}

View File

@@ -46,7 +46,7 @@ class ChaptersDialog : AlertDialogFragment<DialogChaptersBinding>(),
}
val currentId = arguments?.getLong(ARG_CURRENT_ID, 0L) ?: 0L
val currentPosition = chapters.indexOfFirst { it.id == currentId }
val dateFormat = get<AppSettings>().dateFormat()
val dateFormat = get<AppSettings>().getDateFormat()
binding.recyclerViewChapters.adapter = ChaptersAdapter(this).apply {
setItems(chapters.mapIndexed { index, chapter ->
chapter.toListItem(
@@ -96,4 +96,4 @@ class ChaptersDialog : AlertDialogFragment<DialogChaptersBinding>(),
putLong(ARG_CURRENT_ID, currentId)
}.show(fm, TAG)
}
}
}

View File

@@ -56,7 +56,7 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
ActivityResultCallback<Boolean>, ReaderControlDelegate.OnInteractionListener {
private val viewModel by viewModel<ReaderViewModel> {
parametersOf(MangaIntent.from(intent), intent?.getParcelableExtra<ReaderState>(EXTRA_STATE))
parametersOf(MangaIntent(intent), intent?.getParcelableExtra<ReaderState>(EXTRA_STATE))
}
private lateinit var touchHelper: GridTouchHelper
@@ -371,4 +371,4 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
.putExtra(EXTRA_STATE, state)
}
}
}
}

View File

@@ -160,7 +160,7 @@ class ReaderViewModel(
val downloadId = downloadManagerHelper.downloadPage(page, pageUrl)
val uri = downloadManagerHelper.awaitDownload(downloadId)
onPageSaved.postCall(uri)
} catch (e: CancellationException) {
} catch (_: CancellationException) {
} catch (e: Exception) {
onPageSaved.postCall(null)
}
@@ -267,4 +267,4 @@ class ReaderViewModel(
}
}
}
}

View File

@@ -37,7 +37,7 @@ class RemoteListViewModel(
when {
list.isNullOrEmpty() && error != null -> listOf(error.toErrorState(canRetry = true))
list == null -> listOf(LoadingState)
list.isEmpty() -> listOf(EmptyState(R.drawable.ic_book_cross, R.string.nothing_found, R.string._empty))
list.isEmpty() -> listOf(EmptyState(R.drawable.ic_book_cross, R.string.nothing_found, R.string.empty))
else -> {
val result = ArrayList<ListModel>(list.size + 3)
result += headerModel
@@ -128,4 +128,4 @@ class RemoteListViewModel(
}
}
}
}
}

View File

@@ -9,7 +9,6 @@ import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.isActive
import kotlinx.coroutines.withContext
import org.koitharu.kotatsu.base.domain.MangaProviderFactory
import org.koitharu.kotatsu.core.db.MangaDatabase
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaSource
@@ -27,7 +26,7 @@ class MangaSearchRepository(
) {
fun globalSearch(query: String, concurrency: Int = DEFAULT_CONCURRENCY): Flow<Manga> =
MangaProviderFactory.getSources(settings, includeHidden = false).asFlow()
settings.getMangaSources(includeHidden = false).asFlow()
.flatMapMerge(concurrency) { source ->
runCatching {
MangaRepository(source).getList2(
@@ -128,4 +127,4 @@ class MangaSearchRepository(
return false
}
}
}
}

View File

@@ -20,7 +20,7 @@ import org.koitharu.kotatsu.core.github.AppVersion
import org.koitharu.kotatsu.core.github.GithubRepository
import org.koitharu.kotatsu.core.github.VersionId
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.utils.FileSizeUtils
import org.koitharu.kotatsu.utils.FileSize
import org.koitharu.kotatsu.utils.ext.byte2HexFormatted
import java.io.ByteArrayInputStream
import java.io.InputStream
@@ -85,7 +85,7 @@ class AppUpdateChecker(private val activity: ComponentActivity) {
append(
activity.getString(
R.string.size_s,
FileSizeUtils.formatBytes(activity, version.apkSize)
FileSize.BYTES.format(activity, version.apkSize),
)
)
appendLine()
@@ -144,4 +144,4 @@ class AppUpdateChecker(private val activity: ComponentActivity) {
}
}
}
}
}

View File

@@ -5,20 +5,18 @@ import android.view.View
import androidx.preference.Preference
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.koin.android.ext.android.get
import org.koin.android.ext.android.inject
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BasePreferenceFragment
import org.koitharu.kotatsu.core.network.AndroidCookieJar
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.local.data.Cache
import org.koitharu.kotatsu.local.data.CacheDir
import org.koitharu.kotatsu.local.data.LocalStorageManager
import org.koitharu.kotatsu.search.domain.MangaSearchRepository
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
import org.koitharu.kotatsu.utils.CacheUtils
import org.koitharu.kotatsu.utils.FileSizeUtils
import org.koitharu.kotatsu.utils.FileSize
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.viewLifecycleScope
@@ -26,6 +24,7 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach
private val trackerRepo by inject<TrackingRepository>(mode = LazyThreadSafetyMode.NONE)
private val searchRepository by inject<MangaSearchRepository>(mode = LazyThreadSafetyMode.NONE)
private val storageManager by inject<LocalStorageManager>(mode = LazyThreadSafetyMode.NONE)
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.pref_history)
@@ -33,22 +32,8 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
findPreference<Preference>(AppSettings.KEY_PAGES_CACHE_CLEAR)?.let { pref ->
viewLifecycleScope.launchWhenResumed {
val size = withContext(Dispatchers.IO) {
CacheUtils.computeCacheSize(pref.context, Cache.PAGES.dir)
}
pref.summary = FileSizeUtils.formatBytes(pref.context, size)
}
}
findPreference<Preference>(AppSettings.KEY_THUMBS_CACHE_CLEAR)?.let { pref ->
viewLifecycleScope.launchWhenResumed {
val size = withContext(Dispatchers.IO) {
CacheUtils.computeCacheSize(pref.context, Cache.THUMBS.dir)
}
pref.summary = FileSizeUtils.formatBytes(pref.context, size)
}
}
findPreference<Preference>(AppSettings.KEY_PAGES_CACHE_CLEAR)?.bindSummaryToCacheSize(CacheDir.PAGES)
findPreference<Preference>(AppSettings.KEY_THUMBS_CACHE_CLEAR)?.bindSummaryToCacheSize(CacheDir.THUMBS)
findPreference<Preference>(AppSettings.KEY_SEARCH_HISTORY_CLEAR)?.let { pref ->
viewLifecycleScope.launchWhenResumed {
val items = searchRepository.getSearchHistoryCount()
@@ -68,11 +53,11 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach
override fun onPreferenceTreeClick(preference: Preference): Boolean {
return when (preference.key) {
AppSettings.KEY_PAGES_CACHE_CLEAR -> {
clearCache(preference, Cache.PAGES)
clearCache(preference, CacheDir.PAGES)
true
}
AppSettings.KEY_THUMBS_CACHE_CLEAR -> {
clearCache(preference, Cache.THUMBS)
clearCache(preference, CacheDir.THUMBS)
true
}
AppSettings.KEY_COOKIES_CLEAR -> {
@@ -100,16 +85,14 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach
}
}
private fun clearCache(preference: Preference, cache: Cache) {
private fun clearCache(preference: Preference, cache: CacheDir) {
val ctx = preference.context.applicationContext
viewLifecycleScope.launch {
try {
preference.isEnabled = false
val size = withContext(Dispatchers.IO) {
CacheUtils.clearCache(ctx, cache.dir)
CacheUtils.computeCacheSize(ctx, cache.dir)
}
preference.summary = FileSizeUtils.formatBytes(ctx, size)
storageManager.clearCache(cache)
val size = storageManager.computeCacheSize(cache)
preference.summary = FileSize.BYTES.format(ctx, size)
} catch (e: Exception) {
preference.summary = e.getDisplayMessage(ctx.resources)
} finally {
@@ -118,6 +101,11 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach
}
}
private fun Preference.bindSummaryToCacheSize(dir: CacheDir) = viewLifecycleScope.launch {
val size = storageManager.computeCacheSize(dir)
summary = FileSize.BYTES.format(context, size)
}
private fun clearSearchHistory(preference: Preference) {
MaterialAlertDialogBuilder(context ?: return)
.setTitle(R.string.clear_search_history)
@@ -154,4 +142,4 @@ class HistorySettingsFragment : BasePreferenceFragment(R.string.history_and_cach
}
}.show()
}
}
}

View File

@@ -12,18 +12,22 @@ import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreference
import kotlinx.coroutines.launch
import leakcanary.LeakCanary
import org.koin.android.ext.android.inject
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BasePreferenceFragment
import org.koitharu.kotatsu.base.ui.dialog.StorageSelectDialog
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.local.data.LocalStorageManager
import org.koitharu.kotatsu.settings.protect.ProtectSetupActivity
import org.koitharu.kotatsu.settings.utils.SliderPreference
import org.koitharu.kotatsu.utils.ext.getStorageName
import org.koitharu.kotatsu.utils.ext.names
import org.koitharu.kotatsu.utils.ext.setDefaultValueCompat
import org.koitharu.kotatsu.utils.ext.viewLifecycleScope
import java.io.File
import java.util.*
@@ -32,6 +36,8 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings),
SharedPreferences.OnSharedPreferenceChangeListener,
StorageSelectDialog.OnStorageSelectListener {
private val storageManager by inject<LocalStorageManager>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
@@ -56,7 +62,7 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings),
entryValues = arrayOf("", "MM/dd/yy", "dd/MM/yy", "yyyy-MM-dd", "dd MMM yyyy", "MMM dd, yyyy")
val now = Date().time
entries = entryValues.map { value ->
val formattedDate = settings.dateFormat(value.toString()).format(now)
val formattedDate = settings.getDateFormat(value.toString()).format(now)
if (value == "") {
"${context.getString(R.string.system_default)} ($formattedDate)"
} else {
@@ -70,10 +76,7 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings),
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
findPreference<Preference>(AppSettings.KEY_LOCAL_STORAGE)?.run {
summary = settings.getStorageDir(context)?.getStorageName(context)
?: getString(R.string.not_available)
}
findPreference<Preference>(AppSettings.KEY_LOCAL_STORAGE)?.bindStorageName()
findPreference<SwitchPreference>(AppSettings.KEY_PROTECT_APP)?.isChecked =
!settings.appPassword.isNullOrEmpty()
settings.subscribe(this)
@@ -114,10 +117,7 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings),
findPreference<SwitchPreference>(key)?.setSummary(R.string.restart_required)
}
AppSettings.KEY_LOCAL_STORAGE -> {
findPreference<Preference>(key)?.run {
summary = settings.getStorageDir(context)?.getStorageName(context)
?: getString(R.string.not_available)
}
findPreference<Preference>(key)?.bindStorageName()
}
AppSettings.KEY_APP_PASSWORD -> {
findPreference<SwitchPreference>(AppSettings.KEY_PROTECT_APP)
@@ -140,7 +140,7 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings),
return when (preference.key) {
AppSettings.KEY_LOCAL_STORAGE -> {
val ctx = context ?: return false
StorageSelectDialog.Builder(ctx, settings.getStorageDir(ctx), this)
StorageSelectDialog.Builder(ctx, storageManager, this)
.setTitle(preference.title ?: "")
.setNegativeButton(android.R.string.cancel)
.create()
@@ -162,7 +162,13 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings),
}
override fun onStorageSelected(file: File) {
settings.setStorageDir(context ?: return, file)
settings.mangaStorageDir = file
}
}
private fun Preference.bindStorageName() {
viewLifecycleScope.launch {
val storage = storageManager.getDefaultWriteableDir()
summary = storage?.getStorageName(context) ?: getString(R.string.not_available)
}
}
}

View File

@@ -10,14 +10,13 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BasePreferenceFragment
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.settings.utils.RingtonePickContract
import org.koitharu.kotatsu.utils.ext.toUriOrNull
class NotificationSettingsLegacyFragment : BasePreferenceFragment(R.string.notifications) {
private val ringtonePickContract = registerForActivityResult(
RingtonePickContract(get<Context>().getString(R.string.notification_sound))
) { uri ->
settings.notificationSound = uri?.toString() ?: return@registerForActivityResult
settings.notificationSound = uri ?: return@registerForActivityResult
findPreference<Preference>(AppSettings.KEY_NOTIFICATIONS_SOUND)?.run {
summary = RingtoneManager.getRingtone(context, uri)?.getTitle(context)
?: getString(R.string.silent)
@@ -31,7 +30,7 @@ class NotificationSettingsLegacyFragment : BasePreferenceFragment(R.string.notif
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
findPreference<Preference>(AppSettings.KEY_NOTIFICATIONS_SOUND)?.run {
val uri = settings.notificationSound.toUriOrNull()
val uri = settings.notificationSound
summary = RingtoneManager.getRingtone(context, uri)?.getTitle(context)
?: getString(R.string.silent)
}
@@ -40,10 +39,10 @@ class NotificationSettingsLegacyFragment : BasePreferenceFragment(R.string.notif
override fun onPreferenceTreeClick(preference: Preference): Boolean {
return when (preference.key) {
AppSettings.KEY_NOTIFICATIONS_SOUND -> {
ringtonePickContract.launch(settings.notificationSound.toUriOrNull())
ringtonePickContract.launch(settings.notificationSound)
true
}
else -> super.onPreferenceTreeClick(preference)
}
}
}
}

View File

@@ -48,6 +48,12 @@ class AboutSettingsFragment : BasePreferenceFragment(R.string.about) {
resources.getString(R.string.about_feedback_4pda)) })
true
}
AppSettings.KEY_FEEDBACK_DISCORD -> {
startActivity(context?.let { BrowserActivity.newIntent(it,
"https://discord.gg/NNJ5RgVBC5",
"Discord") })
true
}
AppSettings.KEY_FEEDBACK_GITHUB -> {
startActivity(context?.let { BrowserActivity.newIntent(it,
"https://github.com/nv95/Kotatsu/issues",
@@ -89,4 +95,4 @@ class AboutSettingsFragment : BasePreferenceFragment(R.string.about) {
}
}
}
}
}

View File

@@ -3,7 +3,6 @@ package org.koitharu.kotatsu.settings.sources
import androidx.core.os.LocaleListCompat
import androidx.lifecycle.MutableLiveData
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.domain.MangaProviderFactory
import org.koitharu.kotatsu.base.ui.BaseViewModel
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.prefs.AppSettings
@@ -70,7 +69,7 @@ class SourcesSettingsViewModel(
}
private fun buildList() {
val sources = MangaProviderFactory.getSources(settings, includeHidden = true)
val sources = settings.getMangaSources(includeHidden = true)
val hiddenSources = settings.hiddenSources
val query = searchQuery
if (!query.isNullOrEmpty()) {
@@ -155,4 +154,4 @@ class SourcesSettingsViewModel(
}
}
}
}
}

View File

@@ -1,6 +1,5 @@
package org.koitharu.kotatsu.tracker
import org.koin.android.ext.koin.androidContext
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
@@ -11,5 +10,5 @@ val trackerModule
single { TrackingRepository(get()) }
viewModel { FeedViewModel(androidContext(), get()) }
viewModel { FeedViewModel(get()) }
}

View File

@@ -4,7 +4,9 @@ import android.os.Bundle
import android.view.*
import androidx.core.graphics.Insets
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.divider.MaterialDividerItemDecoration
import com.google.android.material.snackbar.Snackbar
import org.koin.android.ext.android.get
import org.koin.androidx.viewmodel.ext.android.viewModel
@@ -50,6 +52,9 @@ class FeedFragment : BaseFragment<FragmentFeedBinding>(), PaginationScrollListen
adapter = feedAdapter
setHasFixedSize(true)
addOnScrollListener(PaginationScrollListener(4, this@FeedFragment))
val dividerDecoration = MaterialDividerItemDecoration(context, RecyclerView.VERTICAL)
dividerDecoration.setDividerInsetStartResource(context, R.dimen.feed_dividers_offset)
addItemDecoration(dividerDecoration)
}
viewModel.content.observe(viewLifecycleOwner, this::onListChanged)

View File

@@ -1,6 +1,5 @@
package org.koitharu.kotatsu.tracker.ui
import android.content.Context
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
@@ -22,7 +21,6 @@ import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
import org.koitharu.kotatsu.utils.ext.mapItems
class FeedViewModel(
context: Context,
private val repository: TrackingRepository
) : BaseViewModel() {
@@ -34,7 +32,7 @@ class FeedViewModel(
val onFeedCleared = SingleLiveEvent<Unit>()
val content = combine(
logList.filterNotNull().mapItems {
it.toFeedItem(context.resources)
it.toFeedItem()
},
hasNextPage
) { list, isHasNextPage ->

View File

@@ -12,6 +12,7 @@ import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.tracker.ui.model.FeedItem
import org.koitharu.kotatsu.utils.ext.enqueueWith
import org.koitharu.kotatsu.utils.ext.newImageRequest
import org.koitharu.kotatsu.utils.ext.textAndVisible
fun feedItemAD(
coil: ImageLoader,
@@ -39,6 +40,11 @@ fun feedItemAD(
binding.textViewTitle.text = item.title
binding.badge.text = item.subtitle
binding.textViewChapters.text = item.chapters
binding.textViewTruncated.textAndVisible = if (item.truncated > 0) {
getString(R.string._and_x_more, item.truncated)
} else {
null
}
}
onViewRecycled {

View File

@@ -9,5 +9,6 @@ data class FeedItem(
val title: String,
val subtitle: String,
val chapters: CharSequence,
val manga: Manga
val manga: Manga,
val truncated: Int,
) : ListModel

View File

@@ -1,19 +1,15 @@
package org.koitharu.kotatsu.tracker.ui.model
import android.content.res.Resources
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.TrackingLogItem
fun TrackingLogItem.toFeedItem(resources: Resources): FeedItem {
val chaptersString = if (chapters.size > MAX_CHAPTERS) {
fun TrackingLogItem.toFeedItem(): FeedItem {
val truncate = chapters.size > MAX_CHAPTERS
val chaptersString = if (truncate) {
chapters.joinToString(
separator = "\n",
limit = MAX_CHAPTERS - 1,
truncated = resources.getString(
R.string._and_x_more,
chapters.size - MAX_CHAPTERS + 1
)
)
truncated = "",
).trimEnd()
} else {
chapters.joinToString("\n")
}
@@ -23,7 +19,8 @@ fun TrackingLogItem.toFeedItem(resources: Resources): FeedItem {
title = manga.title,
subtitle = chapters.size.toString(),
chapters = chaptersString,
manga = manga
manga = manga,
truncated = chapters.size - MAX_CHAPTERS + 1,
)
}

View File

@@ -183,7 +183,7 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) :
setShortcutId(manga.id.toString())
priority = NotificationCompat.PRIORITY_DEFAULT
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
builder.setSound(settings.notificationSound.toUriOrNull())
builder.setSound(settings.notificationSound)
var defaults = if (settings.notificationLight) {
setLights(colorPrimary, 1000, 5000)
NotificationCompat.DEFAULT_LIGHTS
@@ -298,4 +298,4 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) :
}
}
}
}
}

View File

@@ -1,55 +0,0 @@
package org.koitharu.kotatsu.utils
import android.content.Context
import android.os.StatFs
import androidx.annotation.WorkerThread
import okhttp3.Cache
import okhttp3.CacheControl
import org.koitharu.kotatsu.utils.ext.computeSize
import org.koitharu.kotatsu.utils.ext.sub
import java.io.File
object CacheUtils {
const val QUALIFIER_HTTP = "cache_http"
val CONTROL_DISABLED = CacheControl.Builder()
.noStore()
.build()
fun getCacheDirs(context: Context) = (context.externalCacheDirs + context.cacheDir)
.filterNotNull()
.distinctBy { it.absolutePath }
@WorkerThread
fun computeCacheSize(context: Context, name: String) = getCacheDirs(context)
.map { File(it, name) }
.sumOf { x -> x.computeSize() }
@WorkerThread
fun clearCache(context: Context, name: String) = getCacheDirs(context)
.map { File(it, name) }
.forEach { it.deleteRecursively() }
// FIXME need async implementation
fun createHttpCache(context: Context): Cache {
val directory = (context.externalCacheDir ?: context.cacheDir).sub("http")
directory.mkdirs()
val maxSize = calculateDiskCacheSize(directory) // TODO blocking call
return Cache(directory, maxSize)
}
private fun calculateDiskCacheSize(cacheDirectory: File): Long {
return try {
val cacheDir = StatFs(cacheDirectory.absolutePath)
val size = DISK_CACHE_PERCENTAGE * cacheDir.blockCountLong * cacheDir.blockSizeLong
return size.toLong().coerceIn(MIN_DISK_CACHE_SIZE, MAX_DISK_CACHE_SIZE)
} catch (_: Exception) {
MIN_DISK_CACHE_SIZE
}
}
private const val DISK_CACHE_PERCENTAGE = 0.02
private const val MIN_DISK_CACHE_SIZE: Long = 10 * 1024 * 1024 // 10MB
private const val MAX_DISK_CACHE_SIZE: Long = 250 * 1024 * 1024 // 250MB
}

View File

@@ -6,14 +6,14 @@ import java.text.DecimalFormat
import kotlin.math.log10
import kotlin.math.pow
enum class FileSize(private val multiplier: Int) {
object FileSizeUtils {
BYTES(1), KILOBYTES(1024), MEGABYTES(1024 * 1024);
fun mbToBytes(mb: Int) = 1024L * 1024L * mb
fun convert(amount: Long, target: FileSize): Long = amount * multiplier / target.multiplier
fun kbToBytes(kb: Int) = 1024L * kb
fun formatBytes(context: Context, bytes: Long): String {
fun format(context: Context, amount: Long): String {
val bytes = amount * multiplier
val units = context.getString(R.string.text_file_sizes).split('|')
if (bytes <= 0) {
return "0 ${units.first()}"
@@ -23,10 +23,13 @@ object FileSizeUtils {
append(
DecimalFormat("#,##0.#").format(
bytes / 1024.0.pow(digitGroups.toDouble())
).toString()
)
)
append(' ')
append(units.getOrNull(digitGroups).orEmpty())
val unit = units.getOrNull(digitGroups)
if (unit != null) {
append(' ')
append(unit)
}
}
}
}
}

View File

@@ -5,15 +5,17 @@ import android.os.Build
object PendingIntentCompat {
@JvmField
val FLAG_IMMUTABLE = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_IMMUTABLE
} else {
0
}
@JvmField
val FLAG_MUTABLE = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.FLAG_MUTABLE
} else {
0
}
}
}

View File

@@ -91,4 +91,4 @@ sealed class Motion {
anim.interpolator = DecelerateInterpolator()
}
}
}
}

View File

@@ -1,14 +0,0 @@
package org.koitharu.kotatsu.utils.delegates
import android.os.Parcelable
import androidx.fragment.app.Fragment
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
class ParcelableArgumentDelegate<T : Parcelable>(private val name: String) :
ReadOnlyProperty<Fragment, T> {
override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
return thisRef.requireArguments().getParcelable(name)!!
}
}

View File

@@ -1,12 +0,0 @@
package org.koitharu.kotatsu.utils.delegates
import androidx.fragment.app.Fragment
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
class StringArgumentDelegate(private val name: String) : ReadOnlyProperty<Fragment, String?> {
override fun getValue(thisRef: Fragment, property: KProperty<*>): String? {
return thisRef.arguments?.getString(name)
}
}

View File

@@ -1,20 +0,0 @@
package org.koitharu.kotatsu.utils.delegates.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class BoolPreferenceDelegate(private val key: String, private val defaultValue: Boolean) :
ReadWriteProperty<SharedPreferences, Boolean> {
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): Boolean {
return thisRef.getBoolean(key, defaultValue)
}
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: Boolean) {
thisRef.edit {
putBoolean(key, value)
}
}
}

View File

@@ -1,27 +0,0 @@
package org.koitharu.kotatsu.utils.delegates.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class EnumPreferenceDelegate<E : Enum<*>>(
private val cls: Class<E>,
private val key: String,
private val defValue: E
) : ReadWriteProperty<SharedPreferences, E> {
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): E {
val name = thisRef.getString(key, null)
if (name === null) {
return defValue
}
return cls.enumConstants?.find { it.name == name } ?: defValue
}
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: E) {
thisRef.edit {
putString(key, value.name)
}
}
}

View File

@@ -1,28 +0,0 @@
package org.koitharu.kotatsu.utils.delegates.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
@Deprecated("")
class IntEnumPreferenceDelegate<E : Enum<*>>(
private val cls: Class<E>,
private val key: String,
private val defValue: E
) : ReadWriteProperty<SharedPreferences, E> {
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): E {
val ord = thisRef.getInt(key, -1)
if (ord == -1) {
return defValue
}
return cls.enumConstants?.getOrNull(ord) ?: defValue
}
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: E) {
thisRef.edit {
putInt(key, value.ordinal)
}
}
}

View File

@@ -1,20 +0,0 @@
package org.koitharu.kotatsu.utils.delegates.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class IntPreferenceDelegate(private val key: String, private val defaultValue: Int) :
ReadWriteProperty<SharedPreferences, Int> {
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): Int {
return thisRef.getInt(key, defaultValue)
}
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: Int) {
thisRef.edit {
putInt(key, value)
}
}
}

View File

@@ -1,20 +0,0 @@
package org.koitharu.kotatsu.utils.delegates.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class LongPreferenceDelegate(private val key: String, private val defaultValue: Long) :
ReadWriteProperty<SharedPreferences, Long> {
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): Long {
return thisRef.getLong(key, defaultValue)
}
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: Long) {
thisRef.edit {
putLong(key, value)
}
}
}

View File

@@ -1,20 +0,0 @@
package org.koitharu.kotatsu.utils.delegates.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class NullableStringPreferenceDelegate(private val key: String) :
ReadWriteProperty<SharedPreferences, String?> {
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): String? {
return thisRef.getString(key, null)
}
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: String?) {
thisRef.edit {
putString(key, value)
}
}
}

View File

@@ -1,20 +0,0 @@
package org.koitharu.kotatsu.utils.delegates.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class StringIntPreferenceDelegate(private val key: String, private val defValue: Int) :
ReadWriteProperty<SharedPreferences, Int> {
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): Int {
return thisRef.getString(key, defValue.toString())?.toIntOrNull() ?: defValue
}
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: Int) {
thisRef.edit {
putString(key, value.toString())
}
}
}

View File

@@ -1,20 +0,0 @@
package org.koitharu.kotatsu.utils.delegates.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class StringPreferenceDelegate(private val key: String, private val defValue: String) :
ReadWriteProperty<SharedPreferences, String> {
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): String {
return thisRef.getString(key, defValue) ?: defValue
}
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: String) {
thisRef.edit {
putString(key, value)
}
}
}

View File

@@ -1,23 +0,0 @@
package org.koitharu.kotatsu.utils.delegates.prefs
import android.content.SharedPreferences
import androidx.core.content.edit
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class StringSetPreferenceDelegate(
private val key: String,
private val defValue: Set<String> = emptySet()
) :
ReadWriteProperty<SharedPreferences, Set<String>> {
override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): Set<String> {
return thisRef.getStringSet(key, defValue) ?: defValue
}
override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: Set<String>) {
thisRef.edit {
putStringSet(key, value)
}
}
}

View File

@@ -7,18 +7,16 @@ import android.os.Build
import android.os.Environment
import android.os.storage.StorageManager
import android.provider.OpenableColumns
import androidx.annotation.WorkerThread
import androidx.core.database.getStringOrNull
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runInterruptible
import kotlinx.coroutines.withContext
import org.koitharu.kotatsu.R
import java.io.File
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
@Suppress("NOTHING_TO_INLINE")
@Deprecated("Useless", ReplaceWith("File(this, name)", "java.io.File"))
inline fun File.sub(name: String) = File(this, name)
fun File.subdir(name: String) = File(this, name).also {
if (!it.exists()) it.mkdirs()
}
@@ -29,22 +27,6 @@ fun ZipFile.readText(entry: ZipEntry) = getInputStream(entry).bufferedReader().u
it.readText()
}
fun File.computeSize(): Long = listFiles()?.sumOf { x ->
if (x.isDirectory) {
x.computeSize()
} else {
x.length()
}
} ?: 0L
inline fun File.findParent(predicate: (File) -> Boolean): File? {
var current = this
while (!predicate(current)) {
current = current.parentFile ?: return null
}
return current
}
fun File.getStorageName(context: Context): String = runCatching {
val manager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
@@ -78,4 +60,18 @@ fun ContentResolver.resolveName(uri: Uri): String? {
}
}
return fallback
}
suspend fun File.computeSize(): Long = runInterruptible(Dispatchers.IO) {
computeSizeInternal(this)
}
@WorkerThread
private fun computeSizeInternal(file: File): Long {
if (file.isDirectory) {
val files = file.listFiles() ?: return 0L
return files.sumOf { computeSizeInternal(it) }
} else {
return file.length()
}
}

View File

@@ -12,13 +12,13 @@ fun String.longHashCode(): Long {
var h = 1125899906842597L
val len: Int = this.length
for (i in 0 until len) {
h = 31 * h + this[i].toLong()
h = 31 * h + this[i].code
}
return h
}
fun String.removeSurrounding(vararg chars: Char): String {
if (length == 0) {
if (isEmpty()) {
return this
}
for (c in chars) {
@@ -224,4 +224,4 @@ inline fun <T> StringBuilder.appendAll(
}
append(transform(item))
}
}
}

View File

@@ -50,7 +50,7 @@ class ShelfConfigActivity : BaseActivity<ActivityCategoriesBinding>(),
finishAfterTransition()
return
}
config = AppWidgetConfig.getInstance(this, appWidgetId)
config = AppWidgetConfig(this, appWidgetId)
viewModel.checkedId = config.categoryId
viewModel.content.observe(this, this::onContentChanged)
@@ -118,4 +118,4 @@ class ShelfConfigActivity : BaseActivity<ActivityCategoriesBinding>(),
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
sendBroadcast(intent)
}
}
}

View File

@@ -24,7 +24,7 @@ class ShelfListFactory(
) : RemoteViewsService.RemoteViewsFactory {
private val dataSet = ArrayList<Manga>()
private val config = AppWidgetConfig.getInstance(context, widgetId)
private val config = AppWidgetConfig(context, widgetId)
override fun onCreate() {
}
@@ -73,4 +73,4 @@ class ShelfListFactory(
override fun getViewTypeCount() = 1
override fun onDestroy() = Unit
}
}

View File

@@ -3,10 +3,10 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?android:textColorPrimary"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M17,18V5H7V18L12,15.82L17,18M17,3A2,2 0 0,1 19,5V21L12,18L5,21V5C5,3.89 5.9,3 7,3H17M11,7H13V9H15V11H13V13H11V11H9V9H11V7Z" />
</vector>
android:pathData="M2,16H10V14H2M18,14V10H16V14H12V16H16V20H18V16H22V14M14,6H2V8H14M14,10H2V12H14V10Z" />
</vector>

View File

@@ -0,0 +1,11 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M13 2.03V4.05C17.39 4.59 20.5 8.58 19.96 12.97C19.5 16.61 16.64 19.5 13 19.93V21.93C18.5 21.38 22.5 16.5 21.95 11C21.5 6.25 17.73 2.5 13 2.03M11 2.06C9.05 2.25 7.19 3 5.67 4.26L7.1 5.74C8.22 4.84 9.57 4.26 11 4.06V2.06M4.26 5.67C3 7.19 2.25 9.04 2.05 11H4.05C4.24 9.58 4.8 8.23 5.69 7.1L4.26 5.67M2.06 13C2.26 14.96 3.03 16.81 4.27 18.33L5.69 16.9C4.81 15.77 4.24 14.42 4.06 13H2.06M7.1 18.37L5.67 19.74C7.18 21 9.04 21.79 11 22V20C9.58 19.82 8.23 19.25 7.1 18.37M20 4H44M13 18H11V16H13V18M13 15H11C11 11.75 14 12 14 10C14 8.9 13.1 8 12 8S10 8.9 10 10H8C8 7.79 9.79 6 12 6S16 7.79 16 10C16 12.5 13 12.75 13 15Z" />
</vector>

View File

@@ -3,12 +3,14 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="50dp" />
<corners android:radius="10dp" />
<padding
android:paddingLeft="22dp"
android:paddingRight="22dp" />
android:bottom="10dp"
android:left="10dp"
android:right="10dp"
android:top="10dp" />
<solid android:color="?colorPrimary" />
<solid android:color="@color/scrollbar" />
</shape>

View File

@@ -1,13 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/darker_gray" />
<padding
android:bottom="10dp"
android:left="10dp"
android:right="10dp"
android:top="10dp" />
</shape>
android:shape="rectangle" />

View File

@@ -20,13 +20,8 @@
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/grid_spacing_outer"
android:scrollbarStyle="outsideOverlay"
android:scrollbars="vertical"
app:fastScrollEnabled="true"
app:fastScrollHorizontalThumbDrawable="@drawable/list_thumb"
app:fastScrollHorizontalTrackDrawable="@drawable/list_track"
app:fastScrollVerticalThumbDrawable="@drawable/list_thumb"
app:fastScrollVerticalTrackDrawable="@drawable/list_track"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_manga_list" />

View File

@@ -41,16 +41,18 @@
android:visibility="gone"
tools:visibility="visible" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/fab_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:contentDescription="@string/add_new_category"
android:src="@drawable/ic_add"
android:text="@string/create_category"
app:fabSize="normal"
app:icon="@drawable/ic_add"
app:layout_anchor="@id/recyclerView"
app:layout_anchorGravity="bottom|end"
app:layout_dodgeInsetEdges="bottom" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -42,6 +42,6 @@
android:paddingEnd="?android:listPreferredItemPaddingEnd"
android:text="@string/add_new_category"
android:textAppearance="?attr/textAppearanceBodyLarge"
app:drawableEndCompat="@drawable/ic_add" />
app:drawableEndCompat="@drawable/ic_list_add" />
</LinearLayout>
</LinearLayout>

View File

@@ -28,12 +28,7 @@
android:layout_alignParentBottom="true"
android:clipToPadding="false"
android:orientation="vertical"
android:scrollbarStyle="outsideOverlay"
app:fastScrollEnabled="true"
app:fastScrollHorizontalThumbDrawable="@drawable/list_thumb"
app:fastScrollHorizontalTrackDrawable="@drawable/list_track"
app:fastScrollVerticalThumbDrawable="@drawable/list_thumb"
app:fastScrollVerticalTrackDrawable="@drawable/list_track"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_chapter" />

View File

@@ -18,13 +18,8 @@
android:clipToPadding="false"
android:orientation="vertical"
android:padding="@dimen/grid_spacing_outer"
android:scrollbarStyle="outsideOverlay"
android:scrollbars="vertical"
app:fastScrollEnabled="true"
app:fastScrollHorizontalThumbDrawable="@drawable/list_thumb"
app:fastScrollHorizontalTrackDrawable="@drawable/list_track"
app:fastScrollVerticalThumbDrawable="@drawable/list_thumb"
app:fastScrollVerticalTrackDrawable="@drawable/list_track"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_manga_list" />

View File

@@ -6,8 +6,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:paddingStart="8dp"
android:paddingEnd="8dp">
android:paddingVertical="6dp"
android:paddingHorizontal="8dp">
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_cover"
@@ -75,11 +75,26 @@
android:layout_marginStart="16dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:layout_marginTop="4dp"
android:ellipsize="none"
app:layout_constraintBottom_toBottomOf="parent"
android:lineSpacingExtra="4sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/card_cover"
app:layout_constraintTop_toBottomOf="@id/title_container"
tools:text="@tools:sample/lorem[25]" />
<TextView
android:id="@+id/textView_truncated"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="4dp"
android:textColor="?android:textColorHint"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/card_cover"
app:layout_constraintTop_toBottomOf="@id/textView_chapters"
tools:text="@string/_and_x_more" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -12,13 +12,6 @@
android:visible="false"
app:showAsAction="always" />
<item
android:id="@+id/action_bookmark_add"
android:icon="@drawable/ic_bookmark_add"
android:title="@string/add_bookmark"
android:visible="false"
app:showAsAction="always" />
<item
android:id="@+id/action_pages_thumbs"
android:icon="@drawable/ic_grid"
@@ -27,7 +20,7 @@
<item
android:id="@+id/action_reader_mode"
android:icon="@drawable/ic_book_page"
android:icon="@drawable/ic_loading"
android:title="@string/read_mode"
app:showAsAction="always" />
@@ -41,4 +34,4 @@
android:title="@string/settings"
app:showAsAction="never" />
</menu>
</menu>

View File

@@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View File

@@ -54,7 +54,7 @@
<string name="automatic">Аўтаматычна</string>
<string name="pages">Старонкi</string>
<string name="clear">Ачысціць</string>
<string name="text_clear_history_prompt">Вы ўпэўненыя, што жадаеце ачысціць гісторыю? Гэта дзеянне нельга будзе адмяніць.</string>
<string name="text_clear_history_prompt">Вы ўпэўненыя, што жадаеце ачысціць гісторыю\? Гэта дзеянне нельга будзе адмяніць.</string>
<string name="remove">Выдаліць</string>
<string name="_s_removed_from_history">\"%s\" выдалена з гiсторыi</string>
<string name="_s_deleted_from_local_storage">\"%s\" выдалена з прылады</string>
@@ -100,7 +100,7 @@
<string name="app_update_available">Даступна абнаўленне праграмы</string>
<string name="show_notification_app_update">Паказваць апавяшчэнне пры наяўнасці новай версіі</string>
<string name="open_in_browser">Адкрыць у браўзеры</string>
<string name="large_manga_save_confirm">У гэтай манге %s. Вы ўпэўненыя, што хочаце захаваць іх усё?</string>
<string name="large_manga_save_confirm">У гэтай манге %s. Вы ўпэўненыя, што хочаце захаваць іх усё\?</string>
<string name="save_manga">Захаваць мангу</string>
<string name="notifications">Паведамленні</string>
<string name="enabled_d_of_d">Уключана %1$d з %2$d</string>
@@ -194,7 +194,7 @@
<string name="cookies_cleared">Усе кукi выдалены</string>
<string name="chapters_checking_progress">Праверка новых частак: %1$d з %2$d</string>
<string name="clear_feed">Ачысціць стужку</string>
<string name="text_clear_updates_feed_prompt">Уся гісторыя абнаўленняў будзе ачышчана і яе нельга будзе вярнуць. Вы ўпэўненыя?</string>
<string name="text_clear_updates_feed_prompt">Уся гісторыя абнаўленняў будзе ачышчана і яе нельга будзе вярнуць. Вы ўпэўненыя\?</string>
<string name="new_chapters_checking">Праверка новых глаў</string>
<string name="reverse">У адваротным парадку</string>
<string name="sign_in">Увайсці</string>
@@ -249,4 +249,6 @@
<string name="show_pages_numbers">Паказваць нумары старонак</string>
<string name="enabled_sources">Уключаныя крыніцы</string>
<string name="available_sources">Даступныя крыніцы</string>
<string name="dynamic_theme">Дынамічная тэма</string>
<string name="dynamic_theme_summary">Ужывае тэму праграмы, заснаваную на каляровай палітры шпалер на прыладзе</string>
</resources>

View File

@@ -1,22 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="remove">Entfernen</string>
<string name="text_clear_history_prompt">Möchtest du wirklich deinen gesamten Leseverlauf löschen\? Diese Aktion kann nicht rückgängig gemacht werden.</string>
<string name="text_clear_history_prompt">Möchtest du wirklich deinen gesamten Leseverlauf löschen\?</string>
<string name="theme">Design</string>
<string name="pages">Seiten</string>
<string name="automatic">Automatisch</string>
<string name="automatic">Nach System</string>
<string name="dark">Dunkel</string>
<string name="light">Hell</string>
<string name="filter">Filter</string>
<string name="genre">Genre</string>
<string name="sort_order">Sortierung</string>
<string name="sort_order">Sortierreihenfolge</string>
<string name="all">Alle</string>
<string name="by_rating">Nach Bewertung</string>
<string name="by_rating">Bewertung</string>
<string name="newest">Neuestes</string>
<string name="popular">Beliebt</string>
<string name="by_name">Nach Name</string>
<string name="by_name">Name</string>
<string name="downloads">Heruntergeladene</string>
<string name="download_complete">Herunterladen abgeschlossen</string>
<string name="download_complete">Heruntergeladen</string>
<string name="search_manga">Manga suchen</string>
<string name="search">Suchen</string>
<string name="share_s">Teilen %s</string>
@@ -24,12 +24,12 @@
<string name="save">Speichern</string>
<string name="enter_category_name">Gib den Name der Kategorie ein</string>
<string name="add">Hinzufügen</string>
<string name="add_new_category">Neue Kategorie hinzufügen</string>
<string name="add_new_category">Neue Kategorie</string>
<string name="add_to_favourites">Zu Favoriten hinzufügen</string>
<string name="you_have_not_favourites_yet">Du hast noch keine Favoriten</string>
<string name="you_have_not_favourites_yet">Noch keine Favoriten</string>
<string name="add_bookmark">Lesezeichen hinzufügen</string>
<string name="read">Lesen</string>
<string name="history_is_empty">Verlauf ist leer</string>
<string name="history_is_empty">Noch kein Verlauf</string>
<string name="nothing_found">Nichts gefunden</string>
<string name="clear_history">Verlauf löschen</string>
<string name="try_again">Erneut versuchen</string>
@@ -62,21 +62,20 @@
<string name="categories_">Kategorien </string>
<string name="cancelling_">Abbrechen </string>
<string name="processing_">Verarbeiten </string>
<string name="manga_downloading_">Manga wird heruntergeladen </string>
<string name="manga_downloading_">Wird heruntergeladen </string>
<string name="create_shortcut">Verknüpfung erstellen </string>
<string name="loading_">Wird geladen </string>
<string name="category_delete_confirm">Möchtest du die Kategorie „%s“ wirklich aus deinen Favoriten entfernen\?
\nAlle enthaltenen Manga gehen dabei verloren.</string>
<string name="text_delete_local_manga">Möchtest du wirklich „%s“ aus dem lokalen Speicher des Telefons löschen\?
\nDieser Vorgang kann nicht rückgängig gemacht werden.</string>
<string name="text_delete_local_manga">\"%s\" dauerhaft vom Gerät löschen\?</string>
<string name="_s_deleted_from_local_storage">„%s“ aus lokalem Speicher gelöscht</string>
<string name="_s_removed_from_history">„%s“ aus dem Verlauf entfernt</string>
<string name="detailed_list">Detaillierte Liste</string>
<string name="list">Liste</string>
<string name="chapters">Kapitel</string>
<string name="details">Einzelheiten</string>
<string name="network_error">Fehler bei der Netzwerkverbindung</string>
<string name="error_occurred">Ein Fehler ist aufgetreten</string>
<string name="network_error">Keine Verbindung zum Internet möglich</string>
<string name="error_occurred">Es ist ein Fehler aufgetreten</string>
<string name="history">Verlauf</string>
<string name="favourites">Favoriten</string>
<string name="local_storage">Lokaler Speicher</string>
@@ -108,7 +107,7 @@
<string name="default_s">Standard: %s</string>
<string name="auth_required">Du solltest diesen Inhalt autorisieren, um ihn zu sehen</string>
<string name="reverse">Umkehren</string>
<string name="new_chapters_checking">Neue Kapitel prüfen</string>
<string name="new_chapters_checking">Neue Kapitel prüfen</string>
<string name="clear_feed">Feed löschen</string>
<string name="sign_in">Anmelden</string>
<string name="password_length_hint">Das Passwort muss mindestens 4 Zeichen lang sein</string>
@@ -116,7 +115,7 @@
<string name="protect_application_subtitle">Gib das Passwort ein, das beim Starten der Anwendung benötigt wird</string>
<string name="next">Nächste</string>
<string name="other">Anderes</string>
<string name="text_clear_search_history_prompt">Möchtest du wirklich alle letzten Suchanfragen entfernen\? Diese Aktion kann nicht rückgängig gemacht werden.</string>
<string name="text_clear_search_history_prompt">Möchtest du wirklich alle letzten Suchanfragen entfernen\?</string>
<string name="search_only_on_s">Nur auf %s suchen</string>
<string name="hide_toolbar">Symbolleiste beim Blättern ausblenden</string>
<string name="read_more">Mehr erfahren</string>
@@ -187,14 +186,14 @@
<string name="clear_pages_cache">Seitencache löschen</string>
<string name="history_and_cache">Verlauf und Cache</string>
<string name="no_description">Keine Beschreibung</string>
<string name="text_file_not_supported">Ungültige Datei. Nur ZIP und CBZ werden unterstützt.</string>
<string name="text_file_not_supported">Wählen Sie entweder eine ZIP- oder CBZ-Datei.</string>
<string name="operation_not_supported">Dieser Vorgang wird nicht unterstützt</string>
<string name="delete">Löschen</string>
<string name="_import">Importieren</string>
<string name="share_image">Bild teilen</string>
<string name="page_saved">Seite erfolgreich gespeichert</string>
<string name="page_saved">Gespeichert</string>
<string name="save_page">Seite speichern</string>
<string name="wait_for_loading_finish">Warte, bis der Ladevorgang beendet ist</string>
<string name="wait_for_loading_finish">Warte, bis der Ladevorgang beendet ist</string>
<string name="clear">Löschen</string>
<string name="taps_on_edges">Auf Kanten tippen</string>
<string name="captcha_solve">Löse</string>
@@ -228,7 +227,7 @@
<string name="about_support_developer_summary">Wenn diese Anwendung dir gefällt, kannst du über Yoomoney (Yandex.Money) finanziell helfen</string>
<string name="about_license">Lizenz</string>
<string name="about_copyright_and_licenses">Urheberrecht und Lizenzen</string>
<string name="about_gratitudes_summary">Diese Leute machen Kotatsu besser!</string>
<string name="about_gratitudes_summary">Diese Leute machen Kotatsu besser</string>
<string name="about_gratitudes">Danksagung</string>
<string name="about_support_developer">Den Entwickler unterstützen</string>
<string name="about_feedback_4pda">Thema auf 4PDA</string>
@@ -249,4 +248,6 @@
<string name="show_pages_numbers">Seitenzahlen anzeigen</string>
<string name="enabled_sources">Freigegebene Quellen</string>
<string name="available_sources">Verfügbare Quellen</string>
<string name="dynamic_theme">Dynamisches Thema</string>
<string name="dynamic_theme_summary">Wendet ein Thema an, das auf dem Farbschema Ihres Hintergrundbildes basiert</string>
</resources>

View File

@@ -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">Error de red</string>
<string name="network_error">No se pudo conectar a Internet</string>
<string name="details">Detalles</string>
<string name="chapters">Capítulos</string>
<string name="list">Lista</string>
@@ -21,12 +21,12 @@
<string name="try_again">Reintentar</string>
<string name="clear_history">Borrar historial</string>
<string name="nothing_found">No se encontró nada</string>
<string name="history_is_empty">Historial vacío</string>
<string name="history_is_empty">Aún sin historial</string>
<string name="read">Leer</string>
<string name="add_bookmark">Añadir marcador</string>
<string name="you_have_not_favourites_yet">Añade tu manga favorito primero</string>
<string name="you_have_not_favourites_yet">Aún no hay favoritos</string>
<string name="add_to_favourites">Añadir a favoritos</string>
<string name="add_new_category">Añadir categoría</string>
<string name="add_new_category">Nueva categoría</string>
<string name="add">Añadir</string>
<string name="enter_category_name">Introduce categoría</string>
<string name="save">Guardar</string>
@@ -35,40 +35,40 @@
<string name="share_s">Compartir %s</string>
<string name="search">Buscar</string>
<string name="search_manga">Buscar manga</string>
<string name="manga_downloading_">Descargar manga</string>
<string name="manga_downloading_">Descargando</string>
<string name="processing_">Procesando…</string>
<string name="download_complete">Descarga completa</string>
<string name="download_complete">Descargado</string>
<string name="downloads">Descargas</string>
<string name="by_name">Por nombre</string>
<string name="by_name">Nombre</string>
<string name="popular">Popularidad</string>
<string name="updated">Actualización</string>
<string name="newest">Recientes</string>
<string name="by_rating">Por calificación</string>
<string name="by_rating">Calificación</string>
<string name="all">Todo</string>
<string name="sort_order">Establecer orden</string>
<string name="sort_order">Orden de clasificación</string>
<string name="genre">Género</string>
<string name="filter">Filtrar</string>
<string name="theme">Tema</string>
<string name="light">Claro</string>
<string name="dark">Oscuro</string>
<string name="automatic">Automático</string>
<string name="automatic">De acuerdo al sistema</string>
<string name="pages">Páginas</string>
<string name="clear">Borrar</string>
<string name="text_clear_history_prompt">¿Realmente quieres borrar todo tu historial de lectura? Esta acción no se puede deshacer.</string>
<string name="text_clear_history_prompt">¿Realmente quieres borrar todo tu historial de lectura\? Esta acción no se puede deshacer.</string>
<string name="remove">Eliminar</string>
<string name="_s_removed_from_history">«%s» retirado del historial</string>
<string name="_s_deleted_from_local_storage">«%s» borrado del almacenamiento local</string>
<string name="wait_for_loading_finish">Espera que termine la carga</string>
<string name="wait_for_loading_finish">Espere a que termine de cargar…</string>
<string name="save_page">Guardar página</string>
<string name="page_saved">Página guardada con éxito</string>
<string name="page_saved">Guardado</string>
<string name="share_image">Compartir imagen</string>
<string name="_import">Importar</string>
<string name="delete">Borrar</string>
<string name="operation_not_supported">Esta operación no está admitida</string>
<string name="text_file_not_supported">Archivo no válido. Sólo se admiten ZIP y CBZ.</string>
<string name="text_file_not_supported">Elija un archivo ZIP o bien un archivo CBZ.</string>
<string name="no_description">Sin descripción</string>
<string name="history_and_cache">Historial y caché</string>
<string name="clear_pages_cache">Borrar la caché de páginas</string>
<string name="clear_pages_cache">Borrar caché de página</string>
<string name="cache">Caché</string>
<string name="text_file_sizes">B|kB|MB|GB|TB</string>
<string name="standard">Estándar</string>
@@ -77,11 +77,10 @@
<string name="grid_size">Tamaño de la cuadrícula</string>
<string name="search_on_s">Buscar en %s</string>
<string name="delete_manga">Borrar manga</string>
<string name="text_delete_local_manga">¿Realmente quieres borrar «%s» del almacenamiento local de tu teléfono\?
\nEsta operación no se puede deshacer.</string>
<string name="text_delete_local_manga">¿Borrar \"%s\" del dispositivo permanentemente\?</string>
<string name="reader_settings">Ajustes del lector</string>
<string name="switch_pages">Cambiar de página</string>
<string name="taps_on_edges">Tapas en los bordes</string>
<string name="taps_on_edges">Toques de borde</string>
<string name="volume_buttons">Botones de volumen</string>
<string name="_continue">Continuar</string>
<string name="warning">Advertencia</string>
@@ -96,12 +95,12 @@
<string name="internal_storage">Almacenamiento interno</string>
<string name="external_storage">Almacenamiento externo</string>
<string name="domain">Dominio</string>
<string name="application_update">Comprobar actualizaciones automáticamente</string>
<string name="application_update">Comprobar nuevas versiones de la aplicación</string>
<string name="app_update_available">Una nueva versión de la aplicación está disponible</string>
<string name="show_notification_app_update">Mostrar notificación si la actualización está disponible</string>
<string name="open_in_browser">Abrir en el navegador</string>
<string name="large_manga_save_confirm">Este manga tiene %s. ¿Quieres guardarlo todo?</string>
<string name="save_manga">Guardar manga</string>
<string name="show_notification_app_update">Mostrar notificación si una nueva versión está disponible</string>
<string name="open_in_browser">Abrir en navegador web</string>
<string name="large_manga_save_confirm">Este manga tiene %s. ¿Quieres guardarlo todo\?</string>
<string name="save_manga">Guardar</string>
<string name="notifications">Notificaciones</string>
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">Activado %1$d de %2$d</string>
<string name="new_chapters">Nuevos capítulos</string>
@@ -111,26 +110,26 @@
<string name="restart">Reiniciar</string>
<string name="notifications_settings">Configuración de las notificaciones</string>
<string name="notification_sound">Sonido de las notificaciones</string>
<string name="light_indicator">Indicador de luz</string>
<string name="light_indicator">Indicador LED</string>
<string name="vibration">Vibración</string>
<string name="favourites_categories">Categorías favoritas</string>
<string name="categories_">Categorías…</string>
<string name="rename">Renombrar</string>
<string name="category_delete_confirm">¿Realmente quieres eliminar la categoría «%s» de tus favoritos\?
\nTodo el manga en ella se perderá.</string>
<string name="remove_category">Quitar categoría</string>
<string name="text_categories_holder">Puedes usar categorías para organizar tus mangas favoritos. Pulsa «+» para crear una categoría</string>
<string name="manga_shelf">Estante de manga</string>
<string name="recent_manga">Manga reciente</string>
<string name="pages_animation">Animación de páginas</string>
<string name="manga_save_location">Ubicación de descarga del manga</string>
<string name="category_delete_confirm">¿Remover la categoría \"%s\" de sus favoritos\?
\nTodo el manga en esta se perderá.</string>
<string name="remove_category">Remover</string>
<string name="text_categories_holder">Puedes usar categorías para organizar tus favoritos. Pulsa «+» para crear una categoría</string>
<string name="manga_shelf">Estante</string>
<string name="recent_manga">Reciente</string>
<string name="pages_animation">Animación de página</string>
<string name="manga_save_location">Carpeta para descargas</string>
<string name="not_available">No disponible</string>
<string name="cannot_find_available_storage">No se puede encontrar ningún almacenamiento disponible</string>
<string name="cannot_find_available_storage">No hay almacenamiento disponible</string>
<string name="other_storage">Otro almacenamiento</string>
<string name="use_ssl">Utilizar conexión segura (HTTPS)</string>
<string name="done">Aceptar</string>
<string name="all_favourites">Todos los favoritos</string>
<string name="favourites_category_empty">Esta categoría está vacía</string>
<string name="favourites_category_empty">Categoría vacía</string>
<string name="read_later">Leer más tarde</string>
<string name="updates">Actualizaciones</string>
<string name="text_feed_holder">Aquí verás los nuevos episodios del manga que estás leyendo</string>
@@ -149,7 +148,7 @@
<string name="enter_password">Introducir contraseña</string>
<string name="wrong_password">Contraseña incorrecta</string>
<string name="protect_application">Proteger aplicación</string>
<string name="protect_application_summary">Pide la contraseña al iniciar la aplicación</string>
<string name="protect_application_summary">Pedir una contraseña al iniciar Kotatsu</string>
<string name="repeat_password">Repite la contraseña</string>
<string name="passwords_mismatch">Las contraseñas no coinciden</string>
<string name="about">Acerca de</string>
@@ -158,8 +157,8 @@
<string name="checking_for_updates">Comprobar si hay actualizaciones…</string>
<string name="update_check_failed">Fallo en la comprobación de actualizaciones</string>
<string name="no_update_available">No hay actualizaciones disponibles</string>
<string name="right_to_left">Derecha a izquierda</string>
<string name="prefer_rtl_reader">Preferir lector de derecha a izquierda</string>
<string name="right_to_left">Derecha a izquierda (←)</string>
<string name="prefer_rtl_reader">Preferir lector de derecha a izquierda (→)</string>
<string name="prefer_rtl_reader_summary">Puedes configurar el modo de lectura para cada manga por separado</string>
<string name="create_category">Nueva categoría</string>
<string name="report_github">Crear incidencia en GitHub</string>
@@ -171,14 +170,14 @@
<string name="black_dark_theme">Tema oscuro auténtico</string>
<string name="black_dark_theme_summary">Útil para pantallas AMOLED</string>
<string name="restart_required">Se requiere reinicio</string>
<string name="backup_restore"><![CDATA[Respaldo y restauración]]></string>
<string name="backup_restore">Respaldo y restauración</string>
<string name="create_backup">Crear copia de seguridad de datos</string>
<string name="restore_backup">Restaurar desde la copia de seguridad</string>
<string name="data_restored">Datos restaurados</string>
<string name="preparing_">Preparando…</string>
<string name="file_not_found">Archivo no encontrado</string>
<string name="data_restored_success">Todos los datos fueron restaurados con éxito</string>
<string name="data_restored_with_errors">Los datos fueron restaurados, pero hay errores</string>
<string name="data_restored_with_errors">Los datos fueron restaurados, pero hay errores.</string>
<string name="backup_information">Puedes crear una copia de seguridad de tu historial y favoritos para restaurarla</string>
<string name="just_now">Ahora mismo</string>
<string name="yesterday">Ayer</string>
@@ -194,7 +193,7 @@
<string name="cookies_cleared">Se han eliminado todas las cookies</string>
<string name="chapters_checking_progress">Buscando nuevos capítulos: %1$d de %2$d</string>
<string name="clear_feed">Limpiar feed</string>
<string name="text_clear_updates_feed_prompt">Todo el historial de actualizaciones se borrará y esta acción no se puede deshacer. ¿Está seguro?</string>
<string name="text_clear_updates_feed_prompt">Todo el historial de actualizaciones se borrará y esta acción no se puede deshacer. ¿Está seguro\?</string>
<string name="new_chapters_checking">Comprobación de nuevos capítulos</string>
<string name="reverse">Invertir</string>
<string name="sign_in">Iniciar sesión</string>
@@ -207,14 +206,14 @@
<string name="password_length_hint">La contraseña debe tener al menos 4 caracteres</string>
<string name="text_local_holder_secondary">Puedes guardarlo desde fuentes in línea o importarlo desde un archivo.</string>
<string name="text_local_holder_primary">Todavía no tienes ningún manga guardado</string>
<string name="text_history_holder_secondary">Puede encontrar qué leer en el menú lateral.</string>
<string name="text_history_holder_primary">El manga que estás leyendo se mostrará aquí</string>
<string name="text_history_holder_secondary">Encuentra qué leer en el menú lateral.</string>
<string name="text_history_holder_primary">Lo que leas se mostrará aquí</string>
<string name="text_empty_holder_primary">Está un poco vacío aquí…</string>
<string name="about_gratitudes">Agradecimientos</string>
<string name="about_support_developer_summary">Si te gusta esta aplicación, puedes ayudar económicamente a través de Yoomoney (ex. Yandex.Money)</string>
<string name="about_support_developer">Apoyar al desarrollador</string>
<string name="search_only_on_s">Buscar sólo en %s</string>
<string name="about_gratitudes_summary">¡Estas personas hacen que Kotatsu sea mejor!</string>
<string name="about_gratitudes_summary">Todas estas personas hicieron que Kotatsu fuera mejor.</string>
<string name="about_license">Licencia</string>
<string name="about_copyright_and_licenses">Derechos de autor y licencias</string>
<string name="chapter_is_missing">Falta un capítulo</string>
@@ -237,16 +236,18 @@
<string name="state_ongoing">En curso</string>
<string name="hide_toolbar">Ocultar la barra de herramientas al desplazarse</string>
<string name="chapter_is_missing_text">Este capítulo no aparece en su dispositivo. Descárguelo o léalo en línea.</string>
<string name="auth_complete">Autorización completa</string>
<string name="auth_complete">Autorizado</string>
<string name="about_feedback_4pda">Tema sobre 4PDA</string>
<string name="date_format">Formato de la fecha</string>
<string name="system_default">Por defecto</string>
<string name="tracker_warning">Algunos fabricantes pueden cambiar el comportamiento del sistema, lo que podría interrumpir las tareas en segundo plano.</string>
<string name="error_empty_name">El nombre no debe estar vacío</string>
<string name="auth_not_supported_by">Autorización en %s no es compatible</string>
<string name="text_clear_cookies_prompt">Se cerrará la sesión de todas las fuentes en las que esté autorizado</string>
<string name="auth_not_supported_by">No se admite iniciar sesión en %s</string>
<string name="text_clear_cookies_prompt">Serás desconectado de todas las fuentes.</string>
<string name="exclude_nsfw_from_history">Excluye manga NSFW del historial</string>
<string name="show_pages_numbers">Mostrar los números de páginas</string>
<string name="enabled_sources">Fuentes activadas</string>
<string name="available_sources">Fuentes disponibles</string>
<string name="dynamic_theme">Tema dinámico</string>
<string name="dynamic_theme_summary">Aplica un tema creado a partir del esquema de colores de su fondo de pantalla</string>
</resources>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="close_menu">بستن منو</string>
<string name="open_menu">بازکردن منو</string>
<string name="local_storage">محل ذخیره سازی</string>

View File

@@ -1,35 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="wait_for_loading_finish">Attendez la fin du chargement</string>
<string name="wait_for_loading_finish">Attendez la fin du chargement</string>
<string name="read_more">Lire la suite</string>
<string name="tracker_warning">Certains fabricants peuvent modifier le comportement du système, ce qui peut interrompre les tâches d\'arrière-plan.</string>
<string name="backup_saved">Sauvegarde enregistrée avec succès</string>
<string name="tracker_warning">Certains appareils ont un comportement différent du système, ce qui peut interrompre les tâches d\'arrière-plan.</string>
<string name="backup_saved">Sauvegarde enregistrée</string>
<string name="description">Description</string>
<string name="welcome">Bienvenue</string>
<string name="languages">Langues</string>
<string name="other">Autre</string>
<string name="text_clear_search_history_prompt">Voulez-vous vraiment supprimer toutes les requêtes de recherche récentes\? Cette action ne peut pas être annulée.</string>
<string name="text_clear_search_history_prompt">Supprimer définitivement toutes les requêtes de recherche récentes \?</string>
<string name="search_only_on_s">Rechercher uniquement sur %s</string>
<string name="hide_toolbar">Masquer la barre d\'outils lors du défilement</string>
<string name="password_length_hint">Le mot de passe doit comporter au moins 4 caractères</string>
<string name="password_length_hint">Le mot de passe doit comporter 4 caractères ou plus</string>
<string name="confirm">Confirmer</string>
<string name="protect_application_subtitle">Entrez le mot de passe qui sera demandé au démarrage de l\'application</string>
<string name="protect_application_subtitle">Entrez un mot de passe pour démarrer l\'application avec</string>
<string name="next">Suivant</string>
<string name="_and_x_more">… et %1$d autre(s)</string>
<string name="default_s">Par défaut : %s</string>
<string name="auth_required">Vous devez autoriser la visualisation de ce contenu</string>
<string name="auth_required">Connectez-vous pour voir ce contenu</string>
<string name="sign_in">Se connecter</string>
<string name="reverse">Inverser</string>
<string name="new_chapters_checking">Recherche de nouveaux chapitres</string>
<string name="text_clear_updates_feed_prompt">L\'historique des mises à jour sera effacé et cette action ne pourra pas être annulée. Êtes-vous sûr·e\?</string>
<string name="new_chapters_checking">À la recherche de nouveaux chapitres</string>
<string name="text_clear_updates_feed_prompt">Effacer définitivement l\'historique des mises à jour \?</string>
<string name="clear_feed">Effacer le flux</string>
<string name="chapters_checking_progress">Recherche de nouveaux chapitres : %1$d sur %2$d</string>
<string name="cookies_cleared">Tous les cookies ont été retirés</string>
<string name="clear_cookies">Effacer les cookies</string>
<string name="captcha_solve">Résoudre</string>
<string name="captcha_required">CAPTCHA est requis</string>
<string name="captcha_required">CAPTCHA requis</string>
<string name="silent">Silencieux</string>
<string name="reader_mode_hint">La configuration choisie sera mémorisée pour ce manga</string>
<string name="reader_mode_hint">On se souviendra de la configuration choisie pour ce manga</string>
<string name="tap_to_try_again">Appuyez pour réessayer</string>
<string name="today">Aujourd\'hui</string>
<string name="group">Groupe</string>
@@ -38,16 +38,16 @@
<string name="just_now">À l\'instant</string>
<string name="backup_information">Vous pouvez créer une sauvegarde de votre historique et de vos favoris et la restaurer</string>
<string name="data_restored_with_errors">Les données ont été restaurées, mais il y a des erreurs</string>
<string name="data_restored_success">Toutes les données ont été restaurées avec succès</string>
<string name="data_restored_success">Toutes les données ont été restaurées</string>
<string name="file_not_found">Fichier introuvable</string>
<string name="preparing_">Préparation…</string>
<string name="data_restored">Données restaurées</string>
<string name="data_restored">Restauré</string>
<string name="restore_backup">Restaurer à partir d\'une sauvegarde</string>
<string name="create_backup">Créer une sauvegarde des données</string>
<string name="backup_restore">Sauvegarde et restauration</string>
<string name="restart_required">Redémarrage nécessaire</string>
<string name="black_dark_theme_summary">Utile pour les écrans AMOLED</string>
<string name="black_dark_theme">Thème noir foncé</string>
<string name="black_dark_theme_summary">Utilise moins d\'énergie pour les écrans AMOLED</string>
<string name="black_dark_theme">Noir</string>
<string name="zoom_mode_keep_start">Garder au début</string>
<string name="zoom_mode_fit_width">Ajuster à la largeur</string>
<string name="zoom_mode_fit_height">Ajuster à la hauteur</string>
@@ -55,9 +55,9 @@
<string name="scale_mode">Mode mise à l\'échelle</string>
<string name="report_github">Signaler un problème sur GitHub</string>
<string name="create_category">Nouvelle catégorie</string>
<string name="prefer_rtl_reader_summary">Vous pouvez configurer le mode de lecture pour chaque manga séparément</string>
<string name="prefer_rtl_reader">Préférer le lecteur de droite à gauche</string>
<string name="right_to_left">De droite à gauche</string>
<string name="prefer_rtl_reader_summary">Le mode de lecture peut être configuré séparément pour chaque série</string>
<string name="prefer_rtl_reader">Préférer le lecteur de droite à gauche (→)</string>
<string name="right_to_left">De droite à gauche (←)</string>
<string name="no_update_available">Aucune mise à jour disponible</string>
<string name="update_check_failed">Échec de la recherche de mise à jour</string>
<string name="checking_for_updates">Recherche de mises à jour…</string>
@@ -66,49 +66,49 @@
<string name="about">À propos</string>
<string name="passwords_mismatch">Les mots de passe ne correspondent pas</string>
<string name="repeat_password">Répéter le mot de passe</string>
<string name="protect_application_summary">Demander le mot de passe au démarrage de l\'appli</string>
<string name="protect_application_summary">Demander le mot de passe au démarrage de Kotatsu</string>
<string name="protect_application">Protéger l\'application</string>
<string name="wrong_password">Mot de passe erroné</string>
<string name="enter_password">Entrez le mot de passe</string>
<string name="dont_check">Ne pas vérifier</string>
<string name="track_sources">Vérifier les mises à jour pour les mangas</string>
<string name="track_sources">Rechercher les mises à jour</string>
<string name="feed_will_update_soon">La mise à jour des flux commencera bientôt</string>
<string name="update">Mettre à Jour</string>
<string name="rotate_screen">Faire pivoter l\'écran</string>
<string name="updates_feed_cleared">Flux de mises à jour effacé</string>
<string name="updates_feed_cleared">Effacé</string>
<string name="clear_updates_feed">Effacer le flux des mises à jour</string>
<string name="waiting_for_network">En attente du réseau…</string>
<string name="size_s">Taille : %s</string>
<string name="new_version_s">Nouvelle version : %s</string>
<string name="related">Connexes</string>
<string name="search_results">Résultats de la recherche</string>
<string name="text_feed_holder">Ici, vous verrez les nouveaux chapitres du manga que vous lisez</string>
<string name="text_feed_holder">Les nouveaux chapitres de ce que vous lisez sont présentés ici</string>
<string name="updates">Mises à jour</string>
<string name="read_later">Lire plus tard</string>
<string name="favourites_category_empty">Cette catégorie est vide</string>
<string name="favourites_category_empty">Catégorie vide</string>
<string name="all_favourites">Tous les favoris</string>
<string name="done">Terminé</string>
<string name="use_ssl">Utiliser une connexion sécurisée (HTTPS)</string>
<string name="other_storage">Autre stockage</string>
<string name="cannot_find_available_storage">Impossible de trouver un stockage disponible</string>
<string name="cannot_find_available_storage">Pas de stockage disponible</string>
<string name="not_available">Non disponible</string>
<string name="manga_save_location">Emplacement des téléchargements de mangas</string>
<string name="pages_animation">Animation des pages</string>
<string name="recent_manga">Mangas récents</string>
<string name="manga_shelf">Étagère à mangas</string>
<string name="text_local_holder_secondary">Vous pouvez l\'enregistrer à partir de sources en ligne ou l\'importer à partir d\'un fichier.</string>
<string name="text_local_holder_primary">Vous n\'avez pas encore enregistré de manga</string>
<string name="text_history_holder_secondary">Vous pouvez trouver ce qu\'il faut lire dans le menu latéral.</string>
<string name="text_history_holder_primary">Les mangas que vous lisez seront affichés ici</string>
<string name="manga_save_location">Dossier pour les téléchargements</string>
<string name="pages_animation">Animation de page</string>
<string name="recent_manga">Récents</string>
<string name="manga_shelf">Étagère</string>
<string name="text_local_holder_secondary">Enregistrez-le à partir de sources en ligne ou importez des fichiers.</string>
<string name="text_local_holder_primary">Sauvegardez d\'abord quelque chose</string>
<string name="text_history_holder_secondary">Trouvez ce que vous voulez lire dans le menu latéral.</string>
<string name="text_history_holder_primary">Ce que vous lisez sera affiché ici</string>
<string name="text_search_holder_secondary">Essayez de reformuler la requête.</string>
<string name="text_categories_holder">Vous pouvez utiliser des catégories pour organiser vos mangas préférés. Appuyez sur « + » pour créer une catégorie</string>
<string name="text_categories_holder">Vous pouvez utiliser des catégories pour organiser vos favoris. Appuyez sur « + » pour créer une catégorie</string>
<string name="text_empty_holder_primary">C\'est un peu vide ici…</string>
<string name="remove_category">Retirer la catégorie</string>
<string name="category_delete_confirm">Voulez-vous vraiment supprimer la catégorie « %s » de vos favoris\?
<string name="remove_category">Retirer</string>
<string name="category_delete_confirm">Retirer la catégorie « %s » de vos favoris \?
\nTous les mangas qu\'elle contient seront perdus.</string>
<string name="rename">Renommer</string>
<string name="categories_">Catégories…</string>
<string name="favourites_categories">Catégories de favoris</string>
<string name="favourites_categories">Catégories favorites</string>
<string name="vibration">Vibration</string>
<string name="light_indicator">Indicateur lumineux</string>
<string name="notification_sound">Son de notification</string>
@@ -118,33 +118,32 @@
<string name="download">Télécharger</string>
<string name="show_notification_new_chapters">Avertir des mises à jour des mangas que vous lisez</string>
<string name="new_chapters">Nouveaux chapitres</string>
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">Activé %1$d sur %2$d</string>
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">%1$d de %2$d activé(s)</string>
<string name="notifications">Notifications</string>
<string name="large_manga_save_confirm">Ce manga a %s. Voulez-vous l\'enregistrer en entier\?</string>
<string name="save_manga">Enregistrer le manga</string>
<string name="large_manga_save_confirm">Ce manga a %s. Tout sauvegarder \?</string>
<string name="save_manga">Enregistrer</string>
<string name="open_in_browser">Ouvrir dans le navigateur</string>
<string name="show_notification_app_update">Afficher une notification si une mise à jour est disponible</string>
<string name="app_update_available">Une mise à jour de l\'application est disponible</string>
<string name="application_update">Vérifier les mises à jour automatiquement</string>
<string name="show_notification_app_update">Afficher une notification si une nouvelle version est disponible</string>
<string name="app_update_available">Une nouvelle version de l\'application est disponible</string>
<string name="application_update">Vérifier les nouvelles versions de l\'application</string>
<string name="domain">Domaine</string>
<string name="external_storage">Stockage externe</string>
<string name="internal_storage">Stockage interne</string>
<string name="gestures_only">Gestes uniquement</string>
<string name="search_history_cleared">Historique des recherches effacé</string>
<string name="search_history_cleared">Effacé</string>
<string name="clear_search_history">Effacer l\'historique de recherche</string>
<string name="clear_thumbs_cache">Vider le cache des miniatures</string>
<string name="error">Erreur</string>
<string name="cancelling_">Annulation…</string>
<string name="dont_ask_again">Ne plus demander</string>
<string name="network_consumption_warning">Cette opération peut consommer beaucoup de trafic réseau</string>
<string name="network_consumption_warning">Ceci peut transférer beaucoup de données</string>
<string name="warning">Avertissement</string>
<string name="_continue">Continuer</string>
<string name="volume_buttons">Boutons de volume</string>
<string name="taps_on_edges">Appuis sur les bords</string>
<string name="taps_on_edges">Appuis au bord</string>
<string name="switch_pages">Changer de pages</string>
<string name="reader_settings">Paramètres du lecteur</string>
<string name="text_delete_local_manga">Voulez-vous vraiment supprimer « %s » de la mémoire locale de votre téléphone\?
\nCette opération ne peut pas être annulée.</string>
<string name="text_delete_local_manga">Supprimer « %s » de l\'appareil de façon permanente \?</string>
<string name="delete_manga">Supprimer le manga</string>
<string name="search_on_s">Rechercher sur %s</string>
<string name="grid_size">Taille de la grille</string>
@@ -153,23 +152,23 @@
<string name="standard">Standard</string>
<string name="text_file_sizes">o|ko|Mo|Go|To</string>
<string name="cache">Cache</string>
<string name="clear_pages_cache">Vider le cache des pages</string>
<string name="clear_pages_cache">Vider le cache de la page</string>
<string name="history_and_cache">Historique et cache</string>
<string name="no_description">Aucune description</string>
<string name="text_file_not_supported">Fichier invalide. Seuls les fichiers ZIP et CBZ sont pris en charge.</string>
<string name="text_file_not_supported">Choisissez un fichier ZIP ou CBZ.</string>
<string name="operation_not_supported">Cette opération n\'est pas prise en charge</string>
<string name="delete">Supprimer</string>
<string name="_import">Importer</string>
<string name="share_image">Partager l\'image</string>
<string name="page_saved">Page sauvegardée avec succès</string>
<string name="page_saved">Sauvegardé</string>
<string name="save_page">Sauvegarder la page</string>
<string name="_s_deleted_from_local_storage">« %s » supprimé du stockage local</string>
<string name="_s_removed_from_history">« %s » retiré de l\'historique</string>
<string name="remove">Retirer</string>
<string name="text_clear_history_prompt">Voulez-vous vraiment effacer tout votre historique de lecture\? Cette action ne peut pas être annulée.</string>
<string name="text_clear_history_prompt">Effacer définitivement l\'historique de lecture \?</string>
<string name="clear">Effacer</string>
<string name="pages">Pages</string>
<string name="automatic">Automatique</string>
<string name="automatic">Suivre le système</string>
<string name="dark">Sombre</string>
<string name="light">Clair</string>
<string name="theme">Thème</string>
@@ -177,15 +176,15 @@
<string name="genre">Genre</string>
<string name="sort_order">Ordre de tri</string>
<string name="all">Tous</string>
<string name="by_rating">Par évaluation</string>
<string name="by_rating">Évaluation</string>
<string name="newest">Le plus récent</string>
<string name="updated">Mis à jour</string>
<string name="popular">Populaire</string>
<string name="by_name">Par nom</string>
<string name="by_name">Nom</string>
<string name="downloads">Téléchargements</string>
<string name="download_complete">Téléchargement terminé</string>
<string name="download_complete">Téléchargé</string>
<string name="processing_">Traitement…</string>
<string name="manga_downloading_">Téléchargement de mangas</string>
<string name="manga_downloading_">Téléchargement…</string>
<string name="search_manga">Rechercher un manga</string>
<string name="search">Rechercher</string>
<string name="share_s">Partager %s</string>
@@ -194,12 +193,12 @@
<string name="save">Enregistrer</string>
<string name="enter_category_name">Entrez le nom de la catégorie</string>
<string name="add">Ajouter</string>
<string name="add_new_category">Ajouter une nouvelle catégorie</string>
<string name="add_new_category">Nouvelle catégorie</string>
<string name="add_to_favourites">Ajouter aux favoris</string>
<string name="you_have_not_favourites_yet">Vous n\'avez pas encore de favoris</string>
<string name="you_have_not_favourites_yet">Aucun favori pour le moment</string>
<string name="add_bookmark">Ajouter un marque-page</string>
<string name="read">Lire</string>
<string name="history_is_empty">L\'historique est vide</string>
<string name="history_is_empty">Pas encore d\'historique</string>
<string name="nothing_found">Rien n\'a été trouvé</string>
<string name="clear_history">Effacer l\'historique</string>
<string name="try_again">Réessayer</string>
@@ -214,22 +213,22 @@
<string name="list">Liste</string>
<string name="chapters">Chapitres</string>
<string name="details">Détails</string>
<string name="network_error">Erreur de connexion au réseau</string>
<string name="network_error">Impossible de se connecter à Internet</string>
<string name="error_occurred">Une erreur s\'est produite</string>
<string name="history">Historique</string>
<string name="favourites">Favoris</string>
<string name="local_storage">Stockage local</string>
<string name="open_menu">Ouvrir le menu</string>
<string name="close_menu">Fermer le menu</string>
<string name="chapter_is_missing">Chapitre manquant</string>
<string name="chapter_is_missing_text">Ce chapitre est manquant sur votre appareil. Téléchargez-le ou lisez-le en ligne.</string>
<string name="text_downloads_holder">Il n\'y a actuellement aucun téléchargement actif</string>
<string name="chapter_is_missing">Le chapitre est manquant</string>
<string name="chapter_is_missing_text">Téléchargez ou lisez ce chapitre manquant en ligne.</string>
<string name="text_downloads_holder">Aucun téléchargement actif</string>
<string name="queued">En file d\'attente</string>
<string name="about_support_developer_summary">Si vous aimez cette application, vous pouvez aider financièrement par le biais de Yoomoney (Yandex.Money)</string>
<string name="about_support_developer_summary">Si vous aimez cette application, vous pouvez envoyer de l\'argent via Yoomoney (ex. Yandex.Money)</string>
<string name="about_support_developer">Soutenir le concepteur</string>
<string name="about_license">Licence</string>
<string name="about_copyright_and_licenses">Droits d\'auteur et licences</string>
<string name="about_gratitudes_summary">Ces gens font que Kotatsu s\'améliore !</string>
<string name="about_gratitudes_summary">Ces personnes ont toutes rendu Kotatsu meilleur</string>
<string name="about_gratitudes">Remerciements</string>
<string name="about_feedback_4pda">Sujet sur 4PDA</string>
<string name="about_feedback">Remarques</string>
@@ -237,16 +236,18 @@
<string name="about_app_translation">Traduction</string>
<string name="about_app_translation_summary">Traduire cette application</string>
<string name="genres">Genres</string>
<string name="text_clear_cookies_prompt">Vous serez déconnecté·e de toutes les sources pour lesquelles vous avez une autorisation</string>
<string name="auth_not_supported_by">L\'autorisation sur %s n\'est pas prise en charge</string>
<string name="auth_complete">Autorisation complète</string>
<string name="text_clear_cookies_prompt">Vous serez déconnecté de toutes les sources</string>
<string name="auth_not_supported_by">La connexion sur %s n\'est pas prise en charge</string>
<string name="auth_complete">Autorisé</string>
<string name="state_finished">Terminé</string>
<string name="state_ongoing">En cours</string>
<string name="date_format">Format de la date</string>
<string name="system_default">Par défaut</string>
<string name="exclude_nsfw_from_history">Exclure les mangas osés de l\'historique</string>
<string name="error_empty_name">Le nom ne doit pas être vide</string>
<string name="show_pages_numbers">Afficher les numéros de pages</string>
<string name="enabled_sources">Sources activées</string>
<string name="error_empty_name">Vous devez entrer un nom</string>
<string name="show_pages_numbers">Pages numéroes</string>
<string name="enabled_sources">Sources utilisées</string>
<string name="available_sources">Sources disponibles</string>
<string name="dynamic_theme">Thème dynamique</string>
<string name="dynamic_theme_summary">Applique un thème créé d\'après la palette de couleurs de votre fond d\'écran</string>
</resources>

View File

@@ -0,0 +1,253 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="history">履歴</string>
<string name="loading_">ロード中…</string>
<string name="chapter_d_of_d">チャプター %1$d of %2$d</string>
<string name="share">共有</string>
<string name="clear_history">履歴を削除</string>
<string name="search">検索</string>
<string name="search_manga">漫画を検索</string>
<string name="close">閉じる</string>
<string name="favourites">お気に入り</string>
<string name="error_occurred">エラーが発生しました</string>
<string name="details">詳細</string>
<string name="chapters">チャプター</string>
<string name="list">リスト</string>
<string name="detailed_list">詳細リスト</string>
<string name="grid">グリッド</string>
<string name="list_mode">リストモード</string>
<string name="remote_sources">リモートソース</string>
<string name="try_again">再試行</string>
<string name="nothing_found">何も見つかりませんでした</string>
<string name="history_is_empty">まだ履歴はありません</string>
<string name="read">読む</string>
<string name="you_have_not_favourites_yet">お気に入りの本はありません</string>
<string name="add_to_favourites">お気に入りの本</string>
<string name="add_new_category">新たなカテゴリー</string>
<string name="add">追加</string>
<string name="save">保存</string>
<string name="create_shortcut">ショートカットを作成します…</string>
<string name="share_s">共有する%s</string>
<string name="manga_downloading_">ダウンロード中…</string>
<string name="processing_">処理中…</string>
<string name="download_complete">ダウンロードした本</string>
<string name="downloads">ダウンロード</string>
<string name="by_name">名前</string>
<string name="popular">人気</string>
<string name="local_storage">ローカルストレージ</string>
<string name="newest">最新</string>
<string name="by_rating">評価</string>
<string name="all">全て</string>
<string name="sort_order">ソート順に並べ替え</string>
<string name="genre">ジャンル</string>
<string name="automatic">フォローシステム</string>
<string name="clear">クリア</string>
<string name="text_clear_history_prompt">すべての履歴を永久にクリアしますか?</string>
<string name="remove">削除</string>
<string name="_s_removed_from_history">\"%s\"が履歴から削除されました</string>
<string name="_s_deleted_from_local_storage">\"%s \"がローカルストレージから削除されました</string>
<string name="wait_for_loading_finish">読み込みが完了するまでお待ちください…</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="operation_not_supported">この操作はサポートされていません</string>
<string name="no_description">説明がありません</string>
<string name="history_and_cache">履歴とキャッシュ</string>
<string name="clear_pages_cache">ページのキャッシュをクリアする</string>
<string name="text_file_sizes">B|kB|MB|GB|TB</string>
<string name="close_menu">閉じる</string>
<string name="open_menu">開く</string>
<string name="settings">設定</string>
<string name="light">ライトテーマ</string>
<string name="filter">フィルター</string>
<string name="dark">ダークテーマ</string>
<string name="pages">ページ</string>
<string name="theme">テーマ</string>
<string name="network_error">インターネットに接続出来ませんでした</string>
<string name="add_bookmark">ブックマークに追加</string>
<string name="enter_category_name">カテゴリー名を入力してください</string>
<string name="updated">アップデート</string>
<string name="cache">キャッシュ</string>
<string name="text_file_not_supported">ZIPファイルまたはCBZファイルを選択してください。</string>
<string name="standard">標準</string>
<string name="webtoon">ウェブトゥーン</string>
<string name="read_mode">読み取りモード</string>
<string name="grid_size">グリッドのサイズ</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="taps_on_edges">エッジタップ</string>
<string name="volume_buttons">ボリュームボタン</string>
<string name="_continue">続ける</string>
<string name="warning">警告</string>
<string name="network_consumption_warning">これにより大量のデータが転送される可能性があります</string>
<string name="dont_ask_again">二度と表示しない</string>
<string name="cancelling_">キャンセル…</string>
<string name="error">エラー</string>
<string name="clear_thumbs_cache">サムネイルキャッシュをクリア</string>
<string name="search_history_cleared">クリア</string>
<string name="gestures_only">ジェスチャーのみ</string>
<string name="internal_storage">内部ストレージ</string>
<string name="domain">ドメイン</string>
<string name="application_update">Kotatsuの新しい更新を確認</string>
<string name="show_notification_app_update">新しい更新が利用可能かどうか通知を表示</string>
<string name="open_in_browser">ブラウザーで開く</string>
<string name="large_manga_save_confirm">この漫画には%sがあります。 すべて保存しますか?</string>
<string name="save_manga">保存</string>
<string name="notifications">通知</string>
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">%2$dの%1$d</string>
<string name="new_chapters">新しいチャプター</string>
<string name="download">ダウンロード</string>
<string name="read_from_start">最初から読む</string>
<string name="restart">再開</string>
<string name="notifications_settings">通知の設定</string>
<string name="notification_sound">通知音</string>
<string name="light_indicator">LEDインジケータ</string>
<string name="vibration">バイブレーション</string>
<string name="favourites_categories">お気に入りのカテゴリー</string>
<string name="categories_">カテゴリー…</string>
<string name="rename">名前を変更</string>
<string name="remove_category">削除</string>
<string name="text_search_holder_secondary">クエリを再定式化してみてください。</string>
<string name="text_history_holder_primary">読んだ内容がここに表示されます</string>
<string name="text_history_holder_secondary">サイドメニューで何を読むかを見つけてください。</string>
<string name="text_local_holder_primary">最初に何かを保存する</string>
<string name="manga_shelf">本棚</string>
<string name="recent_manga">最近</string>
<string name="pages_animation">ページアニメーション</string>
<string name="manga_save_location">ダウンロード用のフォルダ</string>
<string name="not_available">利用出来ません</string>
<string name="cannot_find_available_storage">使用可能なストレージがありません</string>
<string name="other_storage">その他のストレージ</string>
<string name="use_ssl">安全な(HTTPS)接続を使用する</string>
<string name="done">完了</string>
<string name="all_favourites">全てのお気に入り</string>
<string name="read_later">後で読む</string>
<string name="updates">更新</string>
<string name="text_feed_holder">あなたが読んでいるものの新しいチャプターがここに示されています</string>
<string name="search_results">の検索結果</string>
<string name="related">関連している本</string>
<string name="size_s">サイズ:%s</string>
<string name="waiting_for_network">ネットワークに接続されていません…</string>
<string name="clear_updates_feed">更新フィードをクリア</string>
<string name="updates_feed_cleared">クリア</string>
<string name="update">アップデート</string>
<string name="feed_will_update_soon">フィードの更新はまもなく開始されます</string>
<string name="track_sources">更新を探す</string>
<string name="dont_check">チェックしない</string>
<string name="protect_application_summary">Kotatsuを起動したときにパスワードを入力する</string>
<string name="repeat_password">パスワードを繰り返す</string>
<string name="passwords_mismatch">パスワードが違います</string>
<string name="about">この本の詳細</string>
<string name="app_version">現在のバージョン%s</string>
<string name="clear_search_history">検索履歴をクリア</string>
<string name="external_storage">外部ストレージ</string>
<string name="app_update_available">Kotatsuの新しい更新が利用可能です</string>
<string name="show_notification_new_chapters">あなたが読んでいる漫画の更新について通知</string>
<string name="text_empty_holder_primary">ここは空っぽです…</string>
<string name="text_categories_holder">カテゴリーを使用してお気に入りを整理できます。 «+»を押してカテゴリーを作成出来ます</string>
<string name="favourites_category_empty">空のカテゴリー</string>
<string name="category_delete_confirm">お気に入りから「%s」カテゴリーを削除しますか
\nその中のすべての漫画は失われます。</string>
<string name="text_local_holder_secondary">オンラインソースから保存するかファイルをインポートします。</string>
<string name="new_version_s">新しいバージョン:%s</string>
<string name="rotate_screen">画面を回転させる</string>
<string name="checking_for_updates">アップデートを確認しています…</string>
<string name="enter_password">パスワードを入力してください</string>
<string name="wrong_password">パスワードが間違っています</string>
<string name="protect_application">アプリを保護する</string>
<string name="check_for_updates">最新のアップデートを確認する</string>
<string name="update_check_failed">アップデートを見つける事が出来ませんでした</string>
<string name="no_update_available">利用可能なアップデートはありません</string>
<string name="right_to_left">右から左(←)</string>
<string name="prefer_rtl_reader">右から左(→)の読書を好む</string>
<string name="about_feedback">フィードバック</string>
<string name="about_feedback_4pda">4PDAに関する話題</string>
<string name="about_support_developer">開発者をサポートします(Yoomoneyが開きます)</string>
<string name="about_support_developer_summary">このアプリが気に入ったら、Yoomoney(Yandex.Moneyなど)からサポートできます</string>
<string name="about_license">ライセンス</string>
<string name="auth_complete">承認済み</string>
<string name="auth_not_supported_by">%sへのログインはサポートされていません</string>
<string name="state_finished">完成</string>
<string name="state_ongoing">進行中</string>
<string name="system_default">デフォルト</string>
<string name="show_pages_numbers">ナンバリングページ</string>
<string name="enabled_sources">使用したソース</string>
<string name="available_sources">利用可能なソース</string>
<string name="dynamic_theme">ダイナミックなテーマ</string>
<string name="dynamic_theme_summary">壁紙の配色に合わせて作成したテーマを適用する</string>
<string name="chapters_checking_progress">新しいチャプターの確認:%1$dの%2$d</string>
<string name="create_category">新しいカテゴリー</string>
<string name="zoom_mode_fit_height">高さを合わせる</string>
<string name="hide_toolbar">スクロール時にツールバーを非表示にする</string>
<string name="chapter_is_missing">チャプターがありません</string>
<string name="text_clear_updates_feed_prompt">すべての更新履歴を完全に消去しますか?</string>
<string name="chapter_is_missing_text">この不足した章をダウンロードしたり、オンラインで読んだりすることができます。</string>
<string name="auth_required">このコンテンツを表示するにはサインインしてください</string>
<string name="file_not_found">ファイルが見つかりません</string>
<string name="password_length_hint">パスワードは4文字以上である必要があります</string>
<string name="just_now">ちょうど今</string>
<string name="long_ago">ずっと前</string>
<string name="backup_saved">バックアップを保存</string>
<string name="group">グループ</string>
<string name="clear_feed">フィードをクリア</string>
<string name="restore_backup">バックアップから復元</string>
<string name="confirm">確認</string>
<string name="backup_information">履歴とお気に入りのバックアップを作成して復元できます</string>
<string name="search_only_on_s">%sでのみ検索</string>
<string name="read_more">続きを読む</string>
<string name="_and_x_more">...そして%1$dさらに</string>
<string name="black_dark_theme">ブラック</string>
<string name="other">その他</string>
<string name="data_restored_with_errors">データは復元されましたがエラーが発生しました</string>
<string name="text_clear_search_history_prompt">最近の検索クエリを全て完全に削除しますか?</string>
<string name="zoom_mode_fit_width">幅を合わせる</string>
<string name="new_chapters_checking">新しいチャプターを探しています…</string>
<string name="languages">言語</string>
<string name="protect_application_subtitle">アプリを起動するためのパスワードを入力してください</string>
<string name="create_backup">データバックアップを作成</string>
<string name="captcha_solve">解決しました</string>
<string name="tap_to_try_again">タップして再試行してください</string>
<string name="captcha_required">CAPTCHAが必要です</string>
<string name="default_s">デフォルト:%s</string>
<string name="yesterday">昨日</string>
<string name="about_app_translation">翻訳</string>
<string name="data_restored_success">全てのデータが復元されました</string>
<string name="data_restored">復元</string>
<string name="about_author">著者</string>
<string name="about_gratitudes">感謝の気持ち</string>
<string name="preparing_">準備中…</string>
<string name="zoom_mode_keep_start">開始時に維持</string>
<string name="description">説明</string>
<string name="backup_restore">バックアップと復元</string>
<string name="about_copyright_and_licenses">著作権とライセンス</string>
<string name="reverse">リバース</string>
<string name="reader_mode_hint">選択した構成はこの漫画のために記憶されます</string>
<string name="clear_cookies">クッキーを削除</string>
<string name="report_github">GitHubでissueを作成する(GitHubのアカウントが必要です)</string>
<string name="zoom_mode_fit_center">フィットセンター</string>
<string name="text_downloads_holder">アクティブなダウンロードはありません</string>
<string name="text_clear_cookies_prompt">全てのソースからログアウトされます</string>
<string name="date_format">日付をフォーマット</string>
<string name="exclude_nsfw_from_history">NSFW漫画を履歴から除外する</string>
<string name="queued">キュー</string>
<string name="cookies_cleared">全てのCookieが削除されました</string>
<string name="prefer_rtl_reader_summary">読み取りモードはシリーズごとに設定できます</string>
<string name="restart_required">再起動が必要です</string>
<string name="tracker_warning">一部のデバイスはシステムでの動作が異なり、バックグラウンドタスクが中断される可能性があります。</string>
<string name="genres">ジャンル</string>
<string name="scale_mode">スケールモード</string>
<string name="error_empty_name">名前を入力する必要があります</string>
<string name="today">今日</string>
<string name="about_gratitudes_summary">この人達のお陰でKotatsuがより良くなりました</string>
<string name="about_app_translation_summary">Kotatsuを翻訳する(Weblateのサイトを開きます)</string>
<string name="next">次のページ</string>
<string name="silent">サイレント</string>
<string name="sign_in">サインイン</string>
<string name="welcome">ようこそ</string>
<string name="black_dark_theme_summary">AMOLEDスクリーンでさらに少ない電力を使用</string>
</resources>

View File

@@ -1,24 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="text_file_sizes">B|kB|MB|GB|TB</string>
<string name="cannot_find_available_storage">Finner ikke noen tilgjengelig lagring</string>
<string name="manga_save_location">Nedlastingssted</string>
<string name="text_local_holder_secondary">Du kan lagre fra nettbaserte kilder, eller importere filer.</string>
<string name="cannot_find_available_storage">Ingen tilgjengelig lagring</string>
<string name="manga_save_location">Mappe for nedlastninger</string>
<string name="text_local_holder_secondary">Lagre fra nettbaserte kilder, eller importer filer.</string>
<string name="tracker_warning">Noen enheter endrer systemoppførselen, noe som kan ødelegge for bakgrunnsoppgaver.</string>
<string name="text_clear_search_history_prompt">Fjern alle nylige søkespørringer for godt\?</string>
<string name="hide_toolbar">Skjul verktøylinje under rulling</string>
<string name="password_length_hint">Passord må være minst fire tegn</string>
<string name="password_length_hint">Passordet må være minst fire tegn</string>
<string name="protect_application_subtitle">Skriv inn passord å kreve for å starte programmet</string>
<string name="_and_x_more">… og %1$d til</string>
<string name="auth_required">Autoriser visning av innholdet</string>
<string name="auth_required">Logg inn for å se dette innholdet</string>
<string name="reverse">Inverter</string>
<string name="new_chapters_checking">Sjekk etter nye kapitler</string>
<string name="new_chapters_checking">Sjekker etter nye kapitler</string>
<string name="text_clear_updates_feed_prompt">Tøm all oppdateringshistorikk for godt\?</string>
<string name="clear_feed">Tøm flyt</string>
<string name="chapters_checking_progress">Ser etter nye kapitler: %1$d av %2$d</string>
<string name="reader_mode_hint">Valgt oppsett vil bli husket for denne filen</string>
<string name="backup_information">Du kan opprette en sikkerhetskopi av din historikk og favoritter å gjenopprette senere</string>
<string name="data_restored_with_errors">Data gjenopprettet, men med feil</string>
<string name="data_restored_with_errors">Dataene ble gjenopprettet, men med feil</string>
<string name="file_not_found">Fant ikke filen</string>
<string name="backup_restore">Sikkerhetskopiering og gjenoppretting</string>
<string name="zoom_mode_keep_start">Behold ved oppstart</string>
@@ -27,46 +27,47 @@
<string name="zoom_mode_fit_center">Tilpass sentrum</string>
<string name="scale_mode">Skaleringsmodus</string>
<string name="report_github">Opprett feilrapport på GitHub</string>
<string name="prefer_rtl_reader_summary">Du kan sette opp lesemodus for hver fil</string>
<string name="right_to_left">Høyre-til-venstre </string>
<string name="prefer_rtl_reader">Foretrekk høyre-til-venstre -leser</string>
<string name="prefer_rtl_reader_summary">Lesemodus kan settes opp for hver serie</string>
<string name="right_to_left">Høyre-til-venstre (←)</string>
<string name="prefer_rtl_reader">Foretrekk høyre-til-venstre (→)-leser</string>
<string name="no_update_available">Ingen tilgjengelige oppdateringer</string>
<string name="update_check_failed">Kunne ikke se etter oppdateringer</string>
<string name="checking_for_updates">Ser etter oppdateringer …</string>
<string name="check_for_updates">Se etter oppdateringer</string>
<string name="protect_application_summary">Passord ved programoppstart</string>
<string name="track_sources">Se etter leseoppdateringer</string>
<string name="protect_application_summary">Spør om passord ved programoppstart</string>
<string name="track_sources">Se etter oppdateringer</string>
<string name="feed_will_update_soon">Flytoppdatering starter snart</string>
<string name="update">Oppdater</string>
<string name="updates_feed_cleared">Tømt</string>
<string name="clear_updates_feed">Tøm oppdateringsflyt</string>
<string name="waiting_for_network">Venter på nettverkstilknytning</string>
<string name="waiting_for_network">Venter på nettverk …</string>
<string name="new_version_s">Ny versjon: %s</string>
<string name="text_feed_holder">Her kan du se nye kapitler av det du leser</string>
<string name="text_feed_holder">Nye kapitler av det du leser vises her</string>
<string name="updates">Oppdatering</string>
<string name="use_ssl">Bruk sikker (HTTPS)-tilkobling</string>
<string name="other_storage">Annen lagring</string>
<string name="manga_shelf">Hylle</string>
<string name="text_local_holder_primary">Du har ikke lagret noe enda.</string>
<string name="text_history_holder_secondary">Du kan finne ting å lese i sidemenyen.</string>
<string name="text_local_holder_primary">Lagre noe først</string>
<string name="text_history_holder_secondary">Finn lesestoff i sidemenyen.</string>
<string name="text_history_holder_primary">Det du leser vil vises her</string>
<string name="text_search_holder_secondary">Prøv å reformulere spørringen.</string>
<string name="text_categories_holder">Bruk kategorier til å organisere mapper. Trykk «+» for å opprette en kategori.</string>
<string name="text_categories_holder">Du kan bruke kategorier til å organisere mapper. Trykk «+» for å opprette en kategori</string>
<string name="text_empty_holder_primary">Det er ganske tomt her …</string>
<string name="category_delete_confirm">Fjern «%s»-kategorien fra favoritter og alt i den\?</string>
<string name="category_delete_confirm">Fjern «%s»-kategorien fra favorittene\?
\nAlle mangaer i den vil bli tapt.</string>
<string name="restart">Programomstart</string>
<string name="show_notification_new_chapters">Gi merknad om oppdateringer av det du leser</string>
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">Skrudde på %1$d av %2$d</string>
<string name="large_manga_save_confirm">Denne managaen har %s. Ønsker du å lagre hele\?</string>
<string name="open_in_browser">Åpne i filutforsker</string>
<string name="show_notification_app_update">Vis merknad hvis ny versjon er tilgjengelig</string>
<string name="app_update_available">Ny programversjon tilgjengelig</string>
<string name="application_update">Se etter oppdateringer automatisk</string>
<string name="search_history_cleared">Søkehistorikk tømt</string>
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">%1$d av %2$d påskrudd</string>
<string name="large_manga_save_confirm">Denne mangaen har %s. Lagre hele\?</string>
<string name="open_in_browser">Åpne i nettleser</string>
<string name="show_notification_app_update">Vis merknad hvis en ny versjon er tilgjengelig</string>
<string name="app_update_available">En ny versjon av programmet er tilgjengelig</string>
<string name="application_update">Se etter nye versjoner av programmet</string>
<string name="search_history_cleared">Tømt</string>
<string name="clear_thumbs_cache">Tøm miniatyrbildehurtiglager</string>
<string name="network_consumption_warning">Kan laste ned mye data</string>
<string name="taps_on_edges">Trykk på kanter</string>
<string name="text_delete_local_manga">Slett «%s» fra enhetsminne for godt\?</string>
<string name="network_consumption_warning">Dette kan overføre mye data</string>
<string name="taps_on_edges">Kanttrykk</string>
<string name="text_delete_local_manga">Slett «%s» fra enheten for godt\?</string>
<string name="webtoon">Nettserie</string>
<string name="standard">Forvalg</string>
<string name="cache">Hurtiglager</string>
@@ -76,7 +77,7 @@
<string name="operation_not_supported">Ustøttet handling</string>
<string name="_import">Importer</string>
<string name="page_saved">Lagret</string>
<string name="wait_for_loading_finish">Vent på at innlastingen fullføres</string>
<string name="wait_for_loading_finish">Vent på at innlastingen fullføres</string>
<string name="text_clear_history_prompt">Fjern all lesehistorikk for godt\?</string>
<string name="read">Les</string>
<string name="nothing_found">Resultatløst</string>
@@ -90,9 +91,9 @@
<string name="search_only_on_s">Kun søk på %s</string>
<string name="confirm">Bekreft</string>
<string name="next">Neste</string>
<string name="default_s">Forvalg: %s</string>
<string name="default_s">Standard: %s</string>
<string name="sign_in">Logg inn</string>
<string name="cookies_cleared">Alle kaker ble fjernet</string>
<string name="cookies_cleared">Alle cookies ble fjernet</string>
<string name="clear_cookies">Tøm kaker</string>
<string name="captcha_solve">Løs</string>
<string name="captcha_required">CAPTCHA kreves</string>
@@ -103,19 +104,19 @@
<string name="long_ago">Lenge siden</string>
<string name="yesterday">I går</string>
<string name="just_now">Akkurat nå</string>
<string name="data_restored_success">All data gjenopprettet</string>
<string name="data_restored_success">All data ble gjenopprettet</string>
<string name="preparing_">Forbereder …</string>
<string name="data_restored">Data gjenopprettet</string>
<string name="restore_backup">Gjenopprett fra sikkerhetskopi</string>
<string name="create_backup">Opprett sikkerhetskopi</string>
<string name="restart_required">Omstart kreves</string>
<string name="black_dark_theme_summary">Nyttig for AMOLED-skjermer</string>
<string name="black_dark_theme">Nattsvart drakt</string>
<string name="black_dark_theme_summary">Bruker mindre strøm på AMOLED-skjermer</string>
<string name="black_dark_theme">Svart</string>
<string name="create_category">Ny kategori</string>
<string name="app_version">Versjon %s</string>
<string name="about">Om</string>
<string name="passwords_mismatch">Passordene samsvarer ikke</string>
<string name="repeat_password">Gjenta passord</string>
<string name="repeat_password">Gjenta passordet</string>
<string name="protect_application">Beskytt programmet</string>
<string name="wrong_password">Feil passord</string>
<string name="enter_password">Skriv inn passord</string>
@@ -125,17 +126,17 @@
<string name="related">Relatert</string>
<string name="search_results">Søkeresultater</string>
<string name="read_later">Les senere</string>
<string name="favourites_category_empty">Denne kategorien er tom</string>
<string name="favourites_category_empty">Tom kategori</string>
<string name="all_favourites">Alle favoritter</string>
<string name="done">Ferdig</string>
<string name="not_available">Ikke tilgjengelig</string>
<string name="pages_animation">Sideanimasjon</string>
<string name="recent_manga">Nylig manga</string>
<string name="remove_category">Fjern kategori</string>
<string name="recent_manga">Nylig</string>
<string name="remove_category">Fjern</string>
<string name="rename">Gi nytt navn</string>
<string name="categories_">Kategorier …</string>
<string name="favourites_categories">Favorittkategorier</string>
<string name="light_indicator">Lysindikator</string>
<string name="light_indicator">LED-indikator</string>
<string name="vibration">Vibrasjon</string>
<string name="notification_sound">Merknadslyd</string>
<string name="notifications_settings">Merknadsinnstillinger</string>
@@ -143,7 +144,7 @@
<string name="download">Last ned</string>
<string name="new_chapters">Nye kapittel</string>
<string name="notifications">Merknader</string>
<string name="save_manga">Lagre manga</string>
<string name="save_manga">Lagre</string>
<string name="domain">Domene</string>
<string name="external_storage">Eksternlagring</string>
<string name="internal_storage">Internlagring</string>
@@ -170,7 +171,7 @@
<string name="remove">Fjern</string>
<string name="clear">Tøm</string>
<string name="pages">Sider</string>
<string name="automatic">Automatisk</string>
<string name="automatic">Følg systemet</string>
<string name="dark">Mørk</string>
<string name="light">Lys</string>
<string name="by_name">Navn</string>
@@ -186,7 +187,7 @@
<string name="downloads">Nedlastinger</string>
<string name="download_complete">Nedlastet</string>
<string name="processing_">Behandler …</string>
<string name="manga_downloading_">Laster ned manga </string>
<string name="manga_downloading_">Laster ned …</string>
<string name="search_manga">Søk manga</string>
<string name="search">Søk</string>
<string name="share_s">Del %s</string>
@@ -195,11 +196,11 @@
<string name="save">Lagre</string>
<string name="enter_category_name">Skriv inn kategorinavn</string>
<string name="add">Legg til</string>
<string name="add_new_category">Legg til ny kategori</string>
<string name="add_to_favourites">Legg til i favoritter</string>
<string name="you_have_not_favourites_yet">Du har ingen favoritter enda.</string>
<string name="add_new_category">Ny kategori</string>
<string name="add_to_favourites">Favorittmerk dette</string>
<string name="you_have_not_favourites_yet">Ingen favoritter enda</string>
<string name="add_bookmark">Legg til bokmerke</string>
<string name="history_is_empty">Historikken er tom</string>
<string name="history_is_empty">Ingen historikk enda</string>
<string name="clear_history">Tøm historikk</string>
<string name="try_again">Prøv igjen</string>
<string name="close">Lukk</string>
@@ -212,16 +213,16 @@
<string name="list">Liste</string>
<string name="chapters">Kapittel</string>
<string name="details">Detaljer</string>
<string name="network_error">Nettverktilkoblingsfeil</string>
<string name="network_error">Kunne ikke koble til Internet</string>
<string name="error_occurred">En feil inntraff</string>
<string name="history">Historikk</string>
<string name="favourites">Favoritter</string>
<string name="local_storage">Lokallagring</string>
<string name="open_menu">Åpne meny</string>
<string name="close_menu">Lukk meny</string>
<string name="chapter_is_missing_text">Dette kapitlet mangler på din enhet. Last det ned eller les det på nett.</string>
<string name="chapter_is_missing">Kapittel mangler</string>
<string name="text_downloads_holder">Det er ingen aktive nedlastinger</string>
<string name="chapter_is_missing_text">Last ned eller les dette manglende kapittelet på nett.</string>
<string name="chapter_is_missing">Kapittelet mangler</string>
<string name="text_downloads_holder">Ingen aktive nedlastinger</string>
<string name="queued">I kø</string>
<string name="state_finished">Fullført</string>
<string name="state_ongoing">Pågående</string>
@@ -233,18 +234,20 @@
<string name="about_support_developer">Støtt utvikleren</string>
<string name="about_support_developer_summary">Hvis du liker programmet kan du kronerulle det på Yoomoney (tidligere Yandex.Money)</string>
<string name="about_gratitudes">Takk rettes til</string>
<string name="about_gratitudes_summary">Folk som gjorde Kotatsu enda bedre.</string>
<string name="about_gratitudes_summary">Folk som gjorde Kotatsu enda bedre</string>
<string name="about_copyright_and_licenses">Opphavsrett og lisenser</string>
<string name="about_license">Lisens</string>
<string name="auth_complete">Identitetsbekreftelse fullført</string>
<string name="auth_not_supported_by">Identitetsbekreftelse på %s støttes ikke</string>
<string name="text_clear_cookies_prompt">Du vil bli utlogget fra alle kilder du pålogget i</string>
<string name="auth_complete">Identitetsbekreftet</string>
<string name="auth_not_supported_by">Innlogging på %s støttes ikke</string>
<string name="text_clear_cookies_prompt">Du vil bli utlogget fra alle kilder</string>
<string name="genres">Sjangere</string>
<string name="exclude_nsfw_from_history">Utelat NSFW-manga fra historikk</string>
<string name="date_format">Datoformat</string>
<string name="system_default">Forvalg</string>
<string name="error_empty_name">Navn må angis</string>
<string name="error_empty_name">Du må angi ett navn</string>
<string name="available_sources">Tilgjengelige kilder</string>
<string name="show_pages_numbers">Vis sidenummerering</string>
<string name="enabled_sources">Påskrudde kilder</string>
<string name="show_pages_numbers">Sidenummerering</string>
<string name="enabled_sources">Brukte kilder</string>
<string name="dynamic_theme">Dynamisk tema</string>
<string name="dynamic_theme_summary">Bruker et tema basert på fargene til bakgrunnen din</string>
</resources>

View File

@@ -8,5 +8,6 @@
<color name="errorContainer">#930006</color>
<color name="onError">#680003</color>
<color name="onErrorContainer">#FFDAD4</color>
<color name="scrollbar">#66FFFFFF</color>
</resources>

View File

@@ -3,8 +3,8 @@
<string name="open_menu">Abrir menu</string>
<string name="local_storage">Armazenamento local</string>
<string name="favourites">Favoritos</string>
<string name="error_occurred">Ocorreu um erro</string>
<string name="network_error">Erro de conexão de rede</string>
<string name="error_occurred">Um erro ocorreu</string>
<string name="network_error">Não foi possível conectar à Internet</string>
<string name="details">Detalhes</string>
<string name="list">Lista</string>
<string name="detailed_list">Lista detalhada</string>
@@ -16,11 +16,11 @@
<string name="try_again">Tente novamente</string>
<string name="clear_history">Limpar histórico</string>
<string name="nothing_found">Nada encontrado</string>
<string name="history_is_empty">Histórico vazio</string>
<string name="add_bookmark">Add marca páginas</string>
<string name="you_have_not_favourites_yet">Você não tem favoritos ainda</string>
<string name="add_to_favourites">Adicionar aos favoritos</string>
<string name="add">Add</string>
<string name="history_is_empty">Sem histórico ainda</string>
<string name="add_bookmark">Adicionar marca páginas</string>
<string name="you_have_not_favourites_yet">Ainda não favoritos</string>
<string name="add_to_favourites">Favoritar isso</string>
<string name="add">Adicionar</string>
<string name="enter_category_name">Nomeie a categoria</string>
<string name="save">Salvar</string>
<string name="share">Compartilhar</string>
@@ -28,62 +28,62 @@
<string name="share_s">Compartilhar %s</string>
<string name="search">Pesquisar</string>
<string name="search_manga">Pesquisar mangá</string>
<string name="manga_downloading_">Baixando mangá</string>
<string name="download_complete">Download completo</string>
<string name="manga_downloading_">Baixando…</string>
<string name="download_complete">Baixado</string>
<string name="downloads">Downloads</string>
<string name="by_name">Por nome</string>
<string name="by_name">Nome</string>
<string name="popular">Populares</string>
<string name="by_rating">Por avaliação</string>
<string name="by_rating">Avaliação</string>
<string name="all">Todos</string>
<string name="sort_order">Ordem de classificação</string>
<string name="genre">Gênero</string>
<string name="filter">Filtro</string>
<string name="dark">Escuro</string>
<string name="automatic">Automático</string>
<string name="automatic">Siga o sistema</string>
<string name="pages">Páginas</string>
<string name="clear">Limpar</string>
<string name="text_clear_history_prompt">Você realmente quer limpar todo o seu histórico de leitura\? Essa ação não pode ser desfeita.</string>
<string name="text_clear_history_prompt">Limpar todo o histórico de leitura permanentemente\?</string>
<string name="remove">Remover</string>
<string name="_s_removed_from_history">\"%s\" removido do histórico</string>
<string name="_s_deleted_from_local_storage">\"%s\" deletado do armazenamento local</string>
<string name="wait_for_loading_finish">Aguarde o carregamento para finalizar</string>
<string name="wait_for_loading_finish">Aguarde o carregamento terminar…</string>
<string name="save_page">Salvar página</string>
<string name="page_saved">Página salva com sucesso</string>
<string name="page_saved">Salvou</string>
<string name="share_image">Compartilhar imagem</string>
<string name="_import">Importar</string>
<string name="updated">Utualizado</string>
<string name="delete">Deletar</string>
<string name="operation_not_supported">Essa operação não é suportada</string>
<string name="history_and_cache">Histórico e cache</string>
<string name="clear_pages_cache">Limpar cache de páginas</string>
<string name="clear_pages_cache">Limpar cache de página</string>
<string name="cache">Cache</string>
<string name="text_file_sizes">B|kB|MB|GB|TB</string>
<string name="standard">Padrão</string>
<string name="webtoon">Webtoon</string>
<string name="read_mode">Modo leitura</string>
<string name="grid_size">Tamanho de grade</string>
<string name="grid_size">Tamanho da grade</string>
<string name="search_on_s">Pesquisar em %s</string>
<string name="delete_manga">Deletar mangá</string>
<string name="reader_settings">Configurações de leitura</string>
<string name="switch_pages">Mudar páginas</string>
<string name="taps_on_edges">Cliques na borda</string>
<string name="taps_on_edges">Toques de borda</string>
<string name="volume_buttons">Botões de volume</string>
<string name="network_consumption_warning">Essa operação pode consumir muito tráfego de rede</string>
<string name="network_consumption_warning">Isso pode transferir muitos dados</string>
<string name="dont_ask_again">Não pergunte novamente</string>
<string name="cancelling_">Cancelando…</string>
<string name="error">Erro</string>
<string name="clear_thumbs_cache">Limpar cache de thumbnails</string>
<string name="search_history_cleared">Histórico de pesquisa limpo</string>
<string name="search_history_cleared">Limpo</string>
<string name="gestures_only">Apenas gestos</string>
<string name="internal_storage">Armazenamento interno</string>
<string name="external_storage">Armazenamento externo</string>
<string name="domain">Domínio</string>
<string name="application_update">Verificar automaticamente se há actualizações</string>
<string name="app_update_available">Actualização da aplicação está disponível</string>
<string name="show_notification_app_update">Mostrar notificação se a actualização estiver disponível</string>
<string name="open_in_browser">Aberto no navegador</string>
<string name="large_manga_save_confirm">Esta manga tem %s. Quer salvar tudo isto\?</string>
<string name="save_manga">Salvar mangá</string>
<string name="application_update">Verifique se há novas versões do aplicativo</string>
<string name="app_update_available">Uma nova versão do aplicativo está disponível</string>
<string name="show_notification_app_update">Mostrar notificação se uma nova versão estiver disponível</string>
<string name="open_in_browser">Abrir no navegador da web</string>
<string name="large_manga_save_confirm">Este mangá tem %s. Salvar tudo isso\?</string>
<string name="save_manga">Salve</string>
<string name="notifications">Notificações</string>
<string name="new_chapters">Novos capítulos</string>
<string name="show_notification_new_chapters">Notifique sobre atualizações do mangá que está lendo</string>
@@ -91,11 +91,11 @@
<string name="read_from_start">Ler desde o início</string>
<string name="restart">Reiniciar</string>
<string name="notifications_settings">Configurações das notificações</string>
<string name="light_indicator">Indicador luminoso</string>
<string name="light_indicator">Indicador LED</string>
<string name="close_menu">Fechar menu</string>
<string name="remote_sources">Fontes remotas</string>
<string name="close">Fechar</string>
<string name="light">Brilho</string>
<string name="light">Claro</string>
<string name="history">Histórico</string>
<string name="read">Ler</string>
<string name="processing_">Processando…</string>
@@ -104,54 +104,53 @@
<string name="no_description">Sem descrição</string>
<string name="_continue">Continuar</string>
<string name="chapters">Capítulos</string>
<string name="add_new_category">Add nova categoria</string>
<string name="add_new_category">Nova categoria</string>
<string name="warning">Aviso</string>
<string name="text_delete_local_manga">Você realmente quer deletar \"%s\" do armazenamento local de seu celular\?
\nEssa operação não pode ser desfeita.</string>
<string name="text_file_not_supported">Arquivo inválido. Apenas ZIP e CBZ são suportados.</string>
<string name="text_delete_local_manga">Excluir \"%s\" do dispositivo permanentemente\?</string>
<string name="text_file_not_supported">Escolha um arquivo ZIP ou CBZ.</string>
<string name="clear_search_history">Limpar histórico de pesquisa</string>
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">Activado %1$d de %2$d</string>
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">Ativado %1$d de %2$d</string>
<string name="notification_sound">Som de notificação</string>
<string name="show_pages_numbers">Mostrar números de páginas</string>
<string name="show_pages_numbers">Páginas numeradas</string>
<string name="state_finished">Concluído</string>
<string name="state_ongoing">em andamento</string>
<string name="state_ongoing">Em andamento</string>
<string name="categories_">Categorias…</string>
<string name="rename">Renomear</string>
<string name="remove_category">Remover categoria</string>
<string name="remove_category">Remover</string>
<string name="text_empty_holder_primary">Está meio vazio aqui…</string>
<string name="manga_shelf">Prateleira de manga</string>
<string name="manga_shelf">Estante</string>
<string name="done">Feito</string>
<string name="related">Relacionado</string>
<string name="zoom_mode_keep_start">Manter no início</string>
<string name="clear_updates_feed">Limpar feed de atualizações</string>
<string name="updates_feed_cleared">Feed de atualizações limpo</string>
<string name="updates_feed_cleared">Limpo</string>
<string name="update">Atualizar</string>
<string name="feed_will_update_soon">A atualização do feed começará em breve</string>
<string name="track_sources">Confira as atualizações do mangá</string>
<string name="track_sources">Procure atualizações</string>
<string name="dont_check">Não verifique</string>
<string name="enter_password">Digite a senha</string>
<string name="wrong_password">Senha incorreta</string>
<string name="repeat_password">Repita a senha</string>
<string name="passwords_mismatch">As senhas não coincidem</string>
<string name="passwords_mismatch">Senhas incompatíveis</string>
<string name="about">Cerca de</string>
<string name="app_version">Versão %s</string>
<string name="check_for_updates">Verifique se há atualizações</string>
<string name="checking_for_updates">Verificando atualizações…</string>
<string name="no_update_available">Nenhuma atualização disponível</string>
<string name="right_to_left">Direita para esquerda</string>
<string name="right_to_left">Da direita para a esquerda (←)</string>
<string name="create_category">Nova categoria</string>
<string name="report_github">Criar problema no GitHub</string>
<string name="scale_mode">Modo de escala</string>
<string name="zoom_mode_fit_center">Centro de ajuste</string>
<string name="zoom_mode_fit_width">Ajustar à largura</string>
<string name="restart_required">É necessário reiniciar</string>
<string name="backup_restore">Restauração de backup</string>
<string name="backup_restore">Backup e restauração</string>
<string name="create_backup">Criar backup de dados</string>
<string name="restore_backup">Restaurar do backup</string>
<string name="data_restored">Dados restaurados</string>
<string name="data_restored">Restaurado</string>
<string name="preparing_">Preparando…</string>
<string name="file_not_found">Arquivo não encontrado</string>
<string name="data_restored_success">Todos os dados restaurados com sucesso</string>
<string name="data_restored_success">Todos os dados foram restaurados</string>
<string name="data_restored_with_errors">Os dados foram restaurados, mas há erros</string>
<string name="just_now">Agora mesmo</string>
<string name="yesterday">Ontem</string>
@@ -160,13 +159,13 @@
<string name="today">Hoje</string>
<string name="tap_to_try_again">Toque para tentar novamente</string>
<string name="silent">Silencioso</string>
<string name="captcha_required">O CAPTCHA é obrigatório</string>
<string name="captcha_required">CAPTCHA obrigatório</string>
<string name="captcha_solve">Resolver</string>
<string name="cookies_cleared">Todos os cookies foram removidos</string>
<string name="chapters_checking_progress">Verificando novos capítulos: %1$d de %2$d</string>
<string name="clear_feed">Limpar feed</string>
<string name="text_clear_updates_feed_prompt">Todo o histórico de atualizações será apagado e esta ação não poderá ser desfeita. Tem certeza\?</string>
<string name="new_chapters_checking">Verificação de novos capítulos</string>
<string name="text_clear_updates_feed_prompt">Limpar todo o histórico de atualizações permanentemente\?</string>
<string name="new_chapters_checking">Em busca de novos capítulos</string>
<string name="reverse">Reverter</string>
<string name="sign_in">Entrar</string>
<string name="default_s">Padrão: %s</string>
@@ -174,10 +173,10 @@
<string name="next">Próximo</string>
<string name="protect_application_subtitle">Digite a senha que será necessária quando o aplicativo for iniciado</string>
<string name="confirm">Confirme</string>
<string name="password_length_hint">A senha deve ter pelo menos 4 caracteres</string>
<string name="password_length_hint">A senha deve ter 4 caracteres ou mais</string>
<string name="description">Descrição</string>
<string name="backup_saved">Cópias de segurança salvas com sucesso</string>
<string name="tracker_warning">Alguns fabricantes podem alterar o comportamento do sistema, o que pode quebrar as tarefas de fundo.</string>
<string name="backup_saved">Backup salvo</string>
<string name="tracker_warning">Alguns dispositivos têm um comportamento de sistema diferente, o que pode interromper as tarefas em segundo plano.</string>
<string name="read_more">Leia mais</string>
<string name="hide_toolbar">Ocultar a barra de ferramentas ao rolar</string>
<string name="search_only_on_s">Pesquise apenas em %s</string>
@@ -185,68 +184,70 @@
<string name="languages">Línguas</string>
<string name="welcome">Bem vindo</string>
<string name="available_sources">Fontes disponíveis</string>
<string name="enabled_sources">Fontes ativadas</string>
<string name="enabled_sources">Fontes usadas</string>
<string name="queued">Enfileirado</string>
<string name="text_downloads_holder">No momento, não há downloads ativos</string>
<string name="error_empty_name">O nome não deve estar vazio</string>
<string name="text_downloads_holder">Nenhum download ativo</string>
<string name="error_empty_name">Você deve inserir um nome</string>
<string name="about_app_translation_summary">Traduzir esta aplicação</string>
<string name="about_author">Autor</string>
<string name="about_feedback">Comentar</string>
<string name="about_feedback_4pda">Tópico no 4PDA</string>
<string name="about_support_developer">Apoiar o desenvolvedor</string>
<string name="about_support_developer_summary">Se gostar desta aplicação, pode ajudar financeiramente através de Yoomoney (ex. Yandex.Money)</string>
<string name="about_gratitudes">agradecimento</string>
<string name="about_gratitudes_summary">Estas pessoas fazem o Kotatsu tornar-se melhor!</string>
<string name="about_support_developer_summary">Se você gosta deste aplicativo, você pode enviar dinheiro através do Yoomoney (ex. Yandex.Money)</string>
<string name="about_gratitudes">Agradecimentos</string>
<string name="about_gratitudes_summary">Todas essas pessoas tornaram o Kotatsu melhor</string>
<string name="about_copyright_and_licenses">Direitos de autor e licenças</string>
<string name="about_license">Licença</string>
<string name="chapter_is_missing_text">Este capítulo está em falta no seu dispositivo. Descarregue ou leia-o online.</string>
<string name="chapter_is_missing">Falta um capítulo</string>
<string name="auth_complete">Autorização completa</string>
<string name="auth_not_supported_by">A autorização em %s não é suportada</string>
<string name="chapter_is_missing_text">Baixe ou leia este capítulo perdido online.</string>
<string name="chapter_is_missing">O capítulo está em falta</string>
<string name="auth_complete">Autorizado</string>
<string name="auth_not_supported_by">O login em %s não é suportado</string>
<string name="genres">Géneros</string>
<string name="about_app_translation">Tradução</string>
<string name="text_clear_cookies_prompt">Será desconectado de todas as fontes em que estiver autorizado</string>
<string name="text_clear_cookies_prompt">Você será desconectado de todas as fontes</string>
<string name="vibration">Vibração</string>
<string name="cannot_find_available_storage">Não é possível encontrar nenhum armazenamento disponível</string>
<string name="cannot_find_available_storage">Sem armazenamento disponível</string>
<string name="favourites_categories">Categorias favoritas</string>
<string name="category_delete_confirm">Quer realmente remover a categoria \"%s\" dos seus favoritos\?
\nSerá perdido todos os mangas contidos.</string>
<string name="text_history_holder_secondary">Pode encontrar o que ler no menu lateral.</string>
<string name="text_local_holder_secondary">Pode salvá-lo a partir de fontes online ou importá-lo a partir de ficheiro.</string>
<string name="recent_manga">Manga recente</string>
<string name="category_delete_confirm">Remover a categoria \"%s\" dos seus favoritos\?
\nTodos os mangás nela serão perdidos..</string>
<string name="text_history_holder_secondary">Encontre o que ler no menu lateral.</string>
<string name="text_local_holder_secondary">Salve-o de fontes online ou importe arquivos.</string>
<string name="recent_manga">Recente</string>
<string name="other_storage">Outro armazenamento</string>
<string name="text_search_holder_secondary">Tente reformular a consulta.</string>
<string name="prefer_rtl_reader">Prefira o leitor da direita para a esquerda</string>
<string name="prefer_rtl_reader">Prefira o leitor da direita para a esquerda (→)</string>
<string name="not_available">Não disponível</string>
<string name="size_s">Tamanho: %s</string>
<string name="text_history_holder_primary">Mangá que está a ler será afixada aqui</string>
<string name="text_local_holder_primary">Ainda não tem nenhuma mangá salvo</string>
<string name="pages_animation">Animação de páginas</string>
<string name="favourites_category_empty">Esta categoria está vazia</string>
<string name="text_history_holder_primary">O que você ler será exibido aqui</string>
<string name="text_local_holder_primary">Salve algo primeiro</string>
<string name="pages_animation">Animação de página</string>
<string name="favourites_category_empty">Categoria vazia</string>
<string name="read_later">Leia mais tarde</string>
<string name="updates">atualizações</string>
<string name="updates">Atualizações</string>
<string name="all_favourites">Todos os favoritos</string>
<string name="waiting_for_network">À espera de rede…</string>
<string name="use_ssl">Utilizar ligação segura (HTTPS)</string>
<string name="use_ssl">Usar conexão segura (HTTPS)</string>
<string name="search_results">Resultados da pesquisa</string>
<string name="text_feed_holder">Aqui verá os novos capítulos do mangá que está a ler</string>
<string name="text_feed_holder">Novos capítulos do que você está lendo são mostrados aqui</string>
<string name="new_version_s">Nova versão: %s</string>
<string name="rotate_screen">Girar a tela</string>
<string name="update_check_failed">Falha na verificação de atualização</string>
<string name="protect_application">Proteger aplicativo</string>
<string name="protect_application_summary">Pedir senha no início do aplicativo</string>
<string name="update_check_failed">Não foi possível procurar atualizações</string>
<string name="protect_application">Proteja o aplicativo</string>
<string name="protect_application_summary">Peça a senha ao iniciar o Kotatsu</string>
<string name="zoom_mode_fit_height">Ajustar à altura</string>
<string name="black_dark_theme">Tema Black dark</string>
<string name="black_dark_theme_summary">Útil para telas AMOLED</string>
<string name="black_dark_theme">Escuro</string>
<string name="black_dark_theme_summary">Usa menos energia em telas AMOLED</string>
<string name="reader_mode_hint">A configuração escolhida será lembrada para este mangá</string>
<string name="backup_information">Você pode criar backup de seu histórico e favoritos e restaurá-lo</string>
<string name="clear_cookies">Limpar cookies</string>
<string name="text_clear_search_history_prompt">Você realmente deseja remover todas as consultas de pesquisa recentes\? Essa ação não pode ser desfeita.</string>
<string name="auth_required">Você deve autorizar a visualização deste conteúdo</string>
<string name="text_categories_holder">Pode usar categorias para organizar seu mangá favorito. Pressione &lt;&lt;+&gt;&gt; para criar uma categoria</string>
<string name="prefer_rtl_reader_summary">Você pode configurar o modo de leitura para cada mangá separadamente</string>
<string name="manga_save_location">Local onde serão armazenados os mangás baixados</string>
<string name="exclude_nsfw_from_history">Excluir manga NSFW da história</string>
<string name="text_clear_search_history_prompt">Remover todas as consultas de pesquisa recentes permanentemente\?</string>
<string name="auth_required">Faça login para ver este conteúdo</string>
<string name="text_categories_holder">Você pode usar categorias para organizar seus favoritos. Pressione «+» para criar uma categoria</string>
<string name="prefer_rtl_reader_summary">O modo de leitura pode ser configurado separadamente para cada série</string>
<string name="manga_save_location">Pasta para downloads</string>
<string name="exclude_nsfw_from_history">Excluir mangá NSFW do histórico</string>
<string name="date_format">Formato da data</string>
<string name="system_default">Padrão</string>
<string name="dynamic_theme">Tema dinâmico</string>
<string name="dynamic_theme_summary">Aplica um tema criado no esquema de cores do seu papel de parede</string>
</resources>

View File

@@ -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>
@@ -21,12 +21,12 @@
<string name="try_again">Повторить</string>
<string name="clear_history">Очистить историю</string>
<string name="nothing_found">Ничего не найдено</string>
<string name="history_is_empty">История пуста</string>
<string name="history_is_empty">Истории еще нет</string>
<string name="read">Читать</string>
<string name="add_bookmark">Добавить закладку</string>
<string name="you_have_not_favourites_yet">Добавьте интересующую Вас мангу в избранное, чтобы не потерять её</string>
<string name="you_have_not_favourites_yet">Избранного пока нет</string>
<string name="add_to_favourites">В избранное</string>
<string name="add_new_category">Создать категорию</string>
<string name="add_new_category">Новая категория</string>
<string name="add">Добавить</string>
<string name="enter_category_name">Введите название</string>
<string name="save">Сохранить</string>
@@ -35,37 +35,37 @@
<string name="share_s">Поделиться %s</string>
<string name="search">Поиск</string>
<string name="search_manga">Поиск манги</string>
<string name="manga_downloading_">Загрузка манги</string>
<string name="manga_downloading_">Загрузка…</string>
<string name="processing_">Обработка…</string>
<string name="download_complete">Загрузка завершена</string>
<string name="download_complete">Загружено</string>
<string name="downloads">Загрузки</string>
<string name="by_name">По имени</string>
<string name="by_name">Имя</string>
<string name="popular">Популярная</string>
<string name="updated">Обновлённая</string>
<string name="newest">Новая</string>
<string name="by_rating">По рейтингу</string>
<string name="by_rating">Рейтинг</string>
<string name="all">Все</string>
<string name="sort_order">Сортировка</string>
<string name="sort_order">Порядок сортировки</string>
<string name="genre">Жанр</string>
<string name="filter">Фильтр</string>
<string name="theme">Тема</string>
<string name="light">Светлая</string>
<string name="dark">Тёмная</string>
<string name="automatic">Автоматически</string>
<string name="automatic">Следовать системе</string>
<string name="pages">Страницы</string>
<string name="clear">Очистить</string>
<string name="text_clear_history_prompt">Вы уверены, что хотите очистить историю? Это действие нельзя будет отменить.</string>
<string name="text_clear_history_prompt">Очистить всю историю чтения полностью\?</string>
<string name="remove">Удалить</string>
<string name="_s_removed_from_history">«%s» удалено из истории</string>
<string name="_s_deleted_from_local_storage">«%s» удалено с устройства</string>
<string name="wait_for_loading_finish">Дождитесь окончания загрузки</string>
<string name="wait_for_loading_finish">Дождитесь завершения загрузки</string>
<string name="save_page">Сохранить страницу</string>
<string name="page_saved">Страница сохранена</string>
<string name="page_saved">Сохранено</string>
<string name="share_image">Поделиться изображением</string>
<string name="_import">Импорт</string>
<string name="delete">Удалить</string>
<string name="operation_not_supported">Операция не поддерживается</string>
<string name="text_file_not_supported">Неподдерживаемый файл. Поддерживаются только ZIP и CBZ.</string>
<string name="text_file_not_supported">Выберите ZIP-файл, либо файл CBZ.</string>
<string name="no_description">Нет описания</string>
<string name="history_and_cache">История и кэш</string>
<string name="clear_pages_cache">Очистить кэш страниц</string>
@@ -77,31 +77,30 @@
<string name="grid_size">Размер таблицы</string>
<string name="search_on_s">Поиск по %s</string>
<string name="delete_manga">Удалить мангу</string>
<string name="text_delete_local_manga">Вы уверены, что хотите удалить «%s» с устройства\?
\nЭто действие нельзя будет отменить.</string>
<string name="text_delete_local_manga">Удалить \"%s\" с устройства навсегда\?</string>
<string name="reader_settings">Настройки чтения</string>
<string name="switch_pages">Листание страниц</string>
<string name="taps_on_edges">Нажатия по краям</string>
<string name="volume_buttons">Кнопки громкости</string>
<string name="_continue">Продолжить</string>
<string name="warning">Предупреждение</string>
<string name="network_consumption_warning">Данная операция может привести к большому расходу траффика</string>
<string name="network_consumption_warning">Это может привести к расходу большого количества трафика</string>
<string name="dont_ask_again">Больше не спрашивать</string>
<string name="cancelling_">Отмена…</string>
<string name="error">Ошибка</string>
<string name="clear_thumbs_cache">Очистить кэш миниатюр</string>
<string name="clear_search_history">Очистить историю поиска</string>
<string name="search_history_cleared">История поиска очищена</string>
<string name="search_history_cleared">Очищено</string>
<string name="gestures_only">Только жесты</string>
<string name="internal_storage">Внутренний накопитель</string>
<string name="external_storage">Внешнее хранилище</string>
<string name="domain">Домен</string>
<string name="application_update">Проверять обновление приложения</string>
<string name="app_update_available">Доступно обновление приложения</string>
<string name="show_notification_app_update">Показывать уведомление при наличии новой версии</string>
<string name="open_in_browser">Открыть в браузере</string>
<string name="large_manga_save_confirm">В этой манге %s. Вы уверены, что хотите сохранить их все?</string>
<string name="save_manga">Сохранить мангу</string>
<string name="application_update">Проверять наличие новых версий приложения</string>
<string name="app_update_available">Доступна новая версия приложения</string>
<string name="show_notification_app_update">Показывать уведомление, если доступна новая версия</string>
<string name="open_in_browser">Открыть в веб-браузере</string>
<string name="large_manga_save_confirm">В этой манге %s. Сохранить их все\?</string>
<string name="save_manga">Сохранить</string>
<string name="notifications">Уведомления</string>
<string name="enabled_d_of_d">Включено %1$d из %2$d</string>
<string name="new_chapters">Новые главы</string>
@@ -111,79 +110,79 @@
<string name="restart">Перезапустить</string>
<string name="notifications_settings">Настройки уведомлений</string>
<string name="notification_sound">Звук уведомления</string>
<string name="light_indicator">Световая индикация</string>
<string name="light_indicator">Светодиодная индикация</string>
<string name="vibration">Вибросигнал</string>
<string name="favourites_categories">Категории избранного</string>
<string name="categories_">Категории…</string>
<string name="rename">Переименовать</string>
<string name="category_delete_confirm">Вы уверены, что хотите удалить категорию «%s»\?
\nВся манга из данной категории будет утеряна.</string>
<string name="remove_category">Удалить категорию</string>
<string name="category_delete_confirm">Удалить категорию \"%s\" из избранного\?
\nВся манга в ней будет потеряна.</string>
<string name="remove_category">Удалить</string>
<string name="text_empty_holder_primary">Как-то здесь пусто…</string>
<string name="text_search_holder_secondary">Попробуйте переформулировать запрос.</string>
<string name="text_categories_holder">Категории помогают упорядочивать избранную мангу. Нажмите «+», чтобы создать категорию</string>
<string name="text_history_holder_primary">Здесь будет отображаться манга, которую Вы читаете</string>
<string name="text_history_holder_secondary">Вы можете найти, что почитать, в боковом меню.</string>
<string name="text_local_holder_primary">У Вас пока нет сохранённой манги</string>
<string name="text_local_holder_secondary">Вы можете сохранить мангу из онлайн каталога или импортировать из файла.</string>
<string name="manga_shelf">Полка с мангой</string>
<string name="recent_manga">Недавняя манга</string>
<string name="text_categories_holder">Вы можете использовать категории для организации своих избранных. Нажмите «+», чтобы создать категорию</string>
<string name="text_history_holder_primary">То, что вы прочитаете, будет отображено здесь</string>
<string name="text_history_holder_secondary">Найдите, что почитать, в боковом меню.</string>
<string name="text_local_holder_primary">Сохраните что-нибудь</string>
<string name="text_local_holder_secondary">Сохраните что-нибудь из онлайн-каталога или импортируйте из файла.</string>
<string name="manga_shelf">Полка</string>
<string name="recent_manga">Недавнее</string>
<string name="pages_animation">Анимация листания</string>
<string name="manga_save_location">Место сохранения манги</string>
<string name="manga_save_location">Папка для загрузок</string>
<string name="not_available">Недоступно</string>
<string name="cannot_find_available_storage">Не удалось найти ни одного доступного хранилища</string>
<string name="cannot_find_available_storage">Нет доступного хранилища</string>
<string name="other_storage">Другое хранилище</string>
<string name="use_ssl">Защищённое соединение (HTTPS)</string>
<string name="use_ssl">Использовать безопасное (HTTPS) соединение</string>
<string name="done">Готово</string>
<string name="all_favourites">Всё избранное</string>
<string name="favourites_category_empty">В этой категории ничего нет</string>
<string name="favourites_category_empty">Категория пуста</string>
<string name="read_later">Прочитать позже</string>
<string name="updates">Обновления</string>
<string name="text_feed_holder">Здесь будут отображаться обновления манги, которую Вы читаете</string>
<string name="text_feed_holder">Новые главы из того, что вы читаете, будут показаны здесь</string>
<string name="search_results">Результаты поиска</string>
<string name="related">Похожие</string>
<string name="new_version_s">Новая версия: %s</string>
<string name="size_s">Размер: %s</string>
<string name="waiting_for_network">Ожидание подключения…</string>
<string name="clear_updates_feed">Очистить ленту обновлений</string>
<string name="updates_feed_cleared">Лента обновлений очищена</string>
<string name="updates_feed_cleared">Очищено</string>
<string name="rotate_screen">Повернуть экран</string>
<string name="update">Обновить</string>
<string name="feed_will_update_soon">Обновление скоро начнётся</string>
<string name="track_sources">Проверять обновления манги</string>
<string name="track_sources">Следить за обновлениями</string>
<string name="dont_check">Не проверять</string>
<string name="enter_password">Введите пароль</string>
<string name="wrong_password">Неверный пароль</string>
<string name="protect_application">Защитить приложение</string>
<string name="protect_application_summary">Запрашивать пароль при запуске приложения</string>
<string name="protect_application_summary">Запрашивать пароль при запуске Kotatsu</string>
<string name="repeat_password">Повторите пароль</string>
<string name="passwords_mismatch">Пароли не совпадают</string>
<string name="about">О программе</string>
<string name="app_version">Версия %s</string>
<string name="check_for_updates">Проверить обновления</string>
<string name="checking_for_updates">Проверка обновления…</string>
<string name="update_check_failed">Ошибка при проверке обновления</string>
<string name="update_check_failed">Не удалось проверить обновления</string>
<string name="no_update_available">Нет доступных обновлений</string>
<string name="right_to_left">Справа налево</string>
<string name="prefer_rtl_reader">Предпочитать режим Справа налево</string>
<string name="prefer_rtl_reader_summary">Вы можете настроить режим чтения для каждой манги отдельно</string>
<string name="right_to_left">Справа налево (←)</string>
<string name="prefer_rtl_reader">Предпочитать читать справа налево (←)</string>
<string name="prefer_rtl_reader_summary">Режим чтения может быть настроен отдельно для каждой серии</string>
<string name="create_category">Создать категорию</string>
<string name="scale_mode">Масштабирование</string>
<string name="zoom_mode_fit_center">Вписать в экран</string>
<string name="zoom_mode_fit_height">Подогнать по высоте</string>
<string name="zoom_mode_fit_width">Подогнать по ширине</string>
<string name="zoom_mode_keep_start">Исходный размер</string>
<string name="black_dark_theme">Чёрная тёмная тема</string>
<string name="black_dark_theme_summary">Полезно для AMOLED экранов</string>
<string name="black_dark_theme">Чёрная</string>
<string name="black_dark_theme_summary">Потребляет меньше энергии на экранах AMOLED</string>
<string name="restart_required">Требуется перезапуск</string>
<string name="backup_restore">Резервное копирование и восстановление</string>
<string name="create_backup">Создать резервную копию</string>
<string name="restore_backup">Восстановить данные</string>
<string name="data_restored">Данные восстановлены</string>
<string name="data_restored">Восстановлено</string>
<string name="preparing_">Подготовка…</string>
<string name="file_not_found">Файл не найден</string>
<string name="data_restored_success">Все данные успешно восстановлены</string>
<string name="data_restored_with_errors">Данные восстановлены, но возникли некоторые ошибки</string>
<string name="data_restored_success">Все данные были восстановлены</string>
<string name="data_restored_with_errors">Данные были восстановлены, но возникли некоторые ошибки</string>
<string name="backup_information">Вы можете создать резервную копию избранного и истории и потом восстановить их</string>
<string name="just_now">Только что</string>
<string name="yesterday">Вчера</string>
@@ -196,34 +195,34 @@
<string name="captcha_required">Необходимо пройти CAPTCHA</string>
<string name="captcha_solve">Пройти</string>
<string name="clear_cookies">Очистить куки</string>
<string name="cookies_cleared">Все куки удалены</string>
<string name="cookies_cleared">Все файлы cookie были удалены</string>
<string name="chapters_checking_progress">Проверка новых глав: %1$d из %2$d</string>
<string name="clear_feed">Очистить ленту</string>
<string name="text_clear_updates_feed_prompt">Вся история обновлений будет очищена и её нельзя будет вернуть. Вы уверены?</string>
<string name="new_chapters_checking">Проверка новых глав</string>
<string name="text_clear_updates_feed_prompt">Удалить всю историю обновлений навсегда\?</string>
<string name="new_chapters_checking">Проверка новых глав</string>
<string name="reverse">В обратном порядке</string>
<string name="sign_in">Войти</string>
<string name="auth_required">Для просмотра этого контента требуется авторизация</string>
<string name="auth_required">Авторизуйтесь, чтобы просмотреть этот контент</string>
<string name="default_s">По умолчанию: %s</string>
<string name="_and_x_more">…и ещё %1$d</string>
<string name="next">Далее</string>
<string name="protect_application_subtitle">Введите пароль, который вам понадобится при запуске приложения</string>
<string name="protect_application_subtitle">Введите пароль для запуска приложения</string>
<string name="confirm">Подтвердить</string>
<string name="password_length_hint">Пароль должен содержать не менее 4 символов</string>
<string name="password_length_hint">Пароль должен состоять из 4 символов или более</string>
<string name="hide_toolbar">Прятать заголовок при прокрутке</string>
<string name="search_only_on_s">Поиск только по %s</string>
<string name="other">Другие</string>
<string name="description">Описание</string>
<string name="languages">Языки</string>
<string name="welcome">Добро пожаловать</string>
<string name="text_clear_search_history_prompt">Вы действительно хотите удалить все недавние поисковые запросы? Это действие не может быть отменено.</string>
<string name="backup_saved">Резервная копия успешно сохранена</string>
<string name="tracker_warning">Некоторые производители могут изменять поведение системы, нарушая работу фоновых задач.</string>
<string name="text_clear_search_history_prompt">Удалить все последние поисковые запросы навсегда\?</string>
<string name="backup_saved">Резервная копия сохранена</string>
<string name="tracker_warning">Некоторые устройства имеют различное поведение системы, что может привести к нарушению фоновых задач.</string>
<string name="read_more">Подробнее</string>
<string name="queued">В очереди</string>
<string name="text_downloads_holder">На данный момент нет активных загрузок</string>
<string name="text_downloads_holder">Нет активных загрузок</string>
<string name="chapter_is_missing">Глава отсутствует</string>
<string name="chapter_is_missing_text">Эта глава отсутствует на вашем устройстве. Загрузите её или читайте онлайн.</string>
<string name="chapter_is_missing_text">Скачайте или прочитайте эту недостающую главу онлайн.</string>
<string name="about_app_translation_summary">Помочь с переводом приложения</string>
<string name="about_app_translation">Перевод</string>
<string name="about_author">Автор</string>
@@ -232,20 +231,20 @@
<string name="about_support_developer">Поддержать разработчика</string>
<string name="about_support_developer_summary">Если вам нравится это приложение, вы можете помочь финансово с помощью ЮMoney (бывш. Яндекс.Деньги)</string>
<string name="about_gratitudes">Благодарности</string>
<string name="about_gratitudes_summary">Эти люди помогают Kotatsu стать лучше!</string>
<string name="about_gratitudes_summary">Все эти люди сделали Kotatsu лучше</string>
<string name="about_copyright_and_licenses">Авторские права и лицензии</string>
<string name="about_license">Лицензия</string>
<string name="auth_complete">Авторизация выполнена</string>
<string name="auth_not_supported_by">Авторизация в %s не поддерживается</string>
<string name="text_clear_cookies_prompt">Вы выйдете из всех источников, в которых Вы авторизованы</string>
<string name="auth_not_supported_by">Вход в %s не поддерживается</string>
<string name="text_clear_cookies_prompt">Вы выйдете из всех источников</string>
<string name="genres">Жанры</string>
<string name="state_finished">Завершено</string>
<string name="state_ongoing">Онгоинг</string>
<string name="date_format">Формат даты</string>
<string name="system_default">По умолчанию</string>
<string name="exclude_nsfw_from_history">Исключить NSFW мангу из истории</string>
<string name="error_empty_name">Имя не может быть пустым</string>
<string name="show_pages_numbers">Показывать номера страниц</string>
<string name="error_empty_name">Вы должны ввести имя</string>
<string name="show_pages_numbers">Пронумерованные страницы</string>
<string name="enabled_sources">Включенные источники</string>
<string name="available_sources">Доступные источники</string>
<string name="dynamic_theme">Динамическая тема</string>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="loading_">පූරණය වෙමින්…</string>
<string name="settings">සැකසුම්</string>
<string name="chapters">පරිච්ඡේද</string>

View File

@@ -0,0 +1,60 @@
<?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="close_menu">Menüyü kapat</string>
<string name="open_menu">Menüyü aç</string>
<string name="local_storage">Dahili Depolama</string>
<string name="favourites">Favoriler</string>
<string name="history">Geçmiş</string>
<string name="chapters">Bölümler</string>
<string name="list">Liste</string>
<string name="detailed_list">Detaylı liste</string>
<string name="grid">Izgara</string>
<string name="list_mode">Liste modu</string>
<string name="loading_">Yükleniyor…</string>
<string name="close">Kapat</string>
<string name="try_again">Tekrar dene</string>
<string name="clear_history">Geçmişi temizle</string>
<string name="nothing_found">Hiçbir şey bulunamadı</string>
<string name="history_is_empty">Sen geçmişiniz yok</string>
<string name="read">Oku</string>
<string name="add_bookmark">Favorilere ekle</string>
<string name="you_have_not_favourites_yet">Henüz favorileriniz yok</string>
<string name="add_to_favourites">Favoriniz</string>
<string name="add_new_category">Yeni kategori</string>
<string name="add">Ekle</string>
<string name="enter_category_name">Kategori ismi girin</string>
<string name="save">Kaydet</string>
<string name="share">Paylaş</string>
<string name="share_s">%s Paylaş</string>
<string name="search">Ara</string>
<string name="search_manga">Manga ara</string>
<string name="manga_downloading_">İndiriliyor…</string>
<string name="processing_">İşleniyor…</string>
<string name="download_complete">İndirildi</string>
<string name="downloads">İndirilenler</string>
<string name="by_name">İsim</string>
<string name="updated">Güncellenme</string>
<string name="newest">Yeniler</string>
<string name="by_rating">Puanlama</string>
<string name="all">Hepsi</string>
<string name="genre">Tür</string>
<string name="filter">Litre</string>
<string name="theme">Tema</string>
<string name="light">ık</string>
<string name="dark">Koyu</string>
<string name="automatic">Takip sistemi</string>
<string name="pages">Sayfalar</string>
<string name="clear">Temizle</string>
<string name="text_clear_history_prompt">Tüm okuma geçmişi kalıcı olarak silinsin mi\?</string>
<string name="remove">Kaldır</string>
<string name="_s_removed_from_history">\"%s\" geçmişinden kaldırıldı</string>
<string name="_s_deleted_from_local_storage">\"%s\" yerel depolama alanından sil</string>
<string name="wait_for_loading_finish">Lütfen bitmesini bekleyin…</string>
<string name="save_page">Sayfayı kaydet</string>
<string name="share_image">Resmi paylaş</string>
<string name="popular">Popüler</string>
<string name="details">Detaylar</string>
<string name="settings">Ayarlar</string>
<string name="page_saved">Kaydet</string>
</resources>

View File

@@ -22,5 +22,6 @@
<color name="grey_dark">#212121</color>
<color name="dim">#99000000</color>
<color name="shadow">#99000000</color>
<color name="scrollbar">#66000000</color>
</resources>

View File

@@ -1,22 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="values_theme">
<string name="empty" translatable="false" />
<string-array name="values_theme" translatable="false">
<item>-1</item>
<item>1</item>
<item>2</item>
</string-array>
<string-array name="values_reader_switchers">
<string-array name="values_reader_switchers" translatable="false">
<item>taps</item>
<item>volume</item>
</string-array>
<string-array name="values_reader_switchers_default">
<string-array name="values_reader_switchers_default" translatable="false">
<item>taps</item>
</string-array>
<string-array name="values_track_sources">
<string-array name="values_track_sources" translatable="false">
<item>favourites</item>
<item>history</item>
</string-array>
<string-array name="values_track_sources_default">
<string-array name="values_track_sources_default" translatable="false">
<item>favourites</item>
</string-array>
</resources>
</resources>

View File

@@ -25,6 +25,7 @@
<dimen name="list_footer_height_inner">36dp</dimen>
<dimen name="list_footer_height_outer">48dp</dimen>
<dimen name="screen_padding">16dp</dimen>
<dimen name="feed_dividers_offset">72dp</dimen>
<!--Text dimens-->
<dimen name="text_size_h1">22sp</dimen>

View File

@@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name" translatable="false">Kotatsu</string>
<string name="_empty" translatable="false"/>
<string name="close_menu">Close menu</string>
<string name="open_menu">Open menu</string>
<string name="local_storage">Local storage</string>
@@ -18,6 +16,7 @@
<string name="settings">Settings</string>
<string name="remote_sources">Remote sources</string>
<string name="loading_">Loading…</string>
<string name="computing_">Computing…</string>
<string name="chapter_d_of_d">Chapter %1$d of %2$d</string>
<string name="close">Close</string>
<string name="try_again">Try again</string>
@@ -104,7 +103,7 @@
<string name="large_manga_save_confirm">This manga has %s. Save all of it?</string>
<string name="save_manga">Save</string>
<string name="notifications">Notifications</string>
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">Turned on %1$d of %2$d</string>
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">%1$d of %2$d on</string>
<string name="new_chapters">New chapters</string>
<string name="show_notification_new_chapters">Notify about updates of manga you are reading</string>
<string name="download">Download</string>
@@ -168,7 +167,6 @@
<string name="prefer_rtl_reader">Prefer right-to-left (→) reader</string>
<string name="prefer_rtl_reader_summary">Reading mode can be set up separately for each series</string>
<string name="create_category">New category</string>
<string name="report_github">Create issue on GitHub</string>
<string name="scale_mode">Scale mode</string>
<string name="zoom_mode_fit_center">Fit center</string>
<string name="zoom_mode_fit_height">Fit to height</string>
@@ -177,11 +175,12 @@
<string name="black_dark_theme">Black</string>
<string name="black_dark_theme_summary">Uses less power on AMOLED screens</string>
<string name="restart_required">Restart required</string>
<string name="backup_restore"><![CDATA[Backup and restore]]></string>
<string name="backup_restore">Backup and restore</string>
<string name="create_backup">Create data backup</string>
<string name="restore_backup">Restore from backup</string>
<string name="data_restored">Restored</string>
<string name="preparing_">Preparing…</string>
<string name="report_github">Create issue on GitHub</string>
<string name="file_not_found">File not found</string>
<string name="data_restored_success">All data was restored</string>
<string name="data_restored_with_errors">The data was restored, but there are errors</string>
@@ -251,6 +250,7 @@
<string name="available_sources">Available sources</string>
<string name="dynamic_theme">Dynamic theme</string>
<string name="dynamic_theme_summary">Applies a theme created on the color scheme of your wallpaper</string>
<string name="suggestions">Suggestions</string>
<string name="text_suggestion_holder">Start reading manga and you will get personalized suggestions</string>
<string name="importing_progress">Importing manga: %1$d of %2$d</string>
<string name="suggestions">Suggestions</string>
<string name="text_suggestion_holder">Start reading manga and you will get personalized suggestions</string>
</resources>

View File

@@ -84,6 +84,14 @@
<item name="rippleColor">@null</item>
</style>
<style name="Widget.Kotatsu.RecyclerView" parent="">
<item name="fastScrollHorizontalThumbDrawable">@drawable/list_thumb</item>
<item name="fastScrollVerticalThumbDrawable">@drawable/list_thumb</item>
<item name="fastScrollHorizontalTrackDrawable">@drawable/list_track</item>
<item name="fastScrollVerticalTrackDrawable">@drawable/list_track</item>
<item name="android:scrollbarStyle">outsideOverlay</item>
</style>
<style name="ThemeOverlay.Kotatsu.Switch" parent="">
<item name="elevationOverlayEnabled">@bool/elevation_overlay_enabled</item>
</style>
@@ -140,4 +148,4 @@
<item name="android:layout">@layout/pref_slider</item>
</style>
</resources>
</resources>

View File

@@ -67,6 +67,7 @@
<item name="tabStyle">@style/Widget.Kotatsu.Tabs</item>
<item name="switchStyle">@style/Widget.Kotatsu.Switch</item>
<item name="materialCardViewStyle">@style/Widget.Material3.CardView.Elevated</item>
<item name="recyclerViewStyle">@style/Widget.Kotatsu.RecyclerView</item>
<!-- Preference text appearance -->
<item name="android:textAppearanceListItem">?attr/textAppearanceBodyLarge</item>

View File

@@ -3,6 +3,9 @@
<external-files-path
name="manga-ext"
path="/manga" />
<files-path
name="manga"
path="/manga" />
<external-files-path
name="backups-ext"
path="/backups" />

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