diff --git a/app/build.gradle b/app/build.gradle index a8f594fe0..adf7da43e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -60,6 +60,7 @@ android { '-opt-in=kotlinx.coroutines.FlowPreview', '-opt-in=kotlin.contracts.ExperimentalContracts', '-opt-in=coil.annotation.ExperimentalCoilApi', + '-opt-in=com.google.android.material.badge.ExperimentalBadgeUtils', ] } lint { diff --git a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsActivity.kt b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsActivity.kt index 9add56679..7a14f2403 100644 --- a/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/details/ui/DetailsActivity.kt @@ -19,12 +19,10 @@ import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.core.view.updatePadding import androidx.lifecycle.lifecycleScope -import com.google.android.material.badge.BadgeDrawable import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.BaseTransientBottomBar import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject import kotlinx.coroutines.launch import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.domain.MangaIntent @@ -37,12 +35,17 @@ import org.koitharu.kotatsu.core.ui.MangaErrorDialog import org.koitharu.kotatsu.databinding.ActivityDetailsBinding import org.koitharu.kotatsu.details.ui.model.HistoryInfo import org.koitharu.kotatsu.download.ui.service.DownloadService -import org.koitharu.kotatsu.list.ui.adapter.bindBadge import org.koitharu.kotatsu.main.ui.owners.NoModalBottomSheetOwner import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.reader.ui.ReaderActivity import org.koitharu.kotatsu.reader.ui.ReaderState -import org.koitharu.kotatsu.utils.ext.* +import org.koitharu.kotatsu.utils.ViewBadge +import org.koitharu.kotatsu.utils.ext.assistedViewModels +import org.koitharu.kotatsu.utils.ext.getDisplayMessage +import org.koitharu.kotatsu.utils.ext.isReportable +import org.koitharu.kotatsu.utils.ext.setNavigationBarTransparentCompat +import org.koitharu.kotatsu.utils.ext.textAndVisible +import javax.inject.Inject @AndroidEntryPoint class DetailsActivity : @@ -60,7 +63,7 @@ class DetailsActivity : @Inject lateinit var shortcutsUpdater: ShortcutsUpdater - private var badge: BadgeDrawable? = null + private lateinit var viewBadge: ViewBadge private val viewModel: DetailsViewModel by assistedViewModels { viewModelFactory.create(MangaIntent(intent)) @@ -83,6 +86,7 @@ class DetailsActivity : } binding.buttonRead.setOnClickListener(this) binding.buttonDropdown.setOnClickListener(this) + viewBadge = ViewBadge(binding.buttonRead, this) chaptersMenuProvider = if (binding.layoutBottom != null) { val bsMediator = ChaptersBottomSheetMediator(checkNotNull(binding.layoutBottom)) @@ -151,6 +155,7 @@ class DetailsActivity : ) } } + R.id.button_dropdown -> showBranchPopupMenu() } } @@ -188,10 +193,12 @@ class DetailsActivity : ExceptionResolver.canResolve(e) -> { resolveError(e) } + manga == null -> { Toast.makeText(this, e.getDisplayMessage(resources), Toast.LENGTH_LONG).show() finishAfterTransition() } + else -> { val snackbar = makeSnackbar( e.getDisplayMessage(resources), @@ -242,7 +249,7 @@ class DetailsActivity : } private fun onNewChaptersChanged(newChapters: Int) { - badge = binding.buttonRead.bindBadge(badge, newChapters) + viewBadge.counter = newChapters } fun showChapterMissingDialog(chapterId: Long) { diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/BadgeADUtil.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/BadgeADUtil.kt index ede90b552..fdeafeb00 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/BadgeADUtil.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/BadgeADUtil.kt @@ -1,8 +1,5 @@ -@file:SuppressLint("UnsafeOptInUsageError") - package org.koitharu.kotatsu.list.ui.adapter -import android.annotation.SuppressLint import android.view.View import androidx.annotation.CheckResult import androidx.core.view.doOnNextLayout @@ -42,4 +39,4 @@ private fun initBadge(anchor: View): BadgeDrawable { private fun BadgeDrawable.align() { horizontalOffset = intrinsicWidth verticalOffset = intrinsicHeight -} \ No newline at end of file +} diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ViewBadge.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ViewBadge.kt new file mode 100644 index 000000000..32293308b --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ViewBadge.kt @@ -0,0 +1,62 @@ +package org.koitharu.kotatsu.utils + +import android.view.View +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import com.google.android.material.badge.BadgeDrawable +import com.google.android.material.badge.BadgeUtils + +class ViewBadge( + private val anchor: View, + lifecycleOwner: LifecycleOwner, +) : View.OnLayoutChangeListener, DefaultLifecycleObserver { + + private var badgeDrawable: BadgeDrawable? = null + + var counter: Int + get() = badgeDrawable?.number ?: 0 + set(value) { + val badge = badgeDrawable ?: initBadge() + badge.number = value + badge.isVisible = value > 0 + } + + init { + lifecycleOwner.lifecycle.addObserver(this) + } + + override fun onLayoutChange( + v: View?, + left: Int, + top: Int, + right: Int, + bottom: Int, + oldLeft: Int, + oldTop: Int, + oldRight: Int, + oldBottom: Int, + ) { + val badge = badgeDrawable ?: return + BadgeUtils.setBadgeDrawableBounds(badge, anchor, null) + } + + override fun onDestroy(owner: LifecycleOwner) { + super.onDestroy(owner) + clearBadge() + } + + private fun initBadge(): BadgeDrawable { + val badge = BadgeDrawable.create(anchor.context) + anchor.addOnLayoutChangeListener(this) + BadgeUtils.attachBadgeDrawable(badge, anchor) + badgeDrawable = badge + return badge + } + + private fun clearBadge() { + val badge = badgeDrawable ?: return + anchor.removeOnLayoutChangeListener(this) + BadgeUtils.detachBadgeDrawable(badge, anchor) + badgeDrawable = null + } +}