Add custom tag view for namespaced sources (E-Hentai, nHentai, Hitomi.la, and Pururin)

This commit is contained in:
Jobobby04 2020-07-16 17:27:36 -04:00
parent 74012e0830
commit 8b95d93a96
6 changed files with 129 additions and 12 deletions

View File

@ -7,8 +7,11 @@ import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.load.engine.DiskCacheStrategy
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.data.glide.GlideApp
@ -27,8 +30,14 @@ import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.setTooltip
import eu.kanade.tachiyomi.util.view.visible
import eu.kanade.tachiyomi.util.view.visibleIf
import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.HITOMI_SOURCE_ID
import exh.MERGED_SOURCE_ID
import exh.NHENTAI_SOURCE_ID
import exh.PURURIN_SOURCE_ID
import exh.util.SourceTagsUtil
import exh.util.makeSearchChip
import exh.util.setChipsExtended
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -66,6 +75,8 @@ class MangaInfoHeaderAdapter(
holder.bind()
}
val tagsAdapter: FlexibleAdapter<IFlexible<*>> = FlexibleAdapter(null)
/**
* Update the view with manga information.
*
@ -229,6 +240,11 @@ class MangaInfoHeaderAdapter(
}
// EXH <--
// SY -->
binding.mangaNamespaceTagsRecycler.layoutManager = LinearLayoutManager(itemView.context)
binding.mangaNamespaceTagsRecycler.adapter = tagsAdapter
// SY <--
setMangaInfo(manga, source)
}
@ -320,8 +336,18 @@ class MangaInfoHeaderAdapter(
// Update genres list
if (!manga.genre.isNullOrBlank()) {
// SY -->
binding.mangaGenresTagsCompactChips.setChipsExtended(manga.getGenres(), controller::performSearch, controller::performGlobalSearch, source?.id ?: 0)
if (source != null && (source.id == EH_SOURCE_ID || source.id == EXH_SOURCE_ID || source.id == NHENTAI_SOURCE_ID || source.id == HITOMI_SOURCE_ID || source.id == PURURIN_SOURCE_ID)) {
val genre = manga.getGenres()
if (!genre.isNullOrEmpty()) {
val namespaceTags = genre.map { SourceTagsUtil().parseTag(it) }
.groupBy { it.first }
.mapValues { values -> values.value.map { makeSearchChip(it.second, controller::performSearch, controller::performGlobalSearch, source.id, itemView.context, it.first) } }
.map { NamespaceTagsItem(it.key, it.value) }
tagsAdapter.updateDataSet(namespaceTags)
}
}
binding.mangaGenresTagsFullChips.setChipsExtended(manga.getGenres(), controller::performSearch, controller::performGlobalSearch, source?.id ?: 0)
binding.mangaGenresTagsCompactChips.setChipsExtended(manga.getGenres(), controller::performSearch, controller::performGlobalSearch, source?.id ?: 0)
// SY <--
} else {
binding.mangaGenresTagsWrapper.gone()
@ -387,7 +413,13 @@ class MangaInfoHeaderAdapter(
}
binding.mangaGenresTagsCompact.visibleIf { isExpanded }
binding.mangaGenresTagsFullChips.visibleIf { !isExpanded }
// SY -->
if (source.id == EH_SOURCE_ID || source.id == EXH_SOURCE_ID || source.id == NHENTAI_SOURCE_ID || source.id == HITOMI_SOURCE_ID || source.id == PURURIN_SOURCE_ID) {
binding.mangaNamespaceTagsRecycler.visibleIf { !isExpanded }
} else {
binding.mangaGenresTagsFullChips.visibleIf { !isExpanded }
}
// SY <--
}
/**

View File

@ -0,0 +1,47 @@
package eu.kanade.tachiyomi.ui.manga.info
import android.view.View
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.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFlexible
import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R
open class NamespaceTagsItem(val namespace: String, val tags: List<Chip>) : AbstractFlexibleItem<NamespaceTagsItem.Holder>() {
override fun getLayoutRes(): Int {
return R.layout.manga_info_genre_grouping
}
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): Holder {
return Holder(view, adapter)
}
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>, holder: Holder, position: Int, payloads: List<Any?>?) {
val namespaceChip = Chip(holder.itemView.context)
namespaceChip.text = namespace
holder.namespaceChipGroup.addView(namespaceChip)
tags.forEach {
holder.tagsChipGroup.addView(it)
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
return namespace == (other as NamespaceTagsItem).namespace
}
override fun hashCode(): Int {
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

@ -4,6 +4,7 @@ import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.HITOMI_SOURCE_ID
import exh.NHENTAI_SOURCE_ID
import exh.PURURIN_SOURCE_ID
class SourceTagsUtil {
fun getWrappedTag(sourceId: Long, namespace: String? = null, tag: String? = null, fullTag: String? = null): String? {
@ -13,6 +14,7 @@ class SourceTagsUtil {
when (sourceId) {
HITOMI_SOURCE_ID -> wrapTagHitomi(parsed.first, parsed.second.substringBefore('|').trim())
NHENTAI_SOURCE_ID -> wrapTagNHentai(parsed.first, parsed.second.substringBefore('|').trim())
PURURIN_SOURCE_ID -> parsed.second.substringBefore('|').trim()
else -> wrapTag(parsed.first, parsed.second.substringBefore('|').trim())
}
} else null

View File

@ -1,5 +1,6 @@
package exh.util
import android.content.Context
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
@ -114,16 +115,19 @@ fun ChipGroup.setChipsExtended(items: List<String>?, onClick: (item: String) ->
removeAllViews()
items?.forEach { item ->
val chip = Chip(context).apply {
text = item
val search = SourceTagsUtil().getWrappedTag(sourceId, fullTag = item) ?: item
setOnClickListener { onClick(search) }
setOnLongClickListener {
onLongClick(search)
false
}
}
val chip = makeSearchChip(item, onClick, onLongClick, sourceId, context)
addView(chip)
}
}
fun makeSearchChip(item: String, onClick: (item: String) -> Unit = {}, onLongClick: (item: String) -> Unit = {}, sourceId: Long, context: Context, namespace: String? = null): Chip {
return Chip(context).apply {
text = item
val search = (if (namespace != null) SourceTagsUtil().getWrappedTag(sourceId, namespace = namespace, tag = item) else SourceTagsUtil().getWrappedTag(sourceId, fullTag = item)) ?: item
setOnClickListener { onClick(search) }
setOnLongClickListener {
onLongClick(search)
false
}
}
}

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.chip.ChipGroup
android:id="@+id/namespace"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
app:chipSpacingHorizontal="4dp"/>
<com.google.android.material.chip.ChipGroup
android:id="@+id/tags"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="16dp"
app:chipSpacingHorizontal="4dp"/>
</LinearLayout>

View File

@ -244,6 +244,15 @@
android:textIsSelectable="false"
tools:text="Summary" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/manga_namespace_tags_recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:visibility="gone"
tools:listitem="@layout/manga_info_genre_grouping"/>
<FrameLayout
android:id="@+id/manga_genres_tags_wrapper"
android:layout_width="match_parent"