From 45b2f2337a24a529430f066716d6402d0e493989 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Fri, 21 Jul 2023 10:07:43 +0300 Subject: [PATCH] Favourites manage activity --- .../kotatsu/core/model/FavouriteCategory.kt | 2 +- .../categories/FavouriteCategoriesActivity.kt | 79 +++---------------- .../FavouritesCategoriesViewModel.kt | 51 +++++------- .../ui/categories/adapter/CategoryAD.kt | 10 ++- .../categories/adapter/CategoryListModel.kt | 13 ++- app/src/main/res/drawable/ic_eye.xml | 12 +++ app/src/main/res/layout/item_category.xml | 34 ++++++-- app/src/main/res/menu/opt_categories.xml | 12 --- 8 files changed, 89 insertions(+), 124 deletions(-) create mode 100644 app/src/main/res/drawable/ic_eye.xml delete mode 100644 app/src/main/res/menu/opt_categories.xml diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/FavouriteCategory.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/FavouriteCategory.kt index e2004f11d..19dbeb9b0 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/FavouriteCategory.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/FavouriteCategory.kt @@ -26,7 +26,7 @@ data class FavouriteCategory( if (previousState !is FavouriteCategory) { return null } - return if (isTrackingEnabled != previousState.isTrackingEnabled || isVisibleInLibrary != isVisibleInLibrary) { + return if (isTrackingEnabled != previousState.isTrackingEnabled || isVisibleInLibrary != previousState.isVisibleInLibrary) { ListModelDiffCallback.PAYLOAD_CHECKED_CHANGED } else { null diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt index e72e050fa..2269e4b16 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt @@ -3,16 +3,10 @@ package org.koitharu.kotatsu.favourites.ui.categories import android.content.Context import android.content.Intent import android.os.Bundle -import android.transition.Fade -import android.transition.TransitionManager -import android.view.Menu -import android.view.MenuItem import android.view.View import android.view.ViewGroup -import androidx.activity.OnBackPressedCallback import androidx.activity.viewModels import androidx.core.graphics.Insets -import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding import androidx.recyclerview.widget.ItemTouchHelper @@ -48,16 +42,14 @@ class FavouriteCategoriesActivity : private val viewModel by viewModels() - private lateinit var exitReorderModeCallback: ExitReorderModeCallback private lateinit var adapter: CategoriesAdapter private lateinit var selectionController: ListSelectionController - private var reorderHelper: ItemTouchHelper? = null + private lateinit var reorderHelper: ItemTouchHelper override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(ActivityCategoriesBinding.inflate(layoutInflater)) supportActionBar?.setDisplayHomeAsUpEnabled(true) - exitReorderModeCallback = ExitReorderModeCallback(viewModel) adapter = CategoriesAdapter(coil, this, this, this) selectionController = ListSelectionController( activity = this, @@ -65,49 +57,27 @@ class FavouriteCategoriesActivity : registryOwner = this, callback = CategoriesSelectionCallback(viewBinding.recyclerView, viewModel), ) - viewBinding.buttonDone.setOnClickListener(this) selectionController.attachToRecyclerView(viewBinding.recyclerView) viewBinding.recyclerView.setHasFixedSize(true) viewBinding.recyclerView.adapter = adapter viewBinding.fabAdd.setOnClickListener(this) - onBackPressedDispatcher.addCallback(exitReorderModeCallback) + + reorderHelper = ItemTouchHelper(ReorderHelperCallback()).apply { + attachToRecyclerView(viewBinding.recyclerView) + } viewModel.detalizedCategories.observe(this, ::onCategoriesChanged) viewModel.onError.observeEvent(this, SnackbarErrorObserver(viewBinding.recyclerView, null)) - viewModel.isInReorderMode.observe(this, ::onReorderModeChanged) - } - - override fun onCreateOptionsMenu(menu: Menu): Boolean { - super.onCreateOptionsMenu(menu) - menuInflater.inflate(R.menu.opt_categories, menu) - return true - } - - override fun onPrepareOptionsMenu(menu: Menu): Boolean { - menu.findItem(R.id.action_reorder)?.isVisible = !viewModel.isInReorderMode() && !viewModel.isEmpty() - return super.onPrepareOptionsMenu(menu) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - R.id.action_reorder -> { - viewModel.setReorderMode(true) - true - } - - else -> super.onOptionsItemSelected(item) - } } override fun onClick(v: View) { when (v.id) { - R.id.button_done -> viewModel.setReorderMode(false) R.id.fab_add -> startActivity(FavouritesCategoryEditActivity.newIntent(this)) } } override fun onItemClick(item: FavouriteCategory, view: View) { - if (viewModel.isInReorderMode() || selectionController.onItemClick(item.id)) { + if (selectionController.onItemClick(item.id)) { return } val intent = FavouritesActivity.newIntent(this, item) @@ -116,11 +86,12 @@ class FavouriteCategoriesActivity : } override fun onItemLongClick(item: FavouriteCategory, view: View): Boolean { - return !viewModel.isInReorderMode() && selectionController.onItemLongClick(item.id) + return selectionController.onItemLongClick(item.id) } override fun onDragHandleTouch(holder: RecyclerView.ViewHolder): Boolean { - return reorderHelper?.startDrag(holder) != null + reorderHelper.startDrag(holder) + return true } override fun onRetryClick(error: Throwable) = Unit @@ -147,28 +118,6 @@ class FavouriteCategoriesActivity : invalidateOptionsMenu() } - private fun onReorderModeChanged(isReorderMode: Boolean) { - val transition = Fade().apply { - duration = resources.getInteger(android.R.integer.config_shortAnimTime).toLong() - } - TransitionManager.beginDelayedTransition(viewBinding.toolbar, transition) - reorderHelper?.attachToRecyclerView(null) - reorderHelper = if (isReorderMode) { - selectionController.clear() - viewBinding.fabAdd.hide() - ItemTouchHelper(ReorderHelperCallback()).apply { - attachToRecyclerView(viewBinding.recyclerView) - } - } else { - viewBinding.fabAdd.show() - null - } - viewBinding.recyclerView.isNestedScrollingEnabled = !isReorderMode - invalidateOptionsMenu() - viewBinding.buttonDone.isVisible = isReorderMode - exitReorderModeCallback.isEnabled = isReorderMode - } - private inner class ReorderHelperCallback : ItemTouchHelper.SimpleCallback( ItemTouchHelper.DOWN or ItemTouchHelper.UP, 0, @@ -202,14 +151,10 @@ class FavouriteCategoriesActivity : } override fun isLongPressDragEnabled(): Boolean = false - } - private class ExitReorderModeCallback( - private val viewModel: FavouritesCategoriesViewModel, - ) : OnBackPressedCallback(viewModel.isInReorderMode()) { - - override fun handleOnBackPressed() { - viewModel.setReorderMode(false) + override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { + super.onSelectedChanged(viewHolder, actionState) + viewBinding.recyclerView.isNestedScrollingEnabled = actionState == ItemTouchHelper.ACTION_STATE_IDLE } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt index f7a397df0..952c37382 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt @@ -4,9 +4,8 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.plus import org.koitharu.kotatsu.R @@ -27,30 +26,26 @@ class FavouritesCategoriesViewModel @Inject constructor( ) : BaseViewModel() { private var reorderJob: Job? = null - val isInReorderMode = MutableStateFlow(false) - val detalizedCategories = combine( - repository.observeCategoriesWithCovers(), - isInReorderMode, - ) { list, reordering -> - list.map { (category, covers) -> - CategoryListModel( - mangaCount = covers.size, - covers = covers.take(3), - category = category, - isReorderMode = reordering, - ) - }.ifEmpty { - listOf( - EmptyState( - icon = R.drawable.ic_empty_favourites, - textPrimary = R.string.text_empty_holder_primary, - textSecondary = R.string.empty_favourite_categories, - actionStringRes = 0, - ), - ) - } - }.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(LoadingState)) + val detalizedCategories = repository.observeCategoriesWithCovers() + .map { list -> + list.map { (category, covers) -> + CategoryListModel( + mangaCount = covers.size, + covers = covers.take(3), + category = category, + ) + }.ifEmpty { + listOf( + EmptyState( + icon = R.drawable.ic_empty_favourites, + textPrimary = R.string.text_empty_holder_primary, + textSecondary = R.string.empty_favourite_categories, + actionStringRes = 0, + ), + ) + } + }.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(LoadingState)) fun deleteCategory(id: Long) { launchJob { @@ -68,14 +63,8 @@ class FavouritesCategoriesViewModel @Inject constructor( settings.isAllFavouritesVisible = isVisible } - fun isInReorderMode(): Boolean = isInReorderMode.value - fun isEmpty(): Boolean = detalizedCategories.value.none { it is CategoryListModel } - fun setReorderMode(isReorderMode: Boolean) { - isInReorderMode.value = isReorderMode - } - fun reorderCategories(oldPos: Int, newPos: Int) { val prevJob = reorderJob reorderJob = launchJob(Dispatchers.Default) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryAD.kt index 9ab263483..5a89ced7c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryAD.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.favourites.ui.categories.adapter +import android.annotation.SuppressLint import android.content.res.ColorStateList import android.graphics.Color import android.graphics.drawable.ColorDrawable @@ -25,6 +26,7 @@ import org.koitharu.kotatsu.databinding.ItemCategoryBinding import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesListListener import org.koitharu.kotatsu.list.ui.model.ListModel +@SuppressLint("ClickableViewAccessibility") fun categoryAD( coil: ImageLoader, lifecycleOwner: LifecycleOwner, @@ -35,8 +37,7 @@ fun categoryAD( val eventListener = object : OnClickListener, OnLongClickListener, OnTouchListener { override fun onClick(v: View) = clickListener.onItemClick(item.category, binding.imageViewCover1) override fun onLongClick(v: View) = clickListener.onItemLongClick(item.category, binding.imageViewCover1) - override fun onTouch(v: View?, event: MotionEvent): Boolean = item.isReorderMode && - event.actionMasked == MotionEvent.ACTION_DOWN && + override fun onTouch(v: View?, event: MotionEvent): Boolean = event.actionMasked == MotionEvent.ACTION_DOWN && clickListener.onDragHandleTouch(this@adapterDelegateViewBinding) } val backgroundColor = context.getThemeColor(android.R.attr.colorBackground) @@ -57,10 +58,9 @@ fun categoryAD( val crossFadeDuration = context.getAnimationDuration(R.integer.config_defaultAnimTime).toInt() itemView.setOnClickListener(eventListener) itemView.setOnLongClickListener(eventListener) - itemView.setOnTouchListener(eventListener) + binding.imageViewHandle.setOnTouchListener(eventListener) bind { payloads -> - binding.imageViewHandle.isVisible = item.isReorderMode if (payloads.isNotEmpty()) { return@bind } @@ -74,6 +74,8 @@ fun categoryAD( item.mangaCount, ) } + binding.imageViewTracker.isVisible = item.category.isTrackingEnabled + binding.imageViewVisible.isVisible = item.category.isVisibleInLibrary repeat(coverViews.size) { i -> val cover = item.covers.getOrNull(i) coverViews[i].newImageRequest(lifecycleOwner, cover?.url)?.run { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryListModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryListModel.kt index 5995548d2..bdb3d1f5e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryListModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryListModel.kt @@ -8,7 +8,6 @@ class CategoryListModel( val mangaCount: Int, val covers: List, val category: FavouriteCategory, - val isReorderMode: Boolean, ) : ListModel { override fun areItemsTheSame(other: ListModel): Boolean { @@ -22,20 +21,26 @@ class CategoryListModel( other as CategoryListModel if (mangaCount != other.mangaCount) return false - if (isReorderMode != other.isReorderMode) return false if (covers != other.covers) return false if (category.id != other.category.id) return false if (category.title != other.category.title) return false - return category.order == other.category.order + // ignore the category.sortKey field + if (category.order != other.category.order) return false + if (category.createdAt != other.category.createdAt) return false + if (category.isTrackingEnabled != other.category.isTrackingEnabled) return false + return category.isVisibleInLibrary == other.category.isVisibleInLibrary } override fun hashCode(): Int { var result = mangaCount - result = 31 * result + isReorderMode.hashCode() result = 31 * result + covers.hashCode() result = 31 * result + category.id.hashCode() result = 31 * result + category.title.hashCode() + // ignore the category.sortKey field result = 31 * result + category.order.hashCode() + result = 31 * result + category.createdAt.hashCode() + result = 31 * result + category.isTrackingEnabled.hashCode() + result = 31 * result + category.isVisibleInLibrary.hashCode() return result } } diff --git a/app/src/main/res/drawable/ic_eye.xml b/app/src/main/res/drawable/ic_eye.xml new file mode 100644 index 000000000..17453a301 --- /dev/null +++ b/app/src/main/res/drawable/ic_eye.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/layout/item_category.xml b/app/src/main/res/layout/item_category.xml index ae3269968..d47153d96 100644 --- a/app/src/main/res/layout/item_category.xml +++ b/app/src/main/res/layout/item_category.xml @@ -84,16 +84,42 @@ android:layout_height="wrap_content" android:layout_marginStart="@dimen/margin_normal" android:layout_marginTop="4dp" - android:layout_marginEnd="?listPreferredItemPaddingEnd" + android:layout_marginEnd="8dp" android:singleLine="true" android:textAppearance="?attr/textAppearanceBodySmall" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@id/imageView_handle" + app:layout_constraintEnd_toStartOf="@id/imageView_tracker" + app:layout_constraintHorizontal_bias="0" + app:layout_constraintHorizontal_chainStyle="packed" app:layout_constraintStart_toEndOf="@id/imageView_cover3" app:layout_constraintTop_toBottomOf="@id/textView_title" app:layout_constraintVertical_chainStyle="packed" + app:layout_constraintWidth_default="wrap" tools:text="@tools:sample/lorem[1]" /> + + + + + app:layout_constraintTop_toTopOf="parent" /> diff --git a/app/src/main/res/menu/opt_categories.xml b/app/src/main/res/menu/opt_categories.xml deleted file mode 100644 index 9facdc165..000000000 --- a/app/src/main/res/menu/opt_categories.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - -