Expandable manga description

pull/440/head
Koitharu 3 years ago
parent 80149b1ce7
commit 82a3b93214
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -11,6 +11,7 @@ import androidx.core.content.res.use
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.widget.TextViewCompat import androidx.core.widget.TextViewCompat
var TextView.textAndVisible: CharSequence? var TextView.textAndVisible: CharSequence?
get() = text?.takeIf { visibility == View.VISIBLE } get() = text?.takeIf { visibility == View.VISIBLE }
set(value) { set(value) {
@ -70,3 +71,13 @@ fun TextView.setThemeTextAppearance(@AttrRes resId: Int, @StyleRes fallback: Int
TextViewCompat.setTextAppearance(this, it.getResourceId(0, fallback)) 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
}

@ -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<SheetDescriptionBinding>() {
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)
}
}

@ -2,9 +2,11 @@ package org.koitharu.kotatsu.details.ui
import android.os.Bundle import android.os.Bundle
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
import android.transition.TransitionManager
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.content.ContextCompat 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.drawableTop
import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.enqueueWith
import org.koitharu.kotatsu.core.util.ext.ifNullOrEmpty 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.observe
import org.koitharu.kotatsu.core.util.ext.parentView
import org.koitharu.kotatsu.core.util.ext.resolveDp import org.koitharu.kotatsu.core.util.ext.resolveDp
import org.koitharu.kotatsu.core.util.ext.scaleUpActivityOptionsOf import org.koitharu.kotatsu.core.util.ext.scaleUpActivityOptionsOf
import org.koitharu.kotatsu.core.util.ext.textAndVisible import org.koitharu.kotatsu.core.util.ext.textAndVisible
@ -70,7 +74,7 @@ class DetailsFragment :
BaseFragment<FragmentDetailsBinding>(), BaseFragment<FragmentDetailsBinding>(),
View.OnClickListener, View.OnClickListener,
ChipsView.OnChipClickListener, ChipsView.OnChipClickListener,
OnListItemClickListener<Bookmark> { OnListItemClickListener<Bookmark>, ViewTreeObserver.OnDrawListener {
@Inject @Inject
lateinit var coil: ImageLoader lateinit var coil: ImageLoader
@ -94,6 +98,7 @@ class DetailsFragment :
binding.buttonScrobblingMore.setOnClickListener(this) binding.buttonScrobblingMore.setOnClickListener(this)
binding.buttonRelatedMore.setOnClickListener(this) binding.buttonRelatedMore.setOnClickListener(this)
binding.infoLayout.textViewSource.setOnClickListener(this) binding.infoLayout.textViewSource.setOnClickListener(this)
binding.textViewDescription.viewTreeObserver.addOnDrawListener(this)
binding.textViewDescription.movementMethod = LinkMovementMethod.getInstance() binding.textViewDescription.movementMethod = LinkMovementMethod.getInstance()
binding.chipsTags.onChipClickListener = this binding.chipsTags.onChipClickListener = this
binding.recyclerViewRelated.addItemDecoration( binding.recyclerViewRelated.addItemDecoration(
@ -132,6 +137,13 @@ class DetailsFragment :
return true return true
} }
override fun onDraw() {
viewBinding?.run {
buttonDescriptionMore.isVisible = textViewDescription.maxLines == Int.MAX_VALUE ||
textViewDescription.isTextTruncated
}
}
private fun onMangaUpdated(manga: Manga) { private fun onMangaUpdated(manga: Manga) {
with(requireViewBinding()) { with(requireViewBinding()) {
// Main // Main
@ -189,12 +201,13 @@ class DetailsFragment :
} }
private fun onDescriptionChanged(description: CharSequence?) { private fun onDescriptionChanged(description: CharSequence?) {
val tv = requireViewBinding().textViewDescription
if (description.isNullOrBlank()) { if (description.isNullOrBlank()) {
requireViewBinding().textViewDescription.setText(R.string.no_description) tv.setText(R.string.no_description)
} else { } else {
requireViewBinding().textViewDescription.text = description tv.text = description
} }
requireViewBinding().buttonDescriptionMore.isGone = description.isNullOrBlank() requireViewBinding().buttonDescriptionMore.isVisible = tv.isTextTruncated
} }
private fun onLocalSizeChanged(size: Long) { private fun onLocalSizeChanged(size: Long) {
@ -300,7 +313,13 @@ class DetailsFragment :
} }
R.id.button_description_more -> { 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 -> { R.id.button_scrobbling_more -> {

@ -164,7 +164,7 @@
app:layout_constraintBaseline_toBaselineOf="@id/textView_description_title" app:layout_constraintBaseline_toBaselineOf="@id/textView_description_title"
app:layout_constraintEnd_toEndOf="parent" /> app:layout_constraintEnd_toEndOf="parent" />
<TextView <org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
android:id="@+id/textView_description" android:id="@+id/textView_description"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -173,9 +173,10 @@
android:layout_marginEnd="@dimen/margin_normal" android:layout_marginEnd="@dimen/margin_normal"
android:ellipsize="end" android:ellipsize="end"
android:lineSpacingMultiplier="1.2" android:lineSpacingMultiplier="1.2"
android:maxLines="5" android:maxLines="@integer/details_description_lines"
android:paddingBottom="@dimen/margin_normal" android:paddingBottom="@dimen/margin_normal"
android:textAppearance="?attr/textAppearanceBodyMedium" android:textAppearance="?attr/textAppearanceBodyMedium"
android:textIsSelectable="true"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textView_description_title" app:layout_constraintTop_toBottomOf="@id/textView_description_title"

@ -1,34 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:orientation="vertical">
<org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetHeaderBar
android:id="@+id/headerBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/description" />
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
android:id="@+id/textView_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_normal"
android:layout_marginEnd="@dimen/margin_normal"
android:lineSpacingMultiplier="1.2"
android:paddingBottom="@dimen/margin_normal"
android:textAppearance="?attr/textAppearanceBodyMedium"
android:textIsSelectable="true"
tools:text="@tools:sample/lorem/random" />
</androidx.core.widget.NestedScrollView>
</LinearLayout>

@ -6,4 +6,5 @@
<integer name="manga_badge_max_character_count">3</integer> <integer name="manga_badge_max_character_count">3</integer>
<integer name="explore_buttons_columns">2</integer> <integer name="explore_buttons_columns">2</integer>
<integer name="details_description_lines">5</integer>
</resources> </resources>

Loading…
Cancel
Save