From fee35cceabc6d96cd3ba15584ce72673cdf80442 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sun, 12 Nov 2023 16:30:11 +0200 Subject: [PATCH] Sources settings screen --- .../kotatsu/core/db/dao/MangaSourcesDao.kt | 40 ++++++++++-- .../kotatsu/core/prefs/AppSettings.kt | 7 +++ .../explore/data/MangaSourcesRepository.kt | 50 ++++++++++++--- .../kotatsu/explore/data/SourcesSortOrder.kt | 12 ++++ .../kotatsu/explore/ui/ExploreFragment.kt | 3 +- .../kotatsu/explore/ui/ExploreMenuProvider.kt | 13 +--- .../kotatsu/explore/ui/ExploreViewModel.kt | 9 ++- .../kotatsu/settings/RootSettingsViewModel.kt | 4 +- .../kotatsu/settings/SettingsActivity.kt | 9 ++- .../sources/SourcesSettingsFragment.kt | 63 +++++++++++++++++++ .../sources/SourcesSettingsViewModel.kt | 25 ++++++++ .../adapter/SourceConfigAdapterDelegates.kt | 1 + .../sources/catalog/SourcesCatalogActivity.kt | 6 +- .../catalog/SourcesCatalogViewModel.kt | 4 ++ .../{ => manage}/SourcesListProducer.kt | 8 ++- .../{ => manage}/SourcesManageFragment.kt | 3 +- .../{ => manage}/SourcesManageViewModel.kt | 2 +- app/src/main/res/menu/opt_explore.xml | 6 -- app/src/main/res/values/strings.xml | 3 + app/src/main/res/xml/pref_appearance.xml | 15 ----- app/src/main/res/xml/pref_root.xml | 2 +- app/src/main/res/xml/pref_sources.xml | 40 ++++++++++++ 22 files changed, 262 insertions(+), 63 deletions(-) create mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/explore/data/SourcesSortOrder.kt create mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesSettingsFragment.kt create mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesSettingsViewModel.kt rename app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/{ => manage}/SourcesListProducer.kt (90%) rename app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/{ => manage}/SourcesManageFragment.kt (98%) rename app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/{ => manage}/SourcesManageViewModel.kt (98%) create mode 100644 app/src/main/res/xml/pref_sources.xml diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/db/dao/MangaSourcesDao.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/db/dao/MangaSourcesDao.kt index 59876bc98..9401d5030 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/db/dao/MangaSourcesDao.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/db/dao/MangaSourcesDao.kt @@ -4,10 +4,15 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query +import androidx.room.RawQuery import androidx.room.Transaction import androidx.room.Upsert +import androidx.sqlite.db.SimpleSQLiteQuery +import androidx.sqlite.db.SupportSQLiteQuery import kotlinx.coroutines.flow.Flow +import org.intellij.lang.annotations.Language import org.koitharu.kotatsu.core.db.entity.MangaSourceEntity +import org.koitharu.kotatsu.explore.data.SourcesSortOrder @Dao abstract class MangaSourcesDao { @@ -15,14 +20,11 @@ abstract class MangaSourcesDao { @Query("SELECT * FROM sources ORDER BY sort_key") abstract suspend fun findAll(): List - @Query("SELECT * FROM sources WHERE enabled = 1 ORDER BY sort_key") - abstract suspend fun findAllEnabled(): List - @Query("SELECT * FROM sources WHERE enabled = 0 ORDER BY sort_key") abstract suspend fun findAllDisabled(): List - @Query("SELECT * FROM sources WHERE enabled = 1 ORDER BY sort_key") - abstract fun observeEnabled(): Flow> + @Query("SELECT * FROM sources WHERE enabled = 0") + abstract fun observeDisabled(): Flow> @Query("SELECT * FROM sources ORDER BY sort_key") abstract fun observeAll(): Flow> @@ -43,6 +45,22 @@ abstract class MangaSourcesDao { @Upsert abstract suspend fun upsert(entry: MangaSourceEntity) + fun observeEnabled(order: SourcesSortOrder): Flow> { + val orderBy = getOrderBy(order) + + @Language("RoomSql") + val query = SimpleSQLiteQuery("SELECT * FROM sources WHERE enabled = 1 ORDER BY $orderBy") + return observeImpl(query) + } + + suspend fun findAllEnabled(order: SourcesSortOrder): List { + val orderBy = getOrderBy(order) + + @Language("RoomSql") + val query = SimpleSQLiteQuery("SELECT * FROM sources WHERE enabled = 1 ORDER BY $orderBy") + return findAllImpl(query) + } + @Transaction open suspend fun setEnabled(source: String, isEnabled: Boolean) { if (updateIsEnabled(source, isEnabled) == 0) { @@ -57,4 +75,16 @@ abstract class MangaSourcesDao { @Query("UPDATE sources SET enabled = :isEnabled WHERE source = :source") protected abstract suspend fun updateIsEnabled(source: String, isEnabled: Boolean): Int + + @RawQuery(observedEntities = [MangaSourceEntity::class]) + protected abstract fun observeImpl(query: SupportSQLiteQuery): Flow> + + @RawQuery + protected abstract suspend fun findAllImpl(query: SupportSQLiteQuery): List + + private fun getOrderBy(order: SourcesSortOrder) = when (order) { + SourcesSortOrder.ALPHABETIC -> "source ASC" + SourcesSortOrder.POPULARITY -> "(SELECT COUNT(*) FROM manga WHERE source = sources.source) DESC" + SourcesSortOrder.MANUAL -> "sort_key ASC" + } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt index 14ae7bf61..cf527c6df 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/AppSettings.kt @@ -22,6 +22,7 @@ import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.putEnumValue import org.koitharu.kotatsu.core.util.ext.takeIfReadable import org.koitharu.kotatsu.core.util.ext.toUriOrNull +import org.koitharu.kotatsu.explore.data.SourcesSortOrder import org.koitharu.kotatsu.list.domain.ListSortOrder import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.parsers.util.find @@ -209,6 +210,10 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) { return policy.isNetworkAllowed(connectivityManager) } + var sourcesSortOrder: SourcesSortOrder + get() = prefs.getEnumValue(KEY_SOURCES_ORDER, SourcesSortOrder.MANUAL) + set(value) = prefs.edit { putEnumValue(KEY_SOURCES_ORDER, value) } + var isSourcesGridMode: Boolean get() = prefs.getBoolean(KEY_SOURCES_GRID, false) set(value) = prefs.edit { putBoolean(KEY_SOURCES_GRID, value) } @@ -528,6 +533,8 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) { const val KEY_RELATED_MANGA = "related_manga" const val KEY_NAV_MAIN = "nav_main" const val KEY_32BIT_COLOR = "enhanced_colors" + const val KEY_SOURCES_ORDER = "sources_sort_order" + const val KEY_SOURCES_CATALOG = "sources_catalog" // About const val KEY_APP_UPDATE = "app_update" diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/MangaSourcesRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/MangaSourcesRepository.kt index 9ba481839..6ec61efcd 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/MangaSourcesRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/MangaSourcesRepository.kt @@ -19,6 +19,7 @@ import org.koitharu.kotatsu.core.prefs.observeAsFlow import org.koitharu.kotatsu.core.ui.util.ReversibleHandle import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.util.mapToSet import java.util.Collections import java.util.EnumSet import javax.inject.Inject @@ -43,19 +44,44 @@ class MangaSourcesRepository @Inject constructor( get() = Collections.unmodifiableSet(remoteSources) suspend fun getEnabledSources(): List { - return dao.findAllEnabled().toSources(settings.isNsfwContentDisabled) + val order = settings.sourcesSortOrder + return dao.findAllEnabled(order).toSources(settings.isNsfwContentDisabled, order) } suspend fun getDisabledSources(): List { - return dao.findAllDisabled().toSources(settings.isNsfwContentDisabled) + return dao.findAllDisabled().toSources(settings.isNsfwContentDisabled, null) } - fun observeEnabledSources(): Flow> = observeIsNsfwDisabled().flatMapLatest { skipNsfw -> - dao.observeEnabled().map { - it.toSources(skipNsfw) - } + fun observeEnabledSourcesCount(): Flow { + return combine( + observeIsNsfwDisabled(), + dao.observeEnabled(SourcesSortOrder.MANUAL), + ) { skipNsfw, sources -> + sources.count { skipNsfw || !MangaSource(it.source).isNsfw() } + }.distinctUntilChanged() } + fun observeAvailableSourcesCount(): Flow { + return combine( + observeIsNsfwDisabled(), + dao.observeEnabled(SourcesSortOrder.MANUAL), + ) { skipNsfw, enabledSources -> + val enabled = enabledSources.mapToSet { it.source } + allMangaSources.count { x -> + x.name !in enabled && (!skipNsfw || !x.isNsfw()) + } + }.distinctUntilChanged() + } + + fun observeEnabledSources(): Flow> = combine( + observeIsNsfwDisabled(), + observeSortOrder(), + ) { skipNsfw, order -> + dao.observeEnabled(order).map { + it.toSources(skipNsfw, order) + } + }.flatMapLatest { it } + fun observeAll(): Flow>> = dao.observeAll().map { entities -> val result = ArrayList>(entities.size) for (entity in entities) { @@ -150,7 +176,10 @@ class MangaSourcesRepository @Inject constructor( return result } - private fun List.toSources(skipNsfwSources: Boolean): List { + private fun List.toSources( + skipNsfwSources: Boolean, + sortOrder: SourcesSortOrder?, + ): List { val result = ArrayList(size) for (entity in this) { val source = MangaSource(entity.source) @@ -161,6 +190,9 @@ class MangaSourcesRepository @Inject constructor( result.add(source) } } + if (sortOrder == SourcesSortOrder.ALPHABETIC) { + result.sortBy { it.title } + } return result } @@ -171,4 +203,8 @@ class MangaSourcesRepository @Inject constructor( private fun observeIsNewSourcesEnabled() = settings.observeAsFlow(AppSettings.KEY_SOURCES_NEW) { isNewSourcesTipEnabled } + + private fun observeSortOrder() = settings.observeAsFlow(AppSettings.KEY_SOURCES_ORDER) { + sourcesSortOrder + } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/SourcesSortOrder.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/SourcesSortOrder.kt new file mode 100644 index 000000000..9c42be758 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/SourcesSortOrder.kt @@ -0,0 +1,12 @@ +package org.koitharu.kotatsu.explore.data + +import androidx.annotation.StringRes +import org.koitharu.kotatsu.R + +enum class SourcesSortOrder( + @StringRes val titleResId: Int, +) { + ALPHABETIC(R.string.by_name), + POPULARITY(R.string.popular), + MANUAL(R.string.manual), +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt index 3632bf345..ef3bc0a5e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt @@ -85,7 +85,7 @@ class ExploreFragment : SpanSizeResolver(this, resources.getDimensionPixelSize(R.dimen.explore_grid_width)).attach() addItemDecoration(TypedListSpacingDecoration(context, false)) } - addMenuProvider(ExploreMenuProvider(binding.root.context, viewModel)) + addMenuProvider(ExploreMenuProvider(binding.root.context)) viewModel.content.observe(viewLifecycleOwner) { exploreAdapter?.items = it } @@ -176,7 +176,6 @@ class ExploreFragment : } else { LinearLayoutManager(requireContext()) } - activity?.invalidateOptionsMenu() } private fun showSuggestionsTip() { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreMenuProvider.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreMenuProvider.kt index 96689878b..86211b090 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreMenuProvider.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreMenuProvider.kt @@ -10,7 +10,6 @@ import org.koitharu.kotatsu.settings.SettingsActivity class ExploreMenuProvider( private val context: Context, - private val viewModel: ExploreViewModel, ) : MenuProvider { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { @@ -19,22 +18,12 @@ class ExploreMenuProvider( override fun onMenuItemSelected(menuItem: MenuItem): Boolean { return when (menuItem.itemId) { - R.id.action_grid -> { - viewModel.setGridMode(!menuItem.isChecked) - true - } - R.id.action_manage -> { - context.startActivity(SettingsActivity.newManageSourcesIntent(context)) + context.startActivity(SettingsActivity.newSourcesSettingsIntent(context)) true } else -> false } } - - override fun onPrepareMenu(menu: Menu) { - super.onPrepareMenu(menu) - menu.findItem(R.id.action_grid)?.isChecked = viewModel.isGrid.value == true - } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreViewModel.kt index 3beb078ef..218df582d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreViewModel.kt @@ -21,6 +21,7 @@ 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 @@ -50,11 +51,13 @@ class ExploreViewModel @Inject constructor( valueProducer = { isSourcesGridMode }, ) - val isSuggestionsEnabled = settings.observeAsFlow( + private val isSuggestionsEnabled = settings.observeAsFlow( key = AppSettings.KEY_SUGGESTIONS, valueProducer = { isSuggestionsEnabled }, ) + val sortOrder = MutableStateFlow(SourcesSortOrder.MANUAL) // TODO + val onOpenManga = MutableEventFlow() val onActionDone = MutableEventFlow() val onShowSuggestionsTip = MutableEventFlow() @@ -104,10 +107,6 @@ class ExploreViewModel @Inject constructor( } } - fun setGridMode(value: Boolean) { - settings.isSourcesGridMode = value - } - fun respondSuggestionTip(isAccepted: Boolean) { settings.isSuggestionsEnabled = isAccepted settings.closeTip(TIP_SUGGESTIONS) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/RootSettingsViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/RootSettingsViewModel.kt index 9ede5f2f8..b997b8236 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/RootSettingsViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/RootSettingsViewModel.kt @@ -4,7 +4,6 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.plus import org.koitharu.kotatsu.core.ui.BaseViewModel @@ -18,7 +17,6 @@ class RootSettingsViewModel @Inject constructor( val totalSourcesCount = sourcesRepository.allMangaSources.size - val enabledSourcesCount = sourcesRepository.observeEnabledSources() - .map { it.size } + val enabledSourcesCount = sourcesRepository.observeEnabledSourcesCount() .stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, -1) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt index c97316613..82ba17efa 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt @@ -31,7 +31,8 @@ import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.settings.about.AboutSettingsFragment import org.koitharu.kotatsu.settings.about.AppUpdateDialog import org.koitharu.kotatsu.settings.sources.SourceSettingsFragment -import org.koitharu.kotatsu.settings.sources.SourcesManageFragment +import org.koitharu.kotatsu.settings.sources.SourcesSettingsFragment +import org.koitharu.kotatsu.settings.sources.manage.SourcesManageFragment import org.koitharu.kotatsu.settings.tracker.TrackerSettingsFragment import org.koitharu.kotatsu.settings.userdata.UserDataSettingsFragment @@ -153,6 +154,7 @@ class SettingsActivity : ACTION_SUGGESTIONS -> SuggestionsSettingsFragment() ACTION_HISTORY -> UserDataSettingsFragment() ACTION_TRACKER -> TrackerSettingsFragment() + ACTION_SOURCES -> SourcesSettingsFragment() ACTION_MANAGE_DOWNLOADS -> DownloadsSettingsFragment() ACTION_SOURCE -> SourceSettingsFragment.newInstance( intent.getSerializableExtraCompat(EXTRA_SOURCE) as? MangaSource ?: MangaSource.LOCAL, @@ -182,6 +184,7 @@ class SettingsActivity : private const val ACTION_TRACKER = "${BuildConfig.APPLICATION_ID}.action.MANAGE_TRACKER" private const val ACTION_HISTORY = "${BuildConfig.APPLICATION_ID}.action.MANAGE_HISTORY" private const val ACTION_SOURCE = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCE_SETTINGS" + private const val ACTION_SOURCES = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCES" private const val ACTION_MANAGE_SOURCES = "${BuildConfig.APPLICATION_ID}.action.MANAGE_SOURCES_LIST" private const val ACTION_MANAGE_DOWNLOADS = "${BuildConfig.APPLICATION_ID}.action.MANAGE_DOWNLOADS" private const val EXTRA_SOURCE = "source" @@ -206,6 +209,10 @@ class SettingsActivity : Intent(context, SettingsActivity::class.java) .setAction(ACTION_HISTORY) + fun newSourcesSettingsIntent(context: Context) = + Intent(context, SettingsActivity::class.java) + .setAction(ACTION_SOURCES) + fun newManageSourcesIntent(context: Context) = Intent(context, SettingsActivity::class.java) .setAction(ACTION_MANAGE_SOURCES) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesSettingsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesSettingsFragment.kt new file mode 100644 index 000000000..c4f4e7a83 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesSettingsFragment.kt @@ -0,0 +1,63 @@ +package org.koitharu.kotatsu.settings.sources + +import android.content.Intent +import android.os.Bundle +import android.view.View +import androidx.fragment.app.viewModels +import androidx.preference.ListPreference +import androidx.preference.Preference +import dagger.hilt.android.AndroidEntryPoint +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.core.ui.BasePreferenceFragment +import org.koitharu.kotatsu.core.util.ext.observe +import org.koitharu.kotatsu.core.util.ext.setDefaultValueCompat +import org.koitharu.kotatsu.explore.data.SourcesSortOrder +import org.koitharu.kotatsu.parsers.util.names +import org.koitharu.kotatsu.settings.sources.catalog.SourcesCatalogActivity + +@AndroidEntryPoint +class SourcesSettingsFragment : BasePreferenceFragment(R.string.remote_sources) { + + private val viewModel by viewModels() + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + addPreferencesFromResource(R.xml.pref_sources) + findPreference(AppSettings.KEY_SOURCES_ORDER)?.run { + entryValues = SourcesSortOrder.entries.names() + entries = SourcesSortOrder.entries.map { context.getString(it.titleResId) }.toTypedArray() + setDefaultValueCompat(SourcesSortOrder.MANUAL.name) + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + findPreference(AppSettings.KEY_REMOTE_SOURCES)?.let { pref -> + viewModel.enabledSourcesCount.observe(viewLifecycleOwner) { + pref.summary = if (it >= 0) { + resources.getQuantityString(R.plurals.items, it, it) + } else { + null + } + } + } + findPreference(AppSettings.KEY_SOURCES_CATALOG)?.let { pref -> + viewModel.availableSourcesCount.observe(viewLifecycleOwner) { + pref.summary = if (it >= 0) { + getString(R.string.available_d, it) + } else { + null + } + } + } + } + + override fun onPreferenceTreeClick(preference: Preference): Boolean = when (preference.key) { + AppSettings.KEY_SOURCES_CATALOG -> { + startActivity(Intent(preference.context, SourcesCatalogActivity::class.java)) + true + } + + else -> super.onPreferenceTreeClick(preference) + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesSettingsViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesSettingsViewModel.kt new file mode 100644 index 000000000..f6cb40bba --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesSettingsViewModel.kt @@ -0,0 +1,25 @@ +package org.koitharu.kotatsu.settings.sources + +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.plus +import org.koitharu.kotatsu.core.ui.BaseViewModel +import org.koitharu.kotatsu.explore.data.MangaSourcesRepository +import javax.inject.Inject + +@HiltViewModel +class SourcesSettingsViewModel @Inject constructor( + private val sourcesRepository: MangaSourcesRepository, +) : BaseViewModel() { + + val totalSourcesCount = sourcesRepository.allMangaSources.size + + val enabledSourcesCount = sourcesRepository.observeEnabledSourcesCount() + .stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, -1) + + val availableSourcesCount = sourcesRepository.observeAvailableSourcesCount() + .stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, -1) +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt index e3b055b40..198567789 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/adapter/SourceConfigAdapterDelegates.kt @@ -169,6 +169,7 @@ private fun showSourceMenu( menu.inflate(R.menu.popup_source_config) menu.menu.findItem(R.id.action_shortcut) ?.isVisible = ShortcutManagerCompat.isRequestPinShortcutSupported(anchor.context) + menu.menu.findItem(R.id.action_lift)?.isVisible = item.isDraggable menu.setOnMenuItemClickListener { when (it.itemId) { R.id.action_settings -> listener.onItemSettingsClick(item) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt index e23c47c99..7c3900100 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt @@ -74,7 +74,7 @@ class SourcesCatalogActivity : BaseActivity(), } override fun onTabSelected(tab: TabLayout.Tab) { - viewModel.setContentType(ContentType.entries[tab.position]) + viewModel.setContentType(tab.tag as ContentType) } override fun onTabUnselected(tab: TabLayout.Tab) = Unit @@ -86,8 +86,12 @@ class SourcesCatalogActivity : BaseActivity(), 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) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogViewModel.kt index 9eb9b763f..9a6f864f2 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogViewModel.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.stateIn import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.ui.util.ReversibleAction import org.koitharu.kotatsu.core.util.LocaleComparator @@ -28,6 +29,7 @@ import javax.inject.Inject class SourcesCatalogViewModel @Inject constructor( private val repository: MangaSourcesRepository, private val listProducerFactory: SourcesCatalogListProducer.Factory, + private val settings: AppSettings, ) : BaseViewModel() { private val lifecycle = RetainedLifecycleImpl() @@ -37,6 +39,8 @@ class SourcesCatalogViewModel @Inject constructor( val locales = getLocalesImpl() val locale = MutableStateFlow(locales.firstOrNull()?.language) + val isNsfwDisabled = settings.isNsfwContentDisabled + private val listProducer: StateFlow = combine( locale, contentType, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesListProducer.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesListProducer.kt similarity index 90% rename from app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesListProducer.kt rename to app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesListProducer.kt index 6dad709e8..c519807b4 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesListProducer.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesListProducer.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.settings.sources +package org.koitharu.kotatsu.settings.sources.manage import androidx.room.InvalidationTracker import dagger.hilt.android.ViewModelLifecycle @@ -19,6 +19,7 @@ import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.util.ext.lifecycleScope import org.koitharu.kotatsu.core.util.ext.toEnumSet import org.koitharu.kotatsu.explore.data.MangaSourcesRepository +import org.koitharu.kotatsu.explore.data.SourcesSortOrder import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem import javax.inject.Inject @@ -61,7 +62,8 @@ class SourcesListProducer @Inject constructor( private suspend fun buildList(): List { val enabledSources = repository.getEnabledSources() val isNsfwDisabled = settings.isNsfwContentDisabled - val withTip = settings.isTipEnabled(TIP_REORDER) + val isReorderAvailable = settings.sourcesSortOrder == SourcesSortOrder.MANUAL + val withTip = isReorderAvailable && settings.isTipEnabled(TIP_REORDER) val enabledSet = enabledSources.toEnumSet() if (query.isNotEmpty()) { return enabledSources.mapNotNull { @@ -91,7 +93,7 @@ class SourcesListProducer @Inject constructor( SourceConfigItem.SourceItem( source = it, isEnabled = true, - isDraggable = true, + isDraggable = isReorderAvailable, isAvailable = false, ) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesManageFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesManageFragment.kt similarity index 98% rename from app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesManageFragment.kt rename to app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesManageFragment.kt index 1cc4083f3..059824fce 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesManageFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesManageFragment.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.settings.sources +package org.koitharu.kotatsu.settings.sources.manage import android.content.Intent import android.os.Bundle @@ -31,6 +31,7 @@ import org.koitharu.kotatsu.core.util.ext.viewLifecycleScope import org.koitharu.kotatsu.databinding.FragmentSettingsSourcesBinding import org.koitharu.kotatsu.main.ui.owners.AppBarOwner import org.koitharu.kotatsu.settings.SettingsActivity +import org.koitharu.kotatsu.settings.sources.SourceSettingsFragment import org.koitharu.kotatsu.settings.sources.adapter.SourceConfigAdapter import org.koitharu.kotatsu.settings.sources.adapter.SourceConfigListener import org.koitharu.kotatsu.settings.sources.catalog.SourcesCatalogActivity diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesManageViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesManageViewModel.kt similarity index 98% rename from app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesManageViewModel.kt rename to app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesManageViewModel.kt index 9ca2654b2..5ad9d147c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourcesManageViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesManageViewModel.kt @@ -1,4 +1,4 @@ -package org.koitharu.kotatsu.settings.sources +package org.koitharu.kotatsu.settings.sources.manage import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers diff --git a/app/src/main/res/menu/opt_explore.xml b/app/src/main/res/menu/opt_explore.xml index 63a51f4ea..82fb8d9a1 100644 --- a/app/src/main/res/menu/opt_explore.xml +++ b/app/src/main/res/menu/opt_explore.xml @@ -2,12 +2,6 @@ - - No available manga sources found by your query Catalog Manage sources + Manual + Available: %1$d + Disable NSFW sources and hide adult manga from list if possible diff --git a/app/src/main/res/xml/pref_appearance.xml b/app/src/main/res/xml/pref_appearance.xml index e43a5ddbf..d96c2c602 100644 --- a/app/src/main/res/xml/pref_appearance.xml +++ b/app/src/main/res/xml/pref_appearance.xml @@ -46,21 +46,6 @@ - - - - - - - - diff --git a/app/src/main/res/xml/pref_sources.xml b/app/src/main/res/xml/pref_sources.xml new file mode 100644 index 000000000..27a1b0171 --- /dev/null +++ b/app/src/main/res/xml/pref_sources.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + +