From 15f37644c068e5ad8992cac4b8641a793c1c9e96 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Mon, 30 Dec 2024 09:48:50 +0200 Subject: [PATCH] Update list badges --- .../kotatsu/core/ui/widgets/BadgeView.kt | 100 ++++++++++++++++++ .../kotatsu/list/ui/adapter/BadgeADUtil.kt | 3 + .../list/ui/adapter/MangaGridItemAD.kt | 5 +- .../ui/adapter/MangaListDetailedItemAD.kt | 6 +- .../list/ui/adapter/MangaListItemAD.kt | 5 +- app/src/main/res/layout/item_manga_grid.xml | 11 ++ app/src/main/res/layout/item_manga_list.xml | 12 +++ .../res/layout/item_manga_list_details.xml | 26 +++-- app/src/main/res/values/attrs.xml | 8 ++ app/src/main/res/values/styles.xml | 14 ++- app/src/main/res/values/themes.xml | 1 + 11 files changed, 174 insertions(+), 17 deletions(-) create mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/BadgeView.kt diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/BadgeView.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/BadgeView.kt new file mode 100644 index 000000000..57efef4b1 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/widgets/BadgeView.kt @@ -0,0 +1,100 @@ +package org.koitharu.kotatsu.core.ui.widgets + +import android.content.Context +import android.os.Parcel +import android.os.Parcelable +import android.os.Parcelable.Creator +import android.util.AttributeSet +import androidx.core.content.withStyledAttributes +import androidx.customview.view.AbsSavedState +import com.google.android.material.shape.MaterialShapeDrawable +import com.google.android.material.shape.ShapeAppearanceModel +import com.google.android.material.textview.MaterialTextView +import org.koitharu.kotatsu.R + +class BadgeView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null +) : MaterialTextView(context, attrs, R.attr.badgeViewStyle) { + + private var maxCharacterCount = Int.MAX_VALUE + + var number: Int = 0 + set(value) { + field = value + updateText() + } + + init { + context.withStyledAttributes(attrs, R.styleable.BadgeView) { + maxCharacterCount = getInt(R.styleable.BadgeView_maxCharacterCount, maxCharacterCount) + number = getInt(R.styleable.BadgeView_number, number) + val shape = ShapeAppearanceModel.builder( + context, + getResourceId(R.styleable.BadgeView_shapeAppearance, 0), + 0, + ).build() + background = MaterialShapeDrawable(shape).also { bg -> + bg.fillColor = getColorStateList(R.styleable.BadgeView_backgroundColor) + } + } + } + + override fun onSaveInstanceState(): Parcelable? { + val superState = super.onSaveInstanceState() ?: return null + return SavedState(superState, number) + } + + override fun onRestoreInstanceState(state: Parcelable?) { + if (state is SavedState) { + super.onRestoreInstanceState(state.superState) + number = state.number + } else { + super.onRestoreInstanceState(state) + } + } + + private fun updateText() { + if (number <= 0) { + text = null + return + } + val numberString = number.toString() + text = if (numberString.length > maxCharacterCount) { + buildString(maxCharacterCount) { + repeat(maxCharacterCount - 1) { append('9') } + append('+') + } + } else { + numberString + } + } + + private class SavedState : AbsSavedState { + + val number: Int + + constructor(superState: Parcelable, number: Int) : super(superState) { + this.number = number + } + + constructor(source: Parcel, classLoader: ClassLoader?) : super(source, classLoader) { + number = source.readInt() + } + + override fun writeToParcel(out: Parcel, flags: Int) { + super.writeToParcel(out, flags) + out.writeInt(number) + } + + companion object { + @Suppress("unused") + @JvmField + val CREATOR: Creator = object : Creator { + override fun createFromParcel(`in`: Parcel) = SavedState(`in`, SavedState::class.java.classLoader) + + override fun newArray(size: Int): Array = arrayOfNulls(size) + } + } + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/BadgeADUtil.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/BadgeADUtil.kt index de13d0b2b..f14394b46 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/BadgeADUtil.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/BadgeADUtil.kt @@ -12,16 +12,19 @@ import com.google.android.material.badge.ExperimentalBadgeUtils import org.koitharu.kotatsu.R import org.koitharu.kotatsu.parsers.util.nullIfEmpty +@Deprecated("") @CheckResult fun View.bindBadge(badge: BadgeDrawable?, counter: Int): BadgeDrawable? { return bindBadgeImpl(badge, null, counter) } +@Deprecated("") @CheckResult fun View.bindBadge(badge: BadgeDrawable?, text: String?): BadgeDrawable? { return bindBadgeImpl(badge, text, 0) } +@Deprecated("") fun View.clearBadge(badge: BadgeDrawable?) { BadgeUtils.detachBadgeDrawable(badge, this) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt index 395b294f1..bc7b76292 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaGridItemAD.kt @@ -5,7 +5,6 @@ import androidx.lifecycle.LifecycleOwner import coil3.ImageLoader import coil3.request.allowRgb565 import coil3.request.transformations -import com.google.android.material.badge.BadgeDrawable import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.core.ui.image.CoverSizeResolver import org.koitharu.kotatsu.core.ui.image.TrimTransformation @@ -30,7 +29,6 @@ fun mangaGridItemAD( ) = adapterDelegateViewBinding( { inflater, parent -> ItemMangaGridBinding.inflate(inflater, parent, false) }, ) { - var badge: BadgeDrawable? = null AdapterDelegateClickListenerAdapter(this, clickListener, MangaGridModel::manga).attach(itemView) sizeResolver.attachToView(lifecycleOwner, itemView, binding.textViewTitle, binding.progressView) @@ -47,6 +45,7 @@ fun mangaGridItemAD( mangaExtra(item.manga) enqueueWith(coil) } - badge = itemView.bindBadge(badge, item.counter) + binding.badge.number = item.counter + binding.badge.isVisible = item.counter > 0 } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt index f56a8916f..8d6aa7688 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListDetailedItemAD.kt @@ -1,10 +1,10 @@ package org.koitharu.kotatsu.list.ui.adapter +import androidx.core.view.isVisible import androidx.lifecycle.LifecycleOwner import coil3.ImageLoader import coil3.request.allowRgb565 import coil3.request.transformations -import com.google.android.material.badge.BadgeDrawable import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.core.ui.image.CoverSizeResolver import org.koitharu.kotatsu.core.ui.image.TrimTransformation @@ -26,7 +26,6 @@ fun mangaListDetailedItemAD( ) = adapterDelegateViewBinding( { inflater, parent -> ItemMangaListDetailsBinding.inflate(inflater, parent, false) }, ) { - var badge: BadgeDrawable? = null AdapterDelegateClickListenerAdapter(this, clickListener, MangaDetailedListModel::manga).attach(itemView) @@ -46,6 +45,7 @@ fun mangaListDetailedItemAD( enqueueWith(coil) } binding.textViewTags.text = item.tags.joinToString(separator = ", ") { it.title ?: "" } - badge = itemView.bindBadge(badge, item.counter) + binding.badge.number = item.counter + binding.badge.isVisible = item.counter > 0 } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt index a11da7f82..8bf9ea8d7 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/list/ui/adapter/MangaListItemAD.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.list.ui.adapter +import androidx.core.view.isVisible import androidx.lifecycle.LifecycleOwner import coil3.ImageLoader import coil3.request.allowRgb565 @@ -26,7 +27,6 @@ fun mangaListItemAD( ) = adapterDelegateViewBinding( { inflater, parent -> ItemMangaListBinding.inflate(inflater, parent, false) }, ) { - var badge: BadgeDrawable? = null AdapterDelegateClickListenerAdapter(this, clickListener, MangaCompactListModel::manga).attach(itemView) @@ -40,6 +40,7 @@ fun mangaListItemAD( mangaExtra(item.manga) enqueueWith(coil) } - badge = itemView.bindBadge(badge, item.counter) + binding.badge.number = item.counter + binding.badge.isVisible = item.counter > 0 } } diff --git a/app/src/main/res/layout/item_manga_grid.xml b/app/src/main/res/layout/item_manga_grid.xml index 0828eeefa..546660514 100644 --- a/app/src/main/res/layout/item_manga_grid.xml +++ b/app/src/main/res/layout/item_manga_grid.xml @@ -45,6 +45,17 @@ app:srcCompat="@drawable/ic_heart" app:tint="?colorSurfaceBright" /> + + + + diff --git a/app/src/main/res/layout/item_manga_list_details.xml b/app/src/main/res/layout/item_manga_list_details.xml index ed493de8a..9573fd4e5 100644 --- a/app/src/main/res/layout/item_manga_list_details.xml +++ b/app/src/main/res/layout/item_manga_list_details.xml @@ -3,9 +3,9 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" - android:background="@drawable/custom_selectable_item_background" android:layout_width="match_parent" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + android:background="@drawable/custom_selectable_item_background"> + app:layout_constraintTop_toTopOf="parent"> + + diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 1727ef8c6..10884eb20 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -10,6 +10,7 @@ + @@ -182,4 +183,11 @@ + + + + + + + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index fd3ffe310..d12eb938d 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -163,8 +163,18 @@ @dimen/grid_spacing_outer -