Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9b0dc8b413 | |||
| f43fe5830e | |||
| ce616b328c | |||
| 824a8ff97a | |||
|
|
ef9018b92f | ||
|
|
a088d10935 | ||
| 2e561697ac | |||
| d242acd502 | |||
| d37b44d3f6 | |||
| 6243fc88c9 | |||
|
|
f74e865b06 | ||
|
|
1a2bc02188 | ||
| b0153e9f61 | |||
|
|
7f3fc1b88a | ||
|
|
40bf0c75bf | ||
|
|
9407bd205c | ||
|
|
3317c8dddc |
@@ -15,8 +15,8 @@ android {
|
||||
applicationId 'org.koitharu.kotatsu'
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 33
|
||||
versionCode 494
|
||||
versionName '4.0-a5'
|
||||
versionCode 496
|
||||
versionName '4.0-a7'
|
||||
generatedDensities = []
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
@@ -82,7 +82,7 @@ afterEvaluate {
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
implementation('com.github.KotatsuApp:kotatsu-parsers:f112a06ab6') {
|
||||
implementation('com.github.KotatsuApp:kotatsu-parsers:b3a9c5fcda') {
|
||||
exclude group: 'org.json', module: 'json'
|
||||
}
|
||||
|
||||
@@ -102,7 +102,7 @@ dependencies {
|
||||
implementation 'androidx.preference:preference-ktx:1.2.0'
|
||||
implementation 'androidx.work:work-runtime-ktx:2.7.1'
|
||||
implementation 'androidx.biometric:biometric-ktx:1.2.0-alpha04'
|
||||
implementation 'com.google.android.material:material:1.7.0-beta01'
|
||||
implementation 'com.google.android.material:material:1.7.0-rc01'
|
||||
//noinspection LifecycleAnnotationProcessorWithJava8
|
||||
kapt 'androidx.lifecycle:lifecycle-compiler:2.5.1'
|
||||
|
||||
@@ -122,10 +122,9 @@ dependencies {
|
||||
implementation 'androidx.hilt:hilt-work:1.0.0'
|
||||
kapt 'androidx.hilt:hilt-compiler:1.0.0'
|
||||
|
||||
implementation 'io.coil-kt:coil-base:2.2.0'
|
||||
implementation 'io.coil-kt:coil-svg:2.2.0'
|
||||
// implementation 'com.davemorrissey.labs:subsampling-scale-image-view-androidx:3.10.0'
|
||||
implementation 'com.github.KotatsuApp:subsampling-scale-image-view:2942b797a2'
|
||||
implementation 'io.coil-kt:coil-base:2.2.1'
|
||||
implementation 'io.coil-kt:coil-svg:2.2.1'
|
||||
implementation 'com.github.KotatsuApp:subsampling-scale-image-view:fc92ecb766'
|
||||
implementation 'com.github.solkin:disk-lru-cache:1.4'
|
||||
|
||||
implementation 'ch.acra:acra-http:5.9.6'
|
||||
@@ -145,7 +144,7 @@ dependencies {
|
||||
androidTestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4'
|
||||
|
||||
androidTestImplementation 'androidx.room:room-testing:2.4.3'
|
||||
androidTestImplementation 'com.squareup.moshi:moshi-kotlin:1.13.0'
|
||||
androidTestImplementation 'com.squareup.moshi:moshi-kotlin:1.14.0'
|
||||
|
||||
androidTestImplementation 'com.google.dagger:hilt-android-testing:2.43.2'
|
||||
kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.43.2'
|
||||
|
||||
@@ -12,7 +12,6 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.appcompat.widget.ActionBarContextView
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.view.ViewCompat
|
||||
@@ -92,7 +91,8 @@ abstract class BaseActivity<B : ViewBinding> :
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
if (BuildConfig.DEBUG && keyCode == KeyEvent.KEYCODE_VOLUME_UP) { // TODO remove
|
||||
ActivityCompat.recreate(this)
|
||||
// ActivityCompat.recreate(this)
|
||||
TODO("Test error")
|
||||
return true
|
||||
}
|
||||
return super.onKeyDown(keyCode, event)
|
||||
|
||||
@@ -33,6 +33,7 @@ class ScrollbarAnimator(
|
||||
.animate()
|
||||
.translationX(0f)
|
||||
.alpha(1f)
|
||||
.setListener(null)
|
||||
.setDuration(animationDuration)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@ import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.browser.BrowserActivity
|
||||
@@ -17,9 +20,6 @@ import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.settings.sources.auth.SourceAuthActivity
|
||||
import org.koitharu.kotatsu.utils.TaggedActivityResult
|
||||
import org.koitharu.kotatsu.utils.isSuccess
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
class ExceptionResolver private constructor(
|
||||
private val activity: FragmentActivity?,
|
||||
@@ -65,7 +65,7 @@ class ExceptionResolver private constructor(
|
||||
cont.invokeOnCancellation {
|
||||
continuations.remove(CloudFlareDialog.TAG, cont)
|
||||
fm.clearFragmentResultListener(CloudFlareDialog.TAG)
|
||||
dialog.dismiss()
|
||||
dialog.dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,4 +94,4 @@ class ExceptionResolver private constructor(
|
||||
|
||||
fun canResolve(e: Throwable) = getResolveStringId(e) != 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import org.koitharu.kotatsu.parsers.model.Manga
|
||||
|
||||
// Limits to avoid TransactionTooLargeException
|
||||
private const val MAX_SAFE_SIZE = 1024 * 512 // Assume that 512 kb is safe parcel size
|
||||
private const val MAX_SAFE_CHAPTERS_COUNT = 40 // this is 100% safe
|
||||
private const val MAX_SAFE_CHAPTERS_COUNT = 32 // this is 100% safe
|
||||
|
||||
class ParcelableManga(
|
||||
val manga: Manga,
|
||||
|
||||
@@ -4,7 +4,6 @@ import android.app.ActivityManager
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.ShortcutManager
|
||||
import android.media.ThumbnailUtils
|
||||
import android.os.Build
|
||||
import android.util.Size
|
||||
import androidx.annotation.RequiresApi
|
||||
@@ -15,6 +14,8 @@ import androidx.core.graphics.drawable.IconCompat
|
||||
import androidx.room.InvalidationTracker
|
||||
import coil.ImageLoader
|
||||
import coil.request.ImageRequest
|
||||
import coil.size.Precision
|
||||
import coil.size.Scale
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@@ -112,13 +113,14 @@ class ShortcutsUpdater @Inject constructor(
|
||||
|
||||
private suspend fun buildShortcutInfo(manga: Manga): ShortcutInfoCompat.Builder {
|
||||
val icon = runCatching {
|
||||
val bmp = coil.execute(
|
||||
coil.execute(
|
||||
ImageRequest.Builder(context)
|
||||
.data(manga.coverUrl)
|
||||
.size(iconSize.width, iconSize.height)
|
||||
.precision(Precision.EXACT)
|
||||
.scale(Scale.FILL)
|
||||
.build(),
|
||||
).requireBitmap()
|
||||
ThumbnailUtils.extractThumbnail(bmp, iconSize.width, iconSize.height, 0)
|
||||
}.fold(
|
||||
onSuccess = { IconCompat.createWithAdaptiveBitmap(it) },
|
||||
onFailure = { IconCompat.createWithResource(context, R.drawable.ic_shortcut_default) },
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.koitharu.kotatsu.BuildConfig
|
||||
import org.koitharu.kotatsu.core.model.ZoomMode
|
||||
import org.koitharu.kotatsu.core.network.DoHProvider
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||
import org.koitharu.kotatsu.utils.ext.getEnumValue
|
||||
import org.koitharu.kotatsu.utils.ext.observe
|
||||
import org.koitharu.kotatsu.utils.ext.putEnumValue
|
||||
@@ -208,6 +209,10 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
val dnsOverHttps: DoHProvider
|
||||
get() = prefs.getEnumValue(KEY_DOH, DoHProvider.NONE)
|
||||
|
||||
var localListOrder: SortOrder
|
||||
get() = prefs.getEnumValue(KEY_LOCAL_LIST_ORDER, SortOrder.NEWEST)
|
||||
set(value) = prefs.edit { putEnumValue(KEY_LOCAL_LIST_ORDER, value) }
|
||||
|
||||
fun isPagesPreloadAllowed(cm: ConnectivityManager): Boolean {
|
||||
return when (prefs.getString(KEY_PAGES_PRELOAD, null)?.toIntOrNull()) {
|
||||
NETWORK_ALWAYS -> true
|
||||
@@ -325,6 +330,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
const val KEY_READER_BAR = "reader_bar"
|
||||
const val KEY_SHORTCUTS = "dynamic_shortcuts"
|
||||
const val KEY_READER_TAPS_LTR = "reader_taps_ltr"
|
||||
const val KEY_LOCAL_LIST_ORDER = "local_order"
|
||||
|
||||
// About
|
||||
const val KEY_APP_UPDATE = "app_update"
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.koitharu.kotatsu.local.domain
|
||||
|
||||
import java.io.File
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
|
||||
class LocalManga(
|
||||
val manga: Manga,
|
||||
val file: File,
|
||||
) {
|
||||
|
||||
var createdAt: Long = -1L
|
||||
get() {
|
||||
if (field == -1L) {
|
||||
field = file.lastModified()
|
||||
}
|
||||
return field
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as LocalManga
|
||||
|
||||
if (manga != other.manga) return false
|
||||
if (file != other.file) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = manga.hashCode()
|
||||
result = 31 * result + file.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
fun Collection<LocalManga>.unwrap(): List<Manga> = map { it.manga }
|
||||
|
||||
fun LocalManga.isMatchesQuery(query: String): Boolean {
|
||||
return manga.title.contains(query, ignoreCase = true) ||
|
||||
manga.altTitle?.contains(query, ignoreCase = true) == true
|
||||
}
|
||||
|
||||
fun LocalManga.containsTags(tags: Set<MangaTag>): Boolean {
|
||||
return manga.tags.containsAll(tags)
|
||||
}
|
||||
@@ -45,12 +45,9 @@ class LocalMangaRepository @Inject constructor(private val storageManager: Local
|
||||
}
|
||||
val list = getRawList()
|
||||
if (query.isNotEmpty()) {
|
||||
list.retainAll { x ->
|
||||
x.title.contains(query, ignoreCase = true) ||
|
||||
x.altTitle?.contains(query, ignoreCase = true) == true
|
||||
}
|
||||
list.retainAll { x -> x.isMatchesQuery(query) }
|
||||
}
|
||||
return list
|
||||
return list.unwrap()
|
||||
}
|
||||
|
||||
override suspend fun getList(offset: Int, tags: Set<MangaTag>?, sortOrder: SortOrder?): List<Manga> {
|
||||
@@ -59,15 +56,17 @@ class LocalMangaRepository @Inject constructor(private val storageManager: Local
|
||||
}
|
||||
val list = getRawList()
|
||||
if (!tags.isNullOrEmpty()) {
|
||||
list.retainAll { x ->
|
||||
x.tags.containsAll(tags)
|
||||
}
|
||||
list.retainAll { x -> x.containsTags(tags) }
|
||||
}
|
||||
@Suppress("NON_EXHAUSTIVE_WHEN_STATEMENT")
|
||||
when (sortOrder) {
|
||||
SortOrder.ALPHABETICAL -> list.sortBy { it.title }
|
||||
SortOrder.RATING -> list.sortBy { it.rating }
|
||||
SortOrder.ALPHABETICAL -> list.sortWith(compareBy(AlphanumComparator()) { x -> x.manga.title })
|
||||
SortOrder.RATING -> list.sortByDescending { it.manga.rating }
|
||||
SortOrder.NEWEST,
|
||||
SortOrder.UPDATED,
|
||||
-> list.sortByDescending { it.createdAt }
|
||||
}
|
||||
return list
|
||||
return list.unwrap()
|
||||
}
|
||||
|
||||
override suspend fun getDetails(manga: Manga) = when {
|
||||
@@ -235,9 +234,9 @@ class LocalMangaRepository @Inject constructor(private val storageManager: Local
|
||||
private fun CoroutineScope.getFromFileAsync(
|
||||
file: File,
|
||||
context: CoroutineContext,
|
||||
): Deferred<Manga?> = async(context) {
|
||||
): Deferred<LocalManga?> = async(context) {
|
||||
runInterruptible {
|
||||
runCatching { getFromFile(file) }.getOrNull()
|
||||
runCatching { LocalManga(getFromFile(file), file) }.getOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,7 +282,7 @@ class LocalMangaRepository @Inject constructor(private val storageManager: Local
|
||||
locks.unlock(id)
|
||||
}
|
||||
|
||||
private suspend fun getRawList(): ArrayList<Manga> {
|
||||
private suspend fun getRawList(): ArrayList<LocalManga> {
|
||||
val files = getAllFiles()
|
||||
return coroutineScope {
|
||||
val dispatcher = Dispatchers.IO.limitedParallelism(MAX_PARALLELISM)
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.net.toFile
|
||||
import androidx.core.net.toUri
|
||||
import androidx.fragment.app.viewModels
|
||||
@@ -13,10 +14,11 @@ import com.google.android.material.snackbar.Snackbar
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.list.ListSelectionController
|
||||
import org.koitharu.kotatsu.list.ui.MangaListFragment
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||
import org.koitharu.kotatsu.utils.ShareHelper
|
||||
import org.koitharu.kotatsu.utils.ext.addMenuProvider
|
||||
|
||||
class LocalListFragment : MangaListFragment() {
|
||||
class LocalListFragment : MangaListFragment(), PopupMenu.OnMenuItemClickListener {
|
||||
|
||||
override val viewModel by viewModels<LocalListViewModel>()
|
||||
|
||||
@@ -30,6 +32,14 @@ class LocalListFragment : MangaListFragment() {
|
||||
ImportDialogFragment.show(childFragmentManager)
|
||||
}
|
||||
|
||||
override fun onFilterClick(view: View?) {
|
||||
super.onFilterClick(view)
|
||||
val menu = PopupMenu(requireContext(), view ?: binding.recyclerView)
|
||||
menu.inflate(R.menu.popup_order)
|
||||
menu.setOnMenuItemClickListener(this)
|
||||
menu.show()
|
||||
}
|
||||
|
||||
override fun onScrolledToEnd() = Unit
|
||||
|
||||
override fun onCreateActionMode(controller: ListSelectionController, mode: ActionMode, menu: Menu): Boolean {
|
||||
@@ -53,6 +63,17 @@ class LocalListFragment : MangaListFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
val order = when (item.itemId) {
|
||||
R.id.action_order_new -> SortOrder.NEWEST
|
||||
R.id.action_order_abs -> SortOrder.ALPHABETICAL
|
||||
R.id.action_order_rating -> SortOrder.RATING
|
||||
else -> return false
|
||||
}
|
||||
viewModel.setSortOrder(order)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun showDeletionConfirm(ids: Set<Long>, mode: ActionMode) {
|
||||
MaterialAlertDialogBuilder(context ?: return)
|
||||
.setTitle(R.string.delete_manga)
|
||||
|
||||
@@ -23,4 +23,4 @@ class LocalListMenuProvider(
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,31 @@
|
||||
package org.koitharu.kotatsu.local.ui
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.asFlow
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.widgets.ChipsView
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.download.ui.service.DownloadService
|
||||
import org.koitharu.kotatsu.history.domain.HistoryRepository
|
||||
import org.koitharu.kotatsu.history.domain.PROGRESS_NONE
|
||||
import org.koitharu.kotatsu.list.domain.ListExtraProvider
|
||||
import org.koitharu.kotatsu.list.ui.MangaListViewModel
|
||||
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingState
|
||||
import org.koitharu.kotatsu.list.ui.model.toErrorState
|
||||
import org.koitharu.kotatsu.list.ui.model.toUi
|
||||
import org.koitharu.kotatsu.list.ui.model.*
|
||||
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import org.koitharu.kotatsu.utils.SingleLiveEvent
|
||||
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
|
||||
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
||||
@@ -30,18 +34,24 @@ import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
||||
class LocalListViewModel @Inject constructor(
|
||||
private val repository: LocalMangaRepository,
|
||||
private val historyRepository: HistoryRepository,
|
||||
settings: AppSettings,
|
||||
) : MangaListViewModel(settings) {
|
||||
private val trackingRepository: TrackingRepository,
|
||||
private val settings: AppSettings,
|
||||
) : MangaListViewModel(settings), ListExtraProvider {
|
||||
|
||||
val onMangaRemoved = SingleLiveEvent<Unit>()
|
||||
val sortOrder = MutableLiveData(settings.localListOrder)
|
||||
private val listError = MutableStateFlow<Throwable?>(null)
|
||||
private val mangaList = MutableStateFlow<List<Manga>?>(null)
|
||||
private val selectedTags = MutableStateFlow<Set<MangaTag>>(emptySet())
|
||||
private var refreshJob: Job? = null
|
||||
|
||||
override val content = combine(
|
||||
mangaList,
|
||||
createListModeFlow(),
|
||||
sortOrder.asFlow(),
|
||||
selectedTags,
|
||||
listError,
|
||||
) { list, mode, error ->
|
||||
) { list, mode, order, tags, error ->
|
||||
when {
|
||||
error != null -> listOf(error.toErrorState(canRetry = true))
|
||||
list == null -> listOf(LoadingState)
|
||||
@@ -53,7 +63,10 @@ class LocalListViewModel @Inject constructor(
|
||||
actionStringRes = R.string._import,
|
||||
),
|
||||
)
|
||||
else -> list.toUi(mode)
|
||||
else -> buildList(list.size + 1) {
|
||||
add(createHeader(list, tags, order))
|
||||
list.toUi(this, mode, this@LocalListViewModel)
|
||||
}
|
||||
}
|
||||
}.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState))
|
||||
|
||||
@@ -63,14 +76,27 @@ class LocalListViewModel @Inject constructor(
|
||||
watchDirectories()
|
||||
}
|
||||
|
||||
override fun onUpdateFilter(tags: Set<MangaTag>) {
|
||||
selectedTags.value = tags
|
||||
onRefresh()
|
||||
}
|
||||
|
||||
override fun onRefresh() {
|
||||
launchLoadingJob(Dispatchers.Default) {
|
||||
val prevJob = refreshJob
|
||||
refreshJob = launchLoadingJob(Dispatchers.Default) {
|
||||
prevJob?.cancelAndJoin()
|
||||
doRefresh()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRetry() = onRefresh()
|
||||
|
||||
fun setSortOrder(value: SortOrder) {
|
||||
sortOrder.value = value
|
||||
settings.localListOrder = value
|
||||
onRefresh()
|
||||
}
|
||||
|
||||
fun delete(ids: Set<Long>) {
|
||||
launchLoadingJob {
|
||||
withContext(Dispatchers.Default) {
|
||||
@@ -93,7 +119,7 @@ class LocalListViewModel @Inject constructor(
|
||||
private suspend fun doRefresh() {
|
||||
try {
|
||||
listError.value = null
|
||||
mangaList.value = repository.getList(0, null, null)
|
||||
mangaList.value = repository.getList(0, selectedTags.value, sortOrder.value)
|
||||
} catch (e: Throwable) {
|
||||
listError.value = e
|
||||
}
|
||||
@@ -119,4 +145,46 @@ class LocalListViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createHeader(mangaList: List<Manga>, selectedTags: Set<MangaTag>, order: SortOrder): ListHeader2 {
|
||||
val tags = HashMap<MangaTag, Int>()
|
||||
for (item in mangaList) {
|
||||
for (tag in item.tags) {
|
||||
tags[tag] = tags[tag]?.plus(1) ?: 1
|
||||
}
|
||||
}
|
||||
val topTags = tags.entries.sortedByDescending { it.value }.take(6)
|
||||
val chips = LinkedList<ChipsView.ChipModel>()
|
||||
for ((tag, _) in topTags) {
|
||||
val model = ChipsView.ChipModel(
|
||||
icon = 0,
|
||||
title = tag.title,
|
||||
isCheckable = true,
|
||||
isChecked = tag in selectedTags,
|
||||
data = tag,
|
||||
)
|
||||
if (model.isChecked) {
|
||||
chips.addFirst(model)
|
||||
} else {
|
||||
chips.addLast(model)
|
||||
}
|
||||
}
|
||||
return ListHeader2(
|
||||
chips = chips,
|
||||
sortOrder = order,
|
||||
hasSelectedTags = selectedTags.isNotEmpty(),
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getCounter(mangaId: Long): Int {
|
||||
return trackingRepository.getNewChaptersCount(mangaId)
|
||||
}
|
||||
|
||||
override suspend fun getProgress(mangaId: Long): Float {
|
||||
return if (settings.isReadingIndicatorsEnabled) {
|
||||
historyRepository.getProgress(mangaId)
|
||||
} else {
|
||||
PROGRESS_NONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ import java.io.Serializable
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
inline fun <reified T : Parcelable> Bundle.getParcelableCompat(key: String): T? {
|
||||
if (!containsKey(key)) {
|
||||
return null
|
||||
}
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
getParcelable(key, T::class.java)
|
||||
} else {
|
||||
@@ -17,6 +20,9 @@ inline fun <reified T : Parcelable> Bundle.getParcelableCompat(key: String): T?
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
inline fun <reified T : Parcelable> Intent.getParcelableExtraCompat(key: String): T? {
|
||||
if (!hasExtra(key)) {
|
||||
return null
|
||||
}
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
getParcelableExtra(key, T::class.java)
|
||||
} else {
|
||||
@@ -26,6 +32,9 @@ inline fun <reified T : Parcelable> Intent.getParcelableExtraCompat(key: String)
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
inline fun <reified T : Serializable> Bundle.getSerializableCompat(key: String): T? {
|
||||
if (!containsKey(key)) {
|
||||
return null
|
||||
}
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
getSerializable(key, T::class.java)
|
||||
} else {
|
||||
|
||||
@@ -13,6 +13,7 @@ import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updatePadding
|
||||
import com.google.android.material.R as materialR
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||
@@ -22,6 +23,7 @@ import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.widget.shelf.adapter.CategorySelectAdapter
|
||||
import org.koitharu.kotatsu.widget.shelf.model.CategoryItem
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ShelfConfigActivity :
|
||||
BaseActivity<ActivityCategoriesBinding>(),
|
||||
OnListItemClickListener<CategoryItem>,
|
||||
|
||||
17
app/src/main/res/menu/popup_order.xml
Normal file
17
app/src/main/res/menu/popup_order.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_order_new"
|
||||
android:title="@string/newest" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_order_abs"
|
||||
android:title="@string/by_name" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_order_rating"
|
||||
android:title="@string/by_rating" />
|
||||
|
||||
</menu>
|
||||
@@ -51,7 +51,7 @@
|
||||
<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="right_to_left">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>
|
||||
@@ -320,4 +320,69 @@
|
||||
<string name="invalid_domain_message">Domaine invalide</string>
|
||||
<string name="select_range">Sélectionner une plage</string>
|
||||
<string name="not_found_404">Contenu non trouvé ou supprimé</string>
|
||||
<string name="manga_error_description_pattern">Détails de l\'erreur :<br><tt>%1$s</tt><br><br>1. Essayez de <a href=%2$s>ouvrir le manga dans un navigateur web</a> pour vous assurer qu\'il est disponible sur sa source<br>2. S\'il est disponible, envoyez un rapport d\'erreur aux développeurs.</string>
|
||||
<string name="confirm_exit">Rappuyez sur Retour pour quitter</string>
|
||||
<string name="categories_delete_confirm">Êtes-vous sûr·e de vouloir supprimer les catégories de favoris sélectionnées \?
|
||||
\nTous les mangas qui s\'y trouvent seront perdus et ceci ne peut pas être annulé.</string>
|
||||
<string name="exit_confirmation_summary">Appuyez deux fois sur la touche Retour pour quitter l\'appli</string>
|
||||
<string name="available">Disponible</string>
|
||||
<string name="exit_confirmation">Confirmation de sortie</string>
|
||||
<string name="memory_usage_pattern">%s - %s</string>
|
||||
<string name="feed">Flux</string>
|
||||
<string name="enter_email_text">Entrez votre courriel pour continuer</string>
|
||||
<string name="app_update_available_s">Mise à jour de l\'application disponible : %s</string>
|
||||
<string name="importing_manga">Importation de mangas</string>
|
||||
<string name="removed_from_favourites">Retiré des favoris</string>
|
||||
<string name="clear_all_history">Effacer tout l\'historique</string>
|
||||
<string name="last_2_hours">Les 2 dernières heures</string>
|
||||
<string name="history_cleared">Historique effacé</string>
|
||||
<string name="manage">Gérer</string>
|
||||
<string name="no_bookmarks_yet">Aucun marque-page</string>
|
||||
<string name="no_bookmarks_summary">Vous pouvez créer un marque-page pendant la lecture d\'un manga</string>
|
||||
<string name="bookmarks_removed">Marque-pages supprimés</string>
|
||||
<string name="no_manga_sources">Aucune source de mangas</string>
|
||||
<string name="no_manga_sources_text">Autoriser les sources de mangas de lire des mangas en ligne</string>
|
||||
<string name="random">Aléatoire</string>
|
||||
<string name="reorder">Réordonner</string>
|
||||
<string name="empty">Vide</string>
|
||||
<string name="changelog">Journal des modifications</string>
|
||||
<string name="explore">Explorer</string>
|
||||
<string name="text_shelf_holder_primary">Vos mangas seront affichés ici</string>
|
||||
<string name="text_shelf_holder_secondary">Trouvez ce que vous voulez lire dans la section « Explorer »</string>
|
||||
<string name="canceled">Annulé</string>
|
||||
<string name="account_already_exists">Le compte existe déjà</string>
|
||||
<string name="back">Retour</string>
|
||||
<string name="sync">Synchronisation</string>
|
||||
<string name="sync_title">Synchronisez vos données</string>
|
||||
<string name="email_enter_hint">Entrez votre courriel pour continuer</string>
|
||||
<string name="saved_manga">Mangas sauvegardés</string>
|
||||
<string name="pages_cache">Cache des pages</string>
|
||||
<string name="other_cache">Autre cache</string>
|
||||
<string name="storage_usage">Utilisation du stockage</string>
|
||||
<string name="removed_from_s">Retiré de « %s »</string>
|
||||
<string name="options">Options</string>
|
||||
<string name="downloading_manga">Télécharger des mangas</string>
|
||||
<string name="incognito_mode">Mode incognito</string>
|
||||
<string name="no_chapters">Aucun chapitre</string>
|
||||
<string name="automatic_scroll">Défilement automatique</string>
|
||||
<string name="off_short">Désactivé</string>
|
||||
<string name="seconds_pattern">%s s</string>
|
||||
<string name="reader_info_pattern">Ch. %1$d/%2$d Pg. %3$d/%4$d</string>
|
||||
<string name="reader_info_bar">Afficher la barre d\'infos dans le lecteur</string>
|
||||
<string name="comics_archive">Archives des BD</string>
|
||||
<string name="folder_with_images">Dossier avec des images</string>
|
||||
<string name="import_completed">Importation terminée</string>
|
||||
<string name="import_completed_hint">Vous pouvez supprimer le fichier original du stockage pour gagner de l\'espace</string>
|
||||
<string name="import_will_start_soon">L\'importation va bientôt commencer</string>
|
||||
<string name="text_unsaved_changes_prompt">Vous avez des modifications non sauvegardées, voulez-vous les sauvegarder ou les abandonner \?</string>
|
||||
<string name="discard">Abandonner</string>
|
||||
<string name="history_shortcuts_summary">Rendre les mangas récents disponibles en appuyant longuement sur l\'icône de l\'application</string>
|
||||
<string name="reader_control_ltr_summary">Taper sur le bord droit ou appuyer sur la touche droite permet toujours de passer à la page suivante</string>
|
||||
<string name="reader_control_ltr">Contrôle ergonomique du lecteur</string>
|
||||
<string name="history_shortcuts">Afficher les raccourcis des mangas récents</string>
|
||||
<string name="color_correction">Correction des couleurs</string>
|
||||
<string name="brightness">Luminosité</string>
|
||||
<string name="contrast">Contraste</string>
|
||||
<string name="reset">Réinitialiser</string>
|
||||
<string name="color_correction_hint">Les paramètres de couleurs choisis seront sauvegardés pour ce manga</string>
|
||||
</resources>
|
||||
@@ -326,4 +326,46 @@
|
||||
<string name="removed_from_favourites">Dihapus dari favorit</string>
|
||||
<string name="import_will_start_soon">Impor akan segera dimulai</string>
|
||||
<string name="not_found_404">Konten tidak ditemukan atau dihapus</string>
|
||||
<string name="exit_confirmation_summary">Tekan Kembali dua kali untuk keluar dari aplikasi</string>
|
||||
<string name="exit_confirmation">Konfirmasi keluar</string>
|
||||
<string name="pages_cache">Tembolok halaman</string>
|
||||
<string name="other_cache">Tembolok lainnya</string>
|
||||
<string name="storage_usage">Penggunaan penyimpanan</string>
|
||||
<string name="available">Tersedia</string>
|
||||
<string name="incognito_mode">Mode Incognito</string>
|
||||
<string name="app_update_available_s">Pembaruan aplikasi tersedia: %s</string>
|
||||
<string name="automatic_scroll">Gulir otomatis</string>
|
||||
<string name="comics_archive">Arsip komik</string>
|
||||
<string name="folder_with_images">Folder dengan gambar</string>
|
||||
<string name="import_completed_hint">Anda bisa menghapus berkas asli dari penyimpanan untuk menghemat ruang</string>
|
||||
<string name="feed">Umpan</string>
|
||||
<string name="manga_error_description_pattern">Detail galat:<br><tt>%1$s</tt><br><br>1. Coba untuk <a href=%2$s>membuka manga di peramban web</a> untuk memastikan bahwa itu tersedia di sumbernya<br>2. Jika tersedia, kirim laporan galat ke pengembang.</string>
|
||||
<string name="brightness">Kecerahan</string>
|
||||
<string name="contrast">Kontras</string>
|
||||
<string name="reset">Atur Ulang</string>
|
||||
<string name="color_correction_hint">Pengaturan warna yang dipilih akan diingat untuk manga ini</string>
|
||||
<string name="text_unsaved_changes_prompt">Anda memiliki perubahan belum tersimpan, apakah Anda ingin menyimpannya atau membuangnya\?</string>
|
||||
<string name="discard">Buang</string>
|
||||
<string name="use_fingerprint">Gunakan sidik jari jika tersedia</string>
|
||||
<string name="appwidget_shelf_description">Manga dari favorit Anda</string>
|
||||
<string name="appwidget_recent_description">Manga yang baru-baru ini Anda baca</string>
|
||||
<string name="data_deletion">Penghapusan data</string>
|
||||
<string name="account_already_exists">Akun sudah ada</string>
|
||||
<string name="back">Kembali</string>
|
||||
<string name="disable_battery_optimization_summary">Membantu pemeriksaan pembaruan di latar belakang</string>
|
||||
<string name="crash_text">Ada sesuatu yang salah. Mohon untuk mengirim laporan kutu (bug) ke pengembang untuk membantu kami memperbaikinya.</string>
|
||||
<string name="report">Lapor</string>
|
||||
<string name="exclude_nsfw_from_history_summary">Manga yang ditandai sebagai NSFW tidak akan ditambahkan ke riwayat dan progres Anda tidak akan disimpan</string>
|
||||
<string name="clear_cookies_summary">Bisa membantu dalam beberapa masalah. Seluruh otorisasi akan menjadi tidak valid.</string>
|
||||
<string name="manage">Kelola</string>
|
||||
<string name="no_manga_sources_text">Aktifkan sumber manga untuk membaca manga daring</string>
|
||||
<string name="categories_delete_confirm">Apakah Anda yakin ingin menghapus kategori favorit yang dipilih\?
|
||||
\n Semua manga di sana akan hilang dan ini tidak bisa diurungkan.</string>
|
||||
<string name="reorder">Atur Ulang</string>
|
||||
<string name="saved_manga">Manga tersimpan</string>
|
||||
<string name="confirm_exit">Tekan Kembali lagi untuk keluar</string>
|
||||
<string name="no_chapters">Tidak ada bab</string>
|
||||
<string name="history_shortcuts">Tampilkan pintasan manga baru-baru ini</string>
|
||||
<string name="history_shortcuts_summary">Buat manga baru-baru ini tersedia dengan menekan panjang pada ikon aplikasi</string>
|
||||
<string name="select_range">Pilih jangkauan</string>
|
||||
</resources>
|
||||
@@ -374,4 +374,5 @@
|
||||
<string name="comics_archive">コミックアーカイブ</string>
|
||||
<string name="downloading_manga">漫画をダウンロードする</string>
|
||||
<string name="feed">フィード</string>
|
||||
<string name="manga_error_description_pattern">エラーの詳細:<br><tt>%1$s</tt><br><br>1. <a href=%2$s>Webブラウザで漫画を開いてみて</a>、そのソースで利用できるか確認する<br>2. 利用できる場合は、開発者にエラーレポートを送信する</a>。</string>
|
||||
</resources>
|
||||
@@ -374,4 +374,16 @@
|
||||
<string name="import_will_start_soon">Импорт скоро начнётся</string>
|
||||
<string name="feed">Лента</string>
|
||||
<string name="downloading_manga">Загрузка манги</string>
|
||||
<string name="manga_error_description_pattern">Информация об ошибке:<br><tt>%1$s</tt><br><br>1. Попробуйте <a href=%2$s>открыть мангу в веб-браузере</a>, чтобы убедиться, что она доступна в источнике<br>2. Если она доступна, отправьте отчёт об ошибке разработчикам.</string>
|
||||
<string name="history_shortcuts">Показывать ярлыки последней прочитанной манги</string>
|
||||
<string name="history_shortcuts_summary">Сделать недавно прочитанную доступной по долгому нажатию на иконку приложения</string>
|
||||
<string name="reader_control_ltr_summary">Нажатие на правый край или нажатие правой клавиши всегда переключает на следующую страницу</string>
|
||||
<string name="reader_control_ltr">Эргономичное управление режимом чтения</string>
|
||||
<string name="reset">Сбросить</string>
|
||||
<string name="discard">Отменить</string>
|
||||
<string name="text_unsaved_changes_prompt">У вас есть несохраненные изменения, вы хотите сохранить или отменить их\?</string>
|
||||
<string name="contrast">Контрастность</string>
|
||||
<string name="brightness">Яркость</string>
|
||||
<string name="color_correction">Цветокоррекция</string>
|
||||
<string name="color_correction_hint">Выбранные настройки цвета запомнятся для этой манги</string>
|
||||
</resources>
|
||||
@@ -16,7 +16,7 @@
|
||||
<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="history_is_empty">Geçmiş yok</string>
|
||||
<string name="read">Oku</string>
|
||||
<string name="you_have_not_favourites_yet">Henüz favorileriniz yok</string>
|
||||
<string name="add_to_favourites">Favoriniz</string>
|
||||
@@ -209,7 +209,7 @@
|
||||
<string name="new_version_s">Yeni sürüm: %s</string>
|
||||
<string name="protect_application">Uygulamayı koru</string>
|
||||
<string name="passwords_mismatch">Parolalar eşleşmiyor</string>
|
||||
<string name="right_to_left">Sağdan sola (←)</string>
|
||||
<string name="right_to_left">Sağdan-sola</string>
|
||||
<string name="create_category">Yeni kategori</string>
|
||||
<string name="file_not_found">Dosya bulunamadı</string>
|
||||
<string name="data_restored_success">Tüm veriler geri yüklendi</string>
|
||||
@@ -320,4 +320,30 @@
|
||||
<string name="invalid_domain_message">Geçersiz etki alanı</string>
|
||||
<string name="select_range">Aralık seç</string>
|
||||
<string name="not_found_404">İçerik bulunamadı veya kaldırıldı</string>
|
||||
<string name="text_shelf_holder_primary">Manganız burada görüntülenecek</string>
|
||||
<string name="canceled">İptal edilmiş</string>
|
||||
<string name="account_already_exists">Hesap zaten var</string>
|
||||
<string name="back">Geri</string>
|
||||
<string name="sync">Senkronizasyon</string>
|
||||
<string name="sync_title">Verini yedekle</string>
|
||||
<string name="email_enter_hint">Devam etmek için E-Postanızı girin</string>
|
||||
<string name="clear_all_history">Tüm gecmişi temizle</string>
|
||||
<string name="last_2_hours">Son 2 saat</string>
|
||||
<string name="history_cleared">Geçmiş temizlendi</string>
|
||||
<string name="manage">Yönet</string>
|
||||
<string name="no_bookmarks_yet">Yer işareti yok</string>
|
||||
<string name="no_bookmarks_summary">Manga okurken yer işareti oluşturabilirsiniz</string>
|
||||
<string name="bookmarks_removed">Yer işaretleri kaldırıldı</string>
|
||||
<string name="no_manga_sources">Manga kaynağı yok</string>
|
||||
<string name="no_manga_sources_text">Çevrimiçi manga okumak için manga kaynaklarını aktif edin</string>
|
||||
<string name="random">Rastgele</string>
|
||||
<string name="empty">Boş</string>
|
||||
<string name="explore">Keşfet</string>
|
||||
<string name="confirm_exit">Çıkmak için tekrar Geri tıkla</string>
|
||||
<string name="exit_confirmation_summary">Çıkmak için iki defa Geri tıkla</string>
|
||||
<string name="removed_from_favourites">Favorilerden kaldırıldı</string>
|
||||
<string name="downloading_manga">Manga indiriliyor</string>
|
||||
<string name="changelog">Değişiklik günlüğü</string>
|
||||
<string name="exit_confirmation">Çıkış doğrulaması</string>
|
||||
<string name="enter_email_text">Devam etmek için e-postanızı girin</string>
|
||||
</resources>
|
||||
@@ -373,4 +373,5 @@
|
||||
<string name="removed_from_favourites">Видалено з уподобань</string>
|
||||
<string name="other_cache">Інший кеш</string>
|
||||
<string name="memory_usage_pattern">%s - %s</string>
|
||||
<string name="manga_error_description_pattern">Деталі помилки:<br><tt>%1$s</tt><br><br>1. Спробуйте <a href=%2$s>відкрити манґу у веб-браузері</a>, щоб переконатися, що вона доступна в джерелі<br>2. Якщо вона доступна, то надішліть звіт про помилку розробникам.</string>
|
||||
</resources>
|
||||
27
app/src/main/res/values-zh-rCN/plurals.xml
Normal file
27
app/src/main/res/values-zh-rCN/plurals.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<plurals name="items">
|
||||
<item quantity="other">%1$d 个项目</item>
|
||||
</plurals>
|
||||
<plurals name="new_chapters">
|
||||
<item quantity="other">%1$d 个新章节</item>
|
||||
</plurals>
|
||||
<plurals name="chapters">
|
||||
<item quantity="other">%1$d 章节</item>
|
||||
</plurals>
|
||||
<plurals name="chapters_from_x">
|
||||
<item quantity="other">来自 %2$d 中的 %1$d 个章节</item>
|
||||
</plurals>
|
||||
<plurals name="minutes_ago">
|
||||
<item quantity="other">%1$d 分钟前</item>
|
||||
</plurals>
|
||||
<plurals name="hours_ago">
|
||||
<item quantity="other">%1$d 小时前</item>
|
||||
</plurals>
|
||||
<plurals name="days_ago">
|
||||
<item quantity="other">%1$d 天前</item>
|
||||
</plurals>
|
||||
<plurals name="pages">
|
||||
<item quantity="other">共 %1$d 页</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="settings">设置</string>
|
||||
<string name="local_storage">本地存储</string>
|
||||
<string name="favourites">喜欢</string>
|
||||
@@ -78,7 +78,7 @@
|
||||
<string name="sort_order">排序顺序</string>
|
||||
<string name="filter">过滤器</string>
|
||||
<string name="theme">主题</string>
|
||||
<string name="dark">黑暗</string>
|
||||
<string name="dark">深色</string>
|
||||
<string name="light">浅色</string>
|
||||
<string name="automatic">跟随系统</string>
|
||||
<string name="pages">页数</string>
|
||||
@@ -101,7 +101,7 @@
|
||||
<string name="cache">缓存</string>
|
||||
<string name="text_file_sizes">B|kB|MB|GB|TB</string>
|
||||
<string name="standard">标准</string>
|
||||
<string name="webtoon">Webtoon</string>
|
||||
<string name="webtoon">条漫</string>
|
||||
<string name="read_mode">阅读模式</string>
|
||||
<string name="grid_size">网格大小</string>
|
||||
<string name="search_on_s">在%s上搜索</string>
|
||||
@@ -188,7 +188,7 @@
|
||||
<string name="checking_for_updates">检查更新…</string>
|
||||
<string name="update_check_failed">无法查找更新</string>
|
||||
<string name="no_update_available">没有更新</string>
|
||||
<string name="right_to_left">从右到左 (←)</string>
|
||||
<string name="right_to_left">从右到左</string>
|
||||
<string name="create_category">新类别</string>
|
||||
<string name="scale_mode">缩放模式</string>
|
||||
<string name="zoom_mode_fit_center">适应中心</string>
|
||||
@@ -249,7 +249,7 @@
|
||||
<string name="screenshots_block_all">始终阻止</string>
|
||||
<string name="suggestions">建议</string>
|
||||
<string name="suggestions_enable">启用建议</string>
|
||||
<string name="text_suggestion_holder">开始阅读漫画,你会得到个性化的建议</string>
|
||||
<string name="text_suggestion_holder">开始阅读漫画,你会得到个性化的建议</string>
|
||||
<string name="exclude_nsfw_from_suggestions">请勿推荐18+漫画</string>
|
||||
<string name="enabled">启用</string>
|
||||
<string name="disabled">禁用</string>
|
||||
@@ -290,7 +290,7 @@
|
||||
<string name="dns_over_https">DNS over HTTPS</string>
|
||||
<string name="default_mode">默认模式</string>
|
||||
<string name="detect_reader_mode">自动检测阅读器模式</string>
|
||||
<string name="detect_reader_mode_summary">自动检测漫画是否为网络漫画</string>
|
||||
<string name="detect_reader_mode_summary">自动检测漫画是否为条漫</string>
|
||||
<string name="disable_battery_optimization">禁用电池优化</string>
|
||||
<string name="disable_battery_optimization_summary">帮助进行背景更新检查</string>
|
||||
<string name="crash_text">出了点问题. 请向开发人员提交一份错误报告以帮助我们修复它.</string>
|
||||
@@ -313,4 +313,65 @@
|
||||
<string name="exclude_nsfw_from_history_summary">标记为NSFW的漫画将永远不会被添加到历史中你的进度也不会被保存</string>
|
||||
<string name="clear_cookies_summary">可以在出现一些问题时提供帮助. 所有授权将被视为无效</string>
|
||||
<string name="show_all">显示全部</string>
|
||||
<string name="manga_error_description_pattern">错误详情:<br><tt>%1$s</tt><br><br>1.尝试<a href=%2$s>在网络浏览器中打开漫画</a>以确保在其来源中可用<br>2.如果可用,请向开发人员发送错误报告。</string>
|
||||
<string name="invalid_domain_message">无效域名</string>
|
||||
<string name="text_shelf_holder_primary">此处将显示你的漫画</string>
|
||||
<string name="text_shelf_holder_secondary">在【浏览】页面搜索想要阅读的漫画</string>
|
||||
<string name="percent_string_pattern">%1$s%%</string>
|
||||
<string name="canceled">已取消</string>
|
||||
<string name="account_already_exists">账号已存在</string>
|
||||
<string name="back">返回</string>
|
||||
<string name="sync">同步</string>
|
||||
<string name="sync_title">同步您的数据</string>
|
||||
<string name="email_enter_hint">输入您的邮箱以继续</string>
|
||||
<string name="status_dropped">已放弃</string>
|
||||
<string name="select_range">选择范围</string>
|
||||
<string name="clear_all_history">清除所有历史</string>
|
||||
<string name="last_2_hours">过去2小时</string>
|
||||
<string name="bookmarks_removed">书签已移除</string>
|
||||
<string name="history_cleared">历史已清除</string>
|
||||
<string name="manage">管理</string>
|
||||
<string name="no_bookmarks_yet">还没有书签</string>
|
||||
<string name="no_bookmarks_summary">您可以在阅读漫画时创建书签</string>
|
||||
<string name="no_manga_sources">无漫画源</string>
|
||||
<string name="no_manga_sources_text">启用漫画源以在线阅读漫画</string>
|
||||
<string name="random">随机</string>
|
||||
<string name="categories_delete_confirm">您确定要删除选定的收藏夹吗?
|
||||
\n所有收藏夹中的漫画将丢失且无法恢复。</string>
|
||||
<string name="reorder">重新排序</string>
|
||||
<string name="empty">留空</string>
|
||||
<string name="changelog">变更日志</string>
|
||||
<string name="explore">浏览</string>
|
||||
<string name="automatic_scroll">自动滚动</string>
|
||||
<string name="off_short">关闭</string>
|
||||
<string name="reader_info_bar">在阅读器中显示信息栏</string>
|
||||
<string name="comics_archive">漫画压缩包</string>
|
||||
<string name="folder_with_images">图片文件夹</string>
|
||||
<string name="importing_manga">漫画导入中</string>
|
||||
<string name="seconds_pattern">%ss</string>
|
||||
<string name="reader_info_pattern">Ch. %1$d/%2$d Pg. %3$d/%4$d</string>
|
||||
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">%1$d 的 %2$d 启用</string>
|
||||
<string name="size_s">大小:%s</string>
|
||||
<string name="confirm_exit">再按一次返回键退出</string>
|
||||
<string name="exit_confirmation_summary">按两次返回键退出应用</string>
|
||||
<string name="exit_confirmation">退出确认</string>
|
||||
<string name="saved_manga">已保存漫画</string>
|
||||
<string name="pages_cache">页面缓存</string>
|
||||
<string name="other_cache">其他缓存</string>
|
||||
<string name="storage_usage">存储占用</string>
|
||||
<string name="available">可用</string>
|
||||
<string name="enter_email_text">输入您的邮箱以继续</string>
|
||||
<string name="removed_from_favourites">从收藏中移除</string>
|
||||
<string name="removed_from_s">从 \"%s\" 中移除</string>
|
||||
<string name="options">选项</string>
|
||||
<string name="downloading_manga">下载中的漫画</string>
|
||||
<string name="incognito_mode">隐身模式</string>
|
||||
<string name="app_update_available_s">应用更新可用: %s</string>
|
||||
<string name="no_chapters">无章节</string>
|
||||
<string name="import_completed">导入完毕</string>
|
||||
<string name="import_completed_hint">您可以从存储中删除原文件以节省空间</string>
|
||||
<string name="import_will_start_soon">即将开始导入</string>
|
||||
<string name="feed">订阅源</string>
|
||||
<string name="memory_usage_pattern">%s - %s</string>
|
||||
<string name="not_found_404">内容未找到或已移除</string>
|
||||
</resources>
|
||||
@@ -6,7 +6,7 @@
|
||||
<string name="url_twitter" translatable="false">https://twitter.com/kotatsuapp</string>
|
||||
<string name="url_reddit" translatable="false">https://reddit.com/user/kotatsuapp</string>
|
||||
<string name="url_weblate" translatable="false">https://hosted.weblate.org/engage/kotatsu</string>
|
||||
<string name="url_error_report" translatable="false">http://86.57.183.214:8082/report</string>
|
||||
<string name="url_error_report" translatable="false">https://acra.rumblur.space/report</string>
|
||||
<string name="account_type_sync" translatable="false">org.kotatsu.sync</string>
|
||||
<string name="url_sync_server" translatable="false">http://86.57.183.214:8081</string>
|
||||
<string-array name="values_theme" translatable="false">
|
||||
|
||||
Reference in New Issue
Block a user