Improve list options configuring

This commit is contained in:
Koitharu
2023-10-13 14:19:05 +03:00
parent d4588570e6
commit 5b53f8c27d
15 changed files with 235 additions and 153 deletions

View File

@@ -72,6 +72,14 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
get() = prefs.getInt(KEY_GRID_SIZE, 100)
set(value) = prefs.edit { putInt(KEY_GRID_SIZE, value) }
var historyListMode: ListMode
get() = prefs.getEnumValue(KEY_LIST_MODE_HISTORY, listMode)
set(value) = prefs.edit { putEnumValue(KEY_LIST_MODE_HISTORY, value) }
var favoritesListMode: ListMode
get() = prefs.getEnumValue(KEY_LIST_MODE_FAVORITES, listMode)
set(value) = prefs.edit { putEnumValue(KEY_LIST_MODE_FAVORITES, value) }
var isNsfwContentDisabled: Boolean
get() = prefs.getBoolean(KEY_DISABLE_NSFW, false)
set(value) = prefs.edit { putBoolean(KEY_DISABLE_NSFW, value) }
@@ -407,6 +415,8 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
const val TRACK_FAVOURITES = "favourites"
const val KEY_LIST_MODE = "list_mode_2"
const val KEY_LIST_MODE_HISTORY = "list_mode_history"
const val KEY_LIST_MODE_FAVORITES = "list_mode_favorites"
const val KEY_THEME = "theme"
const val KEY_COLOR_THEME = "color_theme"
const val KEY_THEME_AMOLED = "amoled_theme"

View File

@@ -44,9 +44,7 @@ class FavouritesListMenuProvider(
}
return when (menuItem.itemId) {
R.id.action_edit -> {
context.startActivity(
FavouritesCategoryEditActivity.newIntent(context, viewModel.categoryId),
)
context.startActivity(FavouritesCategoryEditActivity.newIntent(context, viewModel.categoryId))
true
}

View File

@@ -14,6 +14,7 @@ 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.prefs.observeAsFlow
import org.koitharu.kotatsu.core.ui.util.ReversibleAction
import org.koitharu.kotatsu.core.util.ext.call
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
@@ -40,6 +41,9 @@ class FavouritesListViewModel @Inject constructor(
val categoryId: Long = savedStateHandle[ARG_CATEGORY_ID] ?: NO_ID
override val listMode = settings.observeAsFlow(AppSettings.KEY_LIST_MODE_FAVORITES) { favoritesListMode }
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, settings.favoritesListMode)
val sortOrder: StateFlow<SortOrder?> = if (categoryId == NO_ID) {
MutableStateFlow(null)
} else {

View File

@@ -9,9 +9,7 @@ import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.os.NetworkManageIntent
import org.koitharu.kotatsu.core.ui.list.ListSelectionController
import org.koitharu.kotatsu.core.ui.util.MenuInvalidator
import org.koitharu.kotatsu.core.util.ext.addMenuProvider
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.databinding.FragmentListBinding
import org.koitharu.kotatsu.list.ui.MangaListFragment
import org.koitharu.kotatsu.list.ui.size.DynamicItemSizeResolver
@@ -26,9 +24,6 @@ class HistoryListFragment : MangaListFragment() {
override fun onViewBindingCreated(binding: FragmentListBinding, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState)
addMenuProvider(HistoryListMenuProvider(binding.root.context, viewModel))
val menuInvalidator = MenuInvalidator(requireActivity())
viewModel.isGroupingEnabled.observe(viewLifecycleOwner, menuInvalidator)
viewModel.sortOrder.observe(viewLifecycleOwner, menuInvalidator)
}
override fun onScrolledToEnd() = Unit

View File

@@ -5,12 +5,10 @@ import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import androidx.core.view.MenuProvider
import androidx.core.view.forEach
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.dialog.RememberSelectionDialogListener
import org.koitharu.kotatsu.core.util.ext.startOfDay
import org.koitharu.kotatsu.history.domain.model.HistoryOrder
import java.util.Date
import java.util.concurrent.TimeUnit
import com.google.android.material.R as materialR
@@ -22,47 +20,19 @@ class HistoryListMenuProvider(
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.opt_history, menu)
val subMenu = menu.findItem(R.id.action_order)?.subMenu ?: return
for (order in HistoryOrder.entries) {
subMenu.add(R.id.group_order, Menu.NONE, order.ordinal, order.titleResId)
}
subMenu.setGroupCheckable(R.id.group_order, true, true)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
if (menuItem.groupId == R.id.group_order) {
val order = HistoryOrder.entries[menuItem.order]
viewModel.setSortOrder(order)
return true
}
return when (menuItem.itemId) {
R.id.action_clear_history -> {
showClearHistoryDialog()
true
}
R.id.action_history_grouping -> {
viewModel.setGrouping(!menuItem.isChecked)
true
}
else -> false
}
}
override fun onPrepareMenu(menu: Menu) {
val order = viewModel.sortOrder.value
menu.findItem(R.id.action_order)?.subMenu?.forEach { item ->
if (item.order == order.ordinal) {
item.isChecked = true
}
}
menu.findItem(R.id.action_history_grouping)?.run {
isChecked = viewModel.isGroupingEnabled.value == true
isEnabled = order.isGroupingSupported()
}
}
private fun showClearHistoryDialog() {
val selectionListener = RememberSelectionDialogListener(2)
MaterialAlertDialogBuilder(context, materialR.style.ThemeOverlay_Material3_MaterialAlertDialog_Centered)

View File

@@ -54,22 +54,21 @@ class HistoryListViewModel @Inject constructor(
downloadScheduler: DownloadWorker.Scheduler,
) : MangaListViewModel(settings, downloadScheduler) {
val sortOrder: StateFlow<HistoryOrder> = settings.observeAsStateFlow(
private val sortOrder: StateFlow<HistoryOrder> = settings.observeAsStateFlow(
scope = viewModelScope + Dispatchers.IO,
key = AppSettings.KEY_HISTORY_ORDER,
valueProducer = { historySortOrder },
)
val isGroupingEnabled = settings.observeAsFlow(
override val listMode = settings.observeAsFlow(AppSettings.KEY_LIST_MODE_HISTORY) { historyListMode }
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, settings.historyListMode)
private val isGroupingEnabled = settings.observeAsFlow(
key = AppSettings.KEY_HISTORY_GROUPING,
valueProducer = { isHistoryGroupingEnabled },
).combine(sortOrder) { g, s ->
g && s.isGroupingSupported()
}.stateIn(
scope = viewModelScope + Dispatchers.Default,
started = SharingStarted.Eagerly,
initialValue = settings.isHistoryGroupingEnabled && sortOrder.value.isGroupingSupported(),
)
}
override val content = combine(
sortOrder.flatMapLatest { repository.observeAllWithHistory(it) },
@@ -101,10 +100,6 @@ class HistoryListViewModel @Inject constructor(
override fun onRetry() = Unit
fun setSortOrder(order: HistoryOrder) {
settings.historySortOrder = order
}
fun clearHistory(minDate: Long) {
launchJob(Dispatchers.Default) {
val stringRes = if (minDate <= 0) {

View File

@@ -1,78 +0,0 @@
package org.koitharu.kotatsu.list.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentManager
import com.google.android.material.button.MaterialButtonToggleGroup
import com.google.android.material.slider.Slider
import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
import org.koitharu.kotatsu.core.util.ext.setValueRounded
import org.koitharu.kotatsu.core.util.ext.showDistinct
import org.koitharu.kotatsu.core.util.progress.IntPercentLabelFormatter
import org.koitharu.kotatsu.databinding.DialogListModeBinding
import javax.inject.Inject
@AndroidEntryPoint
class ListModeBottomSheet :
BaseAdaptiveSheet<DialogListModeBinding>(),
Slider.OnChangeListener,
MaterialButtonToggleGroup.OnButtonCheckedListener {
@Inject
lateinit var settings: AppSettings
override fun onCreateViewBinding(
inflater: LayoutInflater,
container: ViewGroup?,
) = DialogListModeBinding.inflate(inflater, container, false)
override fun onViewBindingCreated(binding: DialogListModeBinding, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState)
val mode = settings.listMode
binding.buttonList.isChecked = mode == ListMode.LIST
binding.buttonListDetailed.isChecked = mode == ListMode.DETAILED_LIST
binding.buttonGrid.isChecked = mode == ListMode.GRID
binding.textViewGridTitle.isVisible = mode == ListMode.GRID
binding.sliderGrid.isVisible = mode == ListMode.GRID
binding.sliderGrid.setLabelFormatter(IntPercentLabelFormatter(binding.root.context))
binding.sliderGrid.setValueRounded(settings.gridSize.toFloat())
binding.sliderGrid.addOnChangeListener(this)
binding.checkableGroup.addOnButtonCheckedListener(this)
}
override fun onButtonChecked(group: MaterialButtonToggleGroup?, checkedId: Int, isChecked: Boolean) {
if (!isChecked) {
return
}
val mode = when (checkedId) {
R.id.button_list -> ListMode.LIST
R.id.button_list_detailed -> ListMode.DETAILED_LIST
R.id.button_grid -> ListMode.GRID
else -> return
}
requireViewBinding().textViewGridTitle.isVisible = mode == ListMode.GRID
requireViewBinding().sliderGrid.isVisible = mode == ListMode.GRID
settings.listMode = mode
}
override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) {
if (fromUser) {
settings.gridSize = value.toInt()
}
}
companion object {
private const val TAG = "ListModeSelectDialog"
fun show(fm: FragmentManager) = ListModeBottomSheet().showDistinct(fm, TAG)
}
}

View File

@@ -6,6 +6,7 @@ import android.view.MenuItem
import androidx.core.view.MenuProvider
import androidx.fragment.app.Fragment
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.list.ui.config.ListConfigBottomSheet
class MangaListMenuProvider(
private val fragment: Fragment,
@@ -17,7 +18,7 @@ class MangaListMenuProvider(
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) {
R.id.action_list_mode -> {
ListModeBottomSheet.show(fragment.childFragmentManager)
ListConfigBottomSheet.show(fragment.childFragmentManager)
true
}

View File

@@ -24,7 +24,7 @@ abstract class MangaListViewModel(
) : BaseViewModel() {
abstract val content: StateFlow<List<ListModel>>
val listMode = settings.observeAsFlow(AppSettings.KEY_LIST_MODE) { listMode }
open val listMode = settings.observeAsFlow(AppSettings.KEY_LIST_MODE) { listMode }
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, settings.listMode)
val onActionDone = MutableEventFlow<ReversibleAction>()
val gridScale = settings.observeAsStateFlow(

View File

@@ -0,0 +1,143 @@
package org.koitharu.kotatsu.list.ui.config
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.CompoundButton
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentManager
import com.google.android.material.button.MaterialButtonToggleGroup
import com.google.android.material.slider.Slider
import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
import org.koitharu.kotatsu.core.util.ext.setValueRounded
import org.koitharu.kotatsu.core.util.ext.showDistinct
import org.koitharu.kotatsu.core.util.progress.IntPercentLabelFormatter
import org.koitharu.kotatsu.databinding.SheetListModeBinding
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment
import org.koitharu.kotatsu.history.domain.model.HistoryOrder
import org.koitharu.kotatsu.history.ui.HistoryListFragment
import javax.inject.Inject
@AndroidEntryPoint
class ListConfigBottomSheet :
BaseAdaptiveSheet<SheetListModeBinding>(),
Slider.OnChangeListener,
MaterialButtonToggleGroup.OnButtonCheckedListener, CompoundButton.OnCheckedChangeListener,
AdapterView.OnItemSelectedListener {
@Inject
lateinit var settings: AppSettings
override fun onCreateViewBinding(
inflater: LayoutInflater,
container: ViewGroup?,
) = SheetListModeBinding.inflate(inflater, container, false)
override fun onViewBindingCreated(binding: SheetListModeBinding, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState)
val section = getSection()
val mode = when (section) {
Section.GENERAL -> settings.listMode
Section.HISTORY -> settings.historyListMode
Section.FAVORITES -> settings.favoritesListMode
}
binding.buttonList.isChecked = mode == ListMode.LIST
binding.buttonListDetailed.isChecked = mode == ListMode.DETAILED_LIST
binding.buttonGrid.isChecked = mode == ListMode.GRID
binding.textViewGridTitle.isVisible = mode == ListMode.GRID
binding.sliderGrid.isVisible = mode == ListMode.GRID
binding.sliderGrid.setLabelFormatter(IntPercentLabelFormatter(binding.root.context))
binding.sliderGrid.setValueRounded(settings.gridSize.toFloat())
binding.sliderGrid.addOnChangeListener(this)
binding.checkableGroup.addOnButtonCheckedListener(this)
binding.switchGrouping.isVisible = section == Section.HISTORY
if (section == Section.HISTORY) {
binding.switchGrouping.isEnabled = settings.historySortOrder.isGroupingSupported()
}
binding.switchGrouping.isChecked = settings.isHistoryGroupingEnabled
binding.switchGrouping.setOnCheckedChangeListener(this)
if (section == Section.HISTORY) {
binding.textViewOrderTitle.isVisible = true
binding.spinnerOrder.adapter = ArrayAdapter(
binding.spinnerOrder.context,
android.R.layout.simple_spinner_dropdown_item,
android.R.id.text1,
HistoryOrder.entries.map { getString(it.titleResId) },
)
binding.spinnerOrder.setSelection(settings.historySortOrder.ordinal, false)
binding.spinnerOrder.onItemSelectedListener = this
binding.cardOrder.isVisible = true
}
}
override fun onButtonChecked(group: MaterialButtonToggleGroup?, checkedId: Int, isChecked: Boolean) {
if (!isChecked) {
return
}
val mode = when (checkedId) {
R.id.button_list -> ListMode.LIST
R.id.button_list_detailed -> ListMode.DETAILED_LIST
R.id.button_grid -> ListMode.GRID
else -> return
}
requireViewBinding().textViewGridTitle.isVisible = mode == ListMode.GRID
requireViewBinding().sliderGrid.isVisible = mode == ListMode.GRID
when (getSection()) {
Section.GENERAL -> settings.listMode = mode
Section.HISTORY -> settings.historyListMode = mode
Section.FAVORITES -> settings.favoritesListMode = mode
}
}
override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
when (buttonView.id) {
R.id.switch_grouping -> settings.isHistoryGroupingEnabled = isChecked
}
}
override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) {
if (fromUser) {
settings.gridSize = value.toInt()
}
}
override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
when (parent.id) {
R.id.spinner_order -> {
val value = HistoryOrder.entries[position]
settings.historySortOrder = value
viewBinding?.switchGrouping?.isEnabled = value.isGroupingSupported()
}
}
}
override fun onNothingSelected(parent: AdapterView<*>?) = Unit
private fun getSection(): Section = when (parentFragment) {
is HistoryListFragment -> Section.HISTORY
is FavouritesListFragment -> Section.FAVORITES
else -> Section.GENERAL
}
enum class Section {
GENERAL, HISTORY, FAVORITES;
}
companion object {
private const val TAG = "ListModeSelectDialog"
fun show(fm: FragmentManager) = ListConfigBottomSheet().showDistinct(fm, TAG)
}
}