From 82a3b9321432ed8ed536d6c77fcf76cd675ac203 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Thu, 20 Jul 2023 13:33:48 +0300 Subject: [PATCH] Expandable manga description --- .../kotatsu/core/util/ext/TextView.kt | 11 ++++++ .../kotatsu/details/ui/DescriptionSheet.kt | 32 ----------------- .../kotatsu/details/ui/DetailsFragment.kt | 29 +++++++++++++--- app/src/main/res/layout/fragment_details.xml | 5 +-- app/src/main/res/layout/sheet_description.xml | 34 ------------------- app/src/main/res/values/integers.xml | 1 + 6 files changed, 39 insertions(+), 73 deletions(-) delete mode 100644 app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DescriptionSheet.kt delete mode 100644 app/src/main/res/layout/sheet_description.xml diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/TextView.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/TextView.kt index 424e1e77c..038619b66 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/TextView.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/TextView.kt @@ -11,6 +11,7 @@ import androidx.core.content.res.use import androidx.core.view.isGone import androidx.core.widget.TextViewCompat + var TextView.textAndVisible: CharSequence? get() = text?.takeIf { visibility == View.VISIBLE } set(value) { @@ -70,3 +71,13 @@ fun TextView.setThemeTextAppearance(@AttrRes resId: Int, @StyleRes fallback: Int TextViewCompat.setTextAppearance(this, it.getResourceId(0, fallback)) } } + +val TextView.isTextTruncated: Boolean + get() { + val l = layout ?: return false + if (maxLines in 0 until l.lineCount) { + return true + } + val layoutLines = l.lineCount + return layoutLines > 0 && l.getEllipsisCount(layoutLines - 1) > 0 + } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DescriptionSheet.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DescriptionSheet.kt deleted file mode 100644 index b02afc4ea..000000000 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DescriptionSheet.kt +++ /dev/null @@ -1,32 +0,0 @@ -package org.koitharu.kotatsu.details.ui - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.fragment.app.FragmentManager -import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet -import org.koitharu.kotatsu.core.util.ext.showDistinct -import org.koitharu.kotatsu.core.util.ext.withArgs -import org.koitharu.kotatsu.databinding.SheetDescriptionBinding - -class DescriptionSheet : BaseAdaptiveSheet() { - - override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): SheetDescriptionBinding { - return SheetDescriptionBinding.inflate(inflater, container, false) - } - - override fun onViewBindingCreated(binding: SheetDescriptionBinding, savedInstanceState: Bundle?) { - super.onViewBindingCreated(binding, savedInstanceState) - binding.textViewDescription.text = arguments?.getCharSequence(ARG_CONTENT) - } - - companion object { - - private const val ARG_CONTENT = "content" - private const val TAG = "DescriptionSheet" - - fun show(fm: FragmentManager, content: CharSequence) = DescriptionSheet().withArgs(1) { - putCharSequence(ARG_CONTENT, content) - }.showDistinct(fm, TAG) - } -} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsFragment.kt index 6e8021cc2..ff9bdc8ed 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsFragment.kt @@ -2,9 +2,11 @@ package org.koitharu.kotatsu.details.ui import android.os.Bundle import android.text.method.LinkMovementMethod +import android.transition.TransitionManager import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.ViewTreeObserver import android.widget.Toast import androidx.appcompat.widget.PopupMenu import androidx.core.content.ContextCompat @@ -36,7 +38,9 @@ import org.koitharu.kotatsu.core.util.ext.crossfade import org.koitharu.kotatsu.core.util.ext.drawableTop import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.ifNullOrEmpty +import org.koitharu.kotatsu.core.util.ext.isTextTruncated import org.koitharu.kotatsu.core.util.ext.observe +import org.koitharu.kotatsu.core.util.ext.parentView import org.koitharu.kotatsu.core.util.ext.resolveDp import org.koitharu.kotatsu.core.util.ext.scaleUpActivityOptionsOf import org.koitharu.kotatsu.core.util.ext.textAndVisible @@ -70,7 +74,7 @@ class DetailsFragment : BaseFragment(), View.OnClickListener, ChipsView.OnChipClickListener, - OnListItemClickListener { + OnListItemClickListener, ViewTreeObserver.OnDrawListener { @Inject lateinit var coil: ImageLoader @@ -94,6 +98,7 @@ class DetailsFragment : binding.buttonScrobblingMore.setOnClickListener(this) binding.buttonRelatedMore.setOnClickListener(this) binding.infoLayout.textViewSource.setOnClickListener(this) + binding.textViewDescription.viewTreeObserver.addOnDrawListener(this) binding.textViewDescription.movementMethod = LinkMovementMethod.getInstance() binding.chipsTags.onChipClickListener = this binding.recyclerViewRelated.addItemDecoration( @@ -132,6 +137,13 @@ class DetailsFragment : return true } + override fun onDraw() { + viewBinding?.run { + buttonDescriptionMore.isVisible = textViewDescription.maxLines == Int.MAX_VALUE || + textViewDescription.isTextTruncated + } + } + private fun onMangaUpdated(manga: Manga) { with(requireViewBinding()) { // Main @@ -189,12 +201,13 @@ class DetailsFragment : } private fun onDescriptionChanged(description: CharSequence?) { + val tv = requireViewBinding().textViewDescription if (description.isNullOrBlank()) { - requireViewBinding().textViewDescription.setText(R.string.no_description) + tv.setText(R.string.no_description) } else { - requireViewBinding().textViewDescription.text = description + tv.text = description } - requireViewBinding().buttonDescriptionMore.isGone = description.isNullOrBlank() + requireViewBinding().buttonDescriptionMore.isVisible = tv.isTextTruncated } private fun onLocalSizeChanged(size: Long) { @@ -300,7 +313,13 @@ class DetailsFragment : } R.id.button_description_more -> { - DescriptionSheet.show(parentFragmentManager, viewModel.description.value ?: return) + val tv = requireViewBinding().textViewDescription + TransitionManager.beginDelayedTransition(tv.parentView) + if (tv.maxLines in 1 until Integer.MAX_VALUE) { + tv.maxLines = Integer.MAX_VALUE + } else { + tv.maxLines = resources.getInteger(R.integer.details_description_lines) + } } R.id.button_scrobbling_more -> { diff --git a/app/src/main/res/layout/fragment_details.xml b/app/src/main/res/layout/fragment_details.xml index be0bdc3f9..17edfc03e 100644 --- a/app/src/main/res/layout/fragment_details.xml +++ b/app/src/main/res/layout/fragment_details.xml @@ -164,7 +164,7 @@ app:layout_constraintBaseline_toBaselineOf="@id/textView_description_title" app:layout_constraintEnd_toEndOf="parent" /> - - - - - - - - - - - diff --git a/app/src/main/res/values/integers.xml b/app/src/main/res/values/integers.xml index 7cbbd343a..a52bd1d69 100644 --- a/app/src/main/res/values/integers.xml +++ b/app/src/main/res/values/integers.xml @@ -6,4 +6,5 @@ 3 2 + 5