From 4771882f50687730aee83a3d6137d119add2748e Mon Sep 17 00:00:00 2001 From: Koitharu Date: Mon, 9 May 2022 09:02:58 +0300 Subject: [PATCH] Edit favourite category activity --- app/src/main/AndroidManifest.xml | 14 +- .../kotatsu/favourites/FavouritesModule.kt | 2 + .../favourites/data/FavouriteCategoriesDao.kt | 6 + .../favourites/domain/FavouritesRepository.kt | 28 ++++ .../ui/FavouritesContainerFragment.kt | 38 +---- .../ui/categories/CategoriesActivity.kt | 41 +---- .../ui/categories/CategoriesEditDelegate.kt | 46 ------ .../FavouritesCategoriesViewModel.kt | 30 +--- .../edit/FavouritesCategoryEditActivity.kt | 147 ++++++++++++++++++ .../edit/FavouritesCategoryEditViewModel.kt | 53 +++++++ .../select/FavouriteCategoriesDialog.kt | 12 +- .../select/MangaCategoriesViewModel.kt | 6 - .../ui/list/FavouritesListFragment.kt | 51 +++++- .../ui/list/FavouritesListViewModel.kt | 27 +++- .../res/layout-w720dp-land/activity_main.xml | 1 + .../res/layout/activity_category_edit.xml | 81 ++++++++++ app/src/main/res/layout/activity_main.xml | 2 +- app/src/main/res/menu/opt_favourites_list.xml | 19 +++ app/src/main/res/menu/popup_category.xml | 22 +-- app/src/main/res/values-large/themes.xml | 34 ++++ app/src/main/res/values-ru/strings.xml | 3 + app/src/main/res/values/strings.xml | 3 + app/src/main/res/values/styles.xml | 2 + app/src/main/res/values/themes.xml | 5 +- 24 files changed, 480 insertions(+), 193 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditActivity.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditViewModel.kt create mode 100644 app/src/main/res/layout/activity_category_edit.xml create mode 100644 app/src/main/res/menu/opt_favourites_list.xml create mode 100644 app/src/main/res/values-large/themes.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bba1d89b0..13f5cecbf 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -53,7 +53,8 @@ - + android:label="@string/manga_shelf" + android:theme="@style/Theme.Kotatsu.DialogWhenLarge"> @@ -95,9 +97,13 @@ android:windowSoftInputMode="adjustResize" /> - + android:theme="@style/Theme.Kotatsu.DialogWhenLarge" /> + + MangaCategoriesViewModel(manga.get(), get()) } + viewModel { params -> FavouritesCategoryEditViewModel(params[0], get(), get()) } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouriteCategoriesDao.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouriteCategoriesDao.kt index 65d429706..d0ada21da 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouriteCategoriesDao.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouriteCategoriesDao.kt @@ -6,6 +6,9 @@ import kotlinx.coroutines.flow.Flow @Dao abstract class FavouriteCategoriesDao { + @Query("SELECT * FROM favourite_categories WHERE category_id = :id") + abstract suspend fun find(id: Int): FavouriteCategoryEntity + @Query("SELECT * FROM favourite_categories ORDER BY sort_key") abstract suspend fun findAll(): List @@ -27,6 +30,9 @@ abstract class FavouriteCategoriesDao { @Query("UPDATE favourite_categories SET title = :title WHERE category_id = :id") abstract suspend fun updateTitle(id: Long, title: String) + @Query("UPDATE favourite_categories SET title = :title, `order` = :order, `track` = :tracker WHERE category_id = :id") + abstract suspend fun update(id: Long, title: String, order: String, tracker: Boolean) + @Query("UPDATE favourite_categories SET `order` = :order WHERE category_id = :id") abstract suspend fun updateOrder(id: Long, order: String) diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/domain/FavouritesRepository.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/domain/FavouritesRepository.kt index 35a362e95..6e995696b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/domain/FavouritesRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/domain/FavouritesRepository.kt @@ -52,6 +52,11 @@ class FavouritesRepository( }.distinctUntilChanged() } + fun observeCategory(id: Long): Flow { + return db.favouriteCategoriesDao.observe(id) + .map { it.toFavouriteCategory() } + } + fun observeCategories(mangaId: Long): Flow> { return db.favouritesDao.observe(mangaId).map { entity -> entity?.categories?.map { it.toFavouriteCategory() }.orEmpty() @@ -62,6 +67,29 @@ class FavouritesRepository( return db.favouritesDao.observeIds(mangaId).map { it.toSet() } } + suspend fun getCategory(id: Long): FavouriteCategory { + return db.favouriteCategoriesDao.find(id.toInt()).toFavouriteCategory() + } + + suspend fun createCategory(title: String, sortOrder: SortOrder, isTrackerEnabled: Boolean): FavouriteCategory { + val entity = FavouriteCategoryEntity( + title = title, + createdAt = System.currentTimeMillis(), + sortKey = db.favouriteCategoriesDao.getNextSortKey(), + categoryId = 0, + order = sortOrder.name, + track = isTrackerEnabled, + ) + val id = db.favouriteCategoriesDao.insert(entity) + val category = entity.toFavouriteCategory(id) + channels.createChannel(category) + return category + } + + suspend fun updateCategory(id: Long, title: String, sortOrder: SortOrder, isTrackerEnabled: Boolean) { + db.favouriteCategoriesDao.update(id, title, sortOrder.name, isTrackerEnabled) + } + suspend fun addCategory(title: String): FavouriteCategory { val entity = FavouriteCategoryEntity( title = title, diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesContainerFragment.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesContainerFragment.kt index 45cbba17a..61fae0369 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesContainerFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesContainerFragment.kt @@ -16,12 +16,12 @@ import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseFragment import org.koitharu.kotatsu.base.ui.util.ActionModeListener import org.koitharu.kotatsu.core.model.FavouriteCategory -import org.koitharu.kotatsu.core.ui.titleRes import org.koitharu.kotatsu.databinding.FragmentFavouritesBinding import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity import org.koitharu.kotatsu.favourites.ui.categories.CategoriesEditDelegate import org.koitharu.kotatsu.favourites.ui.categories.FavouritesCategoriesViewModel import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel +import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity import org.koitharu.kotatsu.main.ui.AppBarOwner import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.measureHeight @@ -134,28 +134,6 @@ class FavouritesContainerFragment : viewModel.deleteCategory(category.id) } - override fun onRenameCategory(category: FavouriteCategory, newName: String) { - viewModel.renameCategory(category.id, newName) - } - - override fun onCreateCategory(name: String) { - viewModel.createCategory(name) - } - - private fun createOrderSubmenu(menu: Menu, category: FavouriteCategory) { - val submenu = menu.findItem(R.id.action_order)?.subMenu ?: return - for ((i, item) in CategoriesActivity.SORT_ORDERS.withIndex()) { - val menuItem = submenu.add(R.id.group_order, Menu.NONE, i, item.titleRes) - menuItem.isCheckable = true - menuItem.isChecked = item == category.order - } - submenu.setGroupCheckable(R.id.group_order, true, true) - menu.findItem(R.id.action_tracking)?.run { - isVisible = viewModel.isFavouritesTrackerEnabled - isChecked = category.isTrackingEnabled - } - } - private fun TabLayout.setTabsEnabled(enabled: Boolean) { val tabStrip = getChildAt(0) as? ViewGroup ?: return for (tab in tabStrip.children) { @@ -166,19 +144,11 @@ class FavouritesContainerFragment : private fun showCategoryMenu(tabView: View, category: FavouriteCategory) { val menu = PopupMenu(tabView.context, tabView) menu.inflate(R.menu.popup_category) - createOrderSubmenu(menu.menu, category) menu.setOnMenuItemClickListener { when (it.itemId) { R.id.action_remove -> editDelegate.deleteCategory(category) - R.id.action_rename -> editDelegate.renameCategory(category) - R.id.action_create -> editDelegate.createCategory() - R.id.action_tracking -> viewModel.setCategoryTracking(category.id, !category.isTrackingEnabled) - R.id.action_order -> return@setOnMenuItemClickListener false - else -> { - val order = CategoriesActivity.SORT_ORDERS.getOrNull(it.order) - ?: return@setOnMenuItemClickListener false - viewModel.setCategoryOrder(category.id, order) - } + R.id.action_edit -> FavouritesCategoryEditActivity.newIntent(tabView.context, category.id) + else -> return@setOnMenuItemClickListener false } true } @@ -190,7 +160,7 @@ class FavouritesContainerFragment : menu.inflate(R.menu.popup_category_all) menu.setOnMenuItemClickListener { when (it.itemId) { - R.id.action_create -> editDelegate.createCategory() + R.id.action_create -> FavouritesCategoryEditActivity.newIntent(requireContext()) R.id.action_hide -> viewModel.setAllCategoriesVisible(false) } true diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesActivity.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesActivity.kt index d2615c826..80fd2a137 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesActivity.kt @@ -3,7 +3,6 @@ package org.koitharu.kotatsu.favourites.ui.categories import android.content.Context import android.content.Intent import android.os.Bundle -import android.view.Menu import android.view.View import android.view.ViewGroup import androidx.appcompat.widget.PopupMenu @@ -19,9 +18,9 @@ import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BaseActivity import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.model.FavouriteCategory -import org.koitharu.kotatsu.core.ui.titleRes import org.koitharu.kotatsu.databinding.ActivityCategoriesBinding import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel +import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.measureHeight @@ -57,24 +56,17 @@ class CategoriesActivity : override fun onClick(v: View) { when (v.id) { - R.id.fab_add -> editDelegate.createCategory() + R.id.fab_add -> startActivity(FavouritesCategoryEditActivity.newIntent(this)) } } override fun onItemClick(item: FavouriteCategory, view: View) { val menu = PopupMenu(view.context, view) menu.inflate(R.menu.popup_category) - prepareCategoryMenu(menu.menu, item) menu.setOnMenuItemClickListener { menuItem -> when (menuItem.itemId) { R.id.action_remove -> editDelegate.deleteCategory(item) - R.id.action_rename -> editDelegate.renameCategory(item) - R.id.action_tracking -> viewModel.setCategoryTracking(item.id, !item.isTrackingEnabled) - R.id.action_order -> return@setOnMenuItemClickListener false - else -> { - val order = SORT_ORDERS.getOrNull(menuItem.order) ?: return@setOnMenuItemClickListener false - viewModel.setCategoryOrder(item.id, order) - } + R.id.action_edit -> startActivity(FavouritesCategoryEditActivity.newIntent(this, item.id)) } true } @@ -118,33 +110,6 @@ class CategoriesActivity : viewModel.deleteCategory(category.id) } - override fun onRenameCategory(category: FavouriteCategory, newName: String) { - viewModel.renameCategory(category.id, newName) - } - - override fun onCreateCategory(name: String) { - viewModel.createCategory(name) - } - - private fun prepareCategoryMenu(menu: Menu, category: FavouriteCategory) { - val submenu = menu.findItem(R.id.action_order)?.subMenu ?: return - for ((i, item) in SORT_ORDERS.withIndex()) { - val menuItem = submenu.add( - R.id.group_order, - Menu.NONE, - i, - item.titleRes - ) - menuItem.isCheckable = true - menuItem.isChecked = item == category.order - } - submenu.setGroupCheckable(R.id.group_order, true, true) - menu.findItem(R.id.action_tracking)?.run { - isVisible = viewModel.isFavouritesTrackerEnabled - isChecked = category.isTrackingEnabled - } - } - private inner class ReorderHelperCallback : ItemTouchHelper.SimpleCallback( ItemTouchHelper.DOWN or ItemTouchHelper.UP, 0 ) { diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesEditDelegate.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesEditDelegate.kt index 603a70ee3..f7a98c078 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesEditDelegate.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesEditDelegate.kt @@ -1,15 +1,10 @@ package org.koitharu.kotatsu.favourites.ui.categories import android.content.Context -import android.text.InputType -import android.widget.Toast import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.base.ui.dialog.TextInputDialog import org.koitharu.kotatsu.core.model.FavouriteCategory -private const val MAX_TITLE_LENGTH = 24 - class CategoriesEditDelegate( private val context: Context, private val callback: CategoriesEditCallback @@ -26,49 +21,8 @@ class CategoriesEditDelegate( .show() } - fun renameCategory(category: FavouriteCategory) { - TextInputDialog.Builder(context) - .setTitle(R.string.rename) - .setText(category.title) - .setHint(R.string.enter_category_name) - .setInputType(InputType.TYPE_TEXT_VARIATION_PERSON_NAME or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES) - .setNegativeButton(android.R.string.cancel) - .setMaxLength(MAX_TITLE_LENGTH, false) - .setPositiveButton(R.string.rename) { _, name -> - val trimmed = name.trim() - if (trimmed.isEmpty()) { - Toast.makeText(context, R.string.error_empty_name, Toast.LENGTH_SHORT).show() - } else { - callback.onRenameCategory(category, name) - } - }.create() - .show() - } - - fun createCategory() { - TextInputDialog.Builder(context) - .setTitle(R.string.add_new_category) - .setHint(R.string.enter_category_name) - .setInputType(InputType.TYPE_TEXT_VARIATION_PERSON_NAME or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES) - .setNegativeButton(android.R.string.cancel) - .setMaxLength(MAX_TITLE_LENGTH, false) - .setPositiveButton(R.string.add) { _, name -> - val trimmed = name.trim() - if (trimmed.isEmpty()) { - Toast.makeText(context, R.string.error_empty_name, Toast.LENGTH_SHORT).show() - } else { - callback.onCreateCategory(trimmed) - } - }.create() - .show() - } - interface CategoriesEditCallback { fun onDeleteCategory(category: FavouriteCategory) - - fun onRenameCategory(category: FavouriteCategory, newName: String) - - fun onCreateCategory(name: String) } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt index 9e84a9d89..31c1e11de 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt @@ -1,7 +1,6 @@ package org.koitharu.kotatsu.favourites.ui.categories import androidx.lifecycle.viewModelScope -import java.util.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.* @@ -10,8 +9,8 @@ import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel -import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct +import java.util.* class FavouritesCategoriesViewModel( private val repository: FavouritesRepository, @@ -34,39 +33,12 @@ class FavouritesCategoriesViewModel( mapCategories(list, showAll, showAll) }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default) - val isFavouritesTrackerEnabled: Boolean - get() = settings.isTrackerEnabled && AppSettings.TRACK_FAVOURITES in settings.trackSources - - fun createCategory(name: String) { - launchJob { - repository.addCategory(name) - } - } - - fun renameCategory(id: Long, name: String) { - launchJob { - repository.renameCategory(id, name) - } - } - fun deleteCategory(id: Long) { launchJob { repository.removeCategory(id) } } - fun setCategoryOrder(id: Long, order: SortOrder) { - launchJob { - repository.setCategoryOrder(id, order) - } - } - - fun setCategoryTracking(id: Long, isEnabled: Boolean) { - launchJob { - repository.setCategoryTracking(id, isEnabled) - } - } - fun setAllCategoriesVisible(isVisible: Boolean) { settings.isAllFavouritesVisible = isVisible } diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditActivity.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditActivity.kt new file mode 100644 index 000000000..27239c0dc --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditActivity.kt @@ -0,0 +1,147 @@ +package org.koitharu.kotatsu.favourites.ui.categories.edit + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.widget.AdapterView +import android.widget.ArrayAdapter +import androidx.core.graphics.Insets +import androidx.core.view.isVisible +import androidx.core.view.updatePadding +import org.koin.androidx.viewmodel.ext.android.viewModel +import org.koin.core.parameter.parametersOf +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.base.ui.BaseActivity +import org.koitharu.kotatsu.core.model.FavouriteCategory +import org.koitharu.kotatsu.core.ui.titleRes +import org.koitharu.kotatsu.databinding.ActivityCategoryEditBinding +import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity +import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.utils.ext.getDisplayMessage + +class FavouritesCategoryEditActivity : BaseActivity(), AdapterView.OnItemClickListener { + + private val viewModel by viewModel { + parametersOf(intent.getLongExtra(EXTRA_ID, NO_ID)) + } + private var selectedSortOrder: SortOrder? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(ActivityCategoryEditBinding.inflate(layoutInflater)) + supportActionBar?.run { + setDisplayHomeAsUpEnabled(true) + setHomeAsUpIndicator(com.google.android.material.R.drawable.abc_ic_clear_material) + } + initSortSpinner() + + viewModel.onSaved.observe(this) { finishAfterTransition() } + viewModel.category.observe(this, ::onCategoryChanged) + viewModel.isLoading.observe(this, ::onLoadingStateChanged) + viewModel.onError.observe(this, ::onError) + viewModel.isTrackerEnabled.observe(this) { + binding.switchTracker.isVisible = it + } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putSerializable(KEY_SORT_ORDER, selectedSortOrder) + } + + override fun onRestoreInstanceState(savedInstanceState: Bundle) { + super.onRestoreInstanceState(savedInstanceState) + val order = savedInstanceState.getSerializable(KEY_SORT_ORDER) + if (order != null && order is SortOrder) { + selectedSortOrder = order + } + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.opt_config, menu) + menu.findItem(R.id.action_done)?.setTitle(R.string.save) + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { + R.id.action_done -> { + viewModel.save( + title = binding.editName.text?.toString().orEmpty(), + sortOrder = getSelectedSortOrder(), + isTrackerEnabled = binding.switchTracker.isChecked, + ) + true + } + else -> super.onOptionsItemSelected(item) + } + + override fun onWindowInsetsChanged(insets: Insets) { + binding.scrollView.updatePadding( + left = insets.left, + right = insets.right, + bottom = insets.bottom, + ) + binding.toolbar.updatePadding( + top = insets.top, + ) + } + + override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { + selectedSortOrder = CategoriesActivity.SORT_ORDERS.getOrNull(position) + } + + private fun onCategoryChanged(category: FavouriteCategory?) { + setTitle(if (category == null) R.string.create_category else R.string.edit_category) + if (selectedSortOrder != null) { + return + } + binding.editName.setText(category?.title) + selectedSortOrder = category?.order + val sortText = getString((category?.order ?: SortOrder.NEWEST).titleRes) + binding.editSort.setText(sortText, false) + binding.switchTracker.isChecked = category?.isTrackingEnabled ?: true + } + + private fun onError(e: Throwable) { + binding.textViewError.text = e.getDisplayMessage(resources) + binding.textViewError.isVisible = true + } + + private fun onLoadingStateChanged(isLoading: Boolean) { + binding.editSort.isEnabled = !isLoading + binding.editName.isEnabled = !isLoading + binding.switchTracker.isEnabled = !isLoading + if (isLoading) { + binding.textViewError.isVisible = false + } + } + + private fun initSortSpinner() { + val entries = CategoriesActivity.SORT_ORDERS.map { getString(it.titleRes) } + val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, entries) + binding.editSort.setAdapter(adapter) + binding.editSort.onItemClickListener = this + } + + private fun getSelectedSortOrder(): SortOrder { + selectedSortOrder?.let { return it } + val entries = CategoriesActivity.SORT_ORDERS.map { getString(it.titleRes) } + val index = entries.indexOf(binding.editSort.text.toString()) + return CategoriesActivity.SORT_ORDERS.getOrNull(index) ?: SortOrder.NEWEST + } + + companion object { + + private const val EXTRA_ID = "id" + private const val KEY_SORT_ORDER = "sort" + private const val NO_ID = -1L + + fun newIntent(context: Context, id: Long = NO_ID): Intent { + return Intent(context, FavouritesCategoryEditActivity::class.java) + .putExtra(EXTRA_ID, id) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditViewModel.kt new file mode 100644 index 000000000..78446dd14 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/edit/FavouritesCategoryEditViewModel.kt @@ -0,0 +1,53 @@ +package org.koitharu.kotatsu.favourites.ui.categories.edit + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.liveData +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.Dispatchers +import org.koitharu.kotatsu.base.ui.BaseViewModel +import org.koitharu.kotatsu.core.model.FavouriteCategory +import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.favourites.domain.FavouritesRepository +import org.koitharu.kotatsu.parsers.model.SortOrder +import org.koitharu.kotatsu.utils.SingleLiveEvent + +private const val NO_ID = -1L + +class FavouritesCategoryEditViewModel( + private val categoryId: Long, + private val repository: FavouritesRepository, + private val settings: AppSettings, +) : BaseViewModel() { + + val onSaved = SingleLiveEvent() + val category = MutableLiveData() + + val isTrackerEnabled = liveData(viewModelScope.coroutineContext + Dispatchers.Default) { + emit(settings.isTrackerEnabled && AppSettings.TRACK_FAVOURITES in settings.trackSources) + } + + init { + launchLoadingJob { + category.value = if (categoryId != NO_ID) { + repository.getCategory(categoryId) + } else { + null + } + } + } + + fun save( + title: String, + sortOrder: SortOrder, + isTrackerEnabled: Boolean, + ) { + launchLoadingJob { + if (categoryId == NO_ID) { + repository.createCategory(title, sortOrder, isTrackerEnabled) + } else { + repository.updateCategory(categoryId, title, sortOrder, isTrackerEnabled) + } + onSaved.call(Unit) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/FavouriteCategoriesDialog.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/FavouriteCategoriesDialog.kt index 43eec185e..95f733006 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/FavouriteCategoriesDialog.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/FavouriteCategoriesDialog.kt @@ -17,6 +17,7 @@ import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga import org.koitharu.kotatsu.databinding.DialogFavoriteCategoriesBinding import org.koitharu.kotatsu.favourites.ui.categories.CategoriesEditDelegate +import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity import org.koitharu.kotatsu.favourites.ui.categories.select.adapter.MangaCategoriesAdapter import org.koitharu.kotatsu.favourites.ui.categories.select.model.MangaCategoryItem import org.koitharu.kotatsu.parsers.model.Manga @@ -34,9 +35,6 @@ class FavouriteCategoriesDialog : } private var adapter: MangaCategoriesAdapter? = null - private val editDelegate by lazy(LazyThreadSafetyMode.NONE) { - CategoriesEditDelegate(requireContext(), this@FavouriteCategoriesDialog) - } override fun onInflateView( inflater: LayoutInflater, @@ -61,7 +59,7 @@ class FavouriteCategoriesDialog : override fun onMenuItemClick(item: MenuItem): Boolean { return when (item.itemId) { R.id.action_create -> { - editDelegate.createCategory() + FavouritesCategoryEditActivity.newIntent(requireContext()) true } else -> false @@ -74,12 +72,6 @@ class FavouriteCategoriesDialog : override fun onDeleteCategory(category: FavouriteCategory) = Unit - override fun onRenameCategory(category: FavouriteCategory, newName: String) = Unit - - override fun onCreateCategory(name: String) { - viewModel.createCategory(name) - } - private fun onContentChanged(categories: List) { adapter?.items = categories } diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/MangaCategoriesViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/MangaCategoriesViewModel.kt index 84f9cf8f9..b9f906549 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/MangaCategoriesViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/select/MangaCategoriesViewModel.kt @@ -38,12 +38,6 @@ class MangaCategoriesViewModel( } } - fun createCategory(name: String) { - launchJob(Dispatchers.Default) { - favouritesRepository.addCategory(name) - } - } - private fun observeCategoriesIds() = if (manga.size == 1) { // Fast path favouritesRepository.observeCategoriesIds(manga[0].id) diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListFragment.kt index f2748622c..8d4b9e419 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListFragment.kt @@ -1,11 +1,17 @@ package org.koitharu.kotatsu.favourites.ui.list +import android.os.Bundle import android.view.Menu +import android.view.MenuInflater import android.view.MenuItem +import android.view.View import androidx.appcompat.view.ActionMode +import androidx.core.view.iterator import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.ui.titleRes +import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity import org.koitharu.kotatsu.list.ui.MangaListFragment import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.utils.ext.withArgs @@ -17,12 +23,54 @@ class FavouritesListFragment : MangaListFragment() { } private val categoryId: Long - get() = arguments?.getLong(ARG_CATEGORY_ID) ?: 0L + get() = arguments?.getLong(ARG_CATEGORY_ID) ?: NO_ID override val isSwipeRefreshEnabled = false + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + viewModel.sortOrder.observe(viewLifecycleOwner) { activity?.invalidateOptionsMenu() } + } + override fun onScrolledToEnd() = Unit + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) + if (categoryId != NO_ID) { + inflater.inflate(R.menu.opt_favourites_list, menu) + menu.findItem(R.id.action_order)?.subMenu?.let { submenu -> + for ((i, item) in CategoriesActivity.SORT_ORDERS.withIndex()) { + val menuItem = submenu.add(R.id.group_order, Menu.NONE, i, item.titleRes) + menuItem.isCheckable = true + } + submenu.setGroupCheckable(R.id.group_order, true, true) + } + } + } + + override fun onPrepareOptionsMenu(menu: Menu) { + super.onPrepareOptionsMenu(menu) + menu.findItem(R.id.action_order)?.subMenu?.let { submenu -> + val selectedOrder = viewModel.sortOrder.value + for (item in submenu) { + val order = CategoriesActivity.SORT_ORDERS.getOrNull(item.order) + item.isChecked = order == selectedOrder + } + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when { + item.itemId == R.id.action_order -> false + item.groupId == R.id.group_order -> { + val order = CategoriesActivity.SORT_ORDERS.getOrNull(item.order) ?: return false + viewModel.setSortOrder(order) + true + } + else -> super.onOptionsItemSelected(item) + } + } + override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { mode.menuInflater.inflate(R.menu.mode_favourites, menu) return super.onCreateActionMode(mode, menu) @@ -48,6 +96,7 @@ class FavouritesListFragment : MangaListFragment() { companion object { + const val NO_ID = 0L private const val ARG_CATEGORY_ID = "category_id" fun newInstance(categoryId: Long) = FavouritesListFragment().withArgs(1) { diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt index 476fa6fb1..06d5bee3d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt @@ -1,12 +1,16 @@ package org.koitharu.kotatsu.favourites.ui.list +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.favourites.domain.FavouritesRepository +import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment.Companion.NO_ID import org.koitharu.kotatsu.list.domain.CountersProvider import org.koitharu.kotatsu.list.ui.MangaListViewModel import org.koitharu.kotatsu.list.ui.model.EmptyState @@ -24,8 +28,16 @@ class FavouritesListViewModel( settings: AppSettings, ) : MangaListViewModel(settings), CountersProvider { + var sortOrder: LiveData = if (categoryId == NO_ID) { + MutableLiveData(null) + } else { + repository.observeCategory(categoryId) + .map { it.order } + .asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default) + } + override val content = combine( - if (categoryId == 0L) { + if (categoryId == NO_ID) { repository.observeAll(SortOrder.NEWEST) } else { repository.observeAll(categoryId) @@ -37,7 +49,7 @@ class FavouritesListViewModel( EmptyState( icon = R.drawable.ic_heart_outline, textPrimary = R.string.text_empty_holder_primary, - textSecondary = if (categoryId == 0L) { + textSecondary = if (categoryId == NO_ID) { R.string.you_have_not_favourites_yet } else { R.string.favourites_category_empty @@ -60,7 +72,7 @@ class FavouritesListViewModel( return } launchJob { - if (categoryId == 0L) { + if (categoryId == NO_ID) { repository.removeFromFavourites(ids) } else { repository.removeFromCategory(categoryId, ids) @@ -68,6 +80,15 @@ class FavouritesListViewModel( } } + fun setSortOrder(order: SortOrder) { + if (categoryId == NO_ID) { + return + } + launchJob { + repository.setCategoryOrder(categoryId, order) + } + } + override suspend fun getCounter(mangaId: Long): Int { return trackingRepository.getNewChaptersCount(mangaId) } diff --git a/app/src/main/res/layout-w720dp-land/activity_main.xml b/app/src/main/res/layout-w720dp-land/activity_main.xml index ee362d397..a442269b3 100644 --- a/app/src/main/res/layout-w720dp-land/activity_main.xml +++ b/app/src/main/res/layout-w720dp-land/activity_main.xml @@ -62,6 +62,7 @@ android:layout_height="match_parent" android:background="@null" android:drawablePadding="16dp" + android:layout_marginEnd="4dp" android:gravity="center_vertical" android:hint="@string/search_manga" android:imeOptions="actionSearch" diff --git a/app/src/main/res/layout/activity_category_edit.xml b/app/src/main/res/layout/activity_category_edit.xml new file mode 100644 index 000000000..e453df674 --- /dev/null +++ b/app/src/main/res/layout/activity_category_edit.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 43361b15b..6314cf7ef 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -51,7 +51,7 @@ style="@style/Widget.Kotatsu.SearchView" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_marginEnd="2dp" + android:layout_marginEnd="4dp" android:background="@null" android:gravity="center_vertical" android:hint="@string/search_manga" diff --git a/app/src/main/res/menu/opt_favourites_list.xml b/app/src/main/res/menu/opt_favourites_list.xml new file mode 100644 index 000000000..269569df7 --- /dev/null +++ b/app/src/main/res/menu/opt_favourites_list.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/popup_category.xml b/app/src/main/res/menu/popup_category.xml index ee78b4b80..50c1313be 100644 --- a/app/src/main/res/menu/popup_category.xml +++ b/app/src/main/res/menu/popup_category.xml @@ -7,25 +7,7 @@ android:title="@string/remove" /> - - - - - - - - - - - + android:id="@+id/action_edit" + android:title="@string/edit" /> \ No newline at end of file diff --git a/app/src/main/res/values-large/themes.xml b/app/src/main/res/values-large/themes.xml new file mode 100644 index 000000000..56e1ed965 --- /dev/null +++ b/app/src/main/res/values-large/themes.xml @@ -0,0 +1,34 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 87da7d5d0..012bf4aee 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -281,4 +281,7 @@ Вы будете получать уведомления об обновлении манги, которую Вы читаете Вы не будете получать уведомления, но новые главы будут отображаться в списке Включить уведомления + Название + Изменить + Изменить категорию \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 083717035..2bc68e061 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -284,4 +284,7 @@ You will receive notifications about updates of manga you are reading You will not receive notifications but new chapters will be highlighted in the lists Enable notifications + Name + Edit + Edit category \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index b63927f74..b16f50044 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -119,6 +119,8 @@ diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 12ccac10f..f6addbe8f 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -71,7 +71,8 @@ @style/Widget.Kotatsu.RecyclerView @style/Widget.Kotatsu.ListItemTextView - + + @style/TextAppearance.Kotatsu.Menu ?attr/textAppearanceBodyLarge @style/TextAppearance.Kotatsu.Preference.Secondary @@ -80,6 +81,8 @@