diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt index 5dff9c637..ca47583d0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt @@ -20,9 +20,7 @@ import eu.kanade.tachiyomi.ui.base.controller.getMainAppBarHeight import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.view.loadAnyAutoPause -import eu.kanade.tachiyomi.util.view.setChips import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach import reactivecircus.flowbinding.android.view.clicks import reactivecircus.flowbinding.android.view.longClicks @@ -45,13 +43,14 @@ class MangaInfoHeaderAdapter( private lateinit var binding: MangaInfoHeaderBinding - private var initialLoad: Boolean = true - - private val maxLines = 3 - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder { binding = MangaInfoHeaderBinding.inflate(LayoutInflater.from(parent.context), parent, false) updateCoverPosition() + + // Expand manga info if navigated from source listing or explicitly set to + // (e.g. on tablets) + binding.mangaSummarySection.expanded = fromSource || isTablet + return HeaderViewHolder(binding.root) } @@ -180,15 +179,6 @@ class MangaInfoHeaderAdapter( } .launchIn(controller.viewScope) - binding.mangaSummaryText.longClicks() - .onEach { - controller.activity?.copyToClipboard( - view.context.getString(R.string.description), - binding.mangaSummaryText.text.toString() - ) - } - .launchIn(controller.viewScope) - binding.mangaCover.clicks() .onEach { controller.showFullCoverDialog() @@ -201,7 +191,7 @@ class MangaInfoHeaderAdapter( } .launchIn(controller.viewScope) - setMangaInfo(manga, source) + setMangaInfo() } private fun showCoverOptionsDialog() { @@ -231,7 +221,7 @@ class MangaInfoHeaderAdapter( * @param manga manga object containing information about manga. * @param source the source of the manga. */ - private fun setMangaInfo(manga: Manga, source: Source?) { + private fun setMangaInfo() { // Update full title TextView. binding.mangaFullTitle.text = if (manga.title.isBlank()) { view.context.getString(R.string.unknown) @@ -254,27 +244,23 @@ class MangaInfoHeaderAdapter( } // If manga source is known update source TextView. - val mangaSource = source?.toString() + val mangaSource = source.toString() with(binding.mangaSource) { - if (mangaSource != null) { - val enabledLanguages = preferences.enabledLanguages().get() - .filterNot { it in listOf("all", "other") } + val enabledLanguages = preferences.enabledLanguages().get() + .filterNot { it in listOf("all", "other") } - val hasOneActiveLanguages = enabledLanguages.size == 1 - val isInEnabledLanguages = source.lang in enabledLanguages - text = when { - // For edge cases where user disables a source they got manga of in their library. - hasOneActiveLanguages && !isInEnabledLanguages -> mangaSource - // Hide the language tag when only one language is used. - hasOneActiveLanguages && isInEnabledLanguages -> source.name - else -> mangaSource - } + val hasOneActiveLanguages = enabledLanguages.size == 1 + val isInEnabledLanguages = source.lang in enabledLanguages + text = when { + // For edge cases where user disables a source they got manga of in their library. + hasOneActiveLanguages && !isInEnabledLanguages -> mangaSource + // Hide the language tag when only one language is used. + hasOneActiveLanguages && isInEnabledLanguages -> source.name + else -> mangaSource + } - setOnClickListener { - controller.performSearch(sourceManager.getOrStub(source.id).name) - } - } else { - text = view.context.getString(R.string.unknown) + setOnClickListener { + controller.performSearch(sourceManager.getOrStub(source.id).name) } } @@ -296,84 +282,9 @@ class MangaInfoHeaderAdapter( binding.mangaCover.loadAnyAutoPause(manga) // Manga info section - val hasInfoContent = !manga.description.isNullOrBlank() || !manga.genre.isNullOrBlank() - showMangaInfo(hasInfoContent) - if (hasInfoContent) { - // Update description TextView. - binding.mangaSummaryText.text = updateDescription(manga.description, (fromSource || isTablet).not()) - - // Update genres list - if (!manga.genre.isNullOrBlank()) { - binding.mangaGenresTagsCompactChips.setChips( - manga.getGenres(), - controller::performGenreSearch - ) - binding.mangaGenresTagsFullChips.setChips( - manga.getGenres(), - controller::performGenreSearch - ) - } else { - binding.mangaGenresTagsCompact.isVisible = false - binding.mangaGenresTagsCompactChips.isVisible = false - binding.mangaGenresTagsFullChips.isVisible = false - } - - // Handle showing more or less info - merge( - binding.mangaSummaryText.clicks(), - binding.mangaInfoToggleMore.clicks(), - binding.mangaInfoToggleLess.clicks(), - binding.mangaSummarySection.clicks(), - ) - .onEach { toggleMangaInfo() } - .launchIn(controller.viewScope) - - if (initialLoad) { - binding.mangaGenresTagsCompact.requestLayout() - } - - // Expand manga info if navigated from source listing or explicitly set to - // (e.g. on tablets) - if (initialLoad && (fromSource || isTablet)) { - toggleMangaInfo() - initialLoad = false - } - } - } - - private fun showMangaInfo(visible: Boolean) { - binding.mangaSummarySection.isVisible = visible - } - - private fun toggleMangaInfo() { - val isCurrentlyExpanded = binding.mangaSummaryText.maxLines != maxLines - - binding.mangaInfoToggleMore.isVisible = isCurrentlyExpanded - binding.mangaInfoScrim.isVisible = isCurrentlyExpanded - binding.mangaInfoToggleMoreScrim.isVisible = isCurrentlyExpanded - binding.mangaGenresTagsCompact.isVisible = isCurrentlyExpanded - binding.mangaGenresTagsCompactChips.isVisible = isCurrentlyExpanded - - binding.mangaInfoToggleLess.isVisible = !isCurrentlyExpanded - binding.mangaGenresTagsFullChips.isVisible = !isCurrentlyExpanded - - binding.mangaSummaryText.text = updateDescription(manga.description, isCurrentlyExpanded) - - binding.mangaSummaryText.maxLines = when { - isCurrentlyExpanded -> maxLines - else -> Int.MAX_VALUE - } - } - - private fun updateDescription(description: String?, isCurrentlyExpanded: Boolean): CharSequence { - return when { - description.isNullOrBlank() -> view.context.getString(R.string.unknown) - isCurrentlyExpanded -> - description - .replace(Regex(" +\$", setOf(RegexOption.MULTILINE)), "") - .replace(Regex("[\\r\\n]{2,}", setOf(RegexOption.MULTILINE)), "\n") - else -> description - } + binding.mangaSummarySection.isVisible = !manga.description.isNullOrBlank() || !manga.genre.isNullOrBlank() + binding.mangaSummarySection.description = manga.description + binding.mangaSummarySection.setTags(manga.getGenres(), controller::performGenreSearch) } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/MangaSummaryView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/MangaSummaryView.kt new file mode 100644 index 000000000..d16b814d5 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/MangaSummaryView.kt @@ -0,0 +1,188 @@ +package eu.kanade.tachiyomi.widget + +import android.animation.AnimatorSet +import android.animation.ValueAnimator +import android.content.Context +import android.graphics.drawable.Animatable +import android.util.AttributeSet +import android.view.LayoutInflater +import android.widget.FrameLayout +import androidx.annotation.AttrRes +import androidx.annotation.StyleRes +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.content.ContextCompat +import androidx.core.view.doOnNextLayout +import androidx.core.view.isVisible +import androidx.core.view.updateLayoutParams +import androidx.interpolator.view.animation.FastOutSlowInInterpolator +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.databinding.MangaSummaryBinding +import eu.kanade.tachiyomi.util.system.animatorDurationScale +import eu.kanade.tachiyomi.util.system.copyToClipboard +import eu.kanade.tachiyomi.util.view.setChips +import kotlin.math.roundToInt +import kotlin.math.roundToLong + +class MangaSummaryView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + @AttrRes defStyleAttr: Int = 0, + @StyleRes defStyleRes: Int = 0 +) : FrameLayout(context, attrs, defStyleAttr, defStyleRes) { + + private val binding = MangaSummaryBinding.inflate(LayoutInflater.from(context), this, true) + + private var animatorSet: AnimatorSet? = null + + private var recalculateHeights = false + private var descExpandedHeight = -1 + private var descShrunkHeight = -1 + + var expanded = false + set(value) { + if (field != value) { + field = value + updateExpandState() + } + } + + var description: CharSequence? = null + set(value) { + if (field != value) { + field = if (value.isNullOrBlank()) { + context.getString(R.string.unknown) + } else { + value + } + binding.descriptionText.text = field + recalculateHeights = true + doOnNextLayout { + updateExpandState() + } + requestLayout() + } + } + + fun setTags(items: List<String>?, onClick: (item: String) -> Unit) { + binding.tagChipsShrunk.setChips(items, onClick) + binding.tagChipsExpanded.setChips(items, onClick) + } + + private fun updateExpandState() = binding.apply { + val initialSetup = descriptionText.maxHeight < 0 + + val maxHeightTarget = if (expanded) descExpandedHeight else descShrunkHeight + val maxHeightStart = if (initialSetup) maxHeightTarget else descriptionText.maxHeight + val descMaxHeightAnimator = ValueAnimator().apply { + setIntValues(maxHeightStart, maxHeightTarget) + addUpdateListener { + descriptionText.maxHeight = it.animatedValue as Int + } + } + + val toggleDrawable = ContextCompat.getDrawable( + context, + if (expanded) R.drawable.anim_caret_up else R.drawable.anim_caret_down + ) + toggleMore.setImageDrawable(toggleDrawable) + + var pastHalf = false + val toggleTarget = if (expanded) 1F else 0F + val toggleStart = if (initialSetup) { + toggleTarget + } else { + toggleMore.translationY / toggleMore.height + } + val toggleAnimator = ValueAnimator().apply { + setFloatValues(toggleStart, toggleTarget) + addUpdateListener { + val value = it.animatedValue as Float + + toggleMore.translationY = toggleMore.height * value + descriptionScrim.translationY = toggleMore.translationY + toggleMoreScrim.translationY = toggleMore.translationY + tagChipsShrunkContainer.updateLayoutParams<ConstraintLayout.LayoutParams> { + topMargin = toggleMore.translationY.roundToInt() + } + tagChipsExpanded.updateLayoutParams<ConstraintLayout.LayoutParams> { + topMargin = toggleMore.translationY.roundToInt() + } + + // Update non-animatable objects mid-animation makes it feel less abrupt + if (it.animatedFraction >= 0.5F && !pastHalf) { + pastHalf = true + descriptionText.text = trimWhenNeeded(description) + tagChipsShrunkContainer.scrollX = 0 + tagChipsShrunkContainer.isVisible = !expanded + tagChipsExpanded.isVisible = expanded + } + } + } + + animatorSet?.cancel() + animatorSet = AnimatorSet().apply { + interpolator = FastOutSlowInInterpolator() + duration = (TOGGLE_ANIM_DURATION * context.animatorDurationScale).roundToLong() + playTogether(toggleAnimator, descMaxHeightAnimator) + start() + } + (toggleDrawable as? Animatable)?.start() + } + + private fun trimWhenNeeded(text: CharSequence?): CharSequence? { + return if (!expanded) { + text + ?.replace(Regex(" +\$", setOf(RegexOption.MULTILINE)), "") + ?.replace(Regex("[\\r\\n]{2,}", setOf(RegexOption.MULTILINE)), "\n") + } else { + text + } + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + if (!recalculateHeights) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + return + } + recalculateHeights = false + + // Measure with expanded lines + binding.descriptionText.maxLines = Int.MAX_VALUE + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + descExpandedHeight = binding.descriptionText.measuredHeight + + // Measure with shrunk lines + binding.descriptionText.maxLines = SHRUNK_DESC_MAX_LINES + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + descShrunkHeight = binding.descriptionText.measuredHeight + } + + init { + binding.descriptionText.apply { + // So that 1 line of text won't be hidden by scrim + minLines = DESC_MIN_LINES + + setOnLongClickListener { + context.copyToClipboard( + context.getString(R.string.description), + text.toString() + ) + true + } + } + + arrayOf( + binding.descriptionText, + binding.descriptionScrim, + binding.toggleMoreScrim, + binding.toggleMore + ).forEach { + it.setOnClickListener { expanded = !expanded } + } + } +} + +private const val TOGGLE_ANIM_DURATION = 300L + +private const val DESC_MIN_LINES = 2 +private const val SHRUNK_DESC_MAX_LINES = 3 diff --git a/app/src/main/res/drawable/anim_caret_down.xml b/app/src/main/res/drawable/anim_caret_down.xml new file mode 100644 index 000000000..e5288406e --- /dev/null +++ b/app/src/main/res/drawable/anim_caret_down.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="utf-8"?> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <aapt:attr name="android:drawable"> + <vector + android:name="caret_up" + android:width="24.0dip" + android:height="24.0dip" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <group + android:name="caret01" + android:rotation="90.0" + android:translateX="12.0" + android:translateY="15.0"> + <group + android:name="caret_l" + android:rotation="45.0"> + <group + android:name="caret_l_pivot" + android:translateY="4.0"> + <group + android:name="caret_l_rect_position" + android:translateY="-1.0"> + <path + android:name="caret_l_rect" + android:fillColor="@android:color/black" + android:pathData="M -1.0,-4.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" /> + </group> + </group> + </group> + <group + android:name="caret_r" + android:rotation="-45.0"> + <group + android:name="caret_r_pivot" + android:translateY="-4.0"> + <group + android:name="caret_r_rect_position" + android:translateY="1.0"> + <path + android:name="caret_r_rect" + android:fillColor="@android:color/black" + android:pathData="M -1.0,-4.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" /> + </group> + </group> + </group> + </group> + </vector> + </aapt:attr> + + <target android:name="caret01"> + <aapt:attr name="android:animation"> + <objectAnimator + android:duration="300" + android:interpolator="@android:interpolator/fast_out_slow_in" + android:pathData="M 12.0,9.0 c 0.0,0.66667 0.0,5.0 0.0,6.0" + android:propertyXName="translateX" + android:propertyYName="translateY" /> + </aapt:attr> + </target> + <target android:name="caret_l"> + <aapt:attr name="android:animation"> + <objectAnimator + android:duration="300" + android:interpolator="@android:interpolator/fast_out_slow_in" + android:propertyName="rotation" + android:valueFrom="-45.0" + android:valueTo="45.0" + android:valueType="floatType" /> + </aapt:attr> + </target> + <target + android:name="caret_r"> + <aapt:attr name="android:animation"> + <objectAnimator + android:duration="300" + android:interpolator="@android:interpolator/fast_out_slow_in" + android:propertyName="rotation" + android:valueFrom="45.0" + android:valueTo="-45.0" + android:valueType="floatType" /> + </aapt:attr> + </target> +</animated-vector> diff --git a/app/src/main/res/drawable/anim_caret_up.xml b/app/src/main/res/drawable/anim_caret_up.xml new file mode 100644 index 000000000..78b817a16 --- /dev/null +++ b/app/src/main/res/drawable/anim_caret_up.xml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="utf-8"?> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <aapt:attr name="android:drawable"> + <vector + android:name="caret_up" + android:height="24.0dip" + android:width="24.0dip" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <group + android:name="caret02" + android:rotation="90.0" + android:translateX="12.0" + android:translateY="9.0"> + <group + android:name="caret02_l" + android:rotation="-45.0"> + <group + android:name="caret02_l_pivot" + android:translateY="4.0"> + <group + android:name="caret02_l_rect_position" + android:translateY="-1.0"> + <path + android:name="caret02_l_rect" + android:fillColor="@android:color/black" + android:pathData="M -1.0,-4.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" /> + </group> + </group> + </group> + <group + android:name="caret02_r" + android:rotation="45.0"> + <group + android:name="caret02_r_pivot" + android:translateY="-4.0"> + <group + android:name="caret02_r_rect_position" + android:translateY="1.0"> + <path + android:name="caret02_r_rect" + android:fillColor="@android:color/black" + android:pathData="M -1.0,-4.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" /> + </group> + </group> + </group> + </group> + </vector> + </aapt:attr> + + <target android:name="caret02"> + <aapt:attr name="android:animation"> + <objectAnimator + android:interpolator="@android:interpolator/fast_out_slow_in" + android:duration="300" + android:pathData="M 12.0,15.0 c 0.0,-1.0 0.0,-5.33333 0.0,-6.0" + android:propertyXName="translateX" + android:propertyYName="translateY" /> + </aapt:attr> + </target> + <target android:name="caret02_l"> + <aapt:attr name="android:animation"> + <objectAnimator + android:interpolator="@android:interpolator/fast_out_slow_in" + android:duration="300" + android:valueFrom="45.0" + android:valueTo="-45.0" + android:valueType="floatType" + android:propertyName="rotation" /> + </aapt:attr> + </target> + <target android:name="caret02_r"> + <aapt:attr name="android:animation"> + <objectAnimator + android:interpolator="@android:interpolator/fast_out_slow_in" + android:duration="300" + android:valueFrom="-45.0" + android:valueTo="45.0" + android:valueType="floatType" + android:propertyName="rotation" /> + </aapt:attr> + </target> +</animated-vector> diff --git a/app/src/main/res/layout-sw720dp/manga_info_header.xml b/app/src/main/res/layout-sw720dp/manga_info_header.xml index bff6893b1..c76ab43d8 100644 --- a/app/src/main/res/layout-sw720dp/manga_info_header.xml +++ b/app/src/main/res/layout-sw720dp/manga_info_header.xml @@ -188,126 +188,12 @@ </LinearLayout> - <androidx.constraintlayout.widget.ConstraintLayout + <eu.kanade.tachiyomi.widget.MangaSummaryView android:id="@+id/manga_summary_section" - android:layout_width="match_parent" + android:layout_width="0dp" android:layout_height="wrap_content" - android:layout_marginTop="8dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/manga_actions"> - - <TextView - android:id="@+id/manga_summary_text" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginStart="16dp" - android:layout_marginEnd="16dp" - android:clickable="true" - android:ellipsize="end" - android:focusable="true" - android:maxLines="3" - android:textAppearance="?attr/textAppearanceBody2" - android:textColor="?android:attr/textColorSecondary" - android:textIsSelectable="false" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" - tools:text="Collapsed summary content Collapsed summary content Collapsed summary content Collapsed summary content Collapsed summary content Collapsed summary content" /> - - <View - android:id="@+id/manga_info_scrim" - android:layout_width="0dp" - android:layout_height="32sp" - android:background="@drawable/manga_info_gradient" - android:backgroundTint="?android:attr/colorBackground" - app:layout_constraintBottom_toBottomOf="@+id/manga_summary_text" - app:layout_constraintEnd_toEndOf="@+id/manga_summary_text" - app:layout_constraintStart_toStartOf="@+id/manga_summary_text" /> - - <View - android:id="@+id/manga_info_toggle_more_scrim" - android:layout_width="36sp" - android:layout_height="18sp" - android:background="@drawable/manga_info_more_gradient" - android:backgroundTint="?android:attr/colorBackground" - app:layout_constraintBottom_toBottomOf="@+id/manga_info_toggle_more" - app:layout_constraintEnd_toEndOf="@+id/manga_info_toggle_more" - app:layout_constraintStart_toStartOf="@+id/manga_info_toggle_more" - app:layout_constraintTop_toTopOf="@+id/manga_info_toggle_more" /> - - <ImageButton - android:id="@+id/manga_info_toggle_more" - style="@style/Widget.Tachiyomi.Button.InlineButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginBottom="-4dp" - android:paddingStart="0dp" - android:paddingEnd="0dp" - android:background="@android:color/transparent" - android:contentDescription="@string/manga_info_expand" - android:src="@drawable/ic_expand_more_24dp" - app:layout_constraintBottom_toBottomOf="@id/manga_summary_text" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:tint="?android:attr/textColorPrimary" /> - - <ImageButton - android:id="@+id/manga_info_toggle_less" - style="@style/Widget.Tachiyomi.Button.InlineButton" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:paddingStart="8dp" - android:paddingEnd="8dp" - android:background="@android:color/transparent" - android:contentDescription="@string/manga_info_collapse" - android:src="@drawable/ic_expand_less_24dp" - android:visibility="gone" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/manga_summary_text" - app:tint="?android:attr/textColorPrimary" - tools:visibility="visible" /> - - <HorizontalScrollView - android:id="@+id/manga_genres_tags_compact" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:requiresFadingEdge="horizontal" - android:scrollbars="none" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/manga_info_toggle_more"> - - <com.google.android.material.chip.ChipGroup - android:id="@+id/manga_genres_tags_compact_chips" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingStart="16dp" - android:paddingTop="8dp" - android:paddingEnd="16dp" - android:paddingBottom="8dp" - app:chipSpacingHorizontal="4dp" - app:singleLine="true" /> - - </HorizontalScrollView> - - <com.google.android.material.chip.ChipGroup - android:id="@+id/manga_genres_tags_full_chips" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="16dp" - android:layout_marginEnd="16dp" - android:paddingTop="8dp" - android:paddingBottom="8dp" - android:visibility="gone" - app:chipSpacingHorizontal="4dp" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/manga_info_toggle_less" - tools:visibility="gone" /> - - - </androidx.constraintlayout.widget.ConstraintLayout> + app:layout_constraintTop_toBottomOf="@id/manga_actions" /> </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/manga_info_header.xml b/app/src/main/res/layout/manga_info_header.xml index 63f3f62c7..e241c006e 100644 --- a/app/src/main/res/layout/manga_info_header.xml +++ b/app/src/main/res/layout/manga_info_header.xml @@ -200,124 +200,12 @@ </LinearLayout> - <androidx.constraintlayout.widget.ConstraintLayout + <eu.kanade.tachiyomi.widget.MangaSummaryView android:id="@+id/manga_summary_section" android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/manga_actions"> - - <TextView - android:id="@+id/manga_summary_text" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginStart="16dp" - android:layout_marginEnd="16dp" - android:clickable="true" - android:ellipsize="end" - android:focusable="true" - android:maxLines="3" - android:textAppearance="?attr/textAppearanceBody2" - android:textColor="?android:attr/textColorSecondary" - android:textIsSelectable="false" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" - tools:text="Collapsed summary content Collapsed summary content Collapsed summary content Collapsed summary content Collapsed summary content Collapsed summary content" /> - - <View - android:id="@+id/manga_info_scrim" - android:layout_width="0dp" - android:layout_height="32sp" - android:background="@drawable/manga_info_gradient" - android:backgroundTint="?android:attr/colorBackground" - app:layout_constraintBottom_toBottomOf="@+id/manga_summary_text" - app:layout_constraintEnd_toEndOf="@+id/manga_summary_text" - app:layout_constraintStart_toStartOf="@+id/manga_summary_text" /> - - <View - android:id="@+id/manga_info_toggle_more_scrim" - android:layout_width="36sp" - android:layout_height="18sp" - android:background="@drawable/manga_info_more_gradient" - android:backgroundTint="?android:attr/colorBackground" - app:layout_constraintBottom_toBottomOf="@+id/manga_info_toggle_more" - app:layout_constraintEnd_toEndOf="@+id/manga_info_toggle_more" - app:layout_constraintStart_toStartOf="@+id/manga_info_toggle_more" - app:layout_constraintTop_toTopOf="@+id/manga_info_toggle_more" /> - - <ImageButton - android:id="@+id/manga_info_toggle_more" - style="@style/Widget.Tachiyomi.Button.InlineButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginBottom="-4dp" - android:paddingStart="0dp" - android:paddingEnd="0dp" - android:background="@android:color/transparent" - android:contentDescription="@string/manga_info_expand" - android:src="@drawable/ic_expand_more_24dp" - app:layout_constraintBottom_toBottomOf="@id/manga_summary_text" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:tint="?android:attr/textColorPrimary" /> - - <ImageButton - android:id="@+id/manga_info_toggle_less" - style="@style/Widget.Tachiyomi.Button.InlineButton" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:paddingStart="8dp" - android:paddingEnd="8dp" - android:background="@android:color/transparent" - android:contentDescription="@string/manga_info_collapse" - android:src="@drawable/ic_expand_less_24dp" - android:visibility="gone" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/manga_summary_text" - app:tint="?android:attr/textColorPrimary" - tools:visibility="visible" /> - - <HorizontalScrollView - android:id="@+id/manga_genres_tags_compact" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:requiresFadingEdge="horizontal" - android:scrollbars="none" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/manga_info_toggle_more"> - - <com.google.android.material.chip.ChipGroup - android:id="@+id/manga_genres_tags_compact_chips" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingStart="16dp" - android:paddingTop="8dp" - android:paddingEnd="16dp" - android:paddingBottom="8dp" - app:chipSpacingHorizontal="4dp" - app:singleLine="true" /> - - </HorizontalScrollView> - - <com.google.android.material.chip.ChipGroup - android:id="@+id/manga_genres_tags_full_chips" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="16dp" - android:layout_marginEnd="16dp" - android:paddingTop="8dp" - android:paddingBottom="8dp" - android:visibility="gone" - app:chipSpacingHorizontal="4dp" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/manga_info_toggle_less" - tools:visibility="gone" /> - - </androidx.constraintlayout.widget.ConstraintLayout> + app:layout_constraintTop_toBottomOf="@id/manga_actions" /> </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/manga_summary.xml b/app/src/main/res/layout/manga_summary.xml new file mode 100644 index 000000000..0559e8a6e --- /dev/null +++ b/app/src/main/res/layout/manga_summary.xml @@ -0,0 +1,94 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout 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"> + + <TextView + android:id="@+id/description_text" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginHorizontal="16dp" + android:ellipsize="end" + android:textAppearance="?attr/textAppearanceBody2" + android:textColor="?android:attr/textColorSecondary" + android:textIsSelectable="false" + app:firstBaselineToTopHeight="0dp" + app:lastBaselineToBottomHeight="0dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:text="Collapsed summary content Collapsed summary content Collapsed summary content Collapsed summary content Collapsed summary content Collapsed summary content" /> + + <View + android:id="@+id/description_scrim" + android:layout_width="0dp" + android:layout_height="24sp" + android:background="@drawable/manga_info_gradient" + android:backgroundTint="?android:attr/colorBackground" + app:layout_constraintBottom_toBottomOf="@+id/description_text" + app:layout_constraintEnd_toEndOf="@+id/description_text" + app:layout_constraintStart_toStartOf="@+id/description_text" /> + + <View + android:id="@+id/toggle_more_scrim" + android:layout_width="36sp" + android:layout_height="18sp" + android:background="@drawable/manga_info_more_gradient" + android:backgroundTint="?android:attr/colorBackground" + app:layout_constraintBottom_toBottomOf="@+id/toggle_more" + app:layout_constraintEnd_toEndOf="@+id/toggle_more" + app:layout_constraintStart_toStartOf="@+id/toggle_more" + app:layout_constraintTop_toTopOf="@+id/toggle_more" /> + + <ImageButton + android:id="@+id/toggle_more" + style="@style/Widget.Tachiyomi.Button.InlineButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="-6dp" + android:background="@android:color/transparent" + android:contentDescription="@string/manga_info_expand" + android:padding="0dp" + android:src="@drawable/anim_caret_down" + app:layout_constraintBottom_toBottomOf="@id/description_text" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:tint="?android:attr/textColorPrimary" /> + + <HorizontalScrollView + android:id="@+id/tag_chips_shrunk_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:requiresFadingEdge="horizontal" + android:scrollbars="none" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/toggle_more"> + + <com.google.android.material.chip.ChipGroup + android:id="@+id/tag_chips_shrunk" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingHorizontal="16dp" + android:paddingVertical="8dp" + app:chipSpacingHorizontal="4dp" + app:singleLine="true" /> + + </HorizontalScrollView> + + <com.google.android.material.chip.ChipGroup + android:id="@+id/tag_chips_expanded" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:paddingHorizontal="16dp" + android:paddingVertical="8dp" + android:visibility="gone" + app:chipSpacingHorizontal="4dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/toggle_more" + tools:visibility="visible" /> + +</androidx.constraintlayout.widget.ConstraintLayout>