Option to enable all sources
This commit is contained in:
@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.core.db.dao
|
|||||||
|
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import androidx.room.Transaction
|
import androidx.room.Transaction
|
||||||
import org.koitharu.kotatsu.core.db.entity.ChapterEntity
|
import org.koitharu.kotatsu.core.db.entity.ChapterEntity
|
||||||
@@ -24,6 +25,6 @@ abstract class ChaptersDao {
|
|||||||
insert(entities)
|
insert(entities)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Insert
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
protected abstract suspend fun insert(entities: Collection<ChapterEntity>)
|
protected abstract suspend fun insert(entities: Collection<ChapterEntity>)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import androidx.room.Upsert
|
|||||||
import androidx.sqlite.db.SimpleSQLiteQuery
|
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||||
import androidx.sqlite.db.SupportSQLiteQuery
|
import androidx.sqlite.db.SupportSQLiteQuery
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import org.intellij.lang.annotations.Language
|
|
||||||
import org.koitharu.kotatsu.BuildConfig
|
import org.koitharu.kotatsu.BuildConfig
|
||||||
import org.koitharu.kotatsu.core.db.entity.MangaSourceEntity
|
import org.koitharu.kotatsu.core.db.entity.MangaSourceEntity
|
||||||
import org.koitharu.kotatsu.explore.data.SourcesSortOrder
|
import org.koitharu.kotatsu.explore.data.SourcesSortOrder
|
||||||
@@ -61,21 +60,11 @@ abstract class MangaSourcesDao {
|
|||||||
@Query("SELECT * FROM sources WHERE pinned = 1")
|
@Query("SELECT * FROM sources WHERE pinned = 1")
|
||||||
abstract suspend fun findAllPinned(): List<MangaSourceEntity>
|
abstract suspend fun findAllPinned(): List<MangaSourceEntity>
|
||||||
|
|
||||||
fun observeEnabled(order: SourcesSortOrder): Flow<List<MangaSourceEntity>> {
|
fun observeAll(enabledOnly: Boolean, order: SourcesSortOrder): Flow<List<MangaSourceEntity>> =
|
||||||
val orderBy = getOrderBy(order)
|
observeImpl(getQuery(enabledOnly, order))
|
||||||
|
|
||||||
@Language("RoomSql")
|
suspend fun findAll(enabledOnly: Boolean, order: SourcesSortOrder): List<MangaSourceEntity> =
|
||||||
val query = SimpleSQLiteQuery("SELECT * FROM sources WHERE enabled = 1 ORDER BY pinned DESC, $orderBy")
|
findAllImpl(getQuery(enabledOnly, order))
|
||||||
return observeImpl(query)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun findAllEnabled(order: SourcesSortOrder): List<MangaSourceEntity> {
|
|
||||||
val orderBy = getOrderBy(order)
|
|
||||||
|
|
||||||
@Language("RoomSql")
|
|
||||||
val query = SimpleSQLiteQuery("SELECT * FROM sources WHERE enabled = 1 ORDER BY pinned DESC, $orderBy")
|
|
||||||
return findAllImpl(query)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
open suspend fun setEnabled(source: String, isEnabled: Boolean) {
|
open suspend fun setEnabled(source: String, isEnabled: Boolean) {
|
||||||
@@ -101,6 +90,17 @@ abstract class MangaSourcesDao {
|
|||||||
@RawQuery
|
@RawQuery
|
||||||
protected abstract suspend fun findAllImpl(query: SupportSQLiteQuery): List<MangaSourceEntity>
|
protected abstract suspend fun findAllImpl(query: SupportSQLiteQuery): List<MangaSourceEntity>
|
||||||
|
|
||||||
|
private fun getQuery(enabledOnly: Boolean, order: SourcesSortOrder) = SimpleSQLiteQuery(
|
||||||
|
buildString {
|
||||||
|
append("SELECT * FROM sources ")
|
||||||
|
if (enabledOnly) {
|
||||||
|
append("WHERE enabled = 1 ")
|
||||||
|
}
|
||||||
|
append("ORDER BY pinned DESC, ")
|
||||||
|
append(getOrderBy(order))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
private fun getOrderBy(order: SourcesSortOrder) = when (order) {
|
private fun getOrderBy(order: SourcesSortOrder) = when (order) {
|
||||||
SourcesSortOrder.ALPHABETIC -> "source ASC"
|
SourcesSortOrder.ALPHABETIC -> "source ASC"
|
||||||
SourcesSortOrder.POPULARITY -> "(SELECT COUNT(*) FROM manga WHERE source = sources.source) DESC"
|
SourcesSortOrder.POPULARITY -> "(SELECT COUNT(*) FROM manga WHERE source = sources.source) DESC"
|
||||||
|
|||||||
@@ -299,6 +299,10 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
|||||||
get() = prefs.getInt(KEY_SOURCES_VERSION, 0)
|
get() = prefs.getInt(KEY_SOURCES_VERSION, 0)
|
||||||
set(value) = prefs.edit { putInt(KEY_SOURCES_VERSION, value) }
|
set(value) = prefs.edit { putInt(KEY_SOURCES_VERSION, value) }
|
||||||
|
|
||||||
|
var isAllSourcesEnabled: Boolean
|
||||||
|
get() = prefs.getBoolean(KEY_SOURCES_ENABLED_ALL, false)
|
||||||
|
set(value) = prefs.edit { putBoolean(KEY_SOURCES_ENABLED_ALL, value) }
|
||||||
|
|
||||||
val isPagesNumbersEnabled: Boolean
|
val isPagesNumbersEnabled: Boolean
|
||||||
get() = prefs.getBoolean(KEY_PAGES_NUMBERS, false)
|
get() = prefs.getBoolean(KEY_PAGES_NUMBERS, false)
|
||||||
|
|
||||||
@@ -717,6 +721,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
|||||||
const val KEY_FEED_HEADER = "feed_header"
|
const val KEY_FEED_HEADER = "feed_header"
|
||||||
const val KEY_SEARCH_SUGGESTION_TYPES = "search_suggest_types"
|
const val KEY_SEARCH_SUGGESTION_TYPES = "search_suggest_types"
|
||||||
const val KEY_SOURCES_VERSION = "sources_version"
|
const val KEY_SOURCES_VERSION = "sources_version"
|
||||||
|
const val KEY_SOURCES_ENABLED_ALL = "sources_enabled_all"
|
||||||
const val KEY_QUICK_FILTER = "quick_filter"
|
const val KEY_QUICK_FILTER = "quick_filter"
|
||||||
const val KEY_BACKUP_TG_ENABLED = "backup_periodic_tg_enabled"
|
const val KEY_BACKUP_TG_ENABLED = "backup_periodic_tg_enabled"
|
||||||
const val KEY_BACKUP_TG_CHAT = "backup_periodic_tg_chat_id"
|
const val KEY_BACKUP_TG_CHAT = "backup_periodic_tg_chat_id"
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import kotlinx.coroutines.flow.callbackFlow
|
|||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.conflate
|
import kotlinx.coroutines.flow.conflate
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onStart
|
import kotlinx.coroutines.flow.onStart
|
||||||
import org.koitharu.kotatsu.BuildConfig
|
import org.koitharu.kotatsu.BuildConfig
|
||||||
@@ -61,13 +62,14 @@ class MangaSourcesRepository @Inject constructor(
|
|||||||
suspend fun getEnabledSources(): List<MangaSource> {
|
suspend fun getEnabledSources(): List<MangaSource> {
|
||||||
assimilateNewSources()
|
assimilateNewSources()
|
||||||
val order = settings.sourcesSortOrder
|
val order = settings.sourcesSortOrder
|
||||||
return dao.findAllEnabled(order).toSources(settings.isNsfwContentDisabled, order).let { enabled ->
|
return dao.findAll(!settings.isAllSourcesEnabled, order).toSources(settings.isNsfwContentDisabled, order)
|
||||||
val external = getExternalSources()
|
.let { enabled ->
|
||||||
val list = ArrayList<MangaSourceInfo>(enabled.size + external.size)
|
val external = getExternalSources()
|
||||||
external.mapTo(list) { MangaSourceInfo(it, isEnabled = true, isPinned = true) }
|
val list = ArrayList<MangaSourceInfo>(enabled.size + external.size)
|
||||||
list.addAll(enabled)
|
external.mapTo(list) { MangaSourceInfo(it, isEnabled = true, isPinned = true) }
|
||||||
list
|
list.addAll(enabled)
|
||||||
}
|
list
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getPinnedSources(): Set<MangaSource> {
|
suspend fun getPinnedSources(): Set<MangaSource> {
|
||||||
@@ -85,6 +87,9 @@ class MangaSourcesRepository @Inject constructor(
|
|||||||
|
|
||||||
suspend fun getDisabledSources(): Set<MangaSource> {
|
suspend fun getDisabledSources(): Set<MangaSource> {
|
||||||
assimilateNewSources()
|
assimilateNewSources()
|
||||||
|
if (settings.isAllSourcesEnabled) {
|
||||||
|
return emptySet()
|
||||||
|
}
|
||||||
val result = EnumSet.copyOf(allMangaSources)
|
val result = EnumSet.copyOf(allMangaSources)
|
||||||
val enabled = dao.findAllEnabledNames()
|
val enabled = dao.findAllEnabledNames()
|
||||||
for (name in enabled) {
|
for (name in enabled) {
|
||||||
@@ -105,7 +110,7 @@ class MangaSourcesRepository @Inject constructor(
|
|||||||
): List<MangaParserSource> {
|
): List<MangaParserSource> {
|
||||||
assimilateNewSources()
|
assimilateNewSources()
|
||||||
val entities = dao.findAll().toMutableList()
|
val entities = dao.findAll().toMutableList()
|
||||||
if (isDisabledOnly) {
|
if (isDisabledOnly && !settings.isAllSourcesEnabled) {
|
||||||
entities.removeAll { it.isEnabled }
|
entities.removeAll { it.isEnabled }
|
||||||
}
|
}
|
||||||
if (isNewOnly) {
|
if (isNewOnly) {
|
||||||
@@ -141,7 +146,9 @@ class MangaSourcesRepository @Inject constructor(
|
|||||||
fun observeEnabledSourcesCount(): Flow<Int> {
|
fun observeEnabledSourcesCount(): Flow<Int> {
|
||||||
return combine(
|
return combine(
|
||||||
observeIsNsfwDisabled(),
|
observeIsNsfwDisabled(),
|
||||||
dao.observeEnabled(SourcesSortOrder.MANUAL),
|
observeAllEnabled().flatMapLatest { isAllSourcesEnabled ->
|
||||||
|
dao.observeAll(!isAllSourcesEnabled, SourcesSortOrder.MANUAL)
|
||||||
|
},
|
||||||
) { skipNsfw, sources ->
|
) { skipNsfw, sources ->
|
||||||
sources.count {
|
sources.count {
|
||||||
it.source.toMangaSourceOrNull()?.let { s -> !skipNsfw || !s.isNsfw() } == true
|
it.source.toMangaSourceOrNull()?.let { s -> !skipNsfw || !s.isNsfw() } == true
|
||||||
@@ -152,7 +159,9 @@ class MangaSourcesRepository @Inject constructor(
|
|||||||
fun observeAvailableSourcesCount(): Flow<Int> {
|
fun observeAvailableSourcesCount(): Flow<Int> {
|
||||||
return combine(
|
return combine(
|
||||||
observeIsNsfwDisabled(),
|
observeIsNsfwDisabled(),
|
||||||
dao.observeEnabled(SourcesSortOrder.MANUAL),
|
observeAllEnabled().flatMapLatest { isAllSourcesEnabled ->
|
||||||
|
dao.observeAll(!isAllSourcesEnabled, SourcesSortOrder.MANUAL)
|
||||||
|
},
|
||||||
) { skipNsfw, enabledSources ->
|
) { skipNsfw, enabledSources ->
|
||||||
val enabled = enabledSources.mapToSet { it.source }
|
val enabled = enabledSources.mapToSet { it.source }
|
||||||
allMangaSources.count { x ->
|
allMangaSources.count { x ->
|
||||||
@@ -163,9 +172,10 @@ class MangaSourcesRepository @Inject constructor(
|
|||||||
|
|
||||||
fun observeEnabledSources(): Flow<List<MangaSourceInfo>> = combine(
|
fun observeEnabledSources(): Flow<List<MangaSourceInfo>> = combine(
|
||||||
observeIsNsfwDisabled(),
|
observeIsNsfwDisabled(),
|
||||||
|
observeAllEnabled(),
|
||||||
observeSortOrder(),
|
observeSortOrder(),
|
||||||
) { skipNsfw, order ->
|
) { skipNsfw, allEnabled, order ->
|
||||||
dao.observeEnabled(order).map {
|
dao.observeAll(!allEnabled, order).map {
|
||||||
it.toSources(skipNsfw, order)
|
it.toSources(skipNsfw, order)
|
||||||
}
|
}
|
||||||
}.flattenLatest()
|
}.flattenLatest()
|
||||||
@@ -249,10 +259,11 @@ class MangaSourcesRepository @Inject constructor(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
var maxSortKey = dao.getMaxSortKey()
|
var maxSortKey = dao.getMaxSortKey()
|
||||||
|
val isAllEnabled = settings.isAllSourcesEnabled
|
||||||
val entities = new.map { x ->
|
val entities = new.map { x ->
|
||||||
MangaSourceEntity(
|
MangaSourceEntity(
|
||||||
source = x.name,
|
source = x.name,
|
||||||
isEnabled = false,
|
isEnabled = isAllEnabled,
|
||||||
sortKey = ++maxSortKey,
|
sortKey = ++maxSortKey,
|
||||||
addedIn = BuildConfig.VERSION_CODE,
|
addedIn = BuildConfig.VERSION_CODE,
|
||||||
lastUsedAt = 0,
|
lastUsedAt = 0,
|
||||||
@@ -355,6 +366,7 @@ class MangaSourcesRepository @Inject constructor(
|
|||||||
skipNsfwSources: Boolean,
|
skipNsfwSources: Boolean,
|
||||||
sortOrder: SourcesSortOrder?,
|
sortOrder: SourcesSortOrder?,
|
||||||
): MutableList<MangaSourceInfo> {
|
): MutableList<MangaSourceInfo> {
|
||||||
|
val isAllEnabled = settings.isAllSourcesEnabled
|
||||||
val result = ArrayList<MangaSourceInfo>(size)
|
val result = ArrayList<MangaSourceInfo>(size)
|
||||||
for (entity in this) {
|
for (entity in this) {
|
||||||
val source = entity.source.toMangaSourceOrNull() ?: continue
|
val source = entity.source.toMangaSourceOrNull() ?: continue
|
||||||
@@ -365,7 +377,7 @@ class MangaSourcesRepository @Inject constructor(
|
|||||||
result.add(
|
result.add(
|
||||||
MangaSourceInfo(
|
MangaSourceInfo(
|
||||||
mangaSource = source,
|
mangaSource = source,
|
||||||
isEnabled = entity.isEnabled,
|
isEnabled = entity.isEnabled || isAllEnabled,
|
||||||
isPinned = entity.isPinned,
|
isPinned = entity.isPinned,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -385,5 +397,9 @@ class MangaSourcesRepository @Inject constructor(
|
|||||||
sourcesSortOrder
|
sourcesSortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun observeAllEnabled() = settings.observeAsFlow(AppSettings.KEY_SOURCES_ENABLED_ALL) {
|
||||||
|
isAllSourcesEnabled
|
||||||
|
}
|
||||||
|
|
||||||
private fun String.toMangaSourceOrNull(): MangaParserSource? = MangaParserSource.entries.find { it.name == this }
|
private fun String.toMangaSourceOrNull(): MangaParserSource? = MangaParserSource.entries.find { it.name == this }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ class ExploreFragment :
|
|||||||
override fun onListHeaderClick(item: ListHeader, view: View) {
|
override fun onListHeaderClick(item: ListHeader, view: View) {
|
||||||
if (item.payload == R.id.nav_suggestions) {
|
if (item.payload == R.id.nav_suggestions) {
|
||||||
router.openSuggestions()
|
router.openSuggestions()
|
||||||
|
} else if (viewModel.isAllSourcesEnabled.value) {
|
||||||
|
router.openManageSources()
|
||||||
} else {
|
} else {
|
||||||
router.openSourcesCatalog()
|
router.openSourcesCatalog()
|
||||||
}
|
}
|
||||||
@@ -166,7 +168,8 @@ class ExploreFragment :
|
|||||||
menu.findItem(R.id.action_shortcut).isVisible = isSingleSelection
|
menu.findItem(R.id.action_shortcut).isVisible = isSingleSelection
|
||||||
menu.findItem(R.id.action_pin).isVisible = selectedSources.all { !it.isPinned }
|
menu.findItem(R.id.action_pin).isVisible = selectedSources.all { !it.isPinned }
|
||||||
menu.findItem(R.id.action_unpin).isVisible = selectedSources.all { it.isPinned }
|
menu.findItem(R.id.action_unpin).isVisible = selectedSources.all { it.isPinned }
|
||||||
menu.findItem(R.id.action_disable)?.isVisible = selectedSources.all { it.mangaSource is MangaParserSource }
|
menu.findItem(R.id.action_disable)?.isVisible = !viewModel.isAllSourcesEnabled.value &&
|
||||||
|
selectedSources.all { it.mangaSource is MangaParserSource }
|
||||||
menu.findItem(R.id.action_delete)?.isVisible = selectedSources.all { it.mangaSource is ExternalMangaSource }
|
menu.findItem(R.id.action_delete)?.isVisible = selectedSources.all { it.mangaSource is ExternalMangaSource }
|
||||||
return super.onPrepareActionMode(controller, mode, menu)
|
return super.onPrepareActionMode(controller, mode, menu)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
import kotlinx.coroutines.flow.mapLatest
|
import kotlinx.coroutines.flow.mapLatest
|
||||||
@@ -23,6 +22,7 @@ import org.koitharu.kotatsu.core.ui.BaseViewModel
|
|||||||
import org.koitharu.kotatsu.core.ui.util.ReversibleAction
|
import org.koitharu.kotatsu.core.ui.util.ReversibleAction
|
||||||
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
||||||
import org.koitharu.kotatsu.core.util.ext.call
|
import org.koitharu.kotatsu.core.util.ext.call
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.combine
|
||||||
import org.koitharu.kotatsu.explore.data.MangaSourcesRepository
|
import org.koitharu.kotatsu.explore.data.MangaSourcesRepository
|
||||||
import org.koitharu.kotatsu.explore.domain.ExploreRepository
|
import org.koitharu.kotatsu.explore.domain.ExploreRepository
|
||||||
import org.koitharu.kotatsu.explore.ui.model.ExploreButtons
|
import org.koitharu.kotatsu.explore.ui.model.ExploreButtons
|
||||||
@@ -54,6 +54,12 @@ class ExploreViewModel @Inject constructor(
|
|||||||
valueProducer = { isSourcesGridMode },
|
valueProducer = { isSourcesGridMode },
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val isAllSourcesEnabled = settings.observeAsStateFlow(
|
||||||
|
scope = viewModelScope + Dispatchers.IO,
|
||||||
|
key = AppSettings.KEY_SOURCES_ENABLED_ALL,
|
||||||
|
valueProducer = { isAllSourcesEnabled },
|
||||||
|
)
|
||||||
|
|
||||||
private val isSuggestionsEnabled = settings.observeAsFlow(
|
private val isSuggestionsEnabled = settings.observeAsFlow(
|
||||||
key = AppSettings.KEY_SUGGESTIONS,
|
key = AppSettings.KEY_SUGGESTIONS,
|
||||||
valueProducer = { isSuggestionsEnabled },
|
valueProducer = { isSuggestionsEnabled },
|
||||||
@@ -137,9 +143,10 @@ class ExploreViewModel @Inject constructor(
|
|||||||
getSuggestionFlow(),
|
getSuggestionFlow(),
|
||||||
isGrid,
|
isGrid,
|
||||||
isRandomLoading,
|
isRandomLoading,
|
||||||
|
isAllSourcesEnabled,
|
||||||
sourcesRepository.observeHasNewSourcesForBadge(),
|
sourcesRepository.observeHasNewSourcesForBadge(),
|
||||||
) { content, suggestions, grid, randomLoading, newSources ->
|
) { content, suggestions, grid, randomLoading, allSourcesEnabled, newSources ->
|
||||||
buildList(content, suggestions, grid, randomLoading, newSources)
|
buildList(content, suggestions, grid, randomLoading, allSourcesEnabled, newSources)
|
||||||
}.withErrorHandling()
|
}.withErrorHandling()
|
||||||
|
|
||||||
private fun buildList(
|
private fun buildList(
|
||||||
@@ -147,6 +154,7 @@ class ExploreViewModel @Inject constructor(
|
|||||||
recommendation: List<Manga>,
|
recommendation: List<Manga>,
|
||||||
isGrid: Boolean,
|
isGrid: Boolean,
|
||||||
randomLoading: Boolean,
|
randomLoading: Boolean,
|
||||||
|
allSourcesEnabled: Boolean,
|
||||||
hasNewSources: Boolean,
|
hasNewSources: Boolean,
|
||||||
): List<ListModel> {
|
): List<ListModel> {
|
||||||
val result = ArrayList<ListModel>(sources.size + 3)
|
val result = ArrayList<ListModel>(sources.size + 3)
|
||||||
@@ -158,8 +166,8 @@ class ExploreViewModel @Inject constructor(
|
|||||||
if (sources.isNotEmpty()) {
|
if (sources.isNotEmpty()) {
|
||||||
result += ListHeader(
|
result += ListHeader(
|
||||||
textRes = R.string.remote_sources,
|
textRes = R.string.remote_sources,
|
||||||
buttonTextRes = R.string.catalog,
|
buttonTextRes = if (allSourcesEnabled) R.string.manage else R.string.catalog,
|
||||||
badge = if (hasNewSources) "" else null,
|
badge = if (!allSourcesEnabled && hasNewSources) "" else null,
|
||||||
)
|
)
|
||||||
sources.mapTo(result) { MangaSourceItem(it, isGrid) }
|
sources.mapTo(result) { MangaSourceItem(it, isGrid) }
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class SourceSettingsFragment : BasePreferenceFragment(0), Preference.OnPreferenc
|
|||||||
val isValidSource = viewModel.repository !is EmptyMangaRepository
|
val isValidSource = viewModel.repository !is EmptyMangaRepository
|
||||||
|
|
||||||
findPreference<SwitchPreferenceCompat>(KEY_ENABLE)?.run {
|
findPreference<SwitchPreferenceCompat>(KEY_ENABLE)?.run {
|
||||||
isVisible = isValidSource
|
isVisible = isValidSource && !settings.isAllSourcesEnabled
|
||||||
onPreferenceChangeListener = this@SourceSettingsFragment
|
onPreferenceChangeListener = this@SourceSettingsFragment
|
||||||
}
|
}
|
||||||
findPreference<Preference>(KEY_AUTH)?.run {
|
findPreference<Preference>(KEY_AUTH)?.run {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.koitharu.kotatsu.settings.sources
|
package org.koitharu.kotatsu.settings.sources
|
||||||
|
|
||||||
|
import android.content.SharedPreferences
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
@@ -17,7 +18,8 @@ import org.koitharu.kotatsu.explore.data.SourcesSortOrder
|
|||||||
import org.koitharu.kotatsu.parsers.util.names
|
import org.koitharu.kotatsu.parsers.util.names
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class SourcesSettingsFragment : BasePreferenceFragment(R.string.remote_sources) {
|
class SourcesSettingsFragment : BasePreferenceFragment(R.string.remote_sources),
|
||||||
|
SharedPreferences.OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
private val viewModel by viewModels<SourcesSettingsViewModel>()
|
private val viewModel by viewModels<SourcesSettingsViewModel>()
|
||||||
|
|
||||||
@@ -43,10 +45,10 @@ class SourcesSettingsFragment : BasePreferenceFragment(R.string.remote_sources)
|
|||||||
}
|
}
|
||||||
findPreference<Preference>(AppSettings.KEY_SOURCES_CATALOG)?.let { pref ->
|
findPreference<Preference>(AppSettings.KEY_SOURCES_CATALOG)?.let { pref ->
|
||||||
viewModel.availableSourcesCount.observe(viewLifecycleOwner) {
|
viewModel.availableSourcesCount.observe(viewLifecycleOwner) {
|
||||||
pref.summary = if (it >= 0) {
|
pref.summary = when {
|
||||||
getString(R.string.available_d, it)
|
it == 0 -> getString(R.string.all_sources_enabled)
|
||||||
} else {
|
it > 0 -> getString(R.string.available_d, it)
|
||||||
null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -55,6 +57,13 @@ class SourcesSettingsFragment : BasePreferenceFragment(R.string.remote_sources)
|
|||||||
pref.isChecked = it
|
pref.isChecked = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
updateEnableAllDependencies()
|
||||||
|
settings.subscribe(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
settings.unsubscribe(this)
|
||||||
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPreferenceTreeClick(preference: Preference): Boolean = when (preference.key) {
|
override fun onPreferenceTreeClick(preference: Preference): Boolean = when (preference.key) {
|
||||||
@@ -70,4 +79,14 @@ class SourcesSettingsFragment : BasePreferenceFragment(R.string.remote_sources)
|
|||||||
|
|
||||||
else -> super.onPreferenceTreeClick(preference)
|
else -> super.onPreferenceTreeClick(preference)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||||
|
when (key) {
|
||||||
|
AppSettings.KEY_SOURCES_ENABLED_ALL -> updateEnableAllDependencies()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateEnableAllDependencies() {
|
||||||
|
findPreference<Preference>(AppSettings.KEY_SOURCES_CATALOG)?.isEnabled = !settings.isAllSourcesEnabled
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ fun sourceConfigItemDelegate2(
|
|||||||
bind {
|
bind {
|
||||||
binding.textViewTitle.text = item.source.getTitle(context)
|
binding.textViewTitle.text = item.source.getTitle(context)
|
||||||
binding.imageViewAdd.isGone = item.isEnabled || !item.isAvailable
|
binding.imageViewAdd.isGone = item.isEnabled || !item.isAvailable
|
||||||
binding.imageViewRemove.isVisible = item.isEnabled
|
binding.imageViewRemove.isVisible = item.isEnabled && item.isDisableAvailable
|
||||||
binding.imageViewMenu.isVisible = item.isEnabled
|
binding.imageViewMenu.isVisible = item.isEnabled
|
||||||
binding.textViewTitle.drawableStart = if (item.isPinned) iconPinned else null
|
binding.textViewTitle.drawableStart = if (item.isPinned) iconPinned else null
|
||||||
binding.textViewDescription.text = item.source.getSummary(context)
|
binding.textViewDescription.text = item.source.getSummary(context)
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ class SourcesListProducer @Inject constructor(
|
|||||||
val pinned = repository.getPinnedSources().mapToSet { it.name }
|
val pinned = repository.getPinnedSources().mapToSet { it.name }
|
||||||
val isNsfwDisabled = settings.isNsfwContentDisabled
|
val isNsfwDisabled = settings.isNsfwContentDisabled
|
||||||
val isReorderAvailable = settings.sourcesSortOrder == SourcesSortOrder.MANUAL
|
val isReorderAvailable = settings.sourcesSortOrder == SourcesSortOrder.MANUAL
|
||||||
|
val isDisableAvailable = !settings.isAllSourcesEnabled
|
||||||
val withTip = isReorderAvailable && settings.isTipEnabled(TIP_REORDER)
|
val withTip = isReorderAvailable && settings.isTipEnabled(TIP_REORDER)
|
||||||
val enabledSet = enabledSources.toSet()
|
val enabledSet = enabledSources.toSet()
|
||||||
if (query.isNotEmpty()) {
|
if (query.isNotEmpty()) {
|
||||||
@@ -83,6 +84,7 @@ class SourcesListProducer @Inject constructor(
|
|||||||
isDraggable = false,
|
isDraggable = false,
|
||||||
isAvailable = !isNsfwDisabled || !it.isNsfw(),
|
isAvailable = !isNsfwDisabled || !it.isNsfw(),
|
||||||
isPinned = it.name in pinned,
|
isPinned = it.name in pinned,
|
||||||
|
isDisableAvailable = isDisableAvailable,
|
||||||
)
|
)
|
||||||
}.ifEmpty {
|
}.ifEmpty {
|
||||||
listOf(SourceConfigItem.EmptySearchResult)
|
listOf(SourceConfigItem.EmptySearchResult)
|
||||||
@@ -104,6 +106,7 @@ class SourcesListProducer @Inject constructor(
|
|||||||
isDraggable = isReorderAvailable,
|
isDraggable = isReorderAvailable,
|
||||||
isAvailable = false,
|
isAvailable = false,
|
||||||
isPinned = it.name in pinned,
|
isPinned = it.name in pinned,
|
||||||
|
isDisableAvailable = isDisableAvailable,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,6 +171,8 @@ class SourcesManageFragment :
|
|||||||
override fun onPrepareMenu(menu: Menu) {
|
override fun onPrepareMenu(menu: Menu) {
|
||||||
super.onPrepareMenu(menu)
|
super.onPrepareMenu(menu)
|
||||||
menu.findItem(R.id.action_no_nsfw).isChecked = settings.isNsfwContentDisabled
|
menu.findItem(R.id.action_no_nsfw).isChecked = settings.isNsfwContentDisabled
|
||||||
|
menu.findItem(R.id.action_disable_all).isVisible = !settings.isAllSourcesEnabled
|
||||||
|
menu.findItem(R.id.action_catalog).isVisible = !settings.isAllSourcesEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ sealed interface SourceConfigItem : ListModel {
|
|||||||
val isDraggable: Boolean,
|
val isDraggable: Boolean,
|
||||||
val isAvailable: Boolean,
|
val isAvailable: Boolean,
|
||||||
val isPinned: Boolean,
|
val isPinned: Boolean,
|
||||||
|
val isDisableAvailable: Boolean,
|
||||||
) : SourceConfigItem {
|
) : SourceConfigItem {
|
||||||
|
|
||||||
val isNsfw: Boolean
|
val isNsfw: Boolean
|
||||||
|
|||||||
12
app/src/main/res/drawable/ic_disable.xml
Normal file
12
app/src/main/res/drawable/ic_disable.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:pathData="M22.11 21.46L2.39 1.73L1.11 3L4.06 5.95C2.78 7.63 2 9.72 2 12C2 17.5 6.5 22 12 22C14.28 22 16.37 21.23 18.05 19.94L20.84 22.73L22.11 21.46M12 20C7.58 20 4 16.42 4 12C4 10.27 4.56 8.68 5.5 7.38L16.62 18.5C15.32 19.45 13.73 20 12 20M8.17 4.97L6.72 3.5C8.25 2.56 10.06 2 12 2C17.5 2 22 6.5 22 12C22 13.94 21.44 15.75 20.5 17.28L19.03 15.83C19.65 14.69 20 13.39 20 12C20 7.58 16.42 4 12 4C10.61 4 9.31 4.35 8.17 4.97Z" />
|
||||||
|
</vector>
|
||||||
@@ -82,10 +82,10 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?selectableItemBackgroundBorderless"
|
android:background="?selectableItemBackgroundBorderless"
|
||||||
android:contentDescription="@string/remove"
|
android:contentDescription="@string/disable"
|
||||||
android:padding="@dimen/margin_small"
|
android:padding="@dimen/margin_small"
|
||||||
android:scaleType="center"
|
android:scaleType="center"
|
||||||
android:src="@drawable/ic_delete"
|
android:src="@drawable/ic_disable"
|
||||||
android:tooltipText="@string/remove" />
|
android:tooltipText="@string/disable" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -789,4 +789,7 @@
|
|||||||
<string name="open_telegram_bot_summary">Press to open chat with Kotatsu Backup Bot</string>
|
<string name="open_telegram_bot_summary">Press to open chat with Kotatsu Backup Bot</string>
|
||||||
<string name="clear_database">Clear database</string>
|
<string name="clear_database">Clear database</string>
|
||||||
<string name="clear_database_summary">Delete information about manga that is not used</string>
|
<string name="clear_database_summary">Delete information about manga that is not used</string>
|
||||||
|
<string name="enable_all_sources">Enable all manga sources</string>
|
||||||
|
<string name="enable_all_sources_summary">All available manga sources will be enabled permanently</string>
|
||||||
|
<string name="all_sources_enabled">All sources are enabled</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -20,11 +20,17 @@
|
|||||||
android:persistent="false"
|
android:persistent="false"
|
||||||
android:title="@string/manage_sources" />
|
android:title="@string/manage_sources" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="sources_enabled_all"
|
||||||
|
android:summary="@string/enable_all_sources_summary"
|
||||||
|
android:title="@string/enable_all_sources"
|
||||||
|
app:allowDividerAbove="true" />
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="sources_catalog"
|
android:key="sources_catalog"
|
||||||
android:persistent="false"
|
android:persistent="false"
|
||||||
android:title="@string/sources_catalog"
|
android:title="@string/sources_catalog" />
|
||||||
app:allowDividerAbove="true" />
|
|
||||||
|
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
|
|||||||
Reference in New Issue
Block a user