Use SideSheet instead of BottomSheet on landscape

This commit is contained in:
Koitharu
2023-05-30 20:27:38 +03:00
parent 3d05541f61
commit 0c132a521e
38 changed files with 700 additions and 177 deletions

View File

@@ -2,34 +2,29 @@ package org.koitharu.kotatsu.favourites.ui.categories.select
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.viewModels
import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
import org.koitharu.kotatsu.core.ui.BaseBottomSheet
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent
import org.koitharu.kotatsu.core.util.ext.withArgs
import org.koitharu.kotatsu.databinding.SheetFavoriteCategoriesBinding
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.list.ui.model.ListModel
import org.koitharu.kotatsu.parsers.model.Manga
@AndroidEntryPoint
class FavouriteCategoriesBottomSheet :
BaseBottomSheet<SheetFavoriteCategoriesBinding>(),
OnListItemClickListener<MangaCategoryItem>,
View.OnClickListener,
Toolbar.OnMenuItemClickListener {
class FavouriteCategoriesSheet :
BaseAdaptiveSheet<SheetFavoriteCategoriesBinding>(),
OnListItemClickListener<MangaCategoryItem> {
private val viewModel: MangaCategoriesViewModel by viewModels()
@@ -40,13 +35,13 @@ class FavouriteCategoriesBottomSheet :
container: ViewGroup?,
) = SheetFavoriteCategoriesBinding.inflate(inflater, container, false)
override fun onViewBindingCreated(binding: SheetFavoriteCategoriesBinding, savedInstanceState: Bundle?) {
override fun onViewBindingCreated(
binding: SheetFavoriteCategoriesBinding,
savedInstanceState: Bundle?,
) {
super.onViewBindingCreated(binding, savedInstanceState)
adapter = MangaCategoriesAdapter(this)
binding.recyclerViewCategories.adapter = adapter
binding.buttonDone.setOnClickListener(this)
binding.headerBar.toolbar.setOnMenuItemClickListener(this)
viewModel.content.observe(viewLifecycleOwner, this::onContentChanged)
viewModel.onError.observeEvent(viewLifecycleOwner, ::onError)
}
@@ -56,25 +51,11 @@ class FavouriteCategoriesBottomSheet :
super.onDestroyView()
}
override fun onClick(v: View) {
when (v.id) {
R.id.button_done -> dismiss()
}
}
override fun onMenuItemClick(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_create -> startActivity(FavouritesCategoryEditActivity.newIntent(requireContext()))
else -> return false
}
return true
}
override fun onItemClick(item: MangaCategoryItem, view: View) {
viewModel.setChecked(item.id, !item.isChecked)
}
private fun onContentChanged(categories: List<MangaCategoryItem>) {
private fun onContentChanged(categories: List<ListModel>) {
adapter?.items = categories
}
@@ -89,11 +70,17 @@ class FavouriteCategoriesBottomSheet :
fun show(fm: FragmentManager, manga: Manga) = Companion.show(fm, listOf(manga))
fun show(fm: FragmentManager, manga: Collection<Manga>) = FavouriteCategoriesBottomSheet().withArgs(1) {
putParcelableArrayList(
KEY_MANGA_LIST,
manga.mapTo(ArrayList(manga.size)) { ParcelableManga(it, withChapters = false) },
)
}.show(fm, TAG)
fun show(fm: FragmentManager, manga: Collection<Manga>) =
FavouriteCategoriesSheet().withArgs(1) {
putParcelableArrayList(
KEY_MANGA_LIST,
manga.mapTo(ArrayList(manga.size)) {
ParcelableManga(
it,
withChapters = false,
)
},
)
}.show(fm, TAG)
}
}

View File

@@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
@@ -12,8 +13,10 @@ import org.koitharu.kotatsu.core.model.ids
import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
import org.koitharu.kotatsu.favourites.ui.categories.select.FavouriteCategoriesBottomSheet.Companion.KEY_MANGA_LIST
import org.koitharu.kotatsu.favourites.ui.categories.select.FavouriteCategoriesSheet.Companion.KEY_MANGA_LIST
import org.koitharu.kotatsu.favourites.ui.categories.select.model.CategoriesHeaderItem
import org.koitharu.kotatsu.favourites.ui.categories.select.model.MangaCategoryItem
import org.koitharu.kotatsu.list.ui.model.ListModel
import javax.inject.Inject
@HiltViewModel
@@ -23,17 +26,21 @@ class MangaCategoriesViewModel @Inject constructor(
) : BaseViewModel() {
private val manga = requireNotNull(savedStateHandle.get<List<ParcelableManga>>(KEY_MANGA_LIST)).map { it.manga }
private val header = CategoriesHeaderItem()
val content = combine(
val content: StateFlow<List<ListModel>> = combine(
favouritesRepository.observeCategories(),
observeCategoriesIds(),
) { all, checked ->
all.map {
MangaCategoryItem(
id = it.id,
name = it.title,
isChecked = it.id in checked,
)
buildList(all.size + 1) {
add(header)
all.mapTo(this) {
MangaCategoryItem(
id = it.id,
name = it.title,
isChecked = it.id in checked,
)
}
}
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, emptyList())

View File

@@ -0,0 +1,27 @@
package org.koitharu.kotatsu.favourites.ui.categories.select.adapter
import android.view.View
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.databinding.ItemCategoriesHeaderBinding
import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity
import org.koitharu.kotatsu.favourites.ui.categories.edit.FavouritesCategoryEditActivity
import org.koitharu.kotatsu.favourites.ui.categories.select.model.CategoriesHeaderItem
import org.koitharu.kotatsu.list.ui.model.ListModel
fun categoriesHeaderAD() = adapterDelegateViewBinding<CategoriesHeaderItem, ListModel, ItemCategoriesHeaderBinding>(
{ inflater, parent -> ItemCategoriesHeaderBinding.inflate(inflater, parent, false) },
) {
val onClickListener = View.OnClickListener { v ->
val intent = when (v.id) {
R.id.button_create -> FavouritesCategoryEditActivity.newIntent(v.context)
R.id.button_manage -> FavouriteCategoriesActivity.newIntent(v.context)
else -> return@OnClickListener
}
v.context.startActivity(intent)
}
binding.buttonCreate.setOnClickListener(onClickListener)
binding.buttonManage.setOnClickListener(onClickListener)
}

View File

@@ -3,32 +3,39 @@ package org.koitharu.kotatsu.favourites.ui.categories.select.adapter
import androidx.recyclerview.widget.DiffUtil
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.favourites.ui.categories.select.model.CategoriesHeaderItem
import org.koitharu.kotatsu.favourites.ui.categories.select.model.MangaCategoryItem
import org.koitharu.kotatsu.list.ui.model.ListModel
class MangaCategoriesAdapter(
clickListener: OnListItemClickListener<MangaCategoryItem>
) : AsyncListDifferDelegationAdapter<MangaCategoryItem>(DiffCallback()) {
clickListener: OnListItemClickListener<MangaCategoryItem>,
) : AsyncListDifferDelegationAdapter<ListModel>(DiffCallback()) {
init {
delegatesManager.addDelegate(mangaCategoryAD(clickListener))
.addDelegate(categoriesHeaderAD())
}
private class DiffCallback : DiffUtil.ItemCallback<MangaCategoryItem>() {
private class DiffCallback : DiffUtil.ItemCallback<ListModel>() {
override fun areItemsTheSame(
oldItem: MangaCategoryItem,
newItem: MangaCategoryItem
): Boolean = oldItem.id == newItem.id
oldItem: ListModel,
newItem: ListModel,
): Boolean = when {
oldItem is MangaCategoryItem && newItem is MangaCategoryItem -> oldItem.id == newItem.id
oldItem is CategoriesHeaderItem && newItem is CategoriesHeaderItem -> oldItem == newItem
else -> false
}
override fun areContentsTheSame(
oldItem: MangaCategoryItem,
newItem: MangaCategoryItem
oldItem: ListModel,
newItem: ListModel,
): Boolean = oldItem == newItem
override fun getChangePayload(
oldItem: MangaCategoryItem,
newItem: MangaCategoryItem
oldItem: ListModel,
newItem: ListModel,
): Any? {
if (oldItem.isChecked != newItem.isChecked) {
if (oldItem is MangaCategoryItem && newItem is MangaCategoryItem && oldItem.isChecked != newItem.isChecked) {
return newItem.isChecked
}
return super.getChangePayload(oldItem, newItem)

View File

@@ -4,10 +4,11 @@ import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.databinding.ItemCheckableNewBinding
import org.koitharu.kotatsu.favourites.ui.categories.select.model.MangaCategoryItem
import org.koitharu.kotatsu.list.ui.model.ListModel
fun mangaCategoryAD(
clickListener: OnListItemClickListener<MangaCategoryItem>
) = adapterDelegateViewBinding<MangaCategoryItem, MangaCategoryItem, ItemCheckableNewBinding>(
clickListener: OnListItemClickListener<MangaCategoryItem>,
) = adapterDelegateViewBinding<MangaCategoryItem, ListModel, ItemCheckableNewBinding>(
{ inflater, parent -> ItemCheckableNewBinding.inflate(inflater, parent, false) },
) {

View File

@@ -0,0 +1,8 @@
package org.koitharu.kotatsu.favourites.ui.categories.select.model
import org.koitharu.kotatsu.list.ui.model.ListModel
class CategoriesHeaderItem : ListModel {
override fun equals(other: Any?): Boolean = other?.javaClass == CategoriesHeaderItem::class.java
}

View File

@@ -1,7 +1,9 @@
package org.koitharu.kotatsu.favourites.ui.categories.select.model
import org.koitharu.kotatsu.list.ui.model.ListModel
data class MangaCategoryItem(
val id: Long,
val name: String,
val isChecked: Boolean
)
val isChecked: Boolean,
) : ListModel