diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/MangaSource.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/MangaSource.kt index ce77eed36..0abf3138d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/MangaSource.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/MangaSource.kt @@ -2,11 +2,16 @@ package org.koitharu.kotatsu.core.model import android.content.Context import android.graphics.Color +import android.os.Build import android.text.SpannableStringBuilder import android.text.style.ForegroundColorSpan +import android.text.style.ImageSpan import android.text.style.RelativeSizeSpan import android.text.style.SuperscriptSpan +import android.widget.TextView +import androidx.annotation.DrawableRes import androidx.annotation.StringRes +import androidx.core.content.ContextCompat import androidx.core.text.inSpans import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.parser.external.ExternalMangaSource @@ -100,3 +105,16 @@ fun SpannableStringBuilder.appendNsfwLabel(context: Context) = inSpans( ) { append(context.getString(R.string.nsfw)) } + +fun SpannableStringBuilder.appendIcon(textView: TextView, @DrawableRes resId: Int): SpannableStringBuilder { + val icon = ContextCompat.getDrawable(textView.context, resId) ?: return this + icon.setTintList(textView.textColors) + val size = textView.lineHeight + icon.setBounds(0, 0, size, size) + val alignment = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + ImageSpan.ALIGN_CENTER + } else { + ImageSpan.ALIGN_BOTTOM + } + return inSpans(ImageSpan(icon, alignment)) { append(' ') } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt index db6961067..a4d2071a3 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt @@ -94,7 +94,7 @@ import org.koitharu.kotatsu.details.ui.scrobbling.ScrobblingItemDecoration import org.koitharu.kotatsu.details.ui.scrobbling.ScrollingInfoAdapter import org.koitharu.kotatsu.download.ui.dialog.DownloadDialogFragment import org.koitharu.kotatsu.download.ui.worker.DownloadStartedObserver -import org.koitharu.kotatsu.favourites.ui.categories.select.FavoriteSheet +import org.koitharu.kotatsu.favourites.ui.categories.select.FavoriteDialog import org.koitharu.kotatsu.image.ui.ImageActivity import org.koitharu.kotatsu.list.domain.MangaListMapper import org.koitharu.kotatsu.list.ui.adapter.ListItemType @@ -237,7 +237,7 @@ class DetailsActivity : R.id.chip_favorite -> { val manga = viewModel.manga.value ?: return - FavoriteSheet.show(supportFragmentManager, manga) + FavoriteDialog.show(supportFragmentManager, manga) } // R.id.chip_time -> { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteDialog.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteDialog.kt new file mode 100644 index 000000000..47210e74d --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteDialog.kt @@ -0,0 +1,150 @@ +package org.koitharu.kotatsu.favourites.ui.categories.select + +import android.content.DialogInterface +import android.content.Intent +import android.content.res.ColorStateList +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.core.graphics.ColorUtils +import androidx.core.view.isVisible +import androidx.core.widget.ImageViewCompat +import androidx.fragment.app.FragmentManager +import androidx.fragment.app.viewModels +import coil3.ImageLoader +import coil3.request.allowRgb565 +import coil3.request.crossfade +import coil3.request.error +import coil3.request.fallback +import coil3.request.placeholder +import com.google.android.material.checkbox.MaterialCheckBox +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import dagger.hilt.android.AndroidEntryPoint +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga +import org.koitharu.kotatsu.core.ui.AlertDialogFragment +import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener +import org.koitharu.kotatsu.core.util.ext.disposeImageRequest +import org.koitharu.kotatsu.core.util.ext.enqueueWith +import org.koitharu.kotatsu.core.util.ext.getAnimationDuration +import org.koitharu.kotatsu.core.util.ext.getDisplayMessage +import org.koitharu.kotatsu.core.util.ext.getThemeColor +import org.koitharu.kotatsu.core.util.ext.joinToStringWithLimit +import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra +import org.koitharu.kotatsu.core.util.ext.newImageRequest +import org.koitharu.kotatsu.core.util.ext.observe +import org.koitharu.kotatsu.core.util.ext.observeEvent +import org.koitharu.kotatsu.core.util.ext.showDistinct +import org.koitharu.kotatsu.core.util.ext.withArgs +import org.koitharu.kotatsu.databinding.SheetFavoriteCategoriesBinding +import org.koitharu.kotatsu.favourites.ui.categories.FavouriteCategoriesActivity +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 +import javax.inject.Inject + +@AndroidEntryPoint +class FavoriteDialog : AlertDialogFragment(), + OnListItemClickListener, DialogInterface.OnClickListener { + + private val viewModel by viewModels() + + @Inject + lateinit var coil: ImageLoader + + override fun onCreateViewBinding( + inflater: LayoutInflater, + container: ViewGroup?, + ) = SheetFavoriteCategoriesBinding.inflate(inflater, container, false) + + override fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder { + return super.onBuildDialog(builder) + .setPositiveButton(R.string.done, null) + .setNeutralButton(R.string.manage, this) + } + + override fun onViewBindingCreated( + binding: SheetFavoriteCategoriesBinding, + savedInstanceState: Bundle?, + ) { + super.onViewBindingCreated(binding, savedInstanceState) + val adapter = MangaCategoriesAdapter(coil, viewLifecycleOwner, this) + binding.recyclerViewCategories.adapter = adapter + viewModel.content.observe(viewLifecycleOwner, adapter) + viewModel.onError.observeEvent(viewLifecycleOwner, ::onError) + bindHeader() + } + + override fun onItemClick(item: MangaCategoryItem, view: View) { + viewModel.setChecked(item.category.id, item.checkedState != MaterialCheckBox.STATE_CHECKED) + } + + override fun onClick(dialog: DialogInterface?, which: Int) { + startActivity(Intent(context ?: return, FavouriteCategoriesActivity::class.java)) + } + + private fun onError(e: Throwable) { + Toast.makeText(context ?: return, e.getDisplayMessage(resources), Toast.LENGTH_SHORT).show() + } + + private fun bindHeader() { + val manga = viewModel.manga + val binding = viewBinding ?: return + val backgroundColor = binding.root.context.getThemeColor(android.R.attr.colorBackground) + ImageViewCompat.setImageTintList( + binding.imageViewCover3, + ColorStateList.valueOf(ColorUtils.setAlphaComponent(backgroundColor, 153)), + ) + ImageViewCompat.setImageTintList( + binding.imageViewCover2, + ColorStateList.valueOf(ColorUtils.setAlphaComponent(backgroundColor, 76)), + ) + binding.imageViewCover2.backgroundTintList = + ColorStateList.valueOf(ColorUtils.setAlphaComponent(backgroundColor, 76)) + binding.imageViewCover3.backgroundTintList = + ColorStateList.valueOf(ColorUtils.setAlphaComponent(backgroundColor, 153)) + val fallback = ColorDrawable(Color.TRANSPARENT) + val coverViews = arrayOf(binding.imageViewCover1, binding.imageViewCover2, binding.imageViewCover3) + val crossFadeDuration = binding.root.context.getAnimationDuration(R.integer.config_defaultAnimTime).toInt() + + binding.textViewTitle.text = manga.joinToStringWithLimit(binding.root.context, 92) { it.title } + + repeat(coverViews.size) { i -> + val m = manga.getOrNull(i) + val view = coverViews[i] + view.isVisible = m != null + if (m == null) { + view.disposeImageRequest() + } else { + view.newImageRequest(viewLifecycleOwner, m.coverUrl)?.run { + placeholder(R.drawable.ic_placeholder) + fallback(fallback) + mangaSourceExtra(m.source) + crossfade(crossFadeDuration * (i + 1)) + error(R.drawable.ic_error_placeholder) + allowRgb565(true) + enqueueWith(coil) + } + } + } + } + + companion object { + + private const val TAG = "FavoriteSheet" + const val KEY_MANGA_LIST = "manga_list" + + fun show(fm: FragmentManager, manga: Manga) = show(fm, setOf(manga)) + + fun show(fm: FragmentManager, manga: Collection) = FavoriteDialog().withArgs(1) { + putParcelableArrayList( + KEY_MANGA_LIST, + manga.mapTo(ArrayList(manga.size), ::ParcelableManga), + ) + }.showDistinct(fm, TAG) + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheet.kt deleted file mode 100644 index df99011ab..000000000 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheet.kt +++ /dev/null @@ -1,72 +0,0 @@ -package org.koitharu.kotatsu.favourites.ui.categories.select - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Toast -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.viewModels -import coil3.ImageLoader -import dagger.hilt.android.AndroidEntryPoint -import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga -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.showDistinct -import org.koitharu.kotatsu.core.util.ext.withArgs -import org.koitharu.kotatsu.databinding.SheetFavoriteCategoriesBinding -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 -import javax.inject.Inject - -@AndroidEntryPoint -class FavoriteSheet : BaseAdaptiveSheet(), OnListItemClickListener { - - private val viewModel by viewModels() - - @Inject - lateinit var coil: ImageLoader - - override fun onCreateViewBinding( - inflater: LayoutInflater, - container: ViewGroup?, - ) = SheetFavoriteCategoriesBinding.inflate(inflater, container, false) - - override fun onViewBindingCreated( - binding: SheetFavoriteCategoriesBinding, - savedInstanceState: Bundle?, - ) { - super.onViewBindingCreated(binding, savedInstanceState) - val adapter = MangaCategoriesAdapter(coil, viewLifecycleOwner, this) - binding.recyclerViewCategories.adapter = adapter - viewModel.content.observe(viewLifecycleOwner, adapter) - viewModel.onError.observeEvent(viewLifecycleOwner, ::onError) - } - - override fun onItemClick(item: MangaCategoryItem, view: View) { - viewModel.setChecked(item.category.id, !item.isChecked) - } - - private fun onError(e: Throwable) { - Toast.makeText(context ?: return, e.getDisplayMessage(resources), Toast.LENGTH_SHORT).show() - } - - companion object { - - private const val TAG = "FavoriteSheet" - const val KEY_MANGA_LIST = "manga_list" - - fun show(fm: FragmentManager, manga: Manga) = show(fm, setOf(manga)) - - fun show(fm: FragmentManager, manga: Collection) = FavoriteSheet().withArgs(1) { - putParcelableArrayList( - KEY_MANGA_LIST, - manga.mapTo(ArrayList(manga.size), ::ParcelableManga), - ) - }.showDistinct(fm, TAG) - } -} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheetViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheetViewModel.kt index 472e9b863..9a0ceb29d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheetViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/FavoriteSheetViewModel.kt @@ -4,6 +4,7 @@ import androidx.collection.MutableLongObjectMap import androidx.collection.MutableLongSet import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope +import com.google.android.material.checkbox.MaterialCheckBox import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow @@ -11,6 +12,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.plus +import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.model.ids import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga @@ -19,11 +21,10 @@ import org.koitharu.kotatsu.core.prefs.observeAsFlow import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.util.ext.require import org.koitharu.kotatsu.favourites.domain.FavouritesRepository -import org.koitharu.kotatsu.favourites.domain.model.Cover -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.EmptyState import org.koitharu.kotatsu.list.ui.model.ListModel -import org.koitharu.kotatsu.parsers.util.mapToSet +import org.koitharu.kotatsu.list.ui.model.LoadingState import javax.inject.Inject @HiltViewModel @@ -33,18 +34,10 @@ class FavoriteSheetViewModel @Inject constructor( settings: AppSettings, ) : BaseViewModel() { - private val manga = savedStateHandle.require>(FavoriteSheet.KEY_MANGA_LIST).mapToSet { + val manga = savedStateHandle.require>(FavoriteDialog.KEY_MANGA_LIST).map { it.manga } - private val header = CategoriesHeaderItem( - titles = manga.map { it.title }, - covers = manga.take(3).map { - Cover( - url = it.coverUrl, - source = it.source.name, - ) - }, - ) + private val refreshTrigger = MutableStateFlow(Any()) val content = combine( favouritesRepository.observeCategories(), @@ -52,7 +45,7 @@ class FavoriteSheetViewModel @Inject constructor( settings.observeAsFlow(AppSettings.KEY_TRACKER_ENABLED) { isTrackerEnabled }, ) { categories, _, tracker -> mapList(categories, tracker) - }.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(header)) + }.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(LoadingState)) fun setChecked(categoryId: Long, isChecked: Boolean) { launchJob(Dispatchers.Default) { @@ -66,22 +59,32 @@ class FavoriteSheetViewModel @Inject constructor( } private suspend fun mapList(categories: List, tracker: Boolean): List { + if (categories.isEmpty()) { + return listOf( + EmptyState( + icon = 0, + textPrimary = R.string.empty_favourite_categories, + textSecondary = 0, + actionStringRes = 0, + ), + ) + } val cats = MutableLongObjectMap(categories.size) categories.forEach { cats[it.id] = MutableLongSet(manga.size) } for (m in manga) { val ids = favouritesRepository.getCategoriesIds(m.id) ids.forEach { id -> cats[id]?.add(m.id) } } - return buildList(categories.size + 1) { - add(header) - categories.mapTo(this) { cat -> - MangaCategoryItem( - category = cat, - isChecked = cats[cat.id]?.isNotEmpty() == true, - isTrackerEnabled = tracker, - isEnabled = cats[cat.id]?.let { it.size == 0 || it.size == manga.size } == true, - ) - } + return categories.map { cat -> + MangaCategoryItem( + category = cat, + checkedState = when (cats[cat.id]?.size ?: 0) { + 0 -> MaterialCheckBox.STATE_UNCHECKED + manga.size -> MaterialCheckBox.STATE_CHECKED + else -> MaterialCheckBox.STATE_INDETERMINATE + }, + isTrackerEnabled = tracker, + ) } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/adapter/CategoriesHeaderAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/adapter/CategoriesHeaderAD.kt deleted file mode 100644 index 6f8fc332b..000000000 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/adapter/CategoriesHeaderAD.kt +++ /dev/null @@ -1,91 +0,0 @@ -package org.koitharu.kotatsu.favourites.ui.categories.select.adapter - -import android.content.Intent -import android.content.res.ColorStateList -import android.graphics.Color -import android.graphics.drawable.ColorDrawable -import android.view.View -import androidx.core.graphics.ColorUtils -import androidx.core.view.isVisible -import androidx.core.widget.ImageViewCompat -import androidx.lifecycle.LifecycleOwner -import coil3.ImageLoader -import coil3.request.allowRgb565 -import coil3.request.crossfade -import coil3.request.error -import coil3.request.fallback -import coil3.request.placeholder -import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding -import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.core.util.ext.disposeImageRequest -import org.koitharu.kotatsu.core.util.ext.enqueueWith -import org.koitharu.kotatsu.core.util.ext.getAnimationDuration -import org.koitharu.kotatsu.core.util.ext.getThemeColor -import org.koitharu.kotatsu.core.util.ext.joinToStringWithLimit -import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra -import org.koitharu.kotatsu.core.util.ext.newImageRequest -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( - coil: ImageLoader, - lifecycleOwner: LifecycleOwner, -) = adapterDelegateViewBinding( - { inflater, parent -> ItemCategoriesHeaderBinding.inflate(inflater, parent, false) }, -) { - - val onClickListener = View.OnClickListener { v -> - val intent = when (v.id) { - R.id.chip_create -> FavouritesCategoryEditActivity.newIntent(v.context) - R.id.chip_manage -> Intent(v.context, FavouriteCategoriesActivity::class.java) - else -> return@OnClickListener - } - v.context.startActivity(intent) - } - - binding.chipCreate.setOnClickListener(onClickListener) - binding.chipManage.setOnClickListener(onClickListener) - - val backgroundColor = context.getThemeColor(android.R.attr.colorBackground) - ImageViewCompat.setImageTintList( - binding.imageViewCover3, - ColorStateList.valueOf(ColorUtils.setAlphaComponent(backgroundColor, 153)), - ) - ImageViewCompat.setImageTintList( - binding.imageViewCover2, - ColorStateList.valueOf(ColorUtils.setAlphaComponent(backgroundColor, 76)), - ) - binding.imageViewCover2.backgroundTintList = - ColorStateList.valueOf(ColorUtils.setAlphaComponent(backgroundColor, 76)) - binding.imageViewCover3.backgroundTintList = - ColorStateList.valueOf(ColorUtils.setAlphaComponent(backgroundColor, 153)) - val fallback = ColorDrawable(Color.TRANSPARENT) - val coverViews = arrayOf(binding.imageViewCover1, binding.imageViewCover2, binding.imageViewCover3) - val crossFadeDuration = context.getAnimationDuration(R.integer.config_defaultAnimTime).toInt() - - bind { - binding.textViewTitle.text = item.titles.joinToStringWithLimit(context, 120) { it } - - repeat(coverViews.size) { i -> - val cover = item.covers.getOrNull(i) - val view = coverViews[i] - view.isVisible = cover != null - if (cover == null) { - view.disposeImageRequest() - } else { - view.newImageRequest(lifecycleOwner, cover.url)?.run { - placeholder(R.drawable.ic_placeholder) - fallback(fallback) - mangaSourceExtra(cover.mangaSource) - crossfade(crossFadeDuration * (i + 1)) - error(R.drawable.ic_error_placeholder) - allowRgb565(true) - enqueueWith(coil) - } - } - } - } -} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/adapter/MangaCategoriesAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/adapter/MangaCategoriesAdapter.kt index f820d6994..cb697f552 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/adapter/MangaCategoriesAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/adapter/MangaCategoriesAdapter.kt @@ -5,6 +5,9 @@ import coil3.ImageLoader import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.favourites.ui.categories.select.model.MangaCategoryItem +import org.koitharu.kotatsu.list.ui.adapter.ListItemType +import org.koitharu.kotatsu.list.ui.adapter.emptyStateListAD +import org.koitharu.kotatsu.list.ui.adapter.loadingStateAD import org.koitharu.kotatsu.list.ui.model.ListModel class MangaCategoriesAdapter( @@ -14,7 +17,8 @@ class MangaCategoriesAdapter( ) : BaseListAdapter() { init { - delegatesManager.addDelegate(mangaCategoryAD(clickListener)) - .addDelegate(categoriesHeaderAD(coil, lifecycleOwner)) + addDelegate(ListItemType.NAV_ITEM, mangaCategoryAD(clickListener)) + addDelegate(ListItemType.STATE_LOADING, loadingStateAD()) + addDelegate(ListItemType.STATE_EMPTY, emptyStateListAD(coil, lifecycleOwner, null)) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/adapter/MangaCategoryAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/adapter/MangaCategoryAD.kt index 57c0f051a..3338f5068 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/adapter/MangaCategoryAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/adapter/MangaCategoryAD.kt @@ -1,10 +1,10 @@ package org.koitharu.kotatsu.favourites.ui.categories.select.adapter -import androidx.core.view.isGone -import androidx.core.view.isVisible +import androidx.core.text.buildSpannedString import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.appendIcon import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener -import org.koitharu.kotatsu.core.util.ext.setChecked import org.koitharu.kotatsu.databinding.ItemCategoryCheckableBinding import org.koitharu.kotatsu.favourites.ui.categories.select.model.MangaCategoryItem import org.koitharu.kotatsu.list.ui.ListModelDiffCallback @@ -21,11 +21,20 @@ fun mangaCategoryAD( } bind { payloads -> - binding.root.isEnabled = item.isEnabled - binding.checkableImageView.isEnabled = item.isEnabled - binding.checkableImageView.setChecked(item.isChecked, ListModelDiffCallback.PAYLOAD_CHECKED_CHANGED in payloads) - binding.textViewTitle.text = item.category.title - binding.imageViewTracker.isVisible = item.category.isTrackingEnabled && item.isTrackerEnabled - binding.imageViewHidden.isGone = item.category.isVisibleInLibrary + binding.checkBox.checkedState = item.checkedState + if (ListModelDiffCallback.PAYLOAD_CHECKED_CHANGED !in payloads) { + binding.checkBox.text = buildSpannedString { + append(item.category.title) + if (item.isTrackerEnabled && item.category.isTrackingEnabled) { + append(' ') + appendIcon(binding.checkBox, R.drawable.ic_notification) + } + if (!item.category.isVisibleInLibrary) { + append(' ') + appendIcon(binding.checkBox, R.drawable.ic_eye_off) + } + } + binding.checkBox.jumpDrawablesToCurrentState() + } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/model/CategoriesHeaderItem.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/model/CategoriesHeaderItem.kt deleted file mode 100644 index 7c294476d..000000000 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/model/CategoriesHeaderItem.kt +++ /dev/null @@ -1,23 +0,0 @@ -package org.koitharu.kotatsu.favourites.ui.categories.select.model - -import org.koitharu.kotatsu.favourites.domain.model.Cover -import org.koitharu.kotatsu.list.ui.model.ListModel - -data class CategoriesHeaderItem( - val titles: List, - val covers: List, -) : ListModel { - - override fun areItemsTheSame(other: ListModel): Boolean { - return other is CategoriesHeaderItem - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - return javaClass == other?.javaClass - } - - override fun hashCode(): Int { - return javaClass.hashCode() - } -} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/model/MangaCategoryItem.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/model/MangaCategoryItem.kt index 0b76176a9..102200487 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/model/MangaCategoryItem.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/select/model/MangaCategoryItem.kt @@ -1,13 +1,13 @@ package org.koitharu.kotatsu.favourites.ui.categories.select.model +import com.google.android.material.checkbox.MaterialCheckBox.CheckedState import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.list.ui.ListModelDiffCallback import org.koitharu.kotatsu.list.ui.model.ListModel data class MangaCategoryItem( val category: FavouriteCategory, - val isChecked: Boolean, - val isEnabled: Boolean, + @CheckedState val checkedState: Int, val isTrackerEnabled: Boolean, ) : ListModel { @@ -16,7 +16,7 @@ data class MangaCategoryItem( } override fun getChangePayload(previousState: ListModel): Any? { - return if (previousState is MangaCategoryItem && previousState.isChecked != isChecked) { + return if (previousState is MangaCategoryItem && previousState.checkedState != checkedState) { ListModelDiffCallback.PAYLOAD_CHECKED_CHANGED } else { super.getChangePayload(previousState) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListFragment.kt index 2c1cd08b2..95a557ab4 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/MangaListFragment.kt @@ -46,7 +46,7 @@ import org.koitharu.kotatsu.core.util.ext.viewLifecycleScope import org.koitharu.kotatsu.databinding.FragmentListBinding import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.download.ui.dialog.DownloadDialogFragment -import org.koitharu.kotatsu.favourites.ui.categories.select.FavoriteSheet +import org.koitharu.kotatsu.favourites.ui.categories.select.FavoriteDialog import org.koitharu.kotatsu.list.domain.ListFilterOption import org.koitharu.kotatsu.list.domain.QuickFilterListener import org.koitharu.kotatsu.list.ui.adapter.ListItemType @@ -153,11 +153,11 @@ abstract class MangaListFragment : } override fun onItemLongClick(item: Manga, view: View): Boolean { - return selectionController?.onItemLongClick(view, item.id) ?: false + return selectionController?.onItemLongClick(view, item.id) == true } override fun onItemContextClick(item: Manga, view: View): Boolean { - return selectionController?.onItemContextClick(view, item.id) ?: false + return selectionController?.onItemContextClick(view, item.id) == true } override fun onReadClick(manga: Manga, view: View) { @@ -317,7 +317,7 @@ abstract class MangaListFragment : } R.id.action_favourite -> { - FavoriteSheet.show(getChildFragmentManager(), selectedItems) + FavoriteDialog.show(getChildFragmentManager(), selectedItems) mode?.finish() true } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/EmptyStateListAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/EmptyStateListAD.kt index 76fa9f0d7..2c5967262 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/EmptyStateListAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/EmptyStateListAD.kt @@ -1,8 +1,10 @@ package org.koitharu.kotatsu.list.ui.adapter +import androidx.core.view.isVisible import androidx.lifecycle.LifecycleOwner import coil3.ImageLoader import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding +import org.koitharu.kotatsu.core.util.ext.disposeImageRequest import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.newImageRequest import org.koitharu.kotatsu.core.util.ext.setTextAndVisible @@ -23,7 +25,13 @@ fun emptyStateListAD( } bind { - binding.icon.newImageRequest(lifecycleOwner, item.icon)?.enqueueWith(coil) + if (item.icon == 0) { + binding.icon.isVisible = false + binding.icon.disposeImageRequest() + } else { + binding.icon.isVisible = true + binding.icon.newImageRequest(lifecycleOwner, item.icon)?.enqueueWith(coil) + } binding.textPrimary.setText(item.textPrimary) binding.textSecondary.setTextAndVisible(item.textSecondary) if (listener != null) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchActivity.kt index c100efbcc..a6fcda873 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchActivity.kt @@ -27,7 +27,7 @@ import org.koitharu.kotatsu.core.util.ext.observeEvent import org.koitharu.kotatsu.databinding.ActivitySearchBinding import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.download.ui.dialog.DownloadDialogFragment -import org.koitharu.kotatsu.favourites.ui.categories.select.FavoriteSheet +import org.koitharu.kotatsu.favourites.ui.categories.select.FavoriteDialog import org.koitharu.kotatsu.list.domain.ListFilterOption import org.koitharu.kotatsu.list.ui.MangaSelectionDecoration import org.koitharu.kotatsu.list.ui.adapter.MangaListListener @@ -179,7 +179,7 @@ class SearchActivity : } R.id.action_favourite -> { - FavoriteSheet.show(supportFragmentManager, collectSelectedItems()) + FavoriteDialog.show(supportFragmentManager, collectSelectedItems()) mode?.finish() true } diff --git a/app/src/main/res/layout/item_categories_header.xml b/app/src/main/res/layout/item_categories_header.xml deleted file mode 100644 index 97990dd41..000000000 --- a/app/src/main/res/layout/item_categories_header.xml +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/item_category_checkable.xml b/app/src/main/res/layout/item_category_checkable.xml index 204086b17..f263ed4dc 100644 --- a/app/src/main/res/layout/item_category_checkable.xml +++ b/app/src/main/res/layout/item_category_checkable.xml @@ -1,66 +1,22 @@ - + android:orientation="vertical" + android:paddingStart="?listPreferredItemPaddingStart" + android:paddingEnd="?listPreferredItemPaddingEnd"> - + android:clickable="false" + android:gravity="center_vertical" + tools:checkedState="indeterminate" + tools:text="@tools:sample/cities" /> - - - - - - - + diff --git a/app/src/main/res/layout/sheet_favorite_categories.xml b/app/src/main/res/layout/sheet_favorite_categories.xml index 55239f487..0295bdbd6 100644 --- a/app/src/main/res/layout/sheet_favorite_categories.xml +++ b/app/src/main/res/layout/sheet_favorite_categories.xml @@ -1,30 +1,116 @@ - + android:paddingTop="@dimen/margin_normal"> - + android:orientation="vertical" + app:layout_constraintGuide_begin="@dimen/margin_normal" /> + + + + + + + + + + + + + tools:listitem="@layout/item_category_checkable" /> - +