diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ffcc55356..9b1949a57 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -91,7 +91,7 @@ android:configChanges="orientation|screenSize|screenLayout|keyboardHidden" android:windowSoftInputMode="adjustResize" /> (), R.id.button_local -> MangaListActivity.newIntent(v.context, MangaSource.LOCAL) R.id.button_bookmarks -> BookmarksActivity.newIntent(v.context) R.id.button_suggestions -> SuggestionsActivity.newIntent(v.context) - R.id.button_favourites -> CategoriesActivity.newIntent(v.context) + R.id.button_favourites -> FavouriteCategoriesActivity.newIntent(v.context) R.id.button_random -> { viewModel.openRandom() return diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/data/EntityMapping.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/data/EntityMapping.kt index c6a65c78e..7afa80efd 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/data/EntityMapping.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/data/EntityMapping.kt @@ -1,9 +1,9 @@ package org.koitharu.kotatsu.favourites.data -import java.util.* import org.koitharu.kotatsu.core.db.entity.SortOrder import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.parsers.model.SortOrder +import java.util.* fun FavouriteCategoryEntity.toFavouriteCategory(id: Long = categoryId.toLong()) = FavouriteCategory( id = id, 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 148dfd820..96ca167fc 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 @@ -15,6 +15,17 @@ abstract class FavouriteCategoriesDao { @Query("SELECT * FROM favourite_categories ORDER BY sort_key") abstract fun observeAll(): Flow> + @MapInfo(valueColumn = "cover") + @Query( + """ + SELECT favourite_categories.*, manga.cover_url AS cover + FROM favourite_categories JOIN manga ON manga.manga_id IN + (SELECT manga_id FROM favourites WHERE favourites.category_id == favourite_categories.category_id) + ORDER BY favourite_categories.sort_key + """ + ) + abstract fun observeAllWithDetails(): Flow>> + @Query("SELECT * FROM favourite_categories WHERE category_id = :id") abstract fun observe(id: Long): Flow diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouriteCategoryEntity.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouriteCategoryEntity.kt index 5fe02f019..393a7847b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouriteCategoryEntity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/data/FavouriteCategoryEntity.kt @@ -13,4 +13,31 @@ class FavouriteCategoryEntity( @ColumnInfo(name = "title") val title: String, @ColumnInfo(name = "order") val order: String, @ColumnInfo(name = "track") val track: Boolean, -) \ No newline at end of file +) { + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as FavouriteCategoryEntity + + if (categoryId != other.categoryId) return false + if (createdAt != other.createdAt) return false + if (sortKey != other.sortKey) return false + if (title != other.title) return false + if (order != other.order) return false + if (track != other.track) return false + + return true + } + + override fun hashCode(): Int { + var result = categoryId + result = 31 * result + createdAt.hashCode() + result = 31 * result + sortKey + result = 31 * result + title.hashCode() + result = 31 * result + order.hashCode() + result = 31 * result + track.hashCode() + return result + } +} \ No newline at end of file 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 8d763ff29..59b432efe 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 @@ -5,10 +5,7 @@ import kotlinx.coroutines.flow.* import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.core.db.entity.* import org.koitharu.kotatsu.core.model.FavouriteCategory -import org.koitharu.kotatsu.favourites.data.FavouriteCategoryEntity -import org.koitharu.kotatsu.favourites.data.FavouriteEntity -import org.koitharu.kotatsu.favourites.data.FavouriteManga -import org.koitharu.kotatsu.favourites.data.toFavouriteCategory +import org.koitharu.kotatsu.favourites.data.* import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.tracker.work.TrackerNotificationChannels @@ -55,6 +52,13 @@ class FavouritesRepository( }.distinctUntilChanged() } + fun observeCategoriesWithDetails(): Flow>> { + return db.favouriteCategoriesDao.observeAllWithDetails() + .map { + it.mapKeys { (k, _) -> k.toFavouriteCategory() } + } + } + fun observeCategory(id: Long): Flow { return db.favouriteCategoriesDao.observe(id) .map { it?.toFavouriteCategory() } 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 5d2e88751..bb381c5cf 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 @@ -49,14 +49,14 @@ class FavouritesContainerFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val adapter = FavouritesPagerAdapter(this, this) - viewModel.visibleCategories.value?.let(::onCategoriesChanged) + viewModel.allCategories.value?.let(::onCategoriesChanged) binding.pager.adapter = adapter pagerAdapter = adapter TabLayoutMediator(binding.tabs, binding.pager, adapter).attach() actionModeDelegate.addListener(this, viewLifecycleOwner) addMenuProvider(FavouritesContainerMenuProvider(view.context)) - viewModel.visibleCategories.observe(viewLifecycleOwner, ::onCategoriesChanged) + viewModel.allCategories.observe(viewLifecycleOwner, ::onCategoriesChanged) viewModel.onError.observe(viewLifecycleOwner, ::onError) } @@ -103,10 +103,10 @@ class FavouritesContainerFragment : } override fun onTabLongClick(tabView: View, item: CategoryListModel): Boolean { - when (item) { + /*when (item) { is CategoryListModel.All -> showAllCategoriesMenu(tabView) is CategoryListModel.CategoryItem -> showCategoryMenu(tabView, item.category) - } + }*/ return true } @@ -133,7 +133,12 @@ class FavouritesContainerFragment : menu.setOnMenuItemClickListener { when (it.itemId) { R.id.action_remove -> editDelegate.deleteCategory(category) - R.id.action_edit -> startActivity(FavouritesCategoryEditActivity.newIntent(tabView.context, category.id)) + R.id.action_edit -> startActivity( + FavouritesCategoryEditActivity.newIntent( + tabView.context, + category.id + ) + ) else -> return@setOnMenuItemClickListener false } true diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesContainerMenuProvider.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesContainerMenuProvider.kt index 1b07f535d..869771a0d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesContainerMenuProvider.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesContainerMenuProvider.kt @@ -6,7 +6,7 @@ import android.view.MenuInflater import android.view.MenuItem import androidx.core.view.MenuProvider import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity +import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity class FavouritesContainerMenuProvider( private val context: Context, @@ -19,7 +19,7 @@ class FavouritesContainerMenuProvider( override fun onMenuItemSelected(menuItem: MenuItem): Boolean { return when (menuItem.itemId) { R.id.action_categories -> { - context.startActivity(CategoriesActivity.newIntent(context)) + context.startActivity(FavouriteCategoriesActivity.newIntent(context)) true } else -> false diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesPagerAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesPagerAdapter.kt index 329e06751..bde316b14 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesPagerAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesPagerAdapter.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.favourites.ui +import android.annotation.SuppressLint import android.view.View import androidx.fragment.app.Fragment import androidx.recyclerview.widget.AsyncListDiffer @@ -7,7 +8,6 @@ import androidx.recyclerview.widget.DiffUtil import androidx.viewpager2.adapter.FragmentStateAdapter import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayoutMediator -import org.koitharu.kotatsu.R import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment @@ -24,24 +24,21 @@ class FavouritesPagerAdapter( override fun createFragment(position: Int): Fragment { val item = differ.currentList[position] - return FavouritesListFragment.newInstance(item.id) + return FavouritesListFragment.newInstance(item.category.id) } override fun getItemId(position: Int): Long { - return differ.currentList[position].id + return differ.currentList[position].category.id } override fun containsItem(itemId: Long): Boolean { - return differ.currentList.any { it.id == itemId } + return differ.currentList.any { it.category.id == itemId } } override fun onConfigureTab(tab: TabLayout.Tab, position: Int) { val item = differ.currentList[position] - tab.text = when (item) { - is CategoryListModel.All -> tab.view.context.getString(R.string.all_favourites) - is CategoryListModel.CategoryItem -> item.category.title - } - tab.view.tag = item.id + tab.text = item.category.title + tab.view.tag = item.category.id tab.view.setOnLongClickListener(this) } @@ -51,7 +48,7 @@ class FavouritesPagerAdapter( override fun onLongClick(v: View): Boolean { val itemId = v.tag as? Long ?: return false - val item = differ.currentList.find { x -> x.id == itemId } ?: return false + val item = differ.currentList.find { x -> x.category.id == itemId } ?: return false return longClickListener.onTabLongClick(v, item) } @@ -60,14 +57,11 @@ class FavouritesPagerAdapter( override fun areItemsTheSame( oldItem: CategoryListModel, newItem: CategoryListModel - ): Boolean = when { - oldItem is CategoryListModel.All && newItem is CategoryListModel.All -> true - oldItem is CategoryListModel.CategoryItem && newItem is CategoryListModel.CategoryItem -> { - oldItem.category.id == newItem.category.id - } - else -> false + ): Boolean { + return oldItem.category.id == newItem.category.id } + @SuppressLint("DiffUtilEquals") override fun areContentsTheSame( oldItem: CategoryListModel, newItem: CategoryListModel diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/AllCategoriesToggleListener.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/AllCategoriesToggleListener.kt deleted file mode 100644 index 380722b84..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/AllCategoriesToggleListener.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.koitharu.kotatsu.favourites.ui.categories - -interface AllCategoriesToggleListener { - - fun onAllCategoriesToggle(isVisible: Boolean) -} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesAdapter.kt index 7a5620158..cd8418aa6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesAdapter.kt @@ -1,49 +1,49 @@ package org.koitharu.kotatsu.favourites.ui.categories +import androidx.lifecycle.LifecycleOwner import androidx.recyclerview.widget.DiffUtil +import coil.ImageLoader import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel -import org.koitharu.kotatsu.favourites.ui.categories.adapter.allCategoriesAD import org.koitharu.kotatsu.favourites.ui.categories.adapter.categoryAD +import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener +import org.koitharu.kotatsu.list.ui.adapter.emptyStateListAD +import org.koitharu.kotatsu.list.ui.adapter.loadingStateAD +import org.koitharu.kotatsu.list.ui.model.ListModel +import kotlin.jvm.internal.Intrinsics class CategoriesAdapter( + coil: ImageLoader, + lifecycleOwner: LifecycleOwner, onItemClickListener: OnListItemClickListener, - allCategoriesToggleListener: AllCategoriesToggleListener, -) : AsyncListDifferDelegationAdapter(DiffCallback()) { + listListener: ListStateHolderListener, +) : AsyncListDifferDelegationAdapter(DiffCallback()) { init { - delegatesManager.addDelegate(categoryAD(onItemClickListener)) - .addDelegate(allCategoriesAD(allCategoriesToggleListener)) - setHasStableIds(true) + delegatesManager.addDelegate(categoryAD(coil, lifecycleOwner, onItemClickListener)) + .addDelegate(emptyStateListAD(listListener)) + .addDelegate(loadingStateAD()) } - override fun getItemId(position: Int): Long { - return items[position].id - } + private class DiffCallback : DiffUtil.ItemCallback() { - private class DiffCallback : DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: ListModel, newItem: ListModel): Boolean { + return when { + oldItem is CategoryListModel && newItem is CategoryListModel -> { + oldItem.category.id == newItem.category.id + } + else -> oldItem.javaClass == newItem.javaClass + } + } - override fun areItemsTheSame( - oldItem: CategoryListModel, - newItem: CategoryListModel, - ): Boolean = oldItem.id == newItem.id + override fun areContentsTheSame(oldItem: ListModel, newItem: ListModel): Boolean { + return Intrinsics.areEqual(oldItem, newItem) + } - override fun areContentsTheSame( - oldItem: CategoryListModel, - newItem: CategoryListModel, - ): Boolean = oldItem == newItem - - override fun getChangePayload( - oldItem: CategoryListModel, - newItem: CategoryListModel, - ): Any? = when { - oldItem is CategoryListModel.All && newItem is CategoryListModel.All -> Unit - oldItem is CategoryListModel.CategoryItem && - newItem is CategoryListModel.CategoryItem && - oldItem.category.title != newItem.category.title -> null - else -> Unit + override fun getChangePayload(oldItem: ListModel, newItem: ListModel): Any? { + return super.getChangePayload(oldItem, newItem) } } } \ No newline at end of file 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/FavouriteCategoriesActivity.kt similarity index 82% rename from app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesActivity.kt rename to app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt index 80fd2a137..7b418b44c 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/FavouriteCategoriesActivity.kt @@ -1,36 +1,38 @@ package org.koitharu.kotatsu.favourites.ui.categories +import android.app.ActivityOptions import android.content.Context import android.content.Intent import android.os.Bundle import android.view.View import android.view.ViewGroup -import androidx.appcompat.widget.PopupMenu 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 import androidx.recyclerview.widget.RecyclerView import com.google.android.material.snackbar.Snackbar +import org.koin.android.ext.android.get import org.koin.androidx.viewmodel.ext.android.viewModel 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.databinding.ActivityCategoriesBinding -import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel +import org.koitharu.kotatsu.favourites.ui.FavouritesActivity import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity +import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener +import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.measureHeight -class CategoriesActivity : +class FavouriteCategoriesActivity : BaseActivity(), OnListItemClickListener, View.OnClickListener, CategoriesEditDelegate.CategoriesEditCallback, - AllCategoriesToggleListener { + ListStateHolderListener { private val viewModel by viewModel() @@ -42,7 +44,7 @@ class CategoriesActivity : super.onCreate(savedInstanceState) setContentView(ActivityCategoriesBinding.inflate(layoutInflater)) supportActionBar?.setDisplayHomeAsUpEnabled(true) - adapter = CategoriesAdapter(this, this) + adapter = CategoriesAdapter(get(), this, this, this) editDelegate = CategoriesEditDelegate(this, this) binding.recyclerView.setHasFixedSize(true) binding.recyclerView.adapter = adapter @@ -50,7 +52,7 @@ class CategoriesActivity : reorderHelper = ItemTouchHelper(ReorderHelperCallback()) reorderHelper.attachToRecyclerView(binding.recyclerView) - viewModel.allCategories.observe(this, ::onCategoriesChanged) + viewModel.detalizedCategories.observe(this, ::onCategoriesChanged) viewModel.onError.observe(this, ::onError) } @@ -61,7 +63,7 @@ class CategoriesActivity : } override fun onItemClick(item: FavouriteCategory, view: View) { - val menu = PopupMenu(view.context, view) + /*val menu = PopupMenu(view.context, view) menu.inflate(R.menu.popup_category) menu.setOnMenuItemClickListener { menuItem -> when (menuItem.itemId) { @@ -70,7 +72,11 @@ class CategoriesActivity : } true } - menu.show() + menu.show()*/ + val intent = FavouritesActivity.newIntent(this, item) + val options = + ActivityOptions.makeScaleUpAnimation(view, view.width / 2, view.height / 2, view.width, view.height) + startActivity(intent, options.toBundle()) } override fun onItemLongClick(item: FavouriteCategory, view: View): Boolean { @@ -79,9 +85,9 @@ class CategoriesActivity : return true } - override fun onAllCategoriesToggle(isVisible: Boolean) { - viewModel.setAllCategoriesVisible(isVisible) - } + override fun onRetryClick(error: Throwable) = Unit + + override fun onEmptyActionClick() = Unit override fun onWindowInsetsChanged(insets: Insets) { binding.fabAdd.updateLayoutParams { @@ -96,9 +102,8 @@ class CategoriesActivity : ) } - private fun onCategoriesChanged(categories: List) { + private fun onCategoriesChanged(categories: List) { adapter.items = categories - binding.textViewHolder.isVisible = categories.isEmpty() } private fun onError(e: Throwable) { @@ -152,6 +157,6 @@ class CategoriesActivity : SortOrder.RATING, ) - fun newIntent(context: Context) = Intent(context, CategoriesActivity::class.java) + fun newIntent(context: Context) = Intent(context, FavouriteCategoriesActivity::class.java) } } \ 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 1e24d033f..8f727e53d 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 @@ -3,14 +3,15 @@ package org.koitharu.kotatsu.favourites.ui.categories import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map 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.core.prefs.observeAsFlow import org.koitharu.kotatsu.favourites.domain.FavouritesRepository import org.koitharu.kotatsu.favourites.ui.categories.adapter.CategoryListModel +import org.koitharu.kotatsu.list.ui.model.LoadingState import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct +import org.koitharu.kotatsu.utils.ext.mapItems +import org.koitharu.kotatsu.utils.ext.requireValue import java.util.* class FavouritesCategoriesViewModel( @@ -20,19 +21,25 @@ class FavouritesCategoriesViewModel( private var reorderJob: Job? = null - val allCategories = combine( - repository.observeCategories(), - observeAllCategoriesVisible(), - ) { list, showAll -> - mapCategories(list, showAll, true) - }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default) - - val visibleCategories = combine( - repository.observeCategories(), - observeAllCategoriesVisible(), - ) { list, showAll -> - mapCategories(list, showAll, showAll && list.isNotEmpty()) - }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default) + val allCategories = repository.observeCategories() + .mapItems { + CategoryListModel( + mangaCount = 0, + covers = listOf(), + category = it, + ) + }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, emptyList()) + + val detalizedCategories = repository.observeCategoriesWithDetails() + .map { + it.map { (category, covers) -> + CategoryListModel( + mangaCount = covers.size, + covers = covers.take(3), + category = category, + ) + } + }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState)) fun deleteCategory(id: Long) { launchJob { @@ -48,30 +55,13 @@ class FavouritesCategoriesViewModel( val prevJob = reorderJob reorderJob = launchJob(Dispatchers.Default) { prevJob?.join() - val items = allCategories.value ?: error("This should not happen") - val ids = items.mapTo(ArrayList(items.size)) { it.id } + val items = detalizedCategories.requireValue() + val ids = items.mapNotNullTo(ArrayList(items.size)) { + (it as? CategoryListModel)?.category?.id + } Collections.swap(ids, oldPos, newPos) ids.remove(0L) repository.reorderCategories(ids) } } - - private fun mapCategories( - categories: List, - isAllCategoriesVisible: Boolean, - withAllCategoriesItem: Boolean, - ): List { - val result = ArrayList(categories.size + 1) - if (withAllCategoriesItem) { - result.add(CategoryListModel.All(isAllCategoriesVisible)) - } - categories.mapTo(result) { - CategoryListModel.CategoryItem(it) - } - return result - } - - private fun observeAllCategoriesVisible() = settings.observeAsFlow(AppSettings.KEY_ALL_FAVOURITES_VISIBLE) { - isAllFavouritesVisible - } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/adapter/AllCategoriesAD.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/adapter/AllCategoriesAD.kt deleted file mode 100644 index 113198bfa..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/adapter/AllCategoriesAD.kt +++ /dev/null @@ -1,20 +0,0 @@ -package org.koitharu.kotatsu.favourites.ui.categories.adapter - -import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding -import org.koitharu.kotatsu.databinding.ItemCategoriesAllBinding -import org.koitharu.kotatsu.favourites.ui.categories.AllCategoriesToggleListener - -fun allCategoriesAD( - allCategoriesToggleListener: AllCategoriesToggleListener, -) = adapterDelegateViewBinding( - { inflater, parent -> ItemCategoriesAllBinding.inflate(inflater, parent, false) } -) { - - binding.imageViewToggle.setOnClickListener { - allCategoriesToggleListener.onAllCategoriesToggle(!item.isVisible) - } - - bind { - binding.imageViewToggle.isChecked = item.isVisible - } -} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryAD.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryAD.kt index e64e36e5a..6f9de0731 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryAD.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryAD.kt @@ -1,30 +1,65 @@ package org.koitharu.kotatsu.favourites.ui.categories.adapter -import android.view.MotionEvent +import android.view.View +import android.view.View.OnClickListener +import android.view.View.OnLongClickListener +import androidx.lifecycle.LifecycleOwner +import coil.ImageLoader +import coil.request.Disposable +import coil.size.Scale +import coil.util.CoilUtils import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding +import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.databinding.ItemCategoryBinding +import org.koitharu.kotatsu.list.ui.model.ListModel +import org.koitharu.kotatsu.utils.ext.enqueueWith +import org.koitharu.kotatsu.utils.ext.newImageRequest fun categoryAD( + coil: ImageLoader, + lifecycleOwner: LifecycleOwner, clickListener: OnListItemClickListener -) = adapterDelegateViewBinding( +) = adapterDelegateViewBinding( { inflater, parent -> ItemCategoryBinding.inflate(inflater, parent, false) } ) { - binding.imageViewMore.setOnClickListener { - clickListener.onItemClick(item.category, it) + val eventListener = object : OnClickListener, OnLongClickListener { + override fun onClick(v: View) = clickListener.onItemClick(item.category, v) + override fun onLongClick(v: View) = clickListener.onItemLongClick(item.category, v) } - @Suppress("ClickableViewAccessibility") - binding.imageViewHandle.setOnTouchListener { _, event -> - if (event.actionMasked == MotionEvent.ACTION_DOWN) { - clickListener.onItemLongClick(item.category, itemView) - } else { - false + val coverViews = arrayOf(binding.imageViewCover1, binding.imageViewCover2, binding.imageViewCover3) + val imageRequests = arrayOfNulls(coverViews.size) + itemView.setOnClickListener(eventListener) + itemView.setOnLongClickListener(eventListener) + + bind { + imageRequests.forEach { it?.dispose() } + binding.textViewTitle.text = item.category.title + binding.textViewSubtitle.text = context.resources.getQuantityString( + R.plurals.items, + item.mangaCount, + item.mangaCount, + ) + repeat(coverViews.size) { i -> + imageRequests[i] = coverViews[i].newImageRequest(item.covers.getOrNull(i)) + .placeholder(R.drawable.ic_placeholder) + .fallback(null) + .error(R.drawable.ic_placeholder) + .scale(Scale.FILL) + .allowRgb565(true) + .lifecycle(lifecycleOwner) + .enqueueWith(coil) } } - bind { - binding.textViewTitle.text = item.category.title + onViewRecycled { + repeat(coverViews.size) { i -> + imageRequests[i]?.dispose() + imageRequests[i] = null + CoilUtils.dispose(coverViews[i]) + coverViews[i].setImageDrawable(null) + } } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryListModel.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryListModel.kt index 899b73e1c..352ef976a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryListModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/adapter/CategoryListModel.kt @@ -3,59 +3,33 @@ package org.koitharu.kotatsu.favourites.ui.categories.adapter import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.list.ui.model.ListModel -sealed interface CategoryListModel : ListModel { +class CategoryListModel( + val mangaCount: Int, + val covers: List, + val category: FavouriteCategory, +) : ListModel { - val id: Long + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false - class All( - val isVisible: Boolean, - ) : CategoryListModel { + other as CategoryListModel - override val id: Long = 0L + if (mangaCount != other.mangaCount) return false + if (covers != other.covers) return false + if (category.id != other.category.id) return false + if (category.title != other.category.title) return false + if (category.order != other.category.order) return false - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as All - - if (isVisible != other.isVisible) return false - - return true - } - - override fun hashCode(): Int { - return isVisible.hashCode() - } + return true } - class CategoryItem( - val category: FavouriteCategory, - ) : CategoryListModel { - - override val id: Long - get() = category.id - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as CategoryItem - - if (category.id != other.category.id) return false - if (category.title != other.category.title) return false - if (category.order != other.category.order) return false - if (category.isTrackingEnabled != other.category.isTrackingEnabled) return false - - return true - } - - override fun hashCode(): Int { - var result = category.id.hashCode() - result = 31 * result + category.title.hashCode() - result = 31 * result + category.order.hashCode() - result = 31 * result + category.isTrackingEnabled.hashCode() - return result - } + override fun hashCode(): Int { + var result = mangaCount + result = 31 * result + covers.hashCode() + result = 31 * result + category.id.hashCode() + result = 31 * result + category.title.hashCode() + result = 31 * result + category.order.hashCode() + return result } } \ No newline at end of file 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 index 96de7ea86..dab419ba1 100644 --- 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 @@ -18,7 +18,7 @@ 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.favourites.ui.categories.FavouriteCategoriesActivity import org.koitharu.kotatsu.parsers.model.SortOrder import org.koitharu.kotatsu.utils.ext.getDisplayMessage @@ -84,7 +84,7 @@ class FavouritesCategoryEditActivity : BaseActivity } override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - selectedSortOrder = CategoriesActivity.SORT_ORDERS.getOrNull(position) + selectedSortOrder = FavouriteCategoriesActivity.SORT_ORDERS.getOrNull(position) } private fun onCategoryChanged(category: FavouriteCategory?) { @@ -114,7 +114,7 @@ class FavouritesCategoryEditActivity : BaseActivity } private fun initSortSpinner() { - val entries = CategoriesActivity.SORT_ORDERS.map { getString(it.titleRes) } + val entries = FavouriteCategoriesActivity.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 @@ -122,9 +122,9 @@ class FavouritesCategoryEditActivity : BaseActivity private fun getSelectedSortOrder(): SortOrder { selectedSortOrder?.let { return it } - val entries = CategoriesActivity.SORT_ORDERS.map { getString(it.titleRes) } + val entries = FavouriteCategoriesActivity.SORT_ORDERS.map { getString(it.titleRes) } val index = entries.indexOf(binding.editSort.text.toString()) - return CategoriesActivity.SORT_ORDERS.getOrNull(index) ?: SortOrder.NEWEST + return FavouriteCategoriesActivity.SORT_ORDERS.getOrNull(index) ?: SortOrder.NEWEST } companion object { diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListMenuProvider.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListMenuProvider.kt index 2a1b08876..4c8fc4398 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListMenuProvider.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListMenuProvider.kt @@ -7,7 +7,7 @@ import androidx.core.view.MenuProvider import androidx.core.view.iterator import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.ui.titleRes -import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity +import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity class FavouritesListMenuProvider( private val viewModel: FavouritesListViewModel, @@ -16,7 +16,7 @@ class FavouritesListMenuProvider( override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { menuInflater.inflate(R.menu.opt_favourites_list, menu) menu.findItem(R.id.action_order)?.subMenu?.let { submenu -> - for ((i, item) in CategoriesActivity.SORT_ORDERS.withIndex()) { + for ((i, item) in FavouriteCategoriesActivity.SORT_ORDERS.withIndex()) { val menuItem = submenu.add(R.id.group_order, Menu.NONE, i, item.titleRes) menuItem.isCheckable = true } @@ -28,7 +28,7 @@ class FavouritesListMenuProvider( 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) + val order = FavouriteCategoriesActivity.SORT_ORDERS.getOrNull(item.order) item.isChecked = order == selectedOrder } } @@ -38,7 +38,7 @@ class FavouritesListMenuProvider( return when { menuItem.itemId == R.id.action_order -> false menuItem.groupId == R.id.group_order -> { - val order = CategoriesActivity.SORT_ORDERS.getOrNull(menuItem.order) ?: return false + val order = FavouriteCategoriesActivity.SORT_ORDERS.getOrNull(menuItem.order) ?: return false viewModel.setSortOrder(order) true } diff --git a/app/src/main/java/org/koitharu/kotatsu/library/ui/LibraryMenuProvider.kt b/app/src/main/java/org/koitharu/kotatsu/library/ui/LibraryMenuProvider.kt index 849506353..31c832cea 100644 --- a/app/src/main/java/org/koitharu/kotatsu/library/ui/LibraryMenuProvider.kt +++ b/app/src/main/java/org/koitharu/kotatsu/library/ui/LibraryMenuProvider.kt @@ -8,7 +8,7 @@ import androidx.core.view.MenuProvider import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.dialog.RememberSelectionDialogListener -import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity +import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity import org.koitharu.kotatsu.utils.ext.startOfDay import java.util.* import java.util.concurrent.TimeUnit @@ -30,7 +30,7 @@ class LibraryMenuProvider( true } R.id.action_categories -> { - context.startActivity(CategoriesActivity.newIntent(context)) + context.startActivity(FavouriteCategoriesActivity.newIntent(context)) true } else -> false diff --git a/app/src/main/java/org/koitharu/kotatsu/settings/TrackerSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/settings/TrackerSettingsFragment.kt index d64b48b43..7f85fcc2e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/settings/TrackerSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/settings/TrackerSettingsFragment.kt @@ -23,7 +23,7 @@ import org.koin.android.ext.android.inject import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.BasePreferenceFragment import org.koitharu.kotatsu.core.prefs.AppSettings -import org.koitharu.kotatsu.favourites.ui.categories.CategoriesActivity +import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity import org.koitharu.kotatsu.settings.utils.MultiSummaryProvider import org.koitharu.kotatsu.tracker.domain.TrackingRepository import org.koitharu.kotatsu.tracker.work.TrackerNotificationChannels @@ -103,7 +103,7 @@ class TrackerSettingsFragment : } } AppSettings.KEY_TRACK_CATEGORIES -> { - startActivity(CategoriesActivity.newIntent(preference.context)) + startActivity(FavouriteCategoriesActivity.newIntent(preference.context)) true } KEY_IGNORE_DOZE -> { diff --git a/app/src/main/res/layout/activity_categories.xml b/app/src/main/res/layout/activity_categories.xml index e427c9be0..fec151420 100644 --- a/app/src/main/res/layout/activity_categories.xml +++ b/app/src/main/res/layout/activity_categories.xml @@ -51,18 +51,6 @@ app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" /> - - - + android:layout_height="wrap_content" + android:background="@drawable/list_selector" + android:paddingVertical="12dp" + android:paddingStart="?android:listPreferredItemPaddingStart" + android:paddingEnd="?listPreferredItemPaddingEnd"> - + + + + + - + android:layout_marginStart="@dimen/margin_normal" + android:layout_marginTop="4dp" + android:singleLine="true" + android:textAppearance="?attr/textAppearanceBodySmall" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/imageView_cover3" + app:layout_constraintTop_toBottomOf="@id/textView_title" + app:layout_constraintVertical_chainStyle="packed" + tools:text="@tools:sample/lorem[1]" /> - \ No newline at end of file + \ No newline at end of file