Do sources configuration ops in background
This commit is contained in:
@@ -24,7 +24,7 @@ import org.koitharu.kotatsu.databinding.ActivitySettingsBinding
|
||||
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.settings.about.AboutSettingsFragment
|
||||
import org.koitharu.kotatsu.settings.sources.SourcesSettingsFragment
|
||||
import org.koitharu.kotatsu.settings.sources.SourcesListFragment
|
||||
import org.koitharu.kotatsu.settings.tracker.TrackerSettingsFragment
|
||||
import org.koitharu.kotatsu.utils.ext.getSerializableExtraCompat
|
||||
import org.koitharu.kotatsu.utils.ext.isScrolledToTop
|
||||
@@ -133,7 +133,7 @@ class SettingsActivity :
|
||||
intent.getSerializableExtraCompat(EXTRA_SOURCE) as? MangaSource ?: MangaSource.LOCAL,
|
||||
)
|
||||
|
||||
ACTION_MANAGE_SOURCES -> SourcesSettingsFragment()
|
||||
ACTION_MANAGE_SOURCES -> SourcesListFragment()
|
||||
Intent.ACTION_VIEW -> {
|
||||
when (intent.data?.host) {
|
||||
HOST_ABOUT -> AboutSettingsFragment()
|
||||
|
||||
@@ -33,7 +33,7 @@ import org.koitharu.kotatsu.utils.ext.getItem
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SourcesSettingsFragment :
|
||||
class SourcesListFragment :
|
||||
BaseFragment<FragmentSettingsSourcesBinding>(),
|
||||
SourceConfigListener,
|
||||
RecyclerViewOwner {
|
||||
@@ -42,7 +42,7 @@ class SourcesSettingsFragment :
|
||||
lateinit var coil: ImageLoader
|
||||
|
||||
private var reorderHelper: ItemTouchHelper? = null
|
||||
private val viewModel by viewModels<SourcesSettingsViewModel>()
|
||||
private val viewModel by viewModels<SourcesListViewModel>()
|
||||
|
||||
override val recyclerView: RecyclerView
|
||||
get() = binding.recyclerView
|
||||
@@ -3,6 +3,11 @@ package org.koitharu.kotatsu.settings.sources
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runInterruptible
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.base.domain.ReversibleHandle
|
||||
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
||||
@@ -19,34 +24,41 @@ import org.koitharu.kotatsu.utils.ext.move
|
||||
import java.util.Locale
|
||||
import java.util.TreeMap
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
|
||||
private const val KEY_ENABLED = "!"
|
||||
private const val TIP_REORDER = "src_reorder"
|
||||
|
||||
@HiltViewModel
|
||||
class SourcesSettingsViewModel @Inject constructor(
|
||||
class SourcesListViewModel @Inject constructor(
|
||||
private val settings: AppSettings,
|
||||
) : BaseViewModel() {
|
||||
|
||||
val items = MutableLiveData<List<SourceConfigItem>>(emptyList())
|
||||
val onActionDone = SingleLiveEvent<ReversibleAction>()
|
||||
private val mutex = Mutex()
|
||||
|
||||
private val expandedGroups = HashSet<String?>()
|
||||
private var searchQuery: String? = null
|
||||
|
||||
init {
|
||||
buildList()
|
||||
launchAtomicJob(Dispatchers.Default) {
|
||||
buildList()
|
||||
}
|
||||
}
|
||||
|
||||
fun reorderSources(oldPos: Int, newPos: Int): Boolean {
|
||||
val snapshot = items.value?.toMutableList() ?: return false
|
||||
if ((snapshot[oldPos] as? SourceConfigItem.SourceItem)?.isEnabled != true) return false
|
||||
if ((snapshot[newPos] as? SourceConfigItem.SourceItem)?.isEnabled != true) return false
|
||||
snapshot.move(oldPos, newPos)
|
||||
settings.sourcesOrder = snapshot.mapNotNull {
|
||||
(it as? SourceConfigItem.SourceItem)?.source?.name
|
||||
launchAtomicJob(Dispatchers.Default) {
|
||||
snapshot.move(oldPos, newPos)
|
||||
settings.sourcesOrder = snapshot.mapNotNull {
|
||||
(it as? SourceConfigItem.SourceItem)?.source?.name
|
||||
}
|
||||
buildList()
|
||||
}
|
||||
buildList()
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -58,67 +70,79 @@ class SourcesSettingsViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
fun setEnabled(source: MangaSource, isEnabled: Boolean) {
|
||||
settings.hiddenSources = if (isEnabled) {
|
||||
settings.hiddenSources - source.name
|
||||
} else {
|
||||
settings.hiddenSources + source.name
|
||||
}
|
||||
if (isEnabled) {
|
||||
settings.markKnownSources(setOf(source))
|
||||
} else {
|
||||
val rollback = ReversibleHandle {
|
||||
setEnabled(source, true)
|
||||
launchAtomicJob(Dispatchers.Default) {
|
||||
settings.hiddenSources = if (isEnabled) {
|
||||
settings.hiddenSources - source.name
|
||||
} else {
|
||||
settings.hiddenSources + source.name
|
||||
}
|
||||
onActionDone.postCall(ReversibleAction(R.string.source_disabled, rollback))
|
||||
if (isEnabled) {
|
||||
settings.markKnownSources(setOf(source))
|
||||
} else {
|
||||
val rollback = ReversibleHandle {
|
||||
setEnabled(source, true)
|
||||
}
|
||||
onActionDone.postCall(ReversibleAction(R.string.source_disabled, rollback))
|
||||
}
|
||||
buildList()
|
||||
}
|
||||
buildList()
|
||||
}
|
||||
|
||||
fun disableAll() {
|
||||
settings.hiddenSources = settings.getMangaSources(includeHidden = true).mapToSet {
|
||||
it.name
|
||||
launchAtomicJob(Dispatchers.Default) {
|
||||
settings.hiddenSources = settings.getMangaSources(includeHidden = true).mapToSet {
|
||||
it.name
|
||||
}
|
||||
buildList()
|
||||
}
|
||||
buildList()
|
||||
}
|
||||
|
||||
fun expandOrCollapse(headerId: String?) {
|
||||
if (headerId in expandedGroups) {
|
||||
expandedGroups.remove(headerId)
|
||||
} else {
|
||||
expandedGroups.add(headerId)
|
||||
launchAtomicJob {
|
||||
if (headerId in expandedGroups) {
|
||||
expandedGroups.remove(headerId)
|
||||
} else {
|
||||
expandedGroups.add(headerId)
|
||||
}
|
||||
buildList()
|
||||
}
|
||||
buildList()
|
||||
}
|
||||
|
||||
fun performSearch(query: String?) {
|
||||
searchQuery = query?.trim()
|
||||
buildList()
|
||||
launchAtomicJob {
|
||||
searchQuery = query?.trim()
|
||||
buildList()
|
||||
}
|
||||
}
|
||||
|
||||
fun onTipClosed(item: SourceConfigItem.Tip) {
|
||||
settings.closeTip(item.key)
|
||||
buildList()
|
||||
launchAtomicJob(Dispatchers.Default) {
|
||||
settings.closeTip(item.key)
|
||||
buildList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildList() {
|
||||
private suspend fun buildList() = runInterruptible(Dispatchers.Default) {
|
||||
val sources = settings.getMangaSources(includeHidden = true)
|
||||
val hiddenSources = settings.hiddenSources
|
||||
val query = searchQuery
|
||||
if (!query.isNullOrEmpty()) {
|
||||
items.value = sources.mapNotNull {
|
||||
if (!it.title.contains(query, ignoreCase = true)) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
SourceConfigItem.SourceItem(
|
||||
source = it,
|
||||
summary = it.getLocaleTitle(),
|
||||
isEnabled = it.name !in hiddenSources,
|
||||
isDraggable = false,
|
||||
)
|
||||
}.ifEmpty {
|
||||
listOf(SourceConfigItem.EmptySearchResult)
|
||||
}
|
||||
return
|
||||
items.postValue(
|
||||
sources.mapNotNull {
|
||||
if (!it.title.contains(query, ignoreCase = true)) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
SourceConfigItem.SourceItem(
|
||||
source = it,
|
||||
summary = it.getLocaleTitle(),
|
||||
isEnabled = it.name !in hiddenSources,
|
||||
isDraggable = false,
|
||||
)
|
||||
}.ifEmpty {
|
||||
listOf(SourceConfigItem.EmptySearchResult)
|
||||
},
|
||||
)
|
||||
return@runInterruptible
|
||||
}
|
||||
val map = sources.groupByTo(TreeMap(LocaleKeyComparator())) {
|
||||
if (it.name !in hiddenSources) {
|
||||
@@ -165,7 +189,7 @@ class SourcesSettingsViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
items.value = result
|
||||
items.postValue(result)
|
||||
}
|
||||
|
||||
private fun getLocaleTitle(localeKey: String?): String? {
|
||||
@@ -173,6 +197,15 @@ class SourcesSettingsViewModel @Inject constructor(
|
||||
return locale.getDisplayLanguage(locale).toTitleCase(locale)
|
||||
}
|
||||
|
||||
private inline fun launchAtomicJob(
|
||||
context: CoroutineContext = EmptyCoroutineContext,
|
||||
crossinline block: suspend CoroutineScope.() -> Unit
|
||||
) = launchJob(context) {
|
||||
mutex.withLock {
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
private class LocaleKeyComparator : Comparator<String?> {
|
||||
|
||||
private val deviceLocales = LocaleListCompat.getAdjustedDefault()
|
||||
Reference in New Issue
Block a user