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 android.view.ViewGroup
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
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 com.bumptech.glide.load.engine.DiskCacheStrategy 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.R
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.glide.GlideApp 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.setTooltip
import eu.kanade.tachiyomi.util.view.visible import eu.kanade.tachiyomi.util.view.visible
import eu.kanade.tachiyomi.util.view.visibleIf 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.MERGED_SOURCE_ID
import exh.NHENTAI_SOURCE_ID
import exh.PURURIN_SOURCE_ID
import exh.util.SourceTagsUtil import exh.util.SourceTagsUtil
import exh.util.makeSearchChip
import exh.util.setChipsExtended import exh.util.setChipsExtended
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -66,6 +75,8 @@ class MangaInfoHeaderAdapter(
holder.bind() holder.bind()
} }
val tagsAdapter: FlexibleAdapter<IFlexible<*>> = FlexibleAdapter(null)
/** /**
* Update the view with manga information. * Update the view with manga information.
* *
@ -229,6 +240,11 @@ class MangaInfoHeaderAdapter(
} }
// EXH <-- // EXH <--
// SY -->
binding.mangaNamespaceTagsRecycler.layoutManager = LinearLayoutManager(itemView.context)
binding.mangaNamespaceTagsRecycler.adapter = tagsAdapter
// SY <--
setMangaInfo(manga, source) setMangaInfo(manga, source)
} }
@ -320,8 +336,18 @@ class MangaInfoHeaderAdapter(
// Update genres list // Update genres list
if (!manga.genre.isNullOrBlank()) { if (!manga.genre.isNullOrBlank()) {
// SY --> // 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.mangaGenresTagsFullChips.setChipsExtended(manga.getGenres(), controller::performSearch, controller::performGlobalSearch, source?.id ?: 0)
binding.mangaGenresTagsCompactChips.setChipsExtended(manga.getGenres(), controller::performSearch, controller::performGlobalSearch, source?.id ?: 0)
// SY <-- // SY <--
} else { } else {
binding.mangaGenresTagsWrapper.gone() binding.mangaGenresTagsWrapper.gone()
@ -387,7 +413,13 @@ class MangaInfoHeaderAdapter(
} }
binding.mangaGenresTagsCompact.visibleIf { isExpanded } 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.EXH_SOURCE_ID
import exh.HITOMI_SOURCE_ID import exh.HITOMI_SOURCE_ID
import exh.NHENTAI_SOURCE_ID import exh.NHENTAI_SOURCE_ID
import exh.PURURIN_SOURCE_ID
class SourceTagsUtil { class 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? {
@ -13,6 +14,7 @@ class SourceTagsUtil {
when (sourceId) { when (sourceId) {
HITOMI_SOURCE_ID -> wrapTagHitomi(parsed.first, parsed.second.substringBefore('|').trim()) HITOMI_SOURCE_ID -> wrapTagHitomi(parsed.first, parsed.second.substringBefore('|').trim())
NHENTAI_SOURCE_ID -> wrapTagNHentai(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 -> wrapTag(parsed.first, parsed.second.substringBefore('|').trim())
} }
} else null } else null

View File

@ -1,5 +1,6 @@
package exh.util package exh.util
import android.content.Context
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.WindowInsets import android.view.WindowInsets
@ -114,16 +115,19 @@ fun ChipGroup.setChipsExtended(items: List<String>?, onClick: (item: String) ->
removeAllViews() removeAllViews()
items?.forEach { item -> items?.forEach { item ->
val chip = Chip(context).apply { val chip = makeSearchChip(item, onClick, onLongClick, sourceId, context)
text = item
val search = SourceTagsUtil().getWrappedTag(sourceId, fullTag = item) ?: item
setOnClickListener { onClick(search) }
setOnLongClickListener {
onLongClick(search)
false
}
}
addView(chip) 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" android:textIsSelectable="false"
tools:text="Summary" /> 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 <FrameLayout
android:id="@+id/manga_genres_tags_wrapper" android:id="@+id/manga_genres_tags_wrapper"
android:layout_width="match_parent" android:layout_width="match_parent"