Fuck motion layout

This commit is contained in:
Jobobby04 2021-08-27 01:17:33 -04:00
parent 77138aba72
commit 14be5c75ee
17 changed files with 1234 additions and 567 deletions

View File

@ -2,15 +2,10 @@ package eu.kanade.tachiyomi.ui.manga
import android.animation.Animator import android.animation.Animator
import android.animation.AnimatorListenerAdapter import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Point
import android.graphics.Rect
import android.graphics.RectF
import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.BitmapDrawable
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
@ -18,8 +13,6 @@ import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.animation.DecelerateInterpolator
import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.annotation.FloatRange import androidx.annotation.FloatRange
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
@ -31,7 +24,6 @@ import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.imageLoader import coil.imageLoader
import coil.loadAny
import coil.request.ImageRequest import coil.request.ImageRequest
import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType import com.bluelinelabs.conductor.ControllerChangeType
@ -85,7 +77,6 @@ import eu.kanade.tachiyomi.ui.manga.chapter.MangaChaptersHeaderAdapter
import eu.kanade.tachiyomi.ui.manga.chapter.base.BaseChaptersAdapter import eu.kanade.tachiyomi.ui.manga.chapter.base.BaseChaptersAdapter
import eu.kanade.tachiyomi.ui.manga.info.MangaInfoButtonsAdapter import eu.kanade.tachiyomi.ui.manga.info.MangaInfoButtonsAdapter
import eu.kanade.tachiyomi.ui.manga.info.MangaInfoHeaderAdapter import eu.kanade.tachiyomi.ui.manga.info.MangaInfoHeaderAdapter
import eu.kanade.tachiyomi.ui.manga.info.MangaInfoItemAdapter
import eu.kanade.tachiyomi.ui.manga.merged.EditMergedSettingsDialog import eu.kanade.tachiyomi.ui.manga.merged.EditMergedSettingsDialog
import eu.kanade.tachiyomi.ui.manga.track.TrackItem import eu.kanade.tachiyomi.ui.manga.track.TrackItem
import eu.kanade.tachiyomi.ui.manga.track.TrackSearchDialog import eu.kanade.tachiyomi.ui.manga.track.TrackSearchDialog
@ -107,6 +98,7 @@ import eu.kanade.tachiyomi.util.view.snack
import exh.log.xLogD import exh.log.xLogD
import exh.md.similar.MangaDexSimilarController import exh.md.similar.MangaDexSimilarController
import exh.metadata.metadata.base.FlatMetadata import exh.metadata.metadata.base.FlatMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.recs.RecommendsController import exh.recs.RecommendsController
import exh.source.MERGED_SOURCE_ID import exh.source.MERGED_SOURCE_ID
import exh.source.getMainSource import exh.source.getMainSource
@ -117,7 +109,6 @@ import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import reactivecircus.flowbinding.android.view.clicks
import reactivecircus.flowbinding.recyclerview.scrollEvents import reactivecircus.flowbinding.recyclerview.scrollEvents
import reactivecircus.flowbinding.swiperefreshlayout.refreshes import reactivecircus.flowbinding.swiperefreshlayout.refreshes
import timber.log.Timber import timber.log.Timber
@ -192,15 +183,13 @@ class MangaController :
private var mangaInfoAdapter: MangaInfoHeaderAdapter? = null private var mangaInfoAdapter: MangaInfoHeaderAdapter? = null
// SY >--
private var mangaInfoItemAdapter: MangaInfoItemAdapter? = null
private var mangaInfoButtonsAdapter: MangaInfoButtonsAdapter? = null
private var mangaMetaInfoAdapter: RecyclerView.Adapter<*>? = null
// SY <--
private var chaptersHeaderAdapter: MangaChaptersHeaderAdapter? = null private var chaptersHeaderAdapter: MangaChaptersHeaderAdapter? = null
private var chaptersAdapter: ChaptersAdapter? = null private var chaptersAdapter: ChaptersAdapter? = null
// SY -->
private var mangaInfoButtonsAdapter: MangaInfoButtonsAdapter? = null
// SY <--
// Sheet containing filter/sort/display items. // Sheet containing filter/sort/display items.
private var settingsSheet: ChaptersSettingsSheet? = null private var settingsSheet: ChaptersSettingsSheet? = null
@ -237,8 +226,6 @@ class MangaController :
private var editMangaDialog: EditMangaDialog? = null private var editMangaDialog: EditMangaDialog? = null
private var editMergedSettingsDialog: EditMergedSettingsDialog? = null private var editMergedSettingsDialog: EditMergedSettingsDialog? = null
private var currentAnimator: Animator? = null
// EXH <-- // EXH <--
init { init {
@ -298,18 +285,13 @@ class MangaController :
if (manga == null || source == null) return if (manga == null || source == null) return
// Init RecyclerView and adapter // Init RecyclerView and adapter
mangaInfoAdapter = MangaInfoHeaderAdapter(this, binding.infoRecycler != null) mangaInfoAdapter = MangaInfoHeaderAdapter(this, fromSource, binding.infoRecycler != null)
chaptersHeaderAdapter = MangaChaptersHeaderAdapter(this) chaptersHeaderAdapter = MangaChaptersHeaderAdapter(this)
chaptersAdapter = ChaptersAdapter(this, view.context) chaptersAdapter = ChaptersAdapter(this, view.context)
// SY --> // SY -->
val mainSource = presenter.source.getMainSource<MetadataSource<*, *>>()
if (mainSource != null) {
mangaMetaInfoAdapter = mainSource.getDescriptionAdapter(this)
}
if (!preferences.recommendsInOverflow().get() || smartSearchConfig != null) { if (!preferences.recommendsInOverflow().get() || smartSearchConfig != null) {
mangaInfoButtonsAdapter = MangaInfoButtonsAdapter(this) mangaInfoButtonsAdapter = MangaInfoButtonsAdapter(this)
} }
mangaInfoItemAdapter = MangaInfoItemAdapter(this, fromSource, binding.infoRecycler != null)
// SY <-- // SY <--
// Phone layout // Phone layout
@ -317,8 +299,6 @@ class MangaController :
it.adapter = ConcatAdapter( it.adapter = ConcatAdapter(
listOfNotNull( listOfNotNull(
mangaInfoAdapter, mangaInfoAdapter,
mangaMetaInfoAdapter,
mangaInfoItemAdapter,
mangaInfoButtonsAdapter, mangaInfoButtonsAdapter,
chaptersHeaderAdapter, chaptersHeaderAdapter,
chaptersAdapter chaptersAdapter
@ -346,9 +326,7 @@ class MangaController :
it.adapter = ConcatAdapter( it.adapter = ConcatAdapter(
listOfNotNull( listOfNotNull(
mangaInfoAdapter, mangaInfoAdapter,
mangaInfoButtonsAdapter, mangaInfoButtonsAdapter
mangaMetaInfoAdapter,
mangaInfoItemAdapter
) )
) )
@ -459,11 +437,6 @@ class MangaController :
chaptersHeaderAdapter = null chaptersHeaderAdapter = null
chaptersAdapter = null chaptersAdapter = null
settingsSheet = null settingsSheet = null
// SY -->
mangaInfoButtonsAdapter = null
mangaInfoItemAdapter = null
mangaMetaInfoAdapter = null
// SY <--
addSnackbar?.dismiss() addSnackbar?.dismiss()
updateToolbarTitleAlpha(1F) updateToolbarTitleAlpha(1F)
toolbarTextView = null toolbarTextView = null
@ -559,7 +532,7 @@ class MangaController :
val mainSource = presenter.source.getMainSource<MetadataSource<*, *>>() val mainSource = presenter.source.getMainSource<MetadataSource<*, *>>()
if (mainSource != null) { if (mainSource != null) {
presenter.meta = flatMetadata.raise(mainSource.metaClass) presenter.meta = flatMetadata.raise(mainSource.metaClass)
mangaMetaInfoAdapter?.notifyDataSetChanged() mangaInfoAdapter?.notifyMetaAdapter()
updateFilterIconState() updateFilterIconState()
} }
} }
@ -573,13 +546,10 @@ class MangaController :
* @param manga manga object containing information about manga. * @param manga manga object containing information about manga.
* @param source the source of the manga. * @param source the source of the manga.
*/ */
fun onNextMangaInfo(manga: Manga, source: Source) { fun onNextMangaInfo(manga: Manga, source: Source, metadata: RaisedSearchMetadata?) {
if (manga.initialized) { if (manga.initialized) {
// Update view. // Update view.
mangaInfoAdapter?.update(manga, source) mangaInfoAdapter?.update(manga, source, metadata, presenter.mergedMangaReferences)
mangaInfoItemAdapter?.update(manga, source, presenter.meta)
binding.expandedImage.loadAny(manga)
} else { } else {
// Initialize manga. // Initialize manga.
fetchMangaInfoFromSource() fetchMangaInfoFromSource()
@ -843,110 +813,6 @@ class MangaController :
presenter.moveMangaToCategories(manga, categories) presenter.moveMangaToCategories(manga, categories)
} }
// SY -->
fun onThumbnailClick(thumbView: ImageView) {
if (!presenter.manga.initialized || presenter.manga.thumbnail_url == null) return
currentAnimator?.cancel()
val startBoundsInt = Rect()
val finalBoundsInt = Rect()
val globalOffset = Point()
thumbView.getGlobalVisibleRect(startBoundsInt)
binding.root.getGlobalVisibleRect(finalBoundsInt, globalOffset)
startBoundsInt.offset(-globalOffset.x, -globalOffset.y)
finalBoundsInt.offset(-globalOffset.x, -globalOffset.y)
val startBounds = RectF(startBoundsInt)
val finalBounds = RectF(finalBoundsInt)
val startScale: Float
if ((finalBounds.width() / finalBounds.height() > startBounds.width() / startBounds.height())) {
startScale = startBounds.height() / finalBounds.height()
val startWidth: Float = startScale * finalBounds.width()
val deltaWidth: Float = (startWidth - startBounds.width()) / 2
startBounds.left -= deltaWidth.toInt()
startBounds.right += deltaWidth.toInt()
} else {
startScale = startBounds.width() / finalBounds.width()
val startHeight: Float = startScale * finalBounds.height()
val deltaHeight: Float = (startHeight - startBounds.height()) / 2f
startBounds.top -= deltaHeight.toInt()
startBounds.bottom += deltaHeight.toInt()
}
thumbView.alpha = 0f
actionFab?.isVisible = false
binding.expandedImage.isVisible = true
binding.expandedImage.pivotX = 0f
binding.expandedImage.pivotY = 0f
currentAnimator = AnimatorSet().apply {
play(
ObjectAnimator.ofFloat(
binding.expandedImage,
View.X,
startBounds.left,
finalBounds.left
)
).apply {
with(ObjectAnimator.ofFloat(binding.expandedImage, View.Y, startBounds.top, finalBounds.top))
with(ObjectAnimator.ofFloat(binding.expandedImage, View.SCALE_X, startScale, 1f))
with(ObjectAnimator.ofFloat(binding.expandedImage, View.SCALE_Y, startScale, 1f))
}
duration = resources?.getInteger(android.R.integer.config_shortAnimTime)?.toLong() ?: 150L
interpolator = DecelerateInterpolator()
addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
currentAnimator = null
}
override fun onAnimationCancel(animation: Animator) {
currentAnimator = null
}
}
)
start()
}
binding.expandedImage.clicks()
.onEach {
currentAnimator?.cancel()
currentAnimator = AnimatorSet().apply {
play(ObjectAnimator.ofFloat(binding.expandedImage, View.X, startBounds.left)).apply {
with(ObjectAnimator.ofFloat(binding.expandedImage, View.Y, startBounds.top))
with(ObjectAnimator.ofFloat(binding.expandedImage, View.SCALE_X, startScale))
with(ObjectAnimator.ofFloat(binding.expandedImage, View.SCALE_Y, startScale))
}
duration = resources?.getInteger(android.R.integer.config_shortAnimTime)?.toLong() ?: 150L
interpolator = DecelerateInterpolator()
addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
thumbView.alpha = 1f
binding.expandedImage.isVisible = false
actionFab?.isVisible = presenter.filteredAndSortedChapters.any { !it.read }
currentAnimator = null
}
override fun onAnimationCancel(animation: Animator) {
thumbView.alpha = 1f
binding.expandedImage.isVisible = false
actionFab?.isVisible = presenter.filteredAndSortedChapters.any { !it.read }
currentAnimator = null
}
}
)
start()
}
}
.launchIn(viewScope)
}
// SY <--
/** /**
* Perform a global search using the provided query. * Perform a global search using the provided query.
* *

View File

@ -154,6 +154,8 @@ class MangaPresenter(
var mergedManga = emptyMap<Long, Manga>() var mergedManga = emptyMap<Long, Manga>()
private set private set
var mergedMangaReferences = emptyList<MergedMangaReference>()
private set
var dedupe: Boolean = true var dedupe: Boolean = true
@ -166,6 +168,7 @@ class MangaPresenter(
// SY --> // SY -->
if (source is MergedSource) { if (source is MergedSource) {
launchIO { mergedManga = db.getMergedMangas(manga.id!!).executeAsBlocking().associateBy { it.id!! } } launchIO { mergedManga = db.getMergedMangas(manga.id!!).executeAsBlocking().associateBy { it.id!! } }
launchIO { mergedMangaReferences = db.getMergedMangaReferences(manga.id!!).executeAsBlocking() }
} }
// SY <-- // SY <--
@ -188,11 +191,13 @@ class MangaPresenter(
} }
} }
.subscribeLatestCache({ view, (manga, flatMetadata) -> .subscribeLatestCache({ view, (manga, flatMetadata) ->
flatMetadata?.let { metadata -> val mainSource = source.getMainSource<MetadataSource<*, *>>()
view.onNextMetaInfo(metadata) val meta = if (mainSource != null) {
} flatMetadata?.raise(mainSource.metaClass)
} else null
this.meta = meta
// SY <-- // SY <--
view.onNextMangaInfo(manga, source) view.onNextMangaInfo(manga, source, meta)
}) })
getTrackingObservable() getTrackingObservable()
@ -383,7 +388,7 @@ class MangaPresenter(
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribeLatestCache( .subscribeLatestCache(
{ view, _ -> { view, _ ->
view.onNextMangaInfo(manga, source) view.onNextMangaInfo(manga, source, meta)
} }
) )
} }

View File

@ -4,11 +4,13 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import coil.loadAny import coil.loadAny
import coil.target.ImageViewTarget
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.databinding.MangaInfoHeaderBinding import eu.kanade.tachiyomi.databinding.MangaInfoHeaderBinding
@ -16,38 +18,48 @@ import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.all.MergedSource import eu.kanade.tachiyomi.source.online.MetadataSource
import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.copyToClipboard import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.view.setChips
import exh.merged.sql.models.MergedMangaReference import exh.merged.sql.models.MergedMangaReference
import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.source.MERGED_SOURCE_ID import exh.source.MERGED_SOURCE_ID
import exh.source.getMainSource
import exh.util.SourceTagsUtil import exh.util.SourceTagsUtil
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.view.clicks import reactivecircus.flowbinding.android.view.clicks
import reactivecircus.flowbinding.android.view.longClicks import reactivecircus.flowbinding.android.view.longClicks
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
class MangaInfoHeaderAdapter( class MangaInfoHeaderAdapter(
private val controller: MangaController, private val controller: MangaController,
private val isTablet: Boolean private val fromSource: Boolean,
private val isTablet: Boolean,
) : ) :
RecyclerView.Adapter<MangaInfoHeaderAdapter.HeaderViewHolder>() { RecyclerView.Adapter<MangaInfoHeaderAdapter.HeaderViewHolder>() {
private val trackManager: TrackManager by injectLazy() private val trackManager: TrackManager by injectLazy()
// SY --> // SY -->
private val db: DatabaseHelper by injectLazy()
private val sourceManager: SourceManager by injectLazy() private val sourceManager: SourceManager by injectLazy()
private var mergedMangaReferences: List<MergedMangaReference> = emptyList()
// SY <-- // SY <--
private var manga: Manga = controller.presenter.manga private var manga: Manga = controller.presenter.manga
private var source: Source = controller.presenter.source private var source: Source = controller.presenter.source
// SY -->
private var meta: RaisedSearchMetadata? = controller.presenter.meta
private var mergedMangaReferences: List<MergedMangaReference> = controller.presenter.mergedMangaReferences
// SY <--
private var trackCount: Int = 0 private var trackCount: Int = 0
private var metaInfoAdapter: RecyclerView.Adapter<*>? = null
private var mangaTagsInfoAdapter: NamespaceTagsAdapter = NamespaceTagsAdapter(controller, source)
private lateinit var binding: MangaInfoHeaderBinding private lateinit var binding: MangaInfoHeaderBinding
@ -55,6 +67,20 @@ class MangaInfoHeaderAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder {
binding = MangaInfoHeaderBinding.inflate(LayoutInflater.from(parent.context), parent, false) binding = MangaInfoHeaderBinding.inflate(LayoutInflater.from(parent.context), parent, false)
// SY -->
metaInfoAdapter = source.getMainSource<MetadataSource<*, *>>()?.getDescriptionAdapter(controller)
binding.metadataView.isVisible = if (metaInfoAdapter != null) {
binding.metadataView.layoutManager = LinearLayoutManager(binding.root.context)
binding.metadataView.adapter = metaInfoAdapter
true
} else {
false
}
binding.genreGroups.layoutManager = LinearLayoutManager(binding.root.context)
binding.genreGroups.adapter = mangaTagsInfoAdapter
// SY <--
return HeaderViewHolder(binding.root) return HeaderViewHolder(binding.root)
} }
@ -70,16 +96,16 @@ class MangaInfoHeaderAdapter(
* @param manga manga object containing information about manga. * @param manga manga object containing information about manga.
* @param source the source of the manga. * @param source the source of the manga.
*/ */
fun update(manga: Manga, source: Source) { fun update(manga: Manga, source: Source, meta: RaisedSearchMetadata?, mergedMangaReferences: List<MergedMangaReference>) {
this.manga = manga this.manga = manga
this.source = source this.source = source
// SY --> // SY -->
if (source is MergedSource) { this.meta = meta
mergedMangaReferences = db.getMergedMangaReferences(manga.id!!).executeAsBlocking() this.mergedMangaReferences = mergedMangaReferences
}
// SY <-- // SY <--
notifyDataSetChanged() notifyDataSetChanged()
notifyMetaAdapter()
} }
fun setTrackingCount(trackCount: Int) { fun setTrackingCount(trackCount: Int) {
@ -88,14 +114,22 @@ class MangaInfoHeaderAdapter(
notifyDataSetChanged() notifyDataSetChanged()
} }
fun notifyMetaAdapter() {
metaInfoAdapter?.notifyDataSetChanged()
}
inner class HeaderViewHolder(private val view: View) : RecyclerView.ViewHolder(view) { inner class HeaderViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
fun bind() { fun bind() {
// For rounded corners // For rounded corners
binding.mangaCover.clipToOutline = true binding.mangaCover.clipToOutline = true
// SY --> // SY -->
binding.mangaCover.clicks() mangaTagsInfoAdapter.mItemClickListener = FlexibleAdapter.OnItemClickListener { _, _ ->
.onEach { controller.onThumbnailClick(binding.mangaCover) } controller.viewScope.launchUI {
.launchIn(controller.viewScope) toggleMangaInfo()
}
false
}
// SY <-- // SY <--
binding.btnFavorite.clicks() binding.btnFavorite.clicks()
@ -209,13 +243,22 @@ class MangaInfoHeaderAdapter(
} }
.launchIn(controller.viewScope) .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.longClicks() binding.mangaCover.longClicks()
.onEach { .onEach {
showCoverOptionsDialog() showCoverOptionsDialog()
} }
.launchIn(controller.viewScope) .launchIn(controller.viewScope)
setMangaInfo(manga, source) setMangaInfo(manga, source, meta)
} }
private fun showCoverOptionsDialog() { private fun showCoverOptionsDialog() {
@ -245,7 +288,7 @@ class MangaInfoHeaderAdapter(
* @param manga manga object containing information about manga. * @param manga manga object containing information about manga.
* @param source the source of the manga. * @param source the source of the manga.
*/ */
private fun setMangaInfo(manga: Manga, source: Source?) { private fun setMangaInfo(manga: Manga, source: Source?, meta: RaisedSearchMetadata?) {
// Update full title TextView. // Update full title TextView.
binding.mangaFullTitle.text = if (manga.title.isBlank()) { binding.mangaFullTitle.text = if (manga.title.isBlank()) {
view.context.getString(R.string.unknown) view.context.getString(R.string.unknown)
@ -278,7 +321,6 @@ class MangaInfoHeaderAdapter(
} else /* SY <-- */ if (mangaSource != null) { } else /* SY <-- */ if (mangaSource != null) {
text = mangaSource text = mangaSource
setOnClickListener { setOnClickListener {
val sourceManager = Injekt.get<SourceManager>()
controller.performSearch(sourceManager.getOrStub(source.id).name) controller.performSearch(sourceManager.getOrStub(source.id).name)
} }
} else { } else {
@ -305,16 +347,141 @@ class MangaInfoHeaderAdapter(
setFavoriteButtonState(manga.favorite) setFavoriteButtonState(manga.favorite)
// Set cover if changed. // Set cover if changed.
listOf(binding.mangaCover, binding.backdrop).forEach { binding.backdrop.loadAny(manga)
it.loadAny(manga) binding.mangaCover.loadAny(manga) {
listener(
onSuccess = { request, _ ->
(request.target as? ImageViewTarget)?.drawable?.let { drawable ->
val ratio = drawable.minimumWidth / drawable.minimumHeight.toFloat()
binding.root.getConstraintSet(R.id.end)
?.setDimensionRatio(R.id.manga_cover, ratio.toString())
}
}
)
} }
if (initialLoad && isTablet) {
initialLoad = false // Manga info section
// wrap_content and autoFixTextSize can cause unwanted behaviour this tries to solve it val hasInfoContent = !manga.description.isNullOrBlank() || !manga.genre.isNullOrBlank()
binding.mangaFullTitle.requestLayout() showMangaInfo(hasInfoContent)
if (hasInfoContent) {
// Update description TextView.
binding.mangaSummaryText.text = if (manga.description.isNullOrBlank()) {
view.context.getString(R.string.unknown)
} else {
manga.description
}
// SY -->
if (manga.description == "meta") {
binding.mangaSummaryText.text = ""
/*binding.mangaInfoToggleLess.updateLayoutParams<ConstraintLayout.LayoutParams> {
topToBottom = -1
bottomToBottom = binding.mangaSummaryText.id
}*/
}
// SY <--
// Update genres list
if (!manga.genre.isNullOrBlank()) {
binding.mangaGenresTagsCompactChips.setChips(
manga.getGenres(),
controller::performGenreSearch
)
// SY -->
// if (source?.getMainSource<NamespaceSource>() != null) {
setChipsWithNamespace(
manga.genre,
meta
)
// binding.mangaGenresTagsFullChips.isVisible = false
/*} else {
binding.mangaGenresTagsFullChips.setChips(
manga.getGenres(),
controller::performGenreSearch
)
binding.genreGroups.isVisible = false
}*/
// SY <--
} else {
binding.mangaGenresTagsCompactChips.isVisible = false
// binding.mangaGenresTagsFullChips.isVisible = false
// SY -->
binding.genreGroups.isVisible = false
// SY <--
}
// Handle showing more or less info
merge(
binding.mangaSummaryText.clicks(),
binding.mangaInfoToggleMore.clicks(),
binding.mangaInfoToggleLess.clicks(),
binding.mangaSummarySection.clicks()
)
.onEach { toggleMangaInfo() }
.launchIn(controller.viewScope)
// Expand manga info if navigated from source listing or explicitly set to
// (e.g. on tablets)
if (initialLoad && (fromSource || isTablet)) {
toggleMangaInfo()
initialLoad = false
// wrap_content and autoFixTextSize can cause unwanted behaviour this tries to solve it
binding.mangaFullTitle.requestLayout()
}
// Refreshes will change the state and it needs to be set to correct state to display correctly
if (binding.mangaSummaryText.maxLines == 2) {
binding.mangaSummarySection.transitionToState(R.id.start)
} else {
binding.mangaSummarySection.transitionToState(R.id.end)
}
} }
} }
private fun showMangaInfo(visible: Boolean) {
binding.mangaSummarySection.isVisible = visible
}
private fun toggleMangaInfo() {
val isCurrentlyExpanded = binding.mangaSummaryText.maxLines != 2
if (isCurrentlyExpanded) {
binding.mangaSummarySection.transitionToStart()
} else {
binding.mangaSummarySection.transitionToEnd()
}
binding.mangaSummaryText.maxLines = if (isCurrentlyExpanded) {
2
} else {
Int.MAX_VALUE
}
}
private fun setChipsWithNamespace(genre: String?, meta: RaisedSearchMetadata?) {
val namespaceTags = when {
meta != null -> {
meta.tags
.filterNot { it.type == RaisedSearchMetadata.TAG_TYPE_VIRTUAL }
.groupBy { it.namespace }
.map { (namespace, tags) ->
NamespaceTagsItem(
namespace,
tags.map {
it.name to it.type
}
)
}
}
genre != null -> {
listOf(NamespaceTagsItem(null, genre.split(",").map { it.trim() to null }))
}
else -> emptyList()
}
mangaTagsInfoAdapter.updateDataSet(namespaceTags)
}
/** /**
* Update favorite button with correct drawable and text. * Update favorite button with correct drawable and text.
* *

View File

@ -1,229 +0,0 @@
package eu.kanade.tachiyomi.ui.manga.info
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.databinding.MangaInfoItemBinding
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.online.NamespaceSource
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard
import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL
import exh.source.getMainSource
import exh.source.isEhBasedSource
import exh.util.getRaisedTags
import exh.util.makeSearchChip
import exh.util.setChipsExtended
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import reactivecircus.flowbinding.android.view.clicks
import reactivecircus.flowbinding.android.view.longClicks
class MangaInfoItemAdapter(
private val controller: MangaController,
private val fromSource: Boolean,
private val isTablet: Boolean
) :
RecyclerView.Adapter<MangaInfoItemAdapter.HeaderViewHolder>() {
private var manga: Manga = controller.presenter.manga
private var source: Source = controller.presenter.source
private var meta: RaisedSearchMetadata? = controller.presenter.meta
private lateinit var binding: MangaInfoItemBinding
private var initialLoad: Boolean = true
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder {
binding = MangaInfoItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return HeaderViewHolder(binding.root)
}
private var mangaTagsInfoAdapter: FlexibleAdapter<IFlexible<*>>? = FlexibleAdapter(null)
override fun getItemCount(): Int = 1
override fun onBindViewHolder(holder: HeaderViewHolder, position: Int) {
holder.bind()
}
/**
* Update the view with manga information.
*
* @param manga manga object containing information about manga.
* @param source the source of the manga.
*/
fun update(manga: Manga, source: Source, meta: RaisedSearchMetadata?) {
this.manga = manga
this.source = source
this.meta = meta
notifyDataSetChanged()
}
inner class HeaderViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
fun bind() {
binding.mangaSummaryText.longClicks()
.onEach {
controller.activity?.copyToClipboard(
view.context.getString(R.string.description),
binding.mangaSummaryText.text.toString()
)
}
.launchIn(controller.viewScope)
binding.genreGroups.layoutManager = LinearLayoutManager(itemView.context)
binding.genreGroups.adapter = mangaTagsInfoAdapter
// SY -->
mangaTagsInfoAdapter?.mItemClickListener = FlexibleAdapter.OnItemClickListener { _, _ ->
controller.viewScope.launch {
toggleMangaInfo()
}
false
}
// SY <--
setMangaInfo(manga, source)
}
/**
* Update the view with manga information.
*
* @param manga manga object containing information about manga.
* @param source the source of the manga.
*/
private fun setMangaInfo(manga: Manga, source: Source?) {
// Manga info section
val hasInfoContent = !manga.description.isNullOrBlank() || !manga.genre.isNullOrBlank()
showMangaInfo(hasInfoContent)
if (hasInfoContent) {
// Update description TextView.
binding.mangaSummaryText.text = if (manga.description.isNullOrBlank()) {
view.context.getString(R.string.unknown)
} else {
manga.description
}
// SY -->
if (binding.mangaSummaryText.text == "meta") {
binding.mangaSummaryText.text = ""
binding.mangaSummaryText.maxLines = 1
binding.mangaInfoToggleLess.updateLayoutParams<ConstraintLayout.LayoutParams> {
topToBottom = -1
bottomToBottom = binding.mangaSummaryText.id
}
}
// SY <--
// Update genres list
if (!manga.genre.isNullOrBlank()) {
// SY -->
if (source != null && source.getMainSource() is NamespaceSource) {
val metaTags = meta?.tags?.filterNot { it.type == TAG_TYPE_VIRTUAL }?.groupBy { it.namespace }
var namespaceTags: List<NamespaceTagsItem> = emptyList()
if (source.isEhBasedSource() && metaTags != null && metaTags.all { it.key != null }) {
namespaceTags = metaTags
.mapValues { values ->
values.value.map {
itemView.context.makeSearchChip(
it.name,
controller::performSearch,
controller::performGlobalSearch,
source.id,
it.namespace,
it.type
)
}
}
.map { NamespaceTagsItem(it.key!!, it.value) }
} else {
val genre = manga.getRaisedTags()
if (!genre.isNullOrEmpty()) {
namespaceTags = genre
.groupBy { it.namespace }
.mapValues { values ->
values.value.map {
itemView.context.makeSearchChip(
it.name,
controller::performSearch,
controller::performGlobalSearch,
source.id,
it.namespace
)
}
}
.map { NamespaceTagsItem(it.key, it.value) }
}
}
mangaTagsInfoAdapter?.updateDataSet(namespaceTags)
}
binding.mangaGenresTagsFullChips.setChipsExtended(manga.getGenres(), controller::performGenreSearch, controller::performGlobalSearch, source?.id ?: 0)
binding.mangaGenresTagsCompactChips.setChipsExtended(manga.getGenres(), controller::performGenreSearch, controller::performGlobalSearch, source?.id ?: 0)
// SY <--
} else {
binding.mangaGenresTagsCompactChips.isVisible = false
binding.mangaGenresTagsFullChips.isVisible = false
// SY -->
binding.genreGroups.isVisible = false
// SY <--
}
// Handle showing more or less info
merge(
binding.mangaSummarySection.clicks(),
binding.mangaSummaryText.clicks(),
binding.mangaInfoToggleMore.clicks(),
binding.mangaInfoToggleLess.clicks()
)
.onEach { toggleMangaInfo() }
.launchIn(controller.viewScope)
// Expand manga info if navigated from source listing
if (initialLoad && (fromSource || isTablet)) {
toggleMangaInfo()
initialLoad = false
}
}
}
private fun showMangaInfo(visible: Boolean) {
binding.mangaSummarySection.isVisible = visible
}
private fun toggleMangaInfo() {
val isCurrentlyExpanded = binding.mangaSummaryText.maxLines != 2 /* SY --> */ && binding.mangaSummaryText.maxLines != 1 /* SY <-- */
binding.mangaInfoToggleMoreScrim.isVisible = isCurrentlyExpanded
binding.mangaInfoToggleMore.isVisible = isCurrentlyExpanded
binding.mangaInfoToggleLess.isVisible = !isCurrentlyExpanded
binding.mangaSummaryText.maxLines = if (isCurrentlyExpanded) {
/* SY --> */ if (binding.mangaSummaryText.text.isBlank()) 1 else /* SY <-- */ 2
} else {
Int.MAX_VALUE
}
binding.mangaGenresTagsCompact.isVisible = isCurrentlyExpanded
// SY -->
if (source.getMainSource() !is NamespaceSource) {
binding.mangaGenresTagsFullChips.isVisible = !isCurrentlyExpanded
} else {
binding.genreGroups.isVisible = !isCurrentlyExpanded
}
// SY <--
}
}
}

View File

@ -0,0 +1,8 @@
package eu.kanade.tachiyomi.ui.manga.info
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.ui.manga.MangaController
class NamespaceTagsAdapter(val controller: MangaController, val source: Source) :
FlexibleAdapter<NamespaceTagsItem>(null, controller, true)

View File

@ -0,0 +1,40 @@
package eu.kanade.tachiyomi.ui.manga.info
import android.view.View
import com.google.android.material.chip.Chip
import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.databinding.MangaInfoGenreGroupingBinding
import exh.util.makeSearchChip
class NamespaceTagsHolder(
view: View,
val adapter: NamespaceTagsAdapter
) : FlexibleViewHolder(view, adapter) {
val binding = MangaInfoGenreGroupingBinding.bind(view)
fun bind(item: NamespaceTagsItem) {
binding.namespace.removeAllViews()
val namespace = item.namespace
if (namespace != null) {
binding.namespace.addView(
Chip(binding.root.context).apply {
text = namespace
}
)
}
binding.tags.removeAllViews()
item.tags.map { (tag, type) ->
binding.root.context.makeSearchChip(
tag,
adapter.controller::performSearch,
adapter.controller::performGlobalSearch,
adapter.source.id,
namespace,
type
)
}.forEach {
binding.tags.addView(it)
}
}
}

View File

@ -2,46 +2,43 @@ package eu.kanade.tachiyomi.ui.manga.info
import android.view.View import android.view.View
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
open class NamespaceTagsItem(val namespace: String?, val tags: List<Chip>) : AbstractFlexibleItem<NamespaceTagsItem.Holder>() { class NamespaceTagsItem(val namespace: String?, val tags: List<Pair<String, Int?>>) :
AbstractFlexibleItem<NamespaceTagsHolder>() {
override fun getLayoutRes(): Int { override fun getLayoutRes(): Int {
return R.layout.manga_info_genre_grouping return R.layout.manga_info_genre_grouping
} }
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): Holder { override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): NamespaceTagsHolder {
return Holder(view, adapter) return NamespaceTagsHolder(view, adapter as NamespaceTagsAdapter)
} }
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>, holder: Holder, position: Int, payloads: List<Any?>?) { override fun bindViewHolder(
val namespaceChip = Chip(holder.itemView.context) adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
namespaceChip.text = namespace ?: holder.itemView.context.getString(R.string.unknown) holder: NamespaceTagsHolder,
holder.namespaceChipGroup.addView(namespaceChip) position: Int,
payloads: List<Any?>?
tags.forEach { ) {
holder.tagsChipGroup.addView(it) holder.bind(this)
}
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (javaClass != other?.javaClass) return false if (javaClass != other?.javaClass) return false
return namespace == (other as NamespaceTagsItem).namespace
other as NamespaceTagsItem
if (namespace != other.namespace) return false
return true
} }
override fun hashCode(): Int { override fun hashCode(): Int {
return namespace.hashCode() return namespace.hashCode()
} }
class Holder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) {
val namespaceChipGroup: ChipGroup = itemView.findViewById(R.id.namespace)
val tagsChipGroup: ChipGroup = itemView.findViewById(R.id.tags)
}
} }

View File

@ -122,5 +122,3 @@ fun Date.toRelativeString(
else -> dateFormat.format(this) else -> dateFormat.format(this)
} }
} }

View File

@ -12,7 +12,12 @@ import exh.source.nHentaiSourceIds
import java.util.Locale import java.util.Locale
object SourceTagsUtil { object SourceTagsUtil {
fun getWrappedTag(sourceId: Long, namespace: String? = null, tag: String? = null, fullTag: String? = null): String? { fun getWrappedTag(
sourceId: Long?,
namespace: String? = null,
tag: String? = null,
fullTag: String? = null
): String? {
return if (sourceId == EXH_SOURCE_ID || sourceId == EH_SOURCE_ID || sourceId in nHentaiSourceIds || sourceId in hitomiSourceIds) { return if (sourceId == EXH_SOURCE_ID || sourceId == EH_SOURCE_ID || sourceId in nHentaiSourceIds || sourceId in hitomiSourceIds) {
val parsed = when { val parsed = when {
fullTag != null -> parseTag(fullTag) fullTag != null -> parseTag(fullTag)

View File

@ -24,7 +24,14 @@ fun ChipGroup.setChipsExtended(items: List<String>?, onClick: (item: String) ->
} }
} }
fun Context.makeSearchChip(item: String, onClick: (item: String) -> Unit = {}, onLongClick: (item: String) -> Unit = {}, sourceId: Long, namespace: String? = null, type: Int? = null): Chip { fun Context.makeSearchChip(
item: String,
onClick: (item: String) -> Unit = {},
onLongClick: (item: String) -> Unit = {},
sourceId: Long,
namespace: String? = null,
type: Int? = null
): Chip {
return Chip(this).apply { return Chip(this).apply {
text = item text = item
val search = if (namespace != null) { val search = if (namespace != null) {

View File

@ -74,10 +74,4 @@
app:fastScrollerBubbleEnabled="false" app:fastScrollerBubbleEnabled="false"
tools:visibility="visible" /> tools:visibility="visible" />
<ImageView
android:id="@+id/expanded_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,317 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout 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:orientation="vertical"
app:layoutDescription="@xml/manga_info_header_scene_sw720dp"
tools:context=".ui.browse.source.browse.BrowseSourceController">
<ImageView
android:id="@+id/backdrop"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="-32dp"
android:alpha="0.2"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="@+id/manga_cover"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="@mipmap/ic_launcher"
tools:ignore="ContentDescription" />
<View
android:id="@+id/backdrop_overlay"
android:layout_width="0dp"
android:layout_height="0dp"
android:alpha="1"
android:background="@drawable/manga_info_gradient"
android:backgroundTint="?android:attr/colorBackground"
app:layout_constraintBottom_toBottomOf="@+id/backdrop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/manga_cover"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="@dimen/tablet_horizontal_cover_margin"
android:layout_marginTop="32dp"
android:layout_marginEnd="@dimen/tablet_horizontal_cover_margin"
android:background="@drawable/rounded_rectangle"
android:contentDescription="@string/description_cover"
android:scaleType="centerCrop"
app:layout_constraintDimensionRatio="w,3:2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/ic_launcher" />
<LinearLayout
android:id="@+id/manga_detail"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="-8dp"
android:layout_marginEnd="16dp"
android:gravity="center_horizontal"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/backdrop">
<TextView
android:id="@+id/manga_full_title"
style="@style/TextAppearance.Medium.Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:gravity="center"
android:text="@string/manga_info_full_title_label"
android:textIsSelectable="false" />
<TextView
android:id="@+id/manga_author"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAlignment="center"
android:textIsSelectable="false"
tools:text="Author" />
<TextView
android:id="@+id/manga_artist"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:textIsSelectable="false"
tools:text="Artist" />
<LinearLayout
android:id="@+id/manga_status_row"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/manga_status"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
tools:text="Status" />
<TextView
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:text="•"
android:textIsSelectable="false"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/manga_source"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
tools:text="Source" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/manga_actions"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_detail">
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_favorite"
style="@style/Widget.Tachiyomi.Button.ActionButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/add_to_library"
app:icon="@drawable/ic_favorite_border_24dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_tracking"
style="@style/Widget.Tachiyomi.Button.ActionButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/manga_tracking_tab"
android:visibility="gone"
app:icon="@drawable/ic_sync_24dp"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_webview"
style="@style/Widget.Tachiyomi.Button.ActionButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/action_web_view"
android:visibility="gone"
app:icon="@drawable/ic_public_24dp"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_merge"
style="@style/Widget.Tachiyomi.Button.ActionButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/merge"
android:visibility="gone"
app:icon="@drawable/ic_merge_type_24dp"
tools:visibility="visible" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/metadata_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_actions"
tools:visibility="gone" />
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/manga_summary_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layoutDescription="@xml/manga_summary_section_scene"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/metadata_view">
<TextView
android:id="@+id/manga_summary_text"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:clickable="true"
android:focusable="true"
android:maxLines="2"
android:textIsSelectable="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_cover"
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_toggle_more_scrim"
android:layout_width="20dp"
android:layout_height="0dp"
android:background="@drawable/manga_info_more_gradient"
android:backgroundTint="?android:attr/colorBackground"
app:layout_constraintBottom_toBottomOf="@+id/manga_info_toggle_more"
app:layout_constraintEnd_toStartOf="@+id/manga_info_toggle_more"
app:layout_constraintTop_toTopOf="@+id/manga_info_toggle_more" />
<com.google.android.material.button.MaterialButton
android:id="@+id/manga_info_toggle_more"
style="@style/Widget.Tachiyomi.Button.InlineButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="8dp"
android:paddingEnd="16dp"
android:text="@string/manga_info_expand"
android:textAlignment="viewEnd"
app:layout_constraintBottom_toBottomOf="@+id/manga_summary_text"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/manga_info_toggle_less"
style="@style/Widget.Tachiyomi.Button.InlineButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="8dp"
android:paddingEnd="16dp"
android:text="@string/manga_info_collapse"
android:textAlignment="viewEnd"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_summary_text"
tools:visibility="gone" />
<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_summary_text">
<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.recyclerview.widget.RecyclerView
android:id="@+id/genre_groups"
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"
android:nestedScrollingEnabled="false"
tools:listitem="@layout/manga_info_genre_grouping"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_info_toggle_less"/>
</androidx.constraintlayout.motion.widget.MotionLayout>
</androidx.constraintlayout.motion.widget.MotionLayout>

View File

@ -48,10 +48,4 @@
android:layout_gravity="bottom" android:layout_gravity="bottom"
app:layout_dodgeInsetEdges="bottom" /> app:layout_dodgeInsetEdges="bottom" />
<ImageView
android:id="@+id/expanded_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,161 +1,159 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
app:layoutDescription="@xml/manga_info_header_scene"
tools:context=".ui.browse.source.browse.BrowseSourceController"> tools:context=".ui.browse.source.browse.BrowseSourceController">
<androidx.constraintlayout.widget.ConstraintLayout <ImageView
android:id="@+id/backdrop"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="-8dp"
android:alpha="0.2"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="@+id/manga_cover"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="@mipmap/ic_launcher"
tools:ignore="ContentDescription" />
<View
android:id="@+id/backdrop_overlay"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="160dp"
android:background="@drawable/manga_info_gradient"
android:backgroundTint="?android:attr/colorBackground"
app:layout_constraintBottom_toBottomOf="@+id/backdrop" />
<ImageView <ImageView
android:id="@+id/backdrop" android:id="@+id/manga_cover"
android:layout_width="0dp" android:layout_width="100dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginBottom="44dp" android:layout_marginStart="16dp"
android:alpha="0.2" android:layout_marginTop="48dp"
android:scaleType="centerCrop" android:background="@drawable/rounded_rectangle"
app:layout_constraintBottom_toBottomOf="parent" android:contentDescription="@string/description_cover"
app:layout_constraintEnd_toEndOf="parent" android:maxWidth="100dp"
app:layout_constraintStart_toStartOf="parent" android:scaleType="centerCrop"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintDimensionRatio="w,3:2"
tools:background="@mipmap/ic_launcher" /> app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:layout_height="133dp"
tools:src="@mipmap/ic_launcher" />
<View <LinearLayout
android:id="@+id/backdrop_overlay" android:id="@+id/manga_detail"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="@+id/manga_cover"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_cover">
<TextView
android:id="@+id/manga_full_title"
style="@style/TextAppearance.Medium.Title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="160dp" android:layout_height="60sp"
android:layout_marginBottom="44dp" android:layout_marginBottom="4dp"
android:background="@drawable/manga_info_gradient" android:gravity="bottom"
android:backgroundTint="?android:attr/colorBackground" android:text="@string/manga_info_full_title_label"
app:layout_constraintBottom_toBottomOf="parent" /> android:textIsSelectable="false"
app:autoSizeMaxTextSize="20sp"
app:autoSizeMinTextSize="12sp"
app:autoSizeStepGranularity="2sp"
app:autoSizeTextType="uniform" />
<TextView
android:id="@+id/manga_author"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textIsSelectable="false"
tools:text="Author" />
<TextView
android:id="@+id/manga_artist"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textIsSelectable="false"
tools:text="Artist" />
<LinearLayout <LinearLayout
android:id="@+id/manga_info" android:id="@+id/manga_status_row"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:layout_marginTop="4dp">
android:paddingStart="16dp"
android:paddingTop="48dp"
android:paddingEnd="16dp"
android:paddingBottom="8dp"
app:layout_constraintTop_toTopOf="parent">
<eu.kanade.tachiyomi.ui.manga.info.MangaCoverImageView <TextView
android:id="@+id/manga_cover" android:id="@+id/manga_status"
android:layout_width="match_parent" style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_height="wrap_content" android:layout_width="wrap_content"
android:background="@drawable/rounded_rectangle" android:layout_height="match_parent"
android:contentDescription="@string/description_cover" android:ellipsize="end"
android:maxWidth="100dp" android:maxLines="1"
android:scaleType="centerCrop" android:textIsSelectable="false"
tools:src="@mipmap/ic_launcher" /> tools:text="Status" />
<LinearLayout <TextView
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom" android:layout_marginStart="4dp"
android:layout_marginStart="16dp" android:layout_marginEnd="4dp"
android:layout_marginBottom="16dp" android:text="•"
android:orientation="vertical"> android:textIsSelectable="false"
tools:ignore="HardcodedText" />
<TextView <TextView
android:id="@+id/manga_full_title" android:id="@+id/manga_source"
style="@style/TextAppearance.Medium.Title" style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="60sp" android:layout_height="wrap_content"
android:gravity="bottom" android:ellipsize="end"
android:text="@string/manga_info_full_title_label" android:maxLines="1"
android:textIsSelectable="false" android:textIsSelectable="false"
app:autoSizeMaxTextSize="20sp" tools:text="Source" />
app:autoSizeMinTextSize="12sp"
app:autoSizeStepGranularity="2sp"
app:autoSizeTextType="uniform" />
<TextView
android:id="@+id/manga_author"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textIsSelectable="false"
tools:text="Author" />
<TextView
android:id="@+id/manga_artist"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textIsSelectable="false"
tools:text="Artist" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp">
<TextView
android:id="@+id/manga_status"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
tools:text="Status" />
<TextView
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp"
android:text="•"
android:textIsSelectable="false"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/manga_source"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
tools:text="Source" />
</LinearLayout>
</LinearLayout>
</LinearLayout> </LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/manga_actions"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/backdrop">
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btn_favorite" android:id="@+id/btn_favorite"
style="@style/Widget.Tachiyomi.Button.ActionButton" style="@style/Widget.Tachiyomi.Button.ActionButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_weight="1"
android:text="@string/add_to_library" android:text="@string/add_to_library"
app:icon="@drawable/ic_favorite_border_24dp" app:icon="@drawable/ic_favorite_border_24dp" />
app:layout_constraintEnd_toStartOf="@+id/btn_tracking"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_info" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btn_tracking" android:id="@+id/btn_tracking"
style="@style/Widget.Tachiyomi.Button.ActionButton" style="@style/Widget.Tachiyomi.Button.ActionButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/manga_tracking_tab" android:text="@string/manga_tracking_tab"
android:visibility="gone" android:visibility="gone"
app:icon="@drawable/ic_sync_24dp" app:icon="@drawable/ic_sync_24dp"
app:layout_constraintEnd_toStartOf="@+id/btn_webview"
app:layout_constraintStart_toEndOf="@+id/btn_favorite"
app:layout_constraintTop_toBottomOf="@+id/manga_info"
tools:visibility="visible" /> tools:visibility="visible" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
@ -163,29 +161,150 @@
style="@style/Widget.Tachiyomi.Button.ActionButton" style="@style/Widget.Tachiyomi.Button.ActionButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/action_web_view" android:text="@string/action_web_view"
android:visibility="gone" android:visibility="gone"
app:icon="@drawable/ic_public_24dp" app:icon="@drawable/ic_public_24dp"
app:layout_constraintEnd_toStartOf="@+id/btn_merge"
app:layout_constraintStart_toEndOf="@+id/btn_tracking"
app:layout_constraintTop_toBottomOf="@+id/manga_info"
tools:visibility="visible" /> tools:visibility="visible" />
<com.google.android.material.button.MaterialButton <com.google.android.material.button.MaterialButton
android:id="@+id/btn_merge" android:id="@+id/btn_merge"
style="@style/Widget.Tachiyomi.Button.ActionButton" style="@style/Widget.Tachiyomi.Button.ActionButton"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="16dp" android:layout_weight="1"
android:text="@string/merge" android:text="@string/merge"
android:visibility="gone" android:visibility="gone"
app:icon="@drawable/ic_merge_type_24dp" app:icon="@drawable/ic_merge_type_24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/btn_webview"
app:layout_constraintTop_toBottomOf="@+id/manga_info"
tools:visibility="visible" /> tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout> </LinearLayout>
</LinearLayout> <androidx.recyclerview.widget.RecyclerView
android:id="@+id/metadata_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_actions"
tools:visibility="gone" />
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/manga_summary_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutDescription="@xml/manga_summary_section_scene"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/metadata_view">
<TextView
android:id="@+id/manga_summary_text"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:clickable="true"
android:focusable="true"
android:maxLines="2"
android:textIsSelectable="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_cover"
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_toggle_more_scrim"
android:layout_width="20dp"
android:layout_height="0dp"
android:background="@drawable/manga_info_more_gradient"
android:backgroundTint="?android:attr/colorBackground"
app:layout_constraintBottom_toBottomOf="@+id/manga_info_toggle_more"
app:layout_constraintEnd_toStartOf="@+id/manga_info_toggle_more"
app:layout_constraintTop_toTopOf="@+id/manga_info_toggle_more" />
<com.google.android.material.button.MaterialButton
android:id="@+id/manga_info_toggle_more"
style="@style/Widget.Tachiyomi.Button.InlineButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="8dp"
android:paddingEnd="16dp"
android:text="@string/manga_info_expand"
android:textAlignment="viewEnd"
app:layout_constraintBottom_toBottomOf="@+id/manga_summary_text"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/manga_info_toggle_less"
style="@style/Widget.Tachiyomi.Button.InlineButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="8dp"
android:paddingEnd="16dp"
android:text="@string/manga_info_collapse"
android:textAlignment="viewEnd"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_summary_text"
tools:visibility="gone" />
<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_summary_text">
<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.recyclerview.widget.RecyclerView
android:id="@+id/genre_groups"
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"
android:nestedScrollingEnabled="false"
tools:listitem="@layout/manga_info_genre_grouping"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_info_toggle_less"/>
</androidx.constraintlayout.motion.widget.MotionLayout>
</androidx.constraintlayout.motion.widget.MotionLayout>

View File

@ -0,0 +1,158 @@
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:motion="http://schemas.android.com/apk/res-auto">
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@id/start"
motion:duration="@android:integer/config_mediumAnimTime">
<KeyFrameSet></KeyFrameSet>
<OnClick motion:targetId="@+id/manga_cover" />
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/metadata_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@id/manga_actions"
motion:visibilityMode="ignore" />
<Constraint
android:id="@+id/manga_summary_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@id/metadata_view"
motion:visibilityMode="ignore" />
<Constraint
motion:layout_constraintEnd_toEndOf="parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
motion:layout_constraintTop_toBottomOf="@id/backdrop"
motion:layout_constraintStart_toStartOf="parent"
android:id="@+id/manga_actions"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" />
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/backdrop"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="-8dp"
android:alpha="0"
motion:layout_constraintBottom_toBottomOf="@+id/manga_cover"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent"
motion:transitionEasing="cubic(0,1,0,1)" />
<Constraint
android:id="@+id/backdrop_overlay"
android:layout_width="match_parent"
android:layout_height="160dp"
android:layout_marginBottom="-16dp"
android:alpha="0"
motion:layout_constraintBottom_toBottomOf="@+id/backdrop"
motion:transitionEasing="cubic(0,1,0,1)" />
<Constraint
android:id="@+id/manga_cover"
android:layout_marginStart="0dp"
android:layout_marginTop="0dp"
motion:layout_constraintDimensionRatio="2:3"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@+id/btn_favorite"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
motion:layout_constraintEnd_toStartOf="@+id/btn_tracking"
motion:layout_constraintHorizontal_bias="0.5"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@id/manga_detail"
android:layout_marginStart="16dp" />
<Constraint
android:id="@+id/manga_detail"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@id/manga_cover" />
<Constraint
android:id="@+id/btn_tracking"
android:layout_width="0dp"
android:layout_height="wrap_content"
motion:layout_constraintEnd_toStartOf="@+id/btn_webview"
motion:layout_constraintHorizontal_bias="0.5"
motion:layout_constraintStart_toEndOf="@+id/btn_favorite"
motion:layout_constraintTop_toTopOf="@+id/btn_favorite"
motion:visibilityMode="ignore" />
<Constraint
android:id="@+id/btn_tracking"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:visibility="visible"
motion:layout_constraintEnd_toStartOf="@+id/btn_webview"
motion:layout_constraintHorizontal_bias="0.5"
motion:layout_constraintStart_toEndOf="@+id/btn_favorite"
motion:layout_constraintTop_toTopOf="@+id/btn_favorite"
motion:visibilityMode="ignore" />
<Constraint
android:id="@+id/btn_webview"
android:layout_width="0dp"
android:layout_height="wrap_content"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintHorizontal_bias="0.5"
motion:layout_constraintStart_toEndOf="@+id/btn_tracking"
motion:layout_constraintTop_toTopOf="@+id/btn_favorite"
motion:visibilityMode="ignore" />
<Constraint
android:id="@+id/btn_merge"
android:layout_width="0dp"
android:layout_height="wrap_content"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintHorizontal_bias="0.5"
motion:layout_constraintStart_toEndOf="@+id/btn_webview"
motion:layout_constraintTop_toTopOf="@+id/btn_favorite"
motion:visibilityMode="ignore"
android:layout_marginEnd="16dp" />
<Constraint
android:id="@+id/metadata_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@id/manga_actions"
motion:visibilityMode="ignore" />
<Constraint
android:id="@+id/manga_summary_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@id/metadata_view"
motion:visibilityMode="ignore" />
<Constraint
android:id="@+id/manga_actions"
motion:layout_constraintEnd_toEndOf="parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
motion:layout_constraintTop_toBottomOf="@id/manga_detail"
motion:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="8dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp" />
</ConstraintSet>
</MotionScene>

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:motion="http://schemas.android.com/apk/res-auto">
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@id/start"
motion:duration="@android:integer/config_mediumAnimTime">
<KeyFrameSet></KeyFrameSet>
<OnClick motion:targetId="@+id/manga_cover" />
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/manga_summary_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@id/metadata_view"
motion:visibilityMode="ignore" />
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/manga_summary_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@id/metadata_view"
motion:visibilityMode="ignore" />
<Constraint
android:id="@+id/manga_detail"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@+id/manga_cover" />
<Constraint
motion:layout_constraintEnd_toEndOf="parent"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="0dp"
android:layout_marginStart="0dp"
motion:layout_constraintTop_toTopOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintDimensionRatio="w,3:2"
android:layout_marginTop="0dp"
android:id="@+id/manga_cover" />
</ConstraintSet>
</MotionScene>

View File

@ -0,0 +1,162 @@
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:motion="http://schemas.android.com/apk/res-auto">
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@id/start"
motion:duration="1">
<KeyFrameSet></KeyFrameSet>
<OnClick motion:clickAction="toggle" />
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@+id/manga_info_toggle_more_scrim"
android:layout_width="20dp"
android:layout_height="0dp"
android:visibility="visible"
motion:layout_constraintBottom_toBottomOf="@+id/manga_info_toggle_more"
motion:layout_constraintEnd_toStartOf="@+id/manga_info_toggle_more"
motion:layout_constraintTop_toTopOf="@+id/manga_info_toggle_more" />
<Constraint
android:id="@+id/manga_info_toggle_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="visible"
motion:layout_constraintBottom_toBottomOf="@+id/manga_summary_text"
motion:layout_constraintEnd_toEndOf="parent" />
<Constraint
android:id="@+id/manga_info_toggle_less"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintTop_toBottomOf="@+id/manga_summary_text" />
<Constraint
android:id="@+id/manga_genres_tags_compact"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="visible"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@+id/manga_summary_text" />
<!--<Constraint
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:visibility="gone"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@+id/manga_info_toggle_less" />-->
<Constraint
android:id="@+id/genre_groups"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:visibility="gone"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@+id/manga_info_toggle_less" />
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@+id/manga_info_toggle_more_scrim"
android:layout_width="20dp"
android:layout_height="0dp"
android:visibility="gone"
motion:layout_constraintBottom_toBottomOf="@+id/manga_info_toggle_more"
motion:layout_constraintEnd_toStartOf="@+id/manga_info_toggle_more"
motion:layout_constraintTop_toTopOf="@+id/manga_info_toggle_more" />
<Constraint
android:id="@+id/manga_info_toggle_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
motion:layout_constraintBottom_toBottomOf="@+id/manga_summary_text"
motion:layout_constraintEnd_toEndOf="parent" />
<Constraint
android:id="@+id/manga_info_toggle_less"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="visible"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintTop_toBottomOf="@+id/manga_summary_text" />
<Constraint
android:id="@+id/manga_genres_tags_compact"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@+id/manga_summary_text" />
<!-- <Constraint
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:visibility="visible"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@+id/manga_info_toggle_less" />-->
<Constraint
android:id="@+id/genre_groups"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:visibility="visible"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@+id/manga_info_toggle_less" />
<Constraint
android:id="@+id/manga_summary_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@+id/manga_cover"
motion:visibilityMode="ignore" />
<Constraint
android:id="@+id/manga_summary_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@+id/manga_cover"
motion:visibilityMode="ignore" />
<Constraint
android:id="@+id/manga_summary_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@+id/manga_cover"
motion:visibilityMode="ignore" />
<Constraint
android:id="@+id/manga_summary_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toBottomOf="@+id/manga_cover"
motion:visibilityMode="ignore" />
</ConstraintSet>
</MotionScene>