Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
e37455e790
|
|||
|
|
36259ba901 | ||
|
|
5b041b9a49 | ||
|
1734e888d6
|
|||
|
9108646cea
|
|||
|
|
c6d1cf2f72 | ||
|
|
a317236cb0 | ||
|
|
3703c07a98 | ||
|
b7b32dd447
|
|||
|
c103386dc5
|
|||
|
a9d6ee4a95
|
|||
|
71f2c91e5a
|
|||
|
b878f358ff
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,6 +10,7 @@
|
||||
/.idea/compiler.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/ktlint-plugin.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
/.idea/kotlinScripting.xml
|
||||
/.idea/kotlinc.xml
|
||||
|
||||
@@ -16,8 +16,8 @@ android {
|
||||
applicationId 'org.koitharu.kotatsu'
|
||||
minSdk = 21
|
||||
targetSdk = 34
|
||||
versionCode = 607
|
||||
versionName = '6.5.1'
|
||||
versionCode = 609
|
||||
versionName = '6.5.3'
|
||||
generatedDensities = []
|
||||
testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner'
|
||||
ksp {
|
||||
@@ -82,7 +82,7 @@ afterEvaluate {
|
||||
}
|
||||
dependencies {
|
||||
//noinspection GradleDependency
|
||||
implementation('com.github.KotatsuApp:kotatsu-parsers:86a2b95141') {
|
||||
implementation('com.github.KotatsuApp:kotatsu-parsers:a228d71d57') {
|
||||
exclude group: 'org.json', module: 'json'
|
||||
}
|
||||
|
||||
@@ -125,8 +125,8 @@ dependencies {
|
||||
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl:4.3.2'
|
||||
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:4.3.2'
|
||||
|
||||
implementation 'com.google.dagger:hilt-android:2.49'
|
||||
kapt 'com.google.dagger:hilt-compiler:2.49'
|
||||
implementation 'com.google.dagger:hilt-android:2.50'
|
||||
kapt 'com.google.dagger:hilt-compiler:2.50'
|
||||
implementation 'androidx.hilt:hilt-work:1.1.0'
|
||||
kapt 'androidx.hilt:hilt-compiler:1.1.0'
|
||||
|
||||
@@ -155,6 +155,6 @@ dependencies {
|
||||
androidTestImplementation 'androidx.room:room-testing:2.6.1'
|
||||
androidTestImplementation 'com.squareup.moshi:moshi-kotlin:1.15.0'
|
||||
|
||||
androidTestImplementation 'com.google.dagger:hilt-android-testing:2.49'
|
||||
kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.49'
|
||||
androidTestImplementation 'com.google.dagger:hilt-android-testing:2.50'
|
||||
kaptAndroidTest 'com.google.dagger:hilt-android-compiler:2.50'
|
||||
}
|
||||
|
||||
@@ -61,13 +61,20 @@ class CaptchaNotifier(
|
||||
override fun onError(request: ImageRequest, result: ErrorResult) {
|
||||
super.onError(request, result)
|
||||
val e = result.throwable
|
||||
if (e is CloudFlareProtectedException) {
|
||||
if (e is CloudFlareProtectedException && request.parameters.value<Boolean>(PARAM_IGNORE_CAPTCHA) != true) {
|
||||
notify(e)
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
companion object {
|
||||
|
||||
fun ImageRequest.Builder.ignoreCaptchaErrors() = setParameter(
|
||||
key = PARAM_IGNORE_CAPTCHA,
|
||||
value = true,
|
||||
memoryCacheKey = null,
|
||||
)
|
||||
|
||||
private const val PARAM_IGNORE_CAPTCHA = "ignore_captcha"
|
||||
private const val CHANNEL_ID = "captcha"
|
||||
private const val TAG = CHANNEL_ID
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.model.MangaState
|
||||
import org.koitharu.kotatsu.parsers.util.mapToSet
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
@JvmName("mangaIds")
|
||||
fun Collection<Manga>.ids() = mapToSet { it.id }
|
||||
@@ -42,15 +43,17 @@ val MangaState.titleResId: Int
|
||||
MangaState.FINISHED -> R.string.state_finished
|
||||
MangaState.ABANDONED -> R.string.state_abandoned
|
||||
MangaState.PAUSED -> R.string.state_paused
|
||||
MangaState.UPCOMING -> R.string.state_upcoming
|
||||
}
|
||||
|
||||
@get:DrawableRes
|
||||
val MangaState.iconResId: Int
|
||||
get() = when (this) {
|
||||
MangaState.ONGOING -> R.drawable.ic_state_ongoing
|
||||
MangaState.ONGOING -> R.drawable.ic_play
|
||||
MangaState.FINISHED -> R.drawable.ic_state_finished
|
||||
MangaState.ABANDONED -> R.drawable.ic_state_abandoned
|
||||
MangaState.PAUSED -> R.drawable.ic_action_pause
|
||||
MangaState.UPCOMING -> materialR.drawable.ic_clock_black_24dp
|
||||
}
|
||||
|
||||
fun Manga.findChapter(id: Long): MangaChapter? {
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
package org.koitharu.kotatsu.core.model
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.RelativeSizeSpan
|
||||
import android.text.style.SuperscriptSpan
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.text.buildSpannedString
|
||||
import androidx.core.text.inSpans
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.util.ext.getDisplayName
|
||||
import org.koitharu.kotatsu.core.util.ext.getThemeColor
|
||||
import org.koitharu.kotatsu.core.util.ext.toLocale
|
||||
import org.koitharu.kotatsu.parsers.model.ContentType
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.util.toTitleCase
|
||||
import java.util.Locale
|
||||
|
||||
fun MangaSource.getLocaleTitle(): String? {
|
||||
val lc = Locale(locale ?: return null)
|
||||
return lc.getDisplayLanguage(lc).toTitleCase(lc)
|
||||
}
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
fun MangaSource(name: String): MangaSource {
|
||||
MangaSource.entries.forEach {
|
||||
@@ -33,6 +39,24 @@ val ContentType.titleResId
|
||||
|
||||
fun MangaSource.getSummary(context: Context): String {
|
||||
val type = context.getString(contentType.titleResId)
|
||||
val locale = getLocaleTitle() ?: context.getString(R.string.various_languages)
|
||||
val locale = locale?.toLocale().getDisplayName(context)
|
||||
return context.getString(R.string.source_summary_pattern, type, locale)
|
||||
}
|
||||
|
||||
fun MangaSource.getTitle(context: Context): CharSequence = if (isNsfw()) {
|
||||
buildSpannedString {
|
||||
append(title)
|
||||
append(' ')
|
||||
appendNsfwLabel(context)
|
||||
}
|
||||
} else {
|
||||
title
|
||||
}
|
||||
|
||||
private fun SpannableStringBuilder.appendNsfwLabel(context: Context) = inSpans(
|
||||
ForegroundColorSpan(context.getThemeColor(materialR.attr.colorError, Color.RED)),
|
||||
RelativeSizeSpan(0.74f),
|
||||
SuperscriptSpan(),
|
||||
) {
|
||||
append(context.getString(R.string.nsfw))
|
||||
}
|
||||
|
||||
@@ -12,4 +12,5 @@ val SortOrder.titleRes: Int
|
||||
SortOrder.RATING -> R.string.by_rating
|
||||
SortOrder.NEWEST -> R.string.newest
|
||||
SortOrder.ALPHABETICAL -> R.string.by_name
|
||||
SortOrder.ALPHABETICAL_DESC -> R.string.by_name_reverse
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ class WindowInsetsDelegate : OnApplyWindowInsetsListener, View.OnLayoutChangeLis
|
||||
lastInsets = null
|
||||
}
|
||||
|
||||
interface WindowInsetsListener {
|
||||
fun interface WindowInsetsListener {
|
||||
|
||||
fun onWindowInsetsChanged(insets: Insets)
|
||||
}
|
||||
|
||||
@@ -20,12 +20,13 @@ inline fun <T> LocaleListCompat.mapToSet(block: (Locale) -> T): Set<T> {
|
||||
|
||||
fun LocaleListCompat.getOrThrow(index: Int) = get(index) ?: throw NoSuchElementException()
|
||||
|
||||
fun String?.getLocaleDisplayName(context: Context): String {
|
||||
fun String.toLocale() = Locale(this)
|
||||
|
||||
fun Locale?.getDisplayName(context: Context): String {
|
||||
if (this == null) {
|
||||
return context.getString(R.string.various_languages)
|
||||
}
|
||||
val lc = Locale(this)
|
||||
return lc.getDisplayLanguage(lc).toTitleCase(lc)
|
||||
return getDisplayLanguage(this).toTitleCase(this)
|
||||
}
|
||||
|
||||
private class LocaleListCompatIterator(private val list: LocaleListCompat) : ListIterator<Locale> {
|
||||
|
||||
@@ -30,6 +30,8 @@ import org.koitharu.kotatsu.bookmarks.domain.Bookmark
|
||||
import org.koitharu.kotatsu.bookmarks.ui.adapter.BookmarksAdapter
|
||||
import org.koitharu.kotatsu.bookmarks.ui.sheet.BookmarksSheet
|
||||
import org.koitharu.kotatsu.core.model.countChaptersByBranch
|
||||
import org.koitharu.kotatsu.core.model.iconResId
|
||||
import org.koitharu.kotatsu.core.model.titleResId
|
||||
import org.koitharu.kotatsu.core.ui.BaseFragment
|
||||
import org.koitharu.kotatsu.core.ui.BaseListAdapter
|
||||
import org.koitharu.kotatsu.core.ui.image.CoverSizeResolver
|
||||
@@ -66,7 +68,6 @@ import org.koitharu.kotatsu.list.ui.size.StaticItemSizeResolver
|
||||
import org.koitharu.kotatsu.main.ui.owners.NoModalBottomSheetOwner
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.model.MangaState
|
||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderActivity
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo
|
||||
@@ -181,28 +182,13 @@ class DetailsFragment :
|
||||
ratingBar.isVisible = false
|
||||
}
|
||||
|
||||
when (manga.state) {
|
||||
MangaState.FINISHED -> infoLayout.textViewState.apply {
|
||||
textAndVisible = resources.getString(R.string.state_finished)
|
||||
drawableTop = ContextCompat.getDrawable(context, R.drawable.ic_state_finished)
|
||||
infoLayout.textViewState.apply {
|
||||
manga.state?.let { state ->
|
||||
textAndVisible = resources.getString(state.titleResId)
|
||||
drawableTop = ContextCompat.getDrawable(context, state.iconResId)
|
||||
} ?: run {
|
||||
isVisible = false
|
||||
}
|
||||
|
||||
MangaState.ONGOING -> infoLayout.textViewState.apply {
|
||||
textAndVisible = resources.getString(R.string.state_ongoing)
|
||||
drawableTop = ContextCompat.getDrawable(context, R.drawable.ic_state_ongoing)
|
||||
}
|
||||
|
||||
MangaState.ABANDONED -> infoLayout.textViewState.apply {
|
||||
textAndVisible = resources.getString(R.string.state_abandoned)
|
||||
drawableTop = ContextCompat.getDrawable(context, R.drawable.ic_state_abandoned)
|
||||
}
|
||||
|
||||
MangaState.PAUSED -> infoLayout.textViewState.apply {
|
||||
textAndVisible = resources.getString(R.string.state_paused)
|
||||
drawableTop = ContextCompat.getDrawable(context, R.drawable.ic_action_pause)
|
||||
}
|
||||
|
||||
null -> infoLayout.textViewState.isVisible = false
|
||||
}
|
||||
if (manga.source == MangaSource.LOCAL) {
|
||||
infoLayout.textViewSource.isVisible = false
|
||||
|
||||
@@ -53,6 +53,7 @@ import org.koitharu.kotatsu.local.data.LocalStorageChanges
|
||||
import org.koitharu.kotatsu.local.domain.DeleteLocalMangaUseCase
|
||||
import org.koitharu.kotatsu.local.domain.model.LocalManga
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.Scrobbler
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo
|
||||
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingStatus
|
||||
@@ -134,8 +135,14 @@ class DetailsViewModel @Inject constructor(
|
||||
.map { it?.local }
|
||||
.distinctUntilChanged()
|
||||
.map { local ->
|
||||
local?.file?.computeSize() ?: 0L
|
||||
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.WhileSubscribed(), 0)
|
||||
if (local != null) {
|
||||
runCatchingCancellable {
|
||||
local.file.computeSize()
|
||||
}.getOrDefault(0L)
|
||||
} else {
|
||||
0L
|
||||
}
|
||||
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.WhileSubscribed(5000), 0L)
|
||||
|
||||
@Deprecated("")
|
||||
val description = details
|
||||
|
||||
@@ -160,7 +160,7 @@ class ExploreFragment :
|
||||
override fun onRetryClick(error: Throwable) = Unit
|
||||
|
||||
override fun onEmptyActionClick() {
|
||||
startActivity(SettingsActivity.newManageSourcesIntent(context ?: return))
|
||||
startActivity(Intent(context ?: return, SourcesCatalogActivity::class.java))
|
||||
}
|
||||
|
||||
private fun onOpenManga(manga: Manga) {
|
||||
|
||||
@@ -21,7 +21,6 @@ import org.koitharu.kotatsu.core.ui.util.ReversibleAction
|
||||
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
||||
import org.koitharu.kotatsu.core.util.ext.call
|
||||
import org.koitharu.kotatsu.explore.data.MangaSourcesRepository
|
||||
import org.koitharu.kotatsu.explore.data.SourcesSortOrder
|
||||
import org.koitharu.kotatsu.explore.domain.ExploreRepository
|
||||
import org.koitharu.kotatsu.explore.ui.model.ExploreButtons
|
||||
import org.koitharu.kotatsu.explore.ui.model.MangaSourceItem
|
||||
@@ -30,7 +29,6 @@ import org.koitharu.kotatsu.list.ui.model.EmptyHint
|
||||
import org.koitharu.kotatsu.list.ui.model.ListHeader
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.list.ui.model.LoadingState
|
||||
import org.koitharu.kotatsu.list.ui.model.TipModel
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
|
||||
@@ -56,8 +54,6 @@ class ExploreViewModel @Inject constructor(
|
||||
valueProducer = { isSuggestionsEnabled },
|
||||
)
|
||||
|
||||
val sortOrder = MutableStateFlow(SourcesSortOrder.MANUAL) // TODO
|
||||
|
||||
val onOpenManga = MutableEventFlow<Manga>()
|
||||
val onActionDone = MutableEventFlow<ReversibleAction>()
|
||||
val onShowSuggestionsTip = MutableEventFlow<Unit>()
|
||||
@@ -129,31 +125,25 @@ class ExploreViewModel @Inject constructor(
|
||||
randomLoading: Boolean,
|
||||
newSources: Set<MangaSource>,
|
||||
): List<ListModel> {
|
||||
val result = ArrayList<ListModel>(sources.size + 4)
|
||||
val result = ArrayList<ListModel>(sources.size + 3)
|
||||
result += ExploreButtons(randomLoading)
|
||||
if (recommendation != null) {
|
||||
result += ListHeader(R.string.suggestions)
|
||||
result += RecommendationsItem(recommendation)
|
||||
}
|
||||
if (sources.isNotEmpty()) {
|
||||
result += ListHeader(R.string.remote_sources, R.string.catalog)
|
||||
if (newSources.isNotEmpty()) {
|
||||
result += TipModel(
|
||||
key = TIP_NEW_SOURCES,
|
||||
title = R.string.new_sources_text,
|
||||
text = R.string.new_sources_text,
|
||||
icon = R.drawable.ic_explore_normal,
|
||||
primaryButtonText = R.string.manage,
|
||||
secondaryButtonText = R.string.discard,
|
||||
)
|
||||
}
|
||||
result += ListHeader(
|
||||
textRes = R.string.remote_sources,
|
||||
buttonTextRes = R.string.catalog,
|
||||
badge = if (newSources.isNotEmpty()) "" else null,
|
||||
)
|
||||
sources.mapTo(result) { MangaSourceItem(it, isGrid) }
|
||||
} else {
|
||||
result += EmptyHint(
|
||||
icon = R.drawable.ic_empty_common,
|
||||
textPrimary = R.string.no_manga_sources,
|
||||
textSecondary = R.string.no_manga_sources_text,
|
||||
actionStringRes = R.string.manage,
|
||||
actionStringRes = R.string.catalog,
|
||||
)
|
||||
}
|
||||
return result
|
||||
|
||||
@@ -8,6 +8,7 @@ import coil.ImageLoader
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.getSummary
|
||||
import org.koitharu.kotatsu.core.model.getTitle
|
||||
import org.koitharu.kotatsu.core.parser.favicon.faviconUri
|
||||
import org.koitharu.kotatsu.core.ui.image.FaviconDrawable
|
||||
import org.koitharu.kotatsu.core.ui.image.TrimTransformation
|
||||
@@ -112,7 +113,7 @@ fun exploreSourceListItemAD(
|
||||
binding.root.setOnContextClickListenerCompat(eventListener)
|
||||
|
||||
bind {
|
||||
binding.textViewTitle.text = item.source.title
|
||||
binding.textViewTitle.text = item.source.getTitle(context)
|
||||
binding.textViewSubtitle.text = item.source.getSummary(context)
|
||||
val fallbackIcon = FaviconDrawable(context, R.style.FaviconDrawable_Small, item.source.name)
|
||||
binding.imageViewIcon.newImageRequest(lifecycleOwner, item.source.faviconUri())?.run {
|
||||
@@ -147,7 +148,7 @@ fun exploreSourceGridItemAD(
|
||||
binding.root.setOnContextClickListenerCompat(eventListener)
|
||||
|
||||
bind {
|
||||
binding.textViewTitle.text = item.source.title
|
||||
binding.textViewTitle.text = item.source.getTitle(context)
|
||||
val fallbackIcon = FaviconDrawable(context, R.style.FaviconDrawable_Large, item.source.name)
|
||||
binding.imageViewIcon.newImageRequest(lifecycleOwner, item.source.faviconUri())?.run {
|
||||
fallback(fallbackIcon)
|
||||
|
||||
@@ -14,22 +14,37 @@ import com.google.android.material.R as materialR
|
||||
|
||||
@CheckResult
|
||||
fun View.bindBadge(badge: BadgeDrawable?, counter: Int): BadgeDrawable? {
|
||||
return if (counter > 0) {
|
||||
val badgeDrawable = badge ?: initBadge(this)
|
||||
badgeDrawable.number = counter
|
||||
badgeDrawable.isVisible = true
|
||||
badgeDrawable.align(this)
|
||||
badgeDrawable
|
||||
} else {
|
||||
badge?.isVisible = false
|
||||
badge
|
||||
}
|
||||
return bindBadgeImpl(badge, null, counter)
|
||||
}
|
||||
|
||||
@CheckResult
|
||||
fun View.bindBadge(badge: BadgeDrawable?, text: String?): BadgeDrawable? {
|
||||
return bindBadgeImpl(badge, text, 0)
|
||||
}
|
||||
|
||||
fun View.clearBadge(badge: BadgeDrawable?) {
|
||||
BadgeUtils.detachBadgeDrawable(badge, this)
|
||||
}
|
||||
|
||||
private fun View.bindBadgeImpl(
|
||||
badge: BadgeDrawable?,
|
||||
text: String?,
|
||||
counter: Int,
|
||||
): BadgeDrawable? = if (text != null || counter > 0) {
|
||||
val badgeDrawable = badge ?: initBadge(this)
|
||||
if (counter > 0) {
|
||||
badgeDrawable.number = counter
|
||||
} else {
|
||||
badgeDrawable.text = text?.takeUnless { it.isEmpty() }
|
||||
}
|
||||
badgeDrawable.isVisible = true
|
||||
badgeDrawable.align(this)
|
||||
badgeDrawable
|
||||
} else {
|
||||
badge?.isVisible = false
|
||||
badge
|
||||
}
|
||||
|
||||
private fun initBadge(anchor: View): BadgeDrawable {
|
||||
val badge = BadgeDrawable.create(anchor.context)
|
||||
val resources = anchor.resources
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.list.ui.adapter
|
||||
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import com.google.android.material.badge.BadgeDrawable
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.databinding.ItemHeaderButtonBinding
|
||||
import org.koitharu.kotatsu.list.ui.model.ListHeader
|
||||
@@ -12,6 +13,8 @@ fun listHeaderAD(
|
||||
) = adapterDelegateViewBinding<ListHeader, ListModel, ItemHeaderButtonBinding>(
|
||||
{ inflater, parent -> ItemHeaderButtonBinding.inflate(inflater, parent, false) },
|
||||
) {
|
||||
var badge: BadgeDrawable? = null
|
||||
|
||||
if (listener != null) {
|
||||
binding.buttonMore.setOnClickListener {
|
||||
listener.onListHeaderClick(item, it)
|
||||
@@ -23,9 +26,11 @@ fun listHeaderAD(
|
||||
if (item.buttonTextRes == 0) {
|
||||
binding.buttonMore.isInvisible = true
|
||||
binding.buttonMore.text = null
|
||||
binding.buttonMore.clearBadge(badge)
|
||||
} else {
|
||||
binding.buttonMore.setText(item.buttonTextRes)
|
||||
binding.buttonMore.isVisible = true
|
||||
badge = itemView.bindBadge(badge, item.badge)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,38 +6,41 @@ import org.koitharu.kotatsu.core.ui.model.DateTimeAgo
|
||||
|
||||
@Suppress("DataClassPrivateConstructor")
|
||||
data class ListHeader private constructor(
|
||||
private val text: CharSequence?,
|
||||
@StringRes private val textRes: Int,
|
||||
private val dateTimeAgo: DateTimeAgo?,
|
||||
private val textRaw: Any,
|
||||
@StringRes val buttonTextRes: Int,
|
||||
val payload: Any?,
|
||||
val badge: String?,
|
||||
) : ListModel {
|
||||
|
||||
constructor(
|
||||
text: CharSequence,
|
||||
@StringRes buttonTextRes: Int = 0,
|
||||
payload: Any? = null,
|
||||
) : this(text, 0, null, buttonTextRes, payload)
|
||||
badge: String? = null,
|
||||
) : this(textRaw = text, buttonTextRes, payload, badge)
|
||||
|
||||
constructor(
|
||||
@StringRes textRes: Int,
|
||||
@StringRes buttonTextRes: Int = 0,
|
||||
payload: Any? = null,
|
||||
) : this(null, textRes, null, buttonTextRes, payload)
|
||||
badge: String? = null,
|
||||
) : this(textRaw = textRes, buttonTextRes, payload, badge)
|
||||
|
||||
constructor(
|
||||
dateTimeAgo: DateTimeAgo,
|
||||
@StringRes buttonTextRes: Int = 0,
|
||||
payload: Any? = null,
|
||||
) : this(null, 0, dateTimeAgo, buttonTextRes, payload)
|
||||
badge: String? = null,
|
||||
) : this(textRaw = dateTimeAgo, buttonTextRes, payload, badge)
|
||||
|
||||
fun getText(context: Context): CharSequence? = when {
|
||||
text != null -> text
|
||||
textRes != 0 -> context.getString(textRes)
|
||||
else -> dateTimeAgo?.format(context.resources)
|
||||
fun getText(context: Context): CharSequence? = when (textRaw) {
|
||||
is CharSequence -> textRaw
|
||||
is Int -> if (textRaw != 0) context.getString(textRaw) else null
|
||||
is DateTimeAgo -> textRaw.format(context.resources)
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun areItemsTheSame(other: ListModel): Boolean {
|
||||
return other is ListHeader && text == other.text && dateTimeAgo == other.dateTimeAgo && textRes == other.textRes
|
||||
return other is ListHeader && textRaw == other.textRaw
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), AppBarOwner, BottomNav
|
||||
viewModel.onError.observeEvent(this, SnackbarErrorObserver(viewBinding.container, null))
|
||||
viewModel.isLoading.observe(this, this::onLoadingStateChanged)
|
||||
viewModel.isResumeEnabled.observe(this, this::onResumeEnabledChanged)
|
||||
viewModel.counters.observe(this, ::onCountersChanged)
|
||||
viewModel.feedCounter.observe(this, ::onFeedCounterChanged)
|
||||
viewModel.appUpdate.observe(this, MenuInvalidator(this))
|
||||
viewModel.onFirstStart.observeEvent(this) {
|
||||
WelcomeSheet.show(supportFragmentManager)
|
||||
@@ -278,10 +278,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), AppBarOwner, BottomNav
|
||||
startActivity(IntentBuilder(this).manga(manga).build(), options)
|
||||
}
|
||||
|
||||
private fun onCountersChanged(counters: Map<NavItem, Int>) {
|
||||
counters.forEach { (navItem, counter) ->
|
||||
navigationDelegate.setCounter(navItem, counter)
|
||||
}
|
||||
private fun onFeedCounterChanged(counter: Int) {
|
||||
navigationDelegate.setCounter(NavItem.FEED, counter)
|
||||
}
|
||||
|
||||
private fun onIncognitoModeChanged(isIncognito: Boolean) {
|
||||
|
||||
@@ -4,15 +4,11 @@ import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.plus
|
||||
import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException
|
||||
import org.koitharu.kotatsu.core.github.AppUpdateRepository
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.NavItem
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsStateFlow
|
||||
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
||||
@@ -22,7 +18,6 @@ import org.koitharu.kotatsu.history.data.HistoryRepository
|
||||
import org.koitharu.kotatsu.main.domain.ReadingResumeEnabledUseCase
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.tracker.domain.TrackingRepository
|
||||
import java.util.EnumMap
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
@@ -52,19 +47,8 @@ class MainViewModel @Inject constructor(
|
||||
|
||||
val appUpdate = appUpdateRepository.observeAvailableUpdate()
|
||||
|
||||
val counters = combine(
|
||||
trackingRepository.observeUpdatedMangaCount(),
|
||||
observeNewSourcesCount(),
|
||||
) { tracks, newSources ->
|
||||
val em = EnumMap<NavItem, Int>(NavItem::class.java)
|
||||
em[NavItem.EXPLORE] = newSources
|
||||
em[NavItem.FEED] = tracks
|
||||
em
|
||||
}.stateIn(
|
||||
scope = viewModelScope + Dispatchers.Default,
|
||||
started = SharingStarted.WhileSubscribed(5000),
|
||||
initialValue = emptyMap<NavItem, Int>(),
|
||||
)
|
||||
val feedCounter = trackingRepository.observeUpdatedMangaCount()
|
||||
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Lazily, 0)
|
||||
|
||||
init {
|
||||
launchJob {
|
||||
@@ -87,8 +71,4 @@ class MainViewModel @Inject constructor(
|
||||
fun setIncognitoMode(isEnabled: Boolean) {
|
||||
settings.isIncognitoModeEnabled = isEnabled
|
||||
}
|
||||
|
||||
private fun observeNewSourcesCount() = sourcesRepository.observeNewSources()
|
||||
.map { if (sourcesRepository.isSetupRequired()) 0 else it.size }
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package org.koitharu.kotatsu.search.ui.suggestion.adapter
|
||||
|
||||
import androidx.core.text.buildSpannedString
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import coil.ImageLoader
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.getSummary
|
||||
import org.koitharu.kotatsu.core.model.getTitle
|
||||
import org.koitharu.kotatsu.core.parser.favicon.faviconUri
|
||||
import org.koitharu.kotatsu.core.ui.image.FaviconDrawable
|
||||
import org.koitharu.kotatsu.core.util.ext.enqueueWith
|
||||
@@ -14,7 +14,6 @@ import org.koitharu.kotatsu.core.util.ext.source
|
||||
import org.koitharu.kotatsu.databinding.ItemSearchSuggestionSourceBinding
|
||||
import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionListener
|
||||
import org.koitharu.kotatsu.search.ui.suggestion.model.SearchSuggestionItem
|
||||
import org.koitharu.kotatsu.settings.sources.adapter.appendNsfwLabel
|
||||
|
||||
fun searchSuggestionSourceAD(
|
||||
coil: ImageLoader,
|
||||
@@ -32,15 +31,7 @@ fun searchSuggestionSourceAD(
|
||||
}
|
||||
|
||||
bind {
|
||||
binding.textViewTitle.text = if (item.isNsfw) {
|
||||
buildSpannedString {
|
||||
append(item.source.title)
|
||||
append(' ')
|
||||
appendNsfwLabel(context)
|
||||
}
|
||||
} else {
|
||||
item.source.title
|
||||
}
|
||||
binding.textViewTitle.text = item.source.getTitle(context)
|
||||
binding.textViewSubtitle.text = item.source.getSummary(context)
|
||||
binding.switchLocal.isChecked = item.isEnabled
|
||||
val fallbackIcon = FaviconDrawable(context, R.style.FaviconDrawable_Small, item.source.name)
|
||||
|
||||
@@ -1,16 +1,8 @@
|
||||
package org.koitharu.kotatsu.settings.sources.adapter
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.RelativeSizeSpan
|
||||
import android.text.style.SuperscriptSpan
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
import androidx.core.text.buildSpannedString
|
||||
import androidx.core.text.inSpans
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
@@ -19,12 +11,12 @@ import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegate
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.getSummary
|
||||
import org.koitharu.kotatsu.core.model.getTitle
|
||||
import org.koitharu.kotatsu.core.parser.favicon.faviconUri
|
||||
import org.koitharu.kotatsu.core.ui.image.FaviconDrawable
|
||||
import org.koitharu.kotatsu.core.ui.list.OnTipCloseListener
|
||||
import org.koitharu.kotatsu.core.util.ext.crossfade
|
||||
import org.koitharu.kotatsu.core.util.ext.enqueueWith
|
||||
import org.koitharu.kotatsu.core.util.ext.getThemeColor
|
||||
import org.koitharu.kotatsu.core.util.ext.newImageRequest
|
||||
import org.koitharu.kotatsu.core.util.ext.source
|
||||
import org.koitharu.kotatsu.databinding.ItemSourceConfigBinding
|
||||
@@ -51,15 +43,7 @@ fun sourceConfigItemCheckableDelegate(
|
||||
}
|
||||
|
||||
bind {
|
||||
binding.textViewTitle.text = if (item.isNsfw) {
|
||||
buildSpannedString {
|
||||
append(item.source.title)
|
||||
append(' ')
|
||||
appendNsfwLabel(context)
|
||||
}
|
||||
} else {
|
||||
item.source.title
|
||||
}
|
||||
binding.textViewTitle.text = item.source.getTitle(context)
|
||||
binding.switchToggle.isChecked = item.isEnabled
|
||||
binding.switchToggle.isEnabled = item.isAvailable
|
||||
binding.textViewDescription.text = item.source.getSummary(context)
|
||||
@@ -101,15 +85,7 @@ fun sourceConfigItemDelegate2(
|
||||
binding.imageViewMenu.setOnClickListener(eventListener)
|
||||
|
||||
bind {
|
||||
binding.textViewTitle.text = if (item.isNsfw) {
|
||||
buildSpannedString {
|
||||
append(item.source.title)
|
||||
append(' ')
|
||||
appendNsfwLabel(context)
|
||||
}
|
||||
} else {
|
||||
item.source.title
|
||||
}
|
||||
binding.textViewTitle.text = item.source.getTitle(context)
|
||||
binding.imageViewAdd.isGone = item.isEnabled || !item.isAvailable
|
||||
binding.imageViewRemove.isVisible = item.isEnabled
|
||||
binding.imageViewMenu.isVisible = item.isEnabled
|
||||
@@ -147,19 +123,6 @@ fun sourceConfigEmptySearchDelegate() =
|
||||
R.layout.item_sources_empty,
|
||||
) { }
|
||||
|
||||
fun SpannableStringBuilder.appendNsfwLabel(context: Context) = inSpans(
|
||||
ForegroundColorSpan(
|
||||
context.getThemeColor(
|
||||
com.google.android.material.R.attr.colorError,
|
||||
Color.RED,
|
||||
),
|
||||
),
|
||||
RelativeSizeSpan(0.74f),
|
||||
SuperscriptSpan(),
|
||||
) {
|
||||
append(context.getString(R.string.nsfw))
|
||||
}
|
||||
|
||||
private fun showSourceMenu(
|
||||
anchor: View,
|
||||
item: SourceConfigItem.SourceItem,
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
package org.koitharu.kotatsu.settings.sources.catalog
|
||||
|
||||
import androidx.core.text.buildSpannedString
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import coil.ImageLoader
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.browser.cloudflare.CaptchaNotifier.Companion.ignoreCaptchaErrors
|
||||
import org.koitharu.kotatsu.core.model.getSummary
|
||||
import org.koitharu.kotatsu.core.model.isNsfw
|
||||
import org.koitharu.kotatsu.core.model.getTitle
|
||||
import org.koitharu.kotatsu.core.parser.favicon.faviconUri
|
||||
import org.koitharu.kotatsu.core.ui.image.FaviconDrawable
|
||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.ui.util.WindowInsetsDelegate
|
||||
import org.koitharu.kotatsu.core.util.ext.crossfade
|
||||
import org.koitharu.kotatsu.core.util.ext.enqueueWith
|
||||
import org.koitharu.kotatsu.core.util.ext.newImageRequest
|
||||
import org.koitharu.kotatsu.core.util.ext.setTextAndVisible
|
||||
import org.koitharu.kotatsu.core.util.ext.source
|
||||
import org.koitharu.kotatsu.databinding.ItemCatalogPageBinding
|
||||
import org.koitharu.kotatsu.databinding.ItemEmptyHintBinding
|
||||
import org.koitharu.kotatsu.databinding.ItemSourceCatalogBinding
|
||||
import org.koitharu.kotatsu.settings.sources.adapter.appendNsfwLabel
|
||||
|
||||
fun sourceCatalogItemSourceAD(
|
||||
coil: ImageLoader,
|
||||
@@ -35,15 +38,7 @@ fun sourceCatalogItemSourceAD(
|
||||
}
|
||||
|
||||
bind {
|
||||
binding.textViewTitle.text = if (item.source.isNsfw()) {
|
||||
buildSpannedString {
|
||||
append(item.source.title)
|
||||
append(' ')
|
||||
appendNsfwLabel(context)
|
||||
}
|
||||
} else {
|
||||
item.source.title
|
||||
}
|
||||
binding.textViewTitle.text = item.source.getTitle(context)
|
||||
if (item.showSummary) {
|
||||
binding.textViewDescription.text = item.source.getSummary(context)
|
||||
binding.textViewDescription.isVisible = true
|
||||
@@ -57,6 +52,7 @@ fun sourceCatalogItemSourceAD(
|
||||
placeholder(fallbackIcon)
|
||||
fallback(fallbackIcon)
|
||||
source(item.source)
|
||||
ignoreCaptchaErrors()
|
||||
enqueueWith(coil)
|
||||
}
|
||||
}
|
||||
@@ -77,3 +73,30 @@ fun sourceCatalogItemHintAD(
|
||||
binding.textSecondary.setTextAndVisible(item.text)
|
||||
}
|
||||
}
|
||||
|
||||
fun sourceCatalogPageAD(
|
||||
listener: OnListItemClickListener<SourceCatalogItem.Source>,
|
||||
coil: ImageLoader,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
) = adapterDelegateViewBinding<SourceCatalogPage, SourceCatalogPage, ItemCatalogPageBinding>(
|
||||
{ inflater, parent -> ItemCatalogPageBinding.inflate(inflater, parent, false) },
|
||||
) {
|
||||
|
||||
val sourcesAdapter = SourcesCatalogAdapter(listener, coil, lifecycleOwner)
|
||||
with(binding.recyclerView) {
|
||||
setHasFixedSize(true)
|
||||
adapter = sourcesAdapter
|
||||
}
|
||||
val insetsDelegate = WindowInsetsDelegate()
|
||||
ViewCompat.setOnApplyWindowInsetsListener(itemView, insetsDelegate)
|
||||
itemView.addOnLayoutChangeListener(insetsDelegate)
|
||||
insetsDelegate.addInsetsListener { insets ->
|
||||
binding.recyclerView.updatePadding(
|
||||
bottom = insets.bottom + binding.recyclerView.paddingTop,
|
||||
)
|
||||
}
|
||||
|
||||
bind {
|
||||
sourcesAdapter.items = item.items
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.koitharu.kotatsu.settings.sources.catalog
|
||||
|
||||
import org.koitharu.kotatsu.list.ui.ListModelDiffCallback
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.parsers.model.ContentType
|
||||
|
||||
data class SourceCatalogPage(
|
||||
val type: ContentType,
|
||||
val items: List<SourceCatalogItem>,
|
||||
) : ListModel {
|
||||
|
||||
override fun areItemsTheSame(other: ListModel): Boolean {
|
||||
return other is SourceCatalogPage && other.type == type
|
||||
}
|
||||
|
||||
override fun getChangePayload(previousState: ListModel): Any? {
|
||||
return ListModelDiffCallback.PAYLOAD_NESTED_LIST_CHANGED
|
||||
}
|
||||
}
|
||||
@@ -10,30 +10,32 @@ import androidx.core.view.isVisible
|
||||
import androidx.core.view.updatePadding
|
||||
import coil.ImageLoader
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.core.model.titleResId
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
||||
import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver
|
||||
import org.koitharu.kotatsu.core.util.ext.firstVisibleItemPosition
|
||||
import org.koitharu.kotatsu.core.util.ext.getLocaleDisplayName
|
||||
import org.koitharu.kotatsu.core.util.ext.getDisplayName
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
||||
import org.koitharu.kotatsu.core.util.ext.toLocale
|
||||
import org.koitharu.kotatsu.databinding.ActivitySourcesCatalogBinding
|
||||
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
|
||||
import org.koitharu.kotatsu.parsers.model.ContentType
|
||||
import org.koitharu.kotatsu.settings.newsources.NewSourcesDialogFragment
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SourcesCatalogActivity : BaseActivity<ActivitySourcesCatalogBinding>(),
|
||||
TabLayout.OnTabSelectedListener,
|
||||
OnListItemClickListener<SourceCatalogItem.Source>,
|
||||
AppBarOwner, MenuItem.OnActionExpandListener {
|
||||
|
||||
@Inject
|
||||
lateinit var coil: ImageLoader
|
||||
|
||||
private var newSourcesSnackbar: Snackbar? = null
|
||||
|
||||
override val appBar: AppBarLayout
|
||||
get() = viewBinding.appbar
|
||||
|
||||
@@ -43,19 +45,18 @@ class SourcesCatalogActivity : BaseActivity<ActivitySourcesCatalogBinding>(),
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(ActivitySourcesCatalogBinding.inflate(layoutInflater))
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
initTabs()
|
||||
val sourcesAdapter = SourcesCatalogAdapter(this, coil, this)
|
||||
with(viewBinding.recyclerView) {
|
||||
setHasFixedSize(true)
|
||||
adapter = sourcesAdapter
|
||||
}
|
||||
viewModel.content.observe(this, sourcesAdapter)
|
||||
val pagerAdapter = SourcesCatalogPagerAdapter(this, coil, this)
|
||||
viewBinding.pager.adapter = pagerAdapter
|
||||
val tabMediator = TabLayoutMediator(viewBinding.tabs, viewBinding.pager, pagerAdapter)
|
||||
tabMediator.attach()
|
||||
viewModel.content.observe(this, pagerAdapter)
|
||||
viewModel.hasNewSources.observe(this, ::onHasNewSourcesChanged)
|
||||
viewModel.onActionDone.observeEvent(
|
||||
this,
|
||||
ReversibleActionObserver(viewBinding.recyclerView),
|
||||
ReversibleActionObserver(viewBinding.pager),
|
||||
)
|
||||
viewModel.locale.observe(this) {
|
||||
supportActionBar?.subtitle = it.getLocaleDisplayName(this)
|
||||
supportActionBar?.subtitle = it?.toLocale().getDisplayName(this)
|
||||
}
|
||||
addMenuProvider(SourcesCatalogMenuProvider(this, viewModel, this))
|
||||
}
|
||||
@@ -65,27 +66,15 @@ class SourcesCatalogActivity : BaseActivity<ActivitySourcesCatalogBinding>(),
|
||||
left = insets.left,
|
||||
right = insets.right,
|
||||
)
|
||||
viewBinding.recyclerView.updatePadding(
|
||||
bottom = insets.bottom + viewBinding.recyclerView.paddingTop,
|
||||
)
|
||||
}
|
||||
|
||||
override fun onItemClick(item: SourceCatalogItem.Source, view: View) {
|
||||
viewModel.addSource(item.source)
|
||||
}
|
||||
|
||||
override fun onTabSelected(tab: TabLayout.Tab) {
|
||||
viewModel.setContentType(tab.tag as ContentType)
|
||||
}
|
||||
|
||||
override fun onTabUnselected(tab: TabLayout.Tab) = Unit
|
||||
|
||||
override fun onTabReselected(tab: TabLayout.Tab) {
|
||||
viewBinding.recyclerView.firstVisibleItemPosition = 0
|
||||
}
|
||||
|
||||
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
||||
viewBinding.tabs.isVisible = false
|
||||
viewBinding.pager.isUserInputEnabled = false
|
||||
val sq = (item.actionView as? SearchView)?.query?.trim()?.toString().orEmpty()
|
||||
viewModel.performSearch(sq)
|
||||
return true
|
||||
@@ -93,21 +82,35 @@ class SourcesCatalogActivity : BaseActivity<ActivitySourcesCatalogBinding>(),
|
||||
|
||||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
||||
viewBinding.tabs.isVisible = true
|
||||
viewBinding.pager.isUserInputEnabled = true
|
||||
viewModel.performSearch(null)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun initTabs() {
|
||||
val tabs = viewBinding.tabs
|
||||
for (type in ContentType.entries) {
|
||||
if (viewModel.isNsfwDisabled && type == ContentType.HENTAI) {
|
||||
continue
|
||||
private fun onHasNewSourcesChanged(hasNewSources: Boolean) {
|
||||
if (hasNewSources) {
|
||||
if (newSourcesSnackbar?.isShownOrQueued == true) {
|
||||
return
|
||||
}
|
||||
val tab = tabs.newTab()
|
||||
tab.setText(type.titleResId)
|
||||
tab.tag = type
|
||||
tabs.addTab(tab)
|
||||
val snackbar = Snackbar.make(viewBinding.pager, R.string.new_sources_text, Snackbar.LENGTH_INDEFINITE)
|
||||
snackbar.setAction(R.string.explore) {
|
||||
NewSourcesDialogFragment.show(supportFragmentManager)
|
||||
}
|
||||
snackbar.addCallback(
|
||||
object : Snackbar.Callback() {
|
||||
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
||||
super.onDismissed(transientBottomBar, event)
|
||||
if (event == DISMISS_EVENT_SWIPE) {
|
||||
viewModel.skipNewSources()
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
snackbar.show()
|
||||
newSourcesSnackbar = snackbar
|
||||
} else {
|
||||
newSourcesSnackbar?.dismiss()
|
||||
newSourcesSnackbar = null
|
||||
}
|
||||
tabs.addOnTabSelectedListener(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ import androidx.appcompat.widget.PopupMenu
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.view.MenuProvider
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.util.ext.getLocaleDisplayName
|
||||
import org.koitharu.kotatsu.core.util.LocaleComparator
|
||||
import org.koitharu.kotatsu.core.util.ext.getDisplayName
|
||||
import org.koitharu.kotatsu.core.util.ext.toLocale
|
||||
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
|
||||
|
||||
class SourcesCatalogMenuProvider(
|
||||
@@ -57,15 +59,17 @@ class SourcesCatalogMenuProvider(
|
||||
}
|
||||
|
||||
private fun showLocalesMenu() {
|
||||
val locales = viewModel.locales.map {
|
||||
it to it.getLocaleDisplayName(activity)
|
||||
val locales = viewModel.locales.mapTo(ArrayList(viewModel.locales.size)) {
|
||||
it to it?.toLocale()
|
||||
}
|
||||
locales.sortWith(compareBy(nullsFirst(LocaleComparator())) { it.second })
|
||||
|
||||
val anchor: View = (activity as AppBarOwner).appBar.let {
|
||||
it.findViewById<View?>(R.id.toolbar) ?: it
|
||||
}
|
||||
val menu = PopupMenu(activity, anchor)
|
||||
for ((i, lc) in locales.withIndex()) {
|
||||
menu.menu.add(Menu.NONE, Menu.NONE, i, lc.second)
|
||||
menu.menu.add(Menu.NONE, Menu.NONE, i, lc.second.getDisplayName(activity))
|
||||
}
|
||||
menu.setOnMenuItemClickListener {
|
||||
viewModel.setLocale(locales.getOrNull(it.order)?.first)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.koitharu.kotatsu.settings.sources.catalog
|
||||
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import coil.ImageLoader
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import org.koitharu.kotatsu.core.model.titleResId
|
||||
import org.koitharu.kotatsu.core.ui.BaseListAdapter
|
||||
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
|
||||
|
||||
class SourcesCatalogPagerAdapter(
|
||||
listener: OnListItemClickListener<SourceCatalogItem.Source>,
|
||||
coil: ImageLoader,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
) : BaseListAdapter<SourceCatalogPage>(), TabLayoutMediator.TabConfigurationStrategy {
|
||||
|
||||
init {
|
||||
delegatesManager.addDelegate(sourceCatalogPageAD(listener, coil, lifecycleOwner))
|
||||
}
|
||||
|
||||
override fun onConfigureTab(tab: TabLayout.Tab, position: Int) {
|
||||
val item = items.getOrNull(position) ?: return
|
||||
tab.setText(item.type.titleResId)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.koitharu.kotatsu.settings.sources.catalog
|
||||
|
||||
import androidx.annotation.MainThread
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.internal.lifecycle.RetainedLifecycleImpl
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
@@ -8,9 +9,10 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.plus
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
||||
@@ -21,6 +23,8 @@ import org.koitharu.kotatsu.explore.data.MangaSourcesRepository
|
||||
import org.koitharu.kotatsu.parsers.model.ContentType
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.util.mapToSet
|
||||
import java.util.EnumMap
|
||||
import java.util.EnumSet
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -28,30 +32,27 @@ import javax.inject.Inject
|
||||
class SourcesCatalogViewModel @Inject constructor(
|
||||
private val repository: MangaSourcesRepository,
|
||||
private val listProducerFactory: SourcesCatalogListProducer.Factory,
|
||||
settings: AppSettings,
|
||||
private val settings: AppSettings,
|
||||
) : BaseViewModel() {
|
||||
|
||||
private val lifecycle = RetainedLifecycleImpl()
|
||||
private var searchQuery: String? = null
|
||||
val onActionDone = MutableEventFlow<ReversibleAction>()
|
||||
val contentType = MutableStateFlow(ContentType.entries.first())
|
||||
val locales = repository.allMangaSources.mapToSet { it.locale }
|
||||
val locale = MutableStateFlow(Locale.getDefault().language.takeIf { it in locales })
|
||||
|
||||
val isNsfwDisabled = settings.isNsfwContentDisabled
|
||||
val hasNewSources = repository.observeNewSources()
|
||||
.map { it.isNotEmpty() }
|
||||
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Lazily, false)
|
||||
|
||||
private val listProducer: StateFlow<SourcesCatalogListProducer?> = combine(
|
||||
locale,
|
||||
contentType,
|
||||
) { lc, type ->
|
||||
listProducerFactory.create(lc, type, lifecycle).also {
|
||||
it.setQuery(searchQuery)
|
||||
}
|
||||
}.stateIn(viewModelScope, SharingStarted.Eagerly, null)
|
||||
private val listProducers = locale.map { lc ->
|
||||
createListProducers(lc)
|
||||
}.stateIn(viewModelScope, SharingStarted.Eagerly, createListProducers(locale.value))
|
||||
|
||||
val content = listProducer.flatMapLatest {
|
||||
it?.list ?: emptyFlow()
|
||||
}.stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
|
||||
val content: StateFlow<List<SourceCatalogPage>> = listProducers.flatMapLatest {
|
||||
val flows = it.entries.map { (type, producer) -> producer.list.map { x -> SourceCatalogPage(type, x) } }
|
||||
combine<SourceCatalogPage, List<SourceCatalogPage>>(flows, Array<SourceCatalogPage>::toList)
|
||||
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, emptyList())
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
@@ -60,21 +61,36 @@ class SourcesCatalogViewModel @Inject constructor(
|
||||
|
||||
fun performSearch(query: String?) {
|
||||
searchQuery = query
|
||||
listProducer.value?.setQuery(query)
|
||||
listProducers.value.forEach { (_, v) -> v.setQuery(query) }
|
||||
}
|
||||
|
||||
fun setLocale(value: String?) {
|
||||
locale.value = value
|
||||
}
|
||||
|
||||
fun setContentType(value: ContentType) {
|
||||
contentType.value = value
|
||||
}
|
||||
|
||||
fun addSource(source: MangaSource) {
|
||||
launchJob(Dispatchers.Default) {
|
||||
val rollback = repository.setSourceEnabled(source, true)
|
||||
onActionDone.call(ReversibleAction(R.string.source_enabled, rollback))
|
||||
}
|
||||
}
|
||||
|
||||
fun skipNewSources() {
|
||||
launchJob {
|
||||
repository.assimilateNewSources()
|
||||
}
|
||||
}
|
||||
|
||||
@MainThread
|
||||
private fun createListProducers(lc: String?): Map<ContentType, SourcesCatalogListProducer> {
|
||||
val types = EnumSet.allOf(ContentType::class.java)
|
||||
if (settings.isNsfwContentDisabled) {
|
||||
types.remove(ContentType.HENTAI)
|
||||
}
|
||||
return types.associateWithTo(EnumMap(ContentType::class.java)) { type ->
|
||||
listProducerFactory.create(lc, type, lifecycle).also {
|
||||
it.setQuery(searchQuery)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true">
|
||||
android:fitsSystemWindows="true"
|
||||
app:liftOnScroll="false">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@id/toolbar"
|
||||
@@ -27,21 +28,10 @@
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/layout_list"
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||
|
||||
<org.koitharu.kotatsu.core.ui.list.fastscroll.FastScrollRecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_source_config" />
|
||||
|
||||
</FrameLayout>
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
19
app/src/main/res/layout/item_catalog_page.xml
Normal file
19
app/src/main/res/layout/item_catalog_page.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/layout_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<org.koitharu.kotatsu.core.ui.list.fastscroll.FastScrollRecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:listitem="@layout/item_source_config" />
|
||||
|
||||
</FrameLayout>
|
||||
@@ -539,4 +539,8 @@
|
||||
<string name="color_correction_apply_text">Гэтыя налады могуць прымяняцца глабальна або толькі да бягучай мангі. Пры глабальным прымяненні індывідуальныя налады не будуць перавызначаны.</string>
|
||||
<string name="grayscale">Адценні шэрага</string>
|
||||
<string name="disable_battery_optimization_summary_downloads">Можа дапамагчы з пачаткам загрузкі, калі ў вас узнікаюць з ёй праблемы</string>
|
||||
<string name="welcome_text">Выберыце, якія крыніцы змесціва вы хочаце ўключыць. Гэта таксама можна наладзіць пазней у наладах</string>
|
||||
<string name="restore">Аднавіць</string>
|
||||
<string name="backup_date_">Дата стварэння рэзервовай копіі: %s</string>
|
||||
<string name="sync_auth">Увайдзіце, каб сінхранізаваць уліковы запіс</string>
|
||||
</resources>
|
||||
@@ -268,7 +268,7 @@
|
||||
<string name="show_reading_indicators">Mostrare gli indicatori di progresso della lettura</string>
|
||||
<string name="data_deletion">Eliminazione dei dati</string>
|
||||
<string name="show_reading_indicators_summary">Mostra la percentuale di lettura nella cronologia e nei preferiti</string>
|
||||
<string name="exclude_nsfw_from_history_summary">I manga contrassegnati come per adulti non verranno mai aggiunti alla cronologia e i vostri progressi non verranno salvati</string>
|
||||
<string name="exclude_nsfw_from_history_summary">I manga contrassegnati come per adulti non verranno mai aggiunti alla cronologia e i progressi non saranno salvati</string>
|
||||
<string name="clear_cookies_summary">Può aiutare in caso di problemi. Tutte le autorizzazioni saranno invalidate</string>
|
||||
<string name="show_all">Mostra tutto</string>
|
||||
<string name="logout">Esci</string>
|
||||
@@ -345,12 +345,12 @@
|
||||
\n Tutti i manga in esso contenuti andranno persi e questo non può essere annullato.</string>
|
||||
<string name="history_shortcuts_summary">Rendere disponibili i manga recenti premendo a lungo sull\'icona dell\'applicazione</string>
|
||||
<string name="reader_info_bar">Mostra la barra delle informazioni nel lettore</string>
|
||||
<string name="manga_error_description_pattern">Dettagli dell\'errore:<br><tt>%1$s</tt><br>1. Prova ad <a href=%2$s>aprire il manga in un browser web</a> per assicurarsi che sia disponibile sulla sua fonte<br>2. Se è disponibile, inviare una segnalazione di errore agli sviluppatori.</string>
|
||||
<string name="manga_error_description_pattern">Dettagli dell\'errore:<br><tt>%1$s</tt><br><br>1. Prova ad <a href=%2$s>aprire il manga in un browser web</a> per assicurarsi che sia disponibile sulla sua fonte<br>2. Controllare di stare usando la <a href=kotatsu://about>versione più recente di Kotatsu</a><br>3. Se è disponibile, inviare una segnalazione di errore agli sviluppatori.</string>
|
||||
<string name="text_unsaved_changes_prompt">Salvare o eliminare le modifiche non salvate\?</string>
|
||||
<string name="discard">Abbandona</string>
|
||||
<string name="language">Lingua</string>
|
||||
<string name="share_logs">Condividi i registri</string>
|
||||
<string name="enable_logging_summary">Registra alcune azioni a scopo di debug</string>
|
||||
<string name="enable_logging_summary">Registra alcune azioni a scopo di debug. Non attivare se non si è sicuri di cosa si stia facendo</string>
|
||||
<string name="enable_logging">Abilita la registrazione</string>
|
||||
<string name="show_suspicious_content">Mostra il contenuto sospetto</string>
|
||||
<string name="theme_name_dynamic">Dinamico</string>
|
||||
@@ -366,7 +366,181 @@
|
||||
<string name="theme_name_rikka">Rikka</string>
|
||||
<string name="theme_name_miku">Miku</string>
|
||||
<string name="allow_unstable_updates">Permetti aggiornamenti instabili</string>
|
||||
<string name="allow_unstable_updates_summary">Proponi aggiornamenti alle versioni beta dell\'app</string>
|
||||
<string name="allow_unstable_updates_summary">Ricevi notifiche riguardo versioni instabili</string>
|
||||
<string name="download_started">Scaricamento iniziato</string>
|
||||
<string name="theme_name_sakura">Sakura</string>
|
||||
<string name="languages">Lingue</string>
|
||||
<string name="zoom_in">Ingrandire</string>
|
||||
<string name="captcha_required_summary">%s richiede il completamento di un captcha per funzionare correttamente</string>
|
||||
<string name="sources_catalog">Catalogo fonti</string>
|
||||
<string name="download_option_all_unread">Tutti i capitoli non letti</string>
|
||||
<string name="frequency_every_day">Ogni giorno</string>
|
||||
<string name="categories">Categorie</string>
|
||||
<string name="progress">Progresso</string>
|
||||
<string name="cancel_all">Annulla tutti</string>
|
||||
<string name="sync_host_description">Puoi usare un server di sincronizzazione self-hosted oppure uno di default. Non cambiare se non si è sicuri di cosa si stia facendo.</string>
|
||||
<string name="error_corrupted_file">Dati non validi in ritorno o file corrotto</string>
|
||||
<string name="pick_custom_directory">Seleziona cartella personalizzata</string>
|
||||
<string name="list_options">Mostra lista opzioni</string>
|
||||
<string name="related_manga_summary">Mostra una lista di manga correlati. In alcuni casi potrebbe essere imprecisa o mancante</string>
|
||||
<string name="remove_completed_downloads_confirm">La tua cronologia dei download sarà permanentemente cancellata</string>
|
||||
<string name="reader_zoom_buttons_summary">Se mostrare o no i tasti di controllo dello zoom in basso a destra</string>
|
||||
<string name="content_type_manga">Manga</string>
|
||||
<string name="tracker_wifi_only_summary">Non controllare nuovi capitoli mentre è in uso una connessione a consumo</string>
|
||||
<string name="error_multiple_states_not_supported">Questa fonte di manga non supporta il filtraggio per multipli stati</string>
|
||||
<string name="order_added">Aggiunto</string>
|
||||
<string name="source_summary_pattern">%1$s, %2$s</string>
|
||||
<string name="on_device">Su dispositivo</string>
|
||||
<string name="password">Password</string>
|
||||
<string name="download_option_whole_manga">L\'intero manga</string>
|
||||
<string name="settings_apply_restart_required">Riavviare l\'applicazione per applicare queste modifiche</string>
|
||||
<string name="backup_frequency">Frequenza creazione backup</string>
|
||||
<string name="data_and_privacy">Dati e privacy</string>
|
||||
<string name="content_type_hentai">Hentai</string>
|
||||
<string name="clear_source_cookies_summary">Pulisci cookies solo per dominio specifico. Nella maggior parte dei casi invaliderà l\'autorizzazione</string>
|
||||
<string name="downloads_wifi_only_summary">Smetti di scaricare quando si passa a rete mobile</string>
|
||||
<string name="suggest_new_sources">Suggerisci nuove fonti dopo aggiornamento dell\'app</string>
|
||||
<string name="user_agent">Intestazione UserAgent</string>
|
||||
<string name="error_filter_states_genre_not_supported">Questa fonte non supporta il filtraggio per sia genere che stato</string>
|
||||
<string name="ignore_ssl_errors">Ignora errori SSL</string>
|
||||
<string name="error_filter_locale_genre_not_supported">Questa fonte non supporta il filtraggio per sia genere che localizzazione</string>
|
||||
<string name="periodic_backups_enable">Abilita backup periodici</string>
|
||||
<string name="server_address">Indirizzo server</string>
|
||||
<string name="content_type_comics">Fumetti</string>
|
||||
<string name="moved_to_top">Spostato in cima</string>
|
||||
<string name="find_similar">Trova simili</string>
|
||||
<string name="data_not_restored_text">Assicurati di aver selezionato il file di backup corretto</string>
|
||||
<string name="catalog">Catalogo</string>
|
||||
<string name="view_list">Vedi lista</string>
|
||||
<string name="welcome_text">Selezionare le fonti di contenuti da abilitare. Si può configurare anche successivamente nelle impostazioni</string>
|
||||
<string name="unknown">Sconosciuto</string>
|
||||
<string name="in_progress">In progresso</string>
|
||||
<string name="download_option_manual_selection">Selezione manuale capitoli</string>
|
||||
<string name="enhanced_colors_summary">Riduce il banding, ma può impattare la performance</string>
|
||||
<string name="pause">Pausa</string>
|
||||
<string name="remove_completed">Rimuovi completati</string>
|
||||
<string name="items_limit_exceeded">Impossibile aggiungere altri oggetti</string>
|
||||
<string name="frequency_every_2_days">Ogni 2 giorni</string>
|
||||
<string name="suggestions_notifications_summary">A volte mostra le notifiche sui manga suggeriti</string>
|
||||
<string name="invalid_value_message">Valore non valido</string>
|
||||
<string name="downloads_cancelled">I download sono stati annullati</string>
|
||||
<string name="reader_optimize">Riduci consumo di memoria (beta)</string>
|
||||
<string name="apply">Applica</string>
|
||||
<string name="restore">Ripristina</string>
|
||||
<string name="data_not_restored">Dati non ripristinati</string>
|
||||
<string name="manage_sources">Gestisci fonti</string>
|
||||
<string name="directories">Cartelle</string>
|
||||
<string name="local_manga_directories">Cartelle manga locali</string>
|
||||
<string name="manage_categories">Gestisci categorie</string>
|
||||
<string name="no_manga_sources_found">Nessuna fonte manga trovata in base alla tua query</string>
|
||||
<string name="color_light">Chiaro</string>
|
||||
<string name="web_view_unavailable">WebView non disponibile: controlla se è installato un provider di WebView</string>
|
||||
<string name="genres_search_hint">Inizia a scrivere il nome del genere</string>
|
||||
<string name="port">Porta</string>
|
||||
<string name="type">Tipo</string>
|
||||
<string name="search_hint">Inserisci titolo manga, genere o nome fonte</string>
|
||||
<string name="frequency_once_per_week">Una volta a settimana</string>
|
||||
<string name="description">Descrizione</string>
|
||||
<string name="periodic_backups">Backup periodici</string>
|
||||
<string name="reader_zoom_buttons">Mostra tasti zoom</string>
|
||||
<string name="sources_reorder_tip">Tocca e tieni premuto su un elemento per riordinarlo</string>
|
||||
<string name="globally">Globalmente</string>
|
||||
<string name="resume">Riprendi</string>
|
||||
<string name="images_proxy_title">Proxy ottimizzazione immagini</string>
|
||||
<string name="username">Nome utente</string>
|
||||
<string name="frequency_twice_per_month">Due volte al mese</string>
|
||||
<string name="main_screen_sections">Sezioni principali schermo</string>
|
||||
<string name="advanced">Avanzate</string>
|
||||
<string name="downloads_settings_info">Se stai avendo problemi di blocco dal server, puoi abilitare il rallentamento dei download per ogni fonte di manga nelle impostazioni delle fonti</string>
|
||||
<string name="sync_settings">Impostazioni di sincronizzazione</string>
|
||||
<string name="online_variant">Variante online</string>
|
||||
<string name="disable_battery_optimization_summary_downloads">Potrebbe aiutare a far iniziare il download in caso di problemi</string>
|
||||
<string name="error_multiple_genres_not_supported">Questa fonte di manga non supporta il filtraggio per multipli generi</string>
|
||||
<string name="download_option_all_unread_b">Tutti i capitoli non letti (%s)</string>
|
||||
<string name="authorization_optional">Autorizzazione (opzionale)</string>
|
||||
<string name="color_dark">Scuro</string>
|
||||
<string name="this_manga">Questo manga</string>
|
||||
<string name="reader_info_bar_summary">Mostra l\'orario attuale e il progresso di lettura in cima allo schermo</string>
|
||||
<string name="translations">Traduzioni</string>
|
||||
<string name="downloads_paused">I download sono stati messi in pausa</string>
|
||||
<string name="too_many_requests_message">Troppe richieste. Riprova più tardi</string>
|
||||
<string name="downloads_wifi_only">Scarica solo via Wi-Fi</string>
|
||||
<string name="lock_screen_rotation">Blocca rotazione schermo</string>
|
||||
<string name="cancel_all_downloads_confirm">Tutti i download attivi saranno annullati, i dati scaricati parzialmente saranno persi</string>
|
||||
<string name="by_relevance">Rilevanza</string>
|
||||
<string name="related_manga">Manga correlati</string>
|
||||
<string name="state_abandoned">Abbandonato</string>
|
||||
<string name="download_option_first_n_chapters">Primo %s</string>
|
||||
<string name="keep_screen_on">Tieni schermo acceso</string>
|
||||
<string name="paused">In pausa</string>
|
||||
<string name="text_downloads_list_holder">Non hai alcun download</string>
|
||||
<string name="skip">Salta</string>
|
||||
<string name="error_search_not_supported">Questa fonte di manga non supporta la ricerca</string>
|
||||
<string name="invalid_port_number">Numero di porta non valido</string>
|
||||
<string name="suggestions_wifi_only_summary">Non aggiornare suggerimenti mentre è in uso una connessione a consumo</string>
|
||||
<string name="webtoon_zoom_summary">Consenti gesto di zoom in modalità webtoon</string>
|
||||
<string name="frequency_once_per_month">Una volta al mese</string>
|
||||
<string name="network">Rete</string>
|
||||
<string name="downloaded">Scaricato</string>
|
||||
<string name="suggestions_enable_prompt">Vuoi ricevere suggerimenti personalizzati di manga?</string>
|
||||
<string name="manual">Manuale</string>
|
||||
<string name="custom_directory">Cartella personalizzata</string>
|
||||
<string name="more">Di più</string>
|
||||
<string name="reader_optimize_summary">Riduci la qualità delle pagine fuori schermo per usare meno memoria</string>
|
||||
<string name="address">Indirizzo</string>
|
||||
<string name="color_correction_apply_text">Queste impostazioni possono essere applicate globalmente o per solo questo manga. Le applicazioni globali non sovrascrivono quelle individuali.</string>
|
||||
<string name="source_enabled">Fonte abilitata</string>
|
||||
<string name="enhanced_colors">Modalità colori 32-bit</string>
|
||||
<string name="folder_with_images_import_description">Puoi selezionare una cartella contenente archivi o immagini. Ogni archivio (o sottocartella) sarà riconosciuto come singolo capitolo.</string>
|
||||
<string name="default_section">Sezione default</string>
|
||||
<string name="background">Sfondo</string>
|
||||
<string name="disable_nsfw_summary">Disabilita fonti per adulti e nascondi manga per adulti dalla lista se possibile</string>
|
||||
<string name="speed_value">x%.1f</string>
|
||||
<string name="downloads_removed">I download sono stati rimossi</string>
|
||||
<string name="pages_animation_summary">Animazioni cambio pagina</string>
|
||||
<string name="no_access_to_file">Non hai accesso a questo file o cartella</string>
|
||||
<string name="mirror_switching">Scegli mirror automaticamente</string>
|
||||
<string name="restore_summary">Ripristina backup creato in precedenza</string>
|
||||
<string name="show_pages_numbers_summary">Mostra numeri di pagina nell\'angolo inferiore</string>
|
||||
<string name="zoom_out">Rimpicciolire</string>
|
||||
<string name="keep_screen_on_summary">Non spegnere lo schermo durante la lettura di manga</string>
|
||||
<string name="download_option_next_unread_n_chapters">Prossimo %s non letto</string>
|
||||
<string name="clear_network_cache">Pulisci cache di rete</string>
|
||||
<string name="voice_search">Ricerca vocale</string>
|
||||
<string name="enable">Abilita</string>
|
||||
<string name="backup_date_">Data del backup: %s</string>
|
||||
<string name="images_procy_description">Usa il servizio wsrv.nl per ridurre i consumi di rete e velocizzare il caricamento delle immagini, se possibile</string>
|
||||
<string name="no_manga_sources_catalog_text">Non sono disponibili fonti in questa sezione, o potrebbero essere state aggiunte tutte.
|
||||
\nRimani sintonizzato</string>
|
||||
<string name="available_d">Disponibile: %1$d</string>
|
||||
<string name="manga_branch_title_template">%1$s (%2$s)</string>
|
||||
<string name="state">Stato</string>
|
||||
<string name="manga_list">Lista manga</string>
|
||||
<string name="grayscale">Bianco e nero</string>
|
||||
<string name="disable_nsfw">Disabilita contenuti per adulti</string>
|
||||
<string name="last_successful_backup">Ultimo backup completato: %s</string>
|
||||
<string name="color_white">Bianco</string>
|
||||
<string name="downloads_resumed">I download sono stati ripresi</string>
|
||||
<string name="details_button_tip">Tocca e tieni premuto il tasto Leggi per vedere più opzioni</string>
|
||||
<string name="state_paused">In pausa</string>
|
||||
<string name="to_top">Va in cima</string>
|
||||
<string name="show">Mostra</string>
|
||||
<string name="sync_auth_hint">Puoi accedere ad un account esistente o crearne uno nuovo</string>
|
||||
<string name="backups_output_directory">Cartella salvataggio backup</string>
|
||||
<string name="invert_colors">Inverti colori</string>
|
||||
<string name="mirror_switching_summary">Cambiare dominio di fonte del manga automaticamente in caso di errore se sono disponibili mirror</string>
|
||||
<string name="no_thanks">No grazie</string>
|
||||
<string name="suggest_new_sources_summary">Chiedi se aggiungere nuove fonti dopo aggiornamento dell\'applicazione</string>
|
||||
<string name="speed">Velocità</string>
|
||||
<string name="download_option_all_chapters">Tutti i capitoli con traduzioni %s</string>
|
||||
<string name="content_type_other">Altro</string>
|
||||
<string name="suggestion_manga">Consigliato: %s</string>
|
||||
<string name="color_black">Nero</string>
|
||||
<string name="this_month">Questo mese</string>
|
||||
<string name="sync_auth">Accedi per sincronizzare l\'account</string>
|
||||
<string name="proxy">Proxy</string>
|
||||
<string name="restore_backup_description">Importa un backup di dati utente creato precedentemente</string>
|
||||
<string name="got_it">Ho capito</string>
|
||||
<string name="comics_archive_import_description">Puoi selezionare uno o più file .cbz o .zip, ogni file sarà riconosciuto come un manga a parte.</string>
|
||||
<string name="show_on_shelf">Mostra sullo Scaffale</string>
|
||||
</resources>
|
||||
@@ -1,13 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="read">Leitura</string>
|
||||
<string name="read">Iniciar</string>
|
||||
<string name="you_have_not_favourites_yet">Ainda não há favoritos</string>
|
||||
<string name="local_storage">Armazenamento local</string>
|
||||
<string name="favourites">Favoritos</string>
|
||||
<string name="history">Histórico</string>
|
||||
<string name="error_occurred">Ocorreu um erro</string>
|
||||
<string name="network_error">Erro de rede</string>
|
||||
<string name="details">Detalhes</string>
|
||||
<string name="details">Detalhado</string>
|
||||
<string name="chapters">Capítulos</string>
|
||||
<string name="list">Lista</string>
|
||||
<string name="detailed_list">Lista detalhada</string>
|
||||
@@ -104,7 +104,7 @@
|
||||
<string name="favourites_category_empty">Categoria vazia</string>
|
||||
<string name="read_later">Leia mais tarde</string>
|
||||
<string name="updates">Atualizações</string>
|
||||
<string name="search_results">Procurar resultados</string>
|
||||
<string name="search_results">Resultados encontrados</string>
|
||||
<string name="new_version_s">Nova versão: %s</string>
|
||||
<string name="size_s">Tamanho: %s</string>
|
||||
<string name="clear_updates_feed">Limpar feed de atualizações</string>
|
||||
@@ -128,7 +128,7 @@
|
||||
<string name="tap_to_try_again">Toque para tentar novamente</string>
|
||||
<string name="reader_mode_hint">A configuração escolhida será lembrada para este mangá</string>
|
||||
<string name="silent">Silencioso</string>
|
||||
<string name="captcha_required">CAPTCHA obrigatório</string>
|
||||
<string name="captcha_required">CAPTCHA Obrigatório</string>
|
||||
<string name="captcha_solve">Resolver</string>
|
||||
<string name="clear_cookies">Limpar cookies</string>
|
||||
<string name="cookies_cleared">Todos os cookies foram removidos</string>
|
||||
@@ -201,7 +201,7 @@
|
||||
<string name="black_dark_theme_summary">Consome menos bateria em telas AMOLED</string>
|
||||
<string name="create_backup">Criar backup de dados</string>
|
||||
<string name="text_local_holder_secondary">Salve-o de fontes online ou importe arquivos.</string>
|
||||
<string name="check_for_updates">Verifique se há atualizações</string>
|
||||
<string name="check_for_updates">Verificar se há atualizações</string>
|
||||
<string name="text_feed_holder">Novos capítulos do que você está lendo são exibidos aqui</string>
|
||||
<string name="app_version">Versão %s</string>
|
||||
<string name="zoom_mode_fit_width">Ajustar à largura</string>
|
||||
@@ -288,7 +288,7 @@
|
||||
<string name="history_cleared">Histórico apagado</string>
|
||||
<string name="manage">Gerenciar</string>
|
||||
<string name="no_bookmarks_yet">Sem páginas marcadas ainda</string>
|
||||
<string name="no_bookmarks_summary">Você pode criar um marcador de página enquanto lê o mangá</string>
|
||||
<string name="no_bookmarks_summary">Você pode marcar uma página enquanto lê o mangá</string>
|
||||
<string name="bookmarks_removed">Marcadores de página removidos</string>
|
||||
<string name="no_manga_sources">Sem fontes de mangás</string>
|
||||
<string name="select_range">Selecionar intervalo</string>
|
||||
@@ -339,7 +339,7 @@
|
||||
<string name="services">Serviços</string>
|
||||
<string name="nothing_here">Não há nada aqui</string>
|
||||
<string name="server_error">Erro do lado do servidor (%1$d). Por favor, tente novamente mais tarde</string>
|
||||
<string name="compact">Compactar</string>
|
||||
<string name="compact">Compacto</string>
|
||||
<string name="enable_logging">Habilitar registros</string>
|
||||
<string name="share_logs">Compartilhar registros</string>
|
||||
<string name="error_no_space_left">Não há espaço disponível no dispositivo</string>
|
||||
@@ -416,7 +416,7 @@
|
||||
<string name="manga_branch_title_template">%1$s (%2$s)</string>
|
||||
<string name="download_option_all_chapters">Todos os capítulos com tradução %s</string>
|
||||
<string name="download_option_whole_manga">O mangá inteiro</string>
|
||||
<string name="download_option_first_n_chapters">Primeiro %s</string>
|
||||
<string name="download_option_first_n_chapters">Primeiros %s</string>
|
||||
<string name="download_option_next_unread_n_chapters">Próximos %s não lidos</string>
|
||||
<string name="download_option_all_unread">Todos os capítulos não lidos</string>
|
||||
<string name="download_option_all_unread_b">Todos os capítulos não lidos (%s)</string>
|
||||
@@ -445,7 +445,7 @@
|
||||
<string name="local_manga_directories">Diretórios locais de mangá</string>
|
||||
<string name="languages">Idiomas</string>
|
||||
<string name="zoom_in">Aumentar o zoom</string>
|
||||
<string name="captcha_required_summary">%s requer que um captcha seja resolvido para funcionar</string>
|
||||
<string name="captcha_required_summary">%s requer que o captcha seja resolvido para funcionar corretamente</string>
|
||||
<string name="progress">Progresso</string>
|
||||
<string name="error_corrupted_file">Dados inválidos ou o arquivo está corrompido</string>
|
||||
<string name="related_manga_summary">Mostra uma lista de mangás relacionados. Em alguns casos, ela pode estar incorreta ou ausente</string>
|
||||
|
||||
@@ -542,5 +542,5 @@
|
||||
<string name="sync_auth">Войти в аккаунт синхронизации</string>
|
||||
<string name="welcome_text">Пожалуйста, выберите, какие источники вы хотите включить. Это можно изменить в настройках позже</string>
|
||||
<string name="restore">Восстановить</string>
|
||||
<string name="backup_date_">Дата резервного копирования: %s</string>
|
||||
<string name="backup_date_">Дата создания резервной копии: %s</string>
|
||||
</resources>
|
||||
@@ -12,12 +12,12 @@
|
||||
<string name="grid">Табла</string>
|
||||
<string name="list_mode">Режим листе</string>
|
||||
<string name="settings">Подешавања</string>
|
||||
<string name="remote_sources">Манга извори</string>
|
||||
<string name="remote_sources">Извори манге</string>
|
||||
<string name="loading_">Учитавање…</string>
|
||||
<string name="chapter_d_of_d">Поглавље %1$d од %2$d</string>
|
||||
<string name="close">Затвори</string>
|
||||
<string name="try_again">Покушај поново</string>
|
||||
<string name="clear_history">Очисти историју</string>
|
||||
<string name="clear_history">Избриши историју</string>
|
||||
<string name="nothing_found">Ништа није пронађено</string>
|
||||
<string name="history_is_empty">Још нема историје</string>
|
||||
<string name="read">Читај</string>
|
||||
@@ -29,23 +29,23 @@
|
||||
<string name="create_shortcut">Направи пречицу…</string>
|
||||
<string name="invalid_port_number">Неважећи број порта</string>
|
||||
<string name="search_manga">Претражи мангу</string>
|
||||
<string name="by_name">Име</string>
|
||||
<string name="clear_pages_cache">Обришите кеш странице</string>
|
||||
<string name="by_name">Имену</string>
|
||||
<string name="clear_pages_cache">Избриши кеш странице</string>
|
||||
<string name="manga_downloading_">Преузимање…</string>
|
||||
<string name="switch_pages">Окретање страница</string>
|
||||
<string name="switch_pages">Листање страница</string>
|
||||
<string name="delete_manga">Избриши мангу</string>
|
||||
<string name="search_on_s">Претрага по %s</string>
|
||||
<string name="internal_storage">Интерна меморија</string>
|
||||
<string name="internal_storage">Унутрашња меморија</string>
|
||||
<string name="external_storage">Спољна меморија</string>
|
||||
<string name="language">Језик</string>
|
||||
<string name="allow_unstable_updates">Дозволите нестабилна ажурирања</string>
|
||||
<string name="allow_unstable_updates">Дозволи нестабилна ажурирања</string>
|
||||
<string name="text_file_sizes">B|kB|MB|GB|TB</string>
|
||||
<string name="standard">Стандардни</string>
|
||||
<string name="webtoon">Манхва</string>
|
||||
<string name="read_mode">Режим читања</string>
|
||||
<string name="grid_size">Величина мреже</string>
|
||||
<string name="clear_thumbs_cache">Обришите кеш сличица</string>
|
||||
<string name="clear_search_history">Обришите историју претраге</string>
|
||||
<string name="clear_thumbs_cache">Избриши кеш сличица</string>
|
||||
<string name="clear_search_history">Избриши историју претраге</string>
|
||||
<string name="gestures_only">Само покрети</string>
|
||||
<string name="app_update_available">Доступна је нова верзија апликације</string>
|
||||
<string name="open_in_browser">Отворите у веб прегледачу</string>
|
||||
@@ -57,21 +57,21 @@
|
||||
<string name="app_version">Верзија %s</string>
|
||||
<string name="black_dark_theme">Црна</string>
|
||||
<string name="black_dark_theme_summary">Троши мање енергије на AMOLED екранима</string>
|
||||
<string name="show_pages_numbers">Нумерисање страница</string>
|
||||
<string name="preload_pages">Претходно учитавање страница</string>
|
||||
<string name="show_pages_numbers">Приказ бројева на страницама</string>
|
||||
<string name="preload_pages">Преучитавање страница</string>
|
||||
<string name="appearance">Изглед</string>
|
||||
<string name="check_new_chapters_title">Проверите да ли постоје нова поглавља и обавестите их</string>
|
||||
<string name="show_notification_new_chapters_on">Добићете обавештења о ажурирањима манге коју читате</string>
|
||||
<string name="show_notification_new_chapters_on">Добићеш обавештења о ажурирањима манге коју читаш</string>
|
||||
<string name="default_mode">Подразумевани режим</string>
|
||||
<string name="network_unavailable">Мрежа није доступна</string>
|
||||
<string name="network_unavailable_hint">Укључите Wi-Fi или мобилну мрежу да бисте читали мангу на мрежи</string>
|
||||
<string name="allow_unstable_updates_summary">Понуди ажурирања за бета верзије апликације</string>
|
||||
<string name="allow_unstable_updates_summary">Добијај обавештења о нестабилним верзијама апликације</string>
|
||||
<string name="translations">Преводи</string>
|
||||
<string name="data_and_privacy">Подаци и приватност</string>
|
||||
<string name="network">Мрежа</string>
|
||||
<string name="show_pages_numbers_summary">Прикажите бројеве страница у доњем углу</string>
|
||||
<string name="pages_animation_summary">Анимација окретања страница</string>
|
||||
<string name="images_proxy_title">Проки сервер за оптимизацију слика</string>
|
||||
<string name="images_proxy_title">Прокси сервер за оптимизацију слика</string>
|
||||
<string name="username">Корисничко име</string>
|
||||
<string name="reader_info_bar_summary">Прикажите тренутно време и напредак читања на врху екрана</string>
|
||||
<string name="authorization_optional">Ауторизација (опционално)</string>
|
||||
@@ -80,8 +80,8 @@
|
||||
<string name="details_button_tip">Притисните и држите дугме за читање да бисте видели више опција</string>
|
||||
<string name="computing_">Рачунање…</string>
|
||||
<string name="search">Претражи</string>
|
||||
<string name="by_rating">Оцена</string>
|
||||
<string name="sort_order">Редослед распоређивања</string>
|
||||
<string name="by_rating">Оцени</string>
|
||||
<string name="sort_order">Распореди по</string>
|
||||
<string name="newest">Најновије</string>
|
||||
<string name="light">Светла</string>
|
||||
<string name="dark">Мрачна</string>
|
||||
@@ -91,13 +91,13 @@
|
||||
<string name="pages">Странице</string>
|
||||
<string name="operation_not_supported">Ова операција није подржана</string>
|
||||
<string name="error">Грешка</string>
|
||||
<string name="new_chapters">Нова поглавља</string>
|
||||
<string name="new_chapters">Новим поглављима</string>
|
||||
<string name="notifications_settings">Подешавања обавештења</string>
|
||||
<string name="new_version_s">Нова верзија: %s</string>
|
||||
<string name="about_app_translation_summary">Преведите ову апликацију</string>
|
||||
<string name="about_app_translation">Превод</string>
|
||||
<string name="screenshots_policy">Политика снимања екрана</string>
|
||||
<string name="clear_network_cache">Очистите мрежну кеш меморију</string>
|
||||
<string name="clear_network_cache">Избриши мрежну кеш меморију</string>
|
||||
<string name="password">Лозинка</string>
|
||||
<string name="show_in_grid_view">Прикажи као мрежу</string>
|
||||
<string name="page_saved">Сачувано</string>
|
||||
@@ -105,17 +105,17 @@
|
||||
<string name="processing_">Обрада…</string>
|
||||
<string name="download_complete">Преузето</string>
|
||||
<string name="downloads">Преузимања</string>
|
||||
<string name="popular">Популарно</string>
|
||||
<string name="updated">Ажуриран</string>
|
||||
<string name="popular">Популарности</string>
|
||||
<string name="updated">Ажурирано</string>
|
||||
<string name="text_clear_history_prompt">Трајно избрисати сву историју читања\?</string>
|
||||
<string name="remove">Уклони</string>
|
||||
<string name="save_page">Сачувај страницу</string>
|
||||
<string name="share_image">Подели слику</string>
|
||||
<string name="text_file_not_supported">Изаберите ZIP датотеку или CBZ датотеку.</string>
|
||||
<string name="no_description">Нема описа</string>
|
||||
<string name="taps_on_edges">Додири по ивицама</string>
|
||||
<string name="taps_on_edges">Додирни по ивицама</string>
|
||||
<string name="reader_settings">Подешавања читача</string>
|
||||
<string name="volume_buttons">Дугмад за јачину звука</string>
|
||||
<string name="volume_buttons">Притисни дугмад за јачину звука</string>
|
||||
<string name="notifications">Обавештења</string>
|
||||
<string name="pages_cache">Кеш страница</string>
|
||||
<string name="text_shelf_holder_secondary">Пронађите шта да читате у одељку „Преглед“</string>
|
||||
@@ -132,7 +132,7 @@
|
||||
<string name="favourites_categories">Омиљене категорије</string>
|
||||
<string name="remove_category">Избриши</string>
|
||||
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">Омогућено је %1$d од %2$d</string>
|
||||
<string name="clear">Очисти</string>
|
||||
<string name="clear">Избриши</string>
|
||||
<string name="text_history_holder_primary">Оно што прочитате биће приказано овде</string>
|
||||
<string name="delete">Избриши</string>
|
||||
<string name="search_history_cleared">Очишћено</string>
|
||||
@@ -160,7 +160,7 @@
|
||||
<string name="download_started">Преузимање је почело</string>
|
||||
<string name="categories">Категорије</string>
|
||||
<string name="tracking">Пратим</string>
|
||||
<string name="progress">Напредак</string>
|
||||
<string name="progress">Напредку</string>
|
||||
<string name="cancel_all">Откажи све</string>
|
||||
<string name="sync_host_description">Можеш да користиш сервер за синхронизацију који се самостално хостује или подразумевани. Не мењај ово ако ниси сигуран шта радиш.</string>
|
||||
<string name="error_corrupted_file">Враћени су неважећи подаци или је датотека оштећена</string>
|
||||
@@ -181,20 +181,20 @@
|
||||
<string name="disable_all">Онемогући све</string>
|
||||
<string name="tracker_wifi_only_summary">Не проверавај да ли постоје нова поглавља користећи мрежне везе са ограничењем</string>
|
||||
<string name="error_multiple_states_not_supported">Овај извор манга не подржава филтрирање према више стања</string>
|
||||
<string name="order_added">Додато</string>
|
||||
<string name="order_added">Додатим</string>
|
||||
<string name="source_summary_pattern">%1$s, %2$s</string>
|
||||
<string name="enable_logging">Омогући дијагностику</string>
|
||||
<string name="on_device">На уређају</string>
|
||||
<string name="download_option_whole_manga">Цела манга</string>
|
||||
<string name="settings_apply_restart_required">Поново покрени апликацију да би применио ове промене</string>
|
||||
<string name="rotate_screen">Окрени екран</string>
|
||||
<string name="text_clear_updates_feed_prompt">Трајно избрисати сву историју ажурирања?</string>
|
||||
<string name="text_clear_updates_feed_prompt">Трајно избриши сву историју ажурирања?</string>
|
||||
<string name="source_disabled">Извор је онемогућен</string>
|
||||
<string name="backup_frequency">Учесталост прављења резервне копије</string>
|
||||
<string name="clear_cookies_summary">Може помоћи у случају неких проблема. Сва овлашћења ће бити поништена</string>
|
||||
<string name="enable_logging_summary">Снима неке радње у сврху отклањања грешака. Немојте ово укључивати ако нисте сигурни шта радите</string>
|
||||
<string name="suggestions_enable">Омогући предлоге</string>
|
||||
<string name="clear_feed">Очисти фид</string>
|
||||
<string name="clear_feed">Очисти новости</string>
|
||||
<string name="welcome">Добродошли</string>
|
||||
<string name="chapters_empty">Нема поглавља у овој манги</string>
|
||||
<string name="content_type_hentai">Хентаи</string>
|
||||
@@ -208,7 +208,7 @@
|
||||
<string name="import_completed">Увоз је завршен</string>
|
||||
<string name="different_languages">Други језици</string>
|
||||
<string name="show_reading_indicators">Показатељ током читања</string>
|
||||
<string name="read_later">Читај касније</string>
|
||||
<string name="read_later">За касније</string>
|
||||
<string name="backup_saved">Резервна копија је сачувана</string>
|
||||
<string name="local_manga_processing">Сачувана обрада манге</string>
|
||||
<string name="user_agent">Заглавље КорисничкогАгент-а</string>
|
||||
@@ -259,7 +259,7 @@
|
||||
<string name="invalid_value_message">Погрешна вредност</string>
|
||||
<string name="downloads_cancelled">Преузимања су отказана</string>
|
||||
<string name="webtoon_zoom">Webtoon увећање</string>
|
||||
<string name="various_languages">Разни језици</string>
|
||||
<string name="various_languages">Сви језици</string>
|
||||
<string name="removal_completed">Уклањање је завршено</string>
|
||||
<string name="theme_name_miku">Мику</string>
|
||||
<string name="edit">Уреди</string>
|
||||
@@ -271,7 +271,7 @@
|
||||
<string name="directories">Директоријуми</string>
|
||||
<string name="local_manga_directories">Локални директорији манги</string>
|
||||
<string name="manage_categories">Управљај категоријама</string>
|
||||
<string name="update">Ажурирање</string>
|
||||
<string name="update">Ажурирај</string>
|
||||
<string name="scrobbling_empty_hint">Да бисте пратили напредак читања, изаберите Изборник → Прати на екрану са детаљима манге.</string>
|
||||
<string name="removed_from_history">Уклоњено из историје</string>
|
||||
<string name="no_manga_sources_found">Вашим упитом није пронађен ниједан доступан извор манге</string>
|
||||
@@ -282,7 +282,7 @@
|
||||
<string name="port">Прикључак</string>
|
||||
<string name="color_correction_hint">Изабрана подешавања боја биће запамћена за ову мангу</string>
|
||||
<string name="not_found_404">Садржај није пронађен или је уклоњен</string>
|
||||
<string name="feed_will_update_soon">Ажурирање фида ће ускоро почети</string>
|
||||
<string name="feed_will_update_soon">Ажурирање новости ће ускоро почети</string>
|
||||
<string name="appwidget_recent_description">Ваша недавно прочитана манга</string>
|
||||
<string name="got_it">Разумем</string>
|
||||
<string name="type">Тип</string>
|
||||
@@ -293,7 +293,7 @@
|
||||
<string name="disable_battery_optimization_summary">Помаже код провера ажурирања у позадини</string>
|
||||
<string name="auth_not_supported_by">Пријављивање на %s није подржано</string>
|
||||
<string name="status_on_hold">На чекању</string>
|
||||
<string name="last_2_hours">Последња 2 сата</string>
|
||||
<string name="last_2_hours">Од последња 2 сата</string>
|
||||
<string name="periodic_backups">Повремене резервне копије</string>
|
||||
<string name="reader_zoom_buttons">Прикажи дугмад за увећање и смањивање</string>
|
||||
<string name="name">Име</string>
|
||||
@@ -311,7 +311,7 @@
|
||||
<string name="main_screen_sections">Одељци главног екрана</string>
|
||||
<string name="confirm_exit">Притисни Назад поново да изађеш</string>
|
||||
<string name="scale_mode">Режим скалирања</string>
|
||||
<string name="advanced">Напредно</string>
|
||||
<string name="advanced">Напредна</string>
|
||||
<string name="only_using_wifi">Само на ВиФи-ју</string>
|
||||
<string name="sync_settings">Подешавања синхронизације</string>
|
||||
<string name="back">Назад</string>
|
||||
@@ -348,7 +348,7 @@
|
||||
<string name="history_shortcuts_summary">Учини недавну мангу доступном дугим притиском на икону апликације</string>
|
||||
<string name="automatic_scroll">Самостално померање</string>
|
||||
<string name="download_option_first_n_chapters">Прво %s</string>
|
||||
<string name="keep_screen_on">Остави екран укљученим</string>
|
||||
<string name="keep_screen_on">Задржи екран укљученим</string>
|
||||
<string name="reverse">Уназад</string>
|
||||
<string name="track_sources">Потражи ажурирања</string>
|
||||
<string name="paused">Заустављено</string>
|
||||
@@ -389,7 +389,7 @@
|
||||
<string name="sync">Синхронизација</string>
|
||||
<string name="search_chapters">Пронађи поглавље</string>
|
||||
<string name="exit_confirmation">Потврда изласка</string>
|
||||
<string name="manual">Упутство</string>
|
||||
<string name="manual">Ручном избору</string>
|
||||
<string name="comics_archive">Архива стрипова</string>
|
||||
<string name="custom_directory">Прилагођени директоријум</string>
|
||||
<string name="more">Више</string>
|
||||
@@ -440,7 +440,7 @@
|
||||
<string name="chapters_will_removed_background">Поглавља ће бити уклоњена у позадини</string>
|
||||
<string name="import_completed_hint">Можете да избришете оригиналну датотеку из складишта за уштеду простора</string>
|
||||
<string name="theme_name_rikka">Рикка</string>
|
||||
<string name="disabled">Онемогућено</string>
|
||||
<string name="disabled">Онемогући</string>
|
||||
<string name="long_ago">Давно</string>
|
||||
<string name="reader_control_ltr_summary">Додиром на десну ивицу или притиском на десну стрелицу пребацује се на следећу страницу</string>
|
||||
<string name="incognito_mode">Режим без чувања</string>
|
||||
@@ -496,7 +496,7 @@
|
||||
<string name="preparing_">Припрема се…</string>
|
||||
<string name="exit_confirmation_summary">Притисни Назад двапут да би изашао из апликације</string>
|
||||
<string name="bookmarks_removed">Обележивачи су уклоњени</string>
|
||||
<string name="screenshots_block_nsfw">Блокирајте садржај за одрасле</string>
|
||||
<string name="screenshots_block_nsfw">Блокирај садржај за одрасле</string>
|
||||
<string name="enter_password">Унеси лозинку</string>
|
||||
<string name="repeat_password">Поновите лозинку</string>
|
||||
<string name="data_restored_success">Сви подаци су обновљени</string>
|
||||
@@ -520,7 +520,7 @@
|
||||
<string name="bookmarks">Обележивачи</string>
|
||||
<string name="show_all">Покажи све</string>
|
||||
<string name="this_month">Овог месеца</string>
|
||||
<string name="today">Данас</string>
|
||||
<string name="today">Од данас</string>
|
||||
<string name="empty_favourite_categories">Нема омиљених категорија</string>
|
||||
<string name="invalid_domain_message">Неважећи домен</string>
|
||||
<string name="system_default">Подразумевано</string>
|
||||
@@ -539,4 +539,8 @@
|
||||
<string name="skip">Прескочи</string>
|
||||
<string name="color_correction_apply_text">Ова подешавања се могу применити глобално или само на тренутну мангу. Ако се примењују глобално, појединачна подешавања неће бити замењена.</string>
|
||||
<string name="grayscale">Сиви тон</string>
|
||||
<string name="welcome_text">Изабери које изворе садржаја желиш да омогућиш. Ово можеш касније да промениш у подешавањима</string>
|
||||
<string name="restore">Поврати</string>
|
||||
<string name="backup_date_">Датум резервне копије: %s</string>
|
||||
<string name="sync_auth">Пријавите се за синхронизацију налога</string>
|
||||
</resources>
|
||||
@@ -539,4 +539,8 @@
|
||||
<string name="color_correction_apply_text">Ці налаштування можна застосувати глобально або лише до поточної манґи. Користувацькі налаштування не будуть замінені, якщо застосовувати їх глобально.</string>
|
||||
<string name="grayscale">Відтінки сірого</string>
|
||||
<string name="disable_battery_optimization_summary_downloads">Може допомогти з початком завантаження, якщо у вас виникають проблеми з нею</string>
|
||||
<string name="welcome_text">Виберіть, які джерела вмісту ви хочете включити. Це також можна налаштувати пізніше в налаштуваннях</string>
|
||||
<string name="restore">Відновити</string>
|
||||
<string name="backup_date_">Дата створення резервної копії: %s</string>
|
||||
<string name="sync_auth">Увійдіть, щоб синхронізувати обліковий запис</string>
|
||||
</resources>
|
||||
@@ -548,4 +548,6 @@
|
||||
<string name="sync_auth">Login to sync account</string>
|
||||
<string name="restore">Restore</string>
|
||||
<string name="backup_date_">Backup date: %s</string>
|
||||
<string name="state_upcoming">Upcoming</string>
|
||||
<string name="by_name_reverse">Name reversed</string>
|
||||
</resources>
|
||||
|
||||
@@ -6,7 +6,7 @@ buildscript {
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.2.0'
|
||||
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.21'
|
||||
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.49'
|
||||
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.50'
|
||||
classpath 'com.google.devtools.ksp:symbol-processing-gradle-plugin:1.9.21-1.0.15'
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user