Compare commits

...

8 Commits

Author SHA1 Message Date
Макар Разин
c6d1cf2f72 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (546 of 546 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (546 of 546 strings)

Translated using Weblate (Belarusian)

Currently translated at 100.0% (546 of 546 strings)

Co-authored-by: Макар Разин <makarrazin14@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/be/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ru/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/uk/
Translation: Kotatsu/Strings
2023-12-25 13:26:51 +02:00
Anon
a317236cb0 Translated using Weblate (Serbian)
Currently translated at 100.0% (546 of 546 strings)

Co-authored-by: Anon <anonymousprivate76@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/sr/
Translation: Kotatsu/Strings
2023-12-25 13:26:51 +02:00
Deivinni Silva
3703c07a98 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (546 of 546 strings)

Co-authored-by: Deivinni Silva <deivinnimds3656@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/pt_BR/
Translation: Kotatsu/Strings
2023-12-25 13:26:51 +02:00
b7b32dd447 Update parsers 2023-12-25 13:18:28 +02:00
c103386dc5 Fix crash on file size compution 2023-12-25 10:01:43 +02:00
a9d6ee4a95 Update dependencies 2023-12-25 09:57:01 +02:00
71f2c91e5a Improve sources catalog 2023-12-23 08:44:09 +02:00
b878f358ff Improve explore screen 2023-12-23 07:26:31 +02:00
25 changed files with 283 additions and 224 deletions

View File

@@ -16,8 +16,8 @@ android {
applicationId 'org.koitharu.kotatsu'
minSdk = 21
targetSdk = 34
versionCode = 607
versionName = '6.5.1'
versionCode = 608
versionName = '6.5.2'
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:4a0e7221b0') {
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'
}

View File

@@ -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
}

View File

@@ -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))
}

View File

@@ -70,7 +70,7 @@ class WindowInsetsDelegate : OnApplyWindowInsetsListener, View.OnLayoutChangeLis
lastInsets = null
}
interface WindowInsetsListener {
fun interface WindowInsetsListener {
fun onWindowInsetsChanged(insets: Insets)
}

View File

@@ -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> {

View File

@@ -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

View File

@@ -111,7 +111,7 @@ class ExploreFragment :
}
override fun onListHeaderClick(item: ListHeader, view: View) {
startActivity(Intent(view.context, SourcesCatalogActivity::class.java))
startActivity(SettingsActivity.newManageSourcesIntent(view.context))
}
override fun onPrimaryButtonClick(tipView: TipView) {
@@ -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) {

View File

@@ -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
@@ -56,8 +55,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>()
@@ -136,7 +133,7 @@ class ExploreViewModel @Inject constructor(
result += RecommendationsItem(recommendation)
}
if (sources.isNotEmpty()) {
result += ListHeader(R.string.remote_sources, R.string.catalog)
result += ListHeader(R.string.remote_sources, R.string.manage)
if (newSources.isNotEmpty()) {
result += TipModel(
key = TIP_NEW_SOURCES,
@@ -153,7 +150,7 @@ class ExploreViewModel @Inject constructor(
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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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,

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -10,24 +10,21 @@ 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.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.core.model.titleResId
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 javax.inject.Inject
@AndroidEntryPoint
class SourcesCatalogActivity : BaseActivity<ActivitySourcesCatalogBinding>(),
TabLayout.OnTabSelectedListener,
OnListItemClickListener<SourceCatalogItem.Source>,
AppBarOwner, MenuItem.OnActionExpandListener {
@@ -43,19 +40,17 @@ 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.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 +60,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 +76,8 @@ 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
}
val tab = tabs.newTab()
tab.setText(type.titleResId)
tab.tag = type
tabs.addTab(tab)
}
tabs.addOnTabSelectedListener(this)
}
}

View File

@@ -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)

View File

@@ -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)
}
}

View File

@@ -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,23 @@ 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
private val listProducers = locale.map { lc ->
createListProducers(lc)
}.stateIn(viewModelScope, SharingStarted.Eagerly, createListProducers(locale.value))
private val listProducer: StateFlow<SourcesCatalogListProducer?> = combine(
locale,
contentType,
) { lc, type ->
listProducerFactory.create(lc, type, lifecycle).also {
it.setQuery(searchQuery)
}
}.stateIn(viewModelScope, SharingStarted.Eagerly, null)
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 +57,30 @@ 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))
}
}
@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)
}
}
}
}

View File

@@ -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>

View 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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'
}
}