From a6cba5c87dc5e62f40d10d6ba3e290c372f29ac5 Mon Sep 17 00:00:00 2001 From: Jobobby04 Date: Tue, 28 Jul 2020 16:55:33 -0400 Subject: [PATCH] Add special view for browsing E/Exhentai! All the important info is now in front of your face when browsing, it is on by default and can be toggled off in the E-Hentai settings. Let me know if you find any errors --- app/build.gradle | 3 + .../data/preference/PreferenceKeys.kt | 2 + .../data/preference/PreferencesHelper.kt | 2 + .../tachiyomi/source/model/MangasPage.kt | 8 +- .../tachiyomi/source/online/all/EHentai.kt | 121 +++++++++--------- .../source/browse/BrowseSourceController.kt | 8 +- .../source/browse/BrowseSourcePresenter.kt | 10 +- .../ui/browse/source/browse/Pager.kt | 13 +- .../browse/SourceEnhancedEHentaiListHolder.kt | 114 +++++++++++++++++ .../ui/browse/source/browse/SourceItem.kt | 18 ++- .../ui/manga/info/MangaInfoItemAdapter.kt | 10 +- .../ui/manga/info/NamespaceTagsItem.kt | 4 +- .../ui/setting/SettingsEhController.kt | 7 + .../adapters/EHentaiDescriptionAdapter.kt | 23 ++-- .../adapters/HBrowseDescriptionAdapter.kt | 2 +- .../adapters/HitomiDescriptionAdapter.kt | 21 +-- .../adapters/NHentaiDescriptionAdapter.kt | 23 ++-- .../adapters/PervEdenDescriptionAdapter.kt | 11 +- .../adapters/PururinDescriptionAdapter.kt | 15 ++- .../adapters/TsuminoDescriptionAdapter.kt | 13 +- app/src/main/java/exh/util/SourceTagsUtil.kt | 55 ++++++-- .../source_enhanced_ehentai_list_item.xml | 106 +++++++++++++++ app/src/main/res/menu/source_browse.xml | 1 + app/src/main/res/values/strings_sy.xml | 14 +- 24 files changed, 463 insertions(+), 141 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceEnhancedEHentaiListHolder.kt create mode 100644 app/src/main/res/layout/source_enhanced_ehentai_list_item.xml diff --git a/app/build.gradle b/app/build.gradle index 37f505c02..002368cd1 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -341,6 +341,9 @@ dependencies { // Humanize (EH) implementation 'com.github.mfornos:humanize-slim:1.2.2' + // RatingBar (SY) + implementation 'me.zhanghai.android.materialratingbar:library:1.3.1' + implementation 'androidx.gridlayout:gridlayout:1.0.0' final def markwon_version = '4.1.0' diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt index 24a6e0ecc..b7ce4fd6e 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt @@ -272,4 +272,6 @@ object PreferenceKeys { const val recommendsInOverflow = "recommends_in_overflow" const val hitomiAlwaysWebp = "hitomi_always_webp" + + const val enhancedEHentaiView = "enhanced_e_hentai_view" } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index ad177a195..353f3e88f 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -380,4 +380,6 @@ class PreferencesHelper(val context: Context) { fun recommendsInOverflow() = flowPrefs.getBoolean(Keys.recommendsInOverflow, false) fun hitomiAlwaysWebp() = flowPrefs.getBoolean(Keys.hitomiAlwaysWebp, true) + + fun enhancedEHentaiView() = flowPrefs.getBoolean(Keys.enhancedEHentaiView, true) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/model/MangasPage.kt b/app/src/main/java/eu/kanade/tachiyomi/source/model/MangasPage.kt index a377c36ea..68eda4b5a 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/source/model/MangasPage.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/model/MangasPage.kt @@ -1,3 +1,9 @@ package eu.kanade.tachiyomi.source.model -data class MangasPage(val mangas: List, val hasNextPage: Boolean) +import exh.metadata.metadata.base.RaisedSearchMetadata + +/* SY --> */ open /* SY <-- */ class MangasPage(val mangas: List, val hasNextPage: Boolean) + +// SY --> +class MetadataMangasPage(mangas: List, hasNextPage: Boolean, val mangasMetadata: List) : MangasPage(mangas, hasNextPage) +// SY <-- diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt index 96c5bcece..584a812ad 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt @@ -18,7 +18,7 @@ import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList -import eu.kanade.tachiyomi.source.model.MangasPage +import eu.kanade.tachiyomi.source.model.MetadataMangasPage import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SManga @@ -96,9 +96,9 @@ class EHentai( /** * Gallery list entry */ - data class ParsedManga(val fav: Int, val manga: Manga) + data class ParsedManga(val fav: Int, val manga: Manga, val metadata: EHentaiSearchMetadata) - fun extendedGenericMangaParse(doc: Document) = with(doc) { + private fun extendedGenericMangaParse(doc: Document) = with(doc) { // Parse mangas (supports compact + extended layout) val parsedMangas = select(".itg > tbody > tr").filter { // Do not parse header and ads @@ -110,6 +110,8 @@ class EHentai( val infoElement = it.selectFirst(".gl3e") val favElement = column2.children().find { it.attr("style").startsWith("border-color") } + val infoElements = infoElement?.select("div") + val parsedTags = mutableListOf() ParsedManga( fav = FAVORITES_BORDER_HEX_COLORS.indexOf( @@ -122,14 +124,10 @@ class EHentai( // Get image thumbnail_url = thumbnailElement.attr("src") - val tags = mutableListOf() - - val infoElements = infoElement?.select("div") - if (infoElements != null) { linkElement.select("div div")?.getOrNull(1)?.select("tr")?.forEach { row -> val namespace = row.select(".tc").text().removeSuffix(":") - tags.addAll( + parsedTags.addAll( row.select("div").map { element -> RaisedTag( namespace, @@ -143,46 +141,61 @@ class EHentai( } ) } - - getGenre(infoElements[1])?.let { tags += it } - - getDateTag(infoElements[2])?.let { tags += it } - - getRating(infoElements[3])?.let { tags += it } - - getAuthor(infoElements[4])?.let { author = it } } else { val tagElement = it.selectFirst(".gl3c > a") val tagElements = tagElement.select("div") tagElements.forEach { element -> if (element.className() == "gt") { val namespace = element.attr("title").substringBefore(":").trimOrNull() ?: "misc" - tags += RaisedTag( + parsedTags += RaisedTag( namespace, element.attr("title").substringAfter(":").trim(), TAG_TYPE_NORMAL ) } } + } - val genre = it.selectFirst(".gl1c div") - getGenre(genreString = genre?.text()?.nullIfBlank()?.toLowerCase()?.replace(" ", ""))?.let { tags += it } + genre = parsedTags.toGenreString() + }, + metadata = EHentaiSearchMetadata().apply { + tags += parsedTags + + if (infoElements != null) { + getGenre(infoElements.getOrNull(1))?.let { genre = it } + + getDateTag(infoElements.getOrNull(2))?.let { datePosted = it } + + getRating(infoElements.getOrNull(3))?.let { averageRating = it } + + getUploader(infoElements.getOrNull(4))?.let { uploader = it } + + getPageCount(infoElements.getOrNull(5))?.let { length = it } + } else { + val parsedGenre = it.selectFirst(".gl1c div") + getGenre(genreString = parsedGenre?.text()?.nullIfBlank()?.toLowerCase()?.replace(" ", ""))?.let { genre = it } val info = it.selectFirst(".gl2c") val extraInfo = it.selectFirst(".gl4c") val infoList = info.select("div div") - getDateTag(infoList[8])?.let { tags += it } + getDateTag(infoList.getOrNull(8))?.let { datePosted = it } - getRating(infoList[9])?.let { tags += it } + getRating(infoList.getOrNull(9))?.let { averageRating = it } val extraInfoList = extraInfo.select("div") - getAuthor(extraInfoList[1])?.let { author = it } - } + if (extraInfoList.getOrNull(2) == null) { + getUploader(extraInfoList.getOrNull(0))?.let { uploader = it } - genre = tags.toGenreString() + getPageCount(extraInfoList.getOrNull(1))?.let { length = it } + } else { + getUploader(extraInfoList.getOrNull(1))?.let { uploader = it } + + getPageCount(extraInfoList.getOrNull(2))?.let { length = it } + } + } } ) } @@ -202,68 +215,54 @@ class EHentai( Pair(parsedMangas, hasNextPage) } - private fun getGenre(element: Element? = null, genreString: String? = null): RaisedTag? { - val attr = element?.attr("onclick") + private fun getGenre(element: Element? = null, genreString: String? = null): String? { + return element?.attr("onclick") ?.nullIfBlank() ?.substringAfterLast('/') ?.removeSuffix("'") ?.trim() ?.substringAfterLast('/') ?.removeSuffix("'") ?: genreString - return if (attr != null) { - RaisedTag( - EH_GENRE_NAMESPACE, - attr, - TAG_TYPE_NORMAL - ) - } else null } - private fun getDateTag(element: Element?): RaisedTag? { + private fun getDateTag(element: Element?): Long? { val text = element?.text()?.nullIfBlank() return if (text != null) { val date = EX_DATE_FORMAT.parse(text) - if (date != null) { - RaisedTag( - EH_DATE_POSTED_NAMESPACE, - date.time.toString(), - TAG_TYPE_NORMAL - ) - } else null + date?.time } else null } - private fun getRating(element: Element?): RaisedTag? { + private fun getRating(element: Element?): Double? { val ratingStyle = element?.attr("style")?.nullIfBlank() return if (ratingStyle != null) { - val matches = "([0-9]*)px".toRegex().findAll(ratingStyle).mapNotNull { it.groupValues.getOrNull(1)?.toIntOrNull() }.toList() + val matches = RATING_REGEX.findAll(ratingStyle).mapNotNull { it.groupValues.getOrNull(1)?.toIntOrNull() }.toList() if (matches.size == 2) { var rate = 5 - matches[0] / 16 - RaisedTag( - EH_RATING_NAMESPACE, - if (matches[1] == 21) { - rate-- - "$rate.5" - } else rate.toString(), - TAG_TYPE_NORMAL - ) + if (matches[1] == 21) { + rate-- + rate + 0.5 + } else rate.toDouble() } else null } else null } - private fun getAuthor(element: Element?): String? { - return element?.select("a") - ?.attr("href") - ?.nullIfBlank() - ?.trim() - ?.substringAfterLast('/') + private fun getUploader(element: Element?): String? { + return element?.select("a")?.text()?.trimOrNull() + } + + private fun getPageCount(element: Element?): Int? { + val pageCount = element?.text()?.trimOrNull() + return if (pageCount != null) { + PAGE_COUNT_REGEX.find(pageCount)?.value?.toIntOrNull() + } else null } /** * Parse a list of galleries */ fun genericMangaParse(response: Response) = extendedGenericMangaParse(response.asJsoup()).let { - MangasPage(it.first.map { it.manga }, it.second) + MetadataMangasPage(it.first.map { it.manga }, it.second, it.first.map { it.metadata }) } override fun fetchChapterList(manga: SManga) = fetchChapterList(manga) {} @@ -675,7 +674,7 @@ class EHentai( page++ } while (parsed.second) - return Pair(result as List, favNames!!) + return Pair(result.toList(), favNames!!) } fun spPref() = if (exh) { @@ -965,8 +964,8 @@ class EHentai( private const val QUERY_PREFIX = "?f_apply=Apply+Filter" private const val TR_SUFFIX = "TR" private const val REVERSE_PARAM = "TEH_REVERSE" - private const val EH_DATE_POSTED_NAMESPACE = "date_posted" - private const val EH_RATING_NAMESPACE = "rating" + private val PAGE_COUNT_REGEX = "[0-9]*".toRegex() + private val RATING_REGEX = "([0-9]*)px".toRegex() private const val EH_API_BASE = "https://api.e-hentai.org/api.php" private val JSON = "application/json; charset=utf-8".toMediaTypeOrNull()!! diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt index d01d195dc..01351004b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt @@ -54,6 +54,7 @@ import eu.kanade.tachiyomi.util.view.snack import eu.kanade.tachiyomi.widget.AutofitRecyclerView import eu.kanade.tachiyomi.widget.EmptyView import exh.EXHSavedSearch +import exh.isEhBasedSource import kotlinx.android.parcel.Parcelize import kotlinx.coroutines.Job import kotlinx.coroutines.flow.drop @@ -348,7 +349,7 @@ open class BrowseSourceController(bundle: Bundle) : binding.catalogueView.removeView(oldRecycler) } - val recycler = if (preferences.sourceDisplayMode().get() == DisplayMode.LIST) { + val recycler = if (preferences.sourceDisplayMode().get() == DisplayMode.LIST /* SY --> */ || (preferences.enhancedEHentaiView().get() && presenter.source.isEhBasedSource()) /* SY <-- */) { RecyclerView(view.context).apply { id = R.id.recycler layoutManager = LinearLayoutManager(context) @@ -439,6 +440,11 @@ open class BrowseSourceController(bundle: Bundle) : DisplayMode.LIST -> R.id.action_list } menu.findItem(displayItem).isChecked = true + // SY --> + if (preferences.enhancedEHentaiView().get() && presenter.source.isEhBasedSource()) { + menu.findItem(R.id.action_display_mode).isVisible = false + } + // SY <-- } override fun onPrepareOptionsMenu(menu: Menu) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt index b8d9d337a..4d26e794b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt @@ -38,9 +38,9 @@ import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateItem import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateSectionItem import eu.kanade.tachiyomi.util.removeCovers import exh.EXHSavedSearch +import exh.isEhBasedSource import java.lang.RuntimeException import java.util.Date -import kotlinx.coroutines.flow.subscribe import rx.Observable import rx.Subscription import rx.android.schedulers.AndroidSchedulers @@ -165,9 +165,13 @@ open class BrowseSourcePresenter( pagerSubscription?.let { remove(it) } pagerSubscription = pager.results() .observeOn(Schedulers.io()) - .map { pair -> pair.first to pair.second.map { networkToLocalManga(it, sourceId) } } + // SY --> + .map { triple -> Triple(triple.first, triple.second.map { networkToLocalManga(it, sourceId) }, triple.third) } + // SY <-- .doOnNext { initializeMangas(it.second) } - .map { pair -> pair.first to pair.second.map { SourceItem(it, sourceDisplayMode) } } + // SY --> + .map { triple -> triple.first to triple.second.mapIndexed { index, manga -> SourceItem(manga, sourceDisplayMode, if (prefs.enhancedEHentaiView().get() && source.isEhBasedSource()) triple.third?.getOrNull(index) else null) } } + // SY <-- .observeOn(AndroidSchedulers.mainThread()) .subscribeReplay( { view, (page, mangas) -> diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/Pager.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/Pager.kt index 77284e5a8..9a796c342 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/Pager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/Pager.kt @@ -2,7 +2,9 @@ package eu.kanade.tachiyomi.ui.browse.source.browse import com.jakewharton.rxrelay.PublishRelay import eu.kanade.tachiyomi.source.model.MangasPage +import eu.kanade.tachiyomi.source.model.MetadataMangasPage import eu.kanade.tachiyomi.source.model.SManga +import exh.metadata.metadata.base.RaisedSearchMetadata import rx.Observable /** @@ -13,9 +15,9 @@ abstract class Pager(var currentPage: Int = 1) { var hasNextPage = true private set - protected val results: PublishRelay>> = PublishRelay.create() + protected val results: PublishRelay< /* SY --> */ Triple /* SY <-- */ /* SY --> */, List? /* SY <-- */ >> = PublishRelay.create() - fun results(): Observable>> { + fun results(): Observable< /* SY --> */ Triple /* SY <-- */ /* SY --> */, List?> /* SY <-- */> { return results.asObservable() } @@ -25,6 +27,11 @@ abstract class Pager(var currentPage: Int = 1) { val page = currentPage currentPage++ hasNextPage = mangasPage.hasNextPage && mangasPage.mangas.isNotEmpty() - results.call(Pair(page, mangasPage.mangas)) + // SY --> + val mangasMetadata = if (mangasPage is MetadataMangasPage) { + mangasPage.mangasMetadata + } else null + // SY <-- + results.call( /* SY <-- */ Triple /* SY <-- */ (page, mangasPage.mangas /* SY --> */, mangasMetadata /* SY <-- */)) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceEnhancedEHentaiListHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceEnhancedEHentaiListHolder.kt new file mode 100644 index 000000000..02f0e1d53 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceEnhancedEHentaiListHolder.kt @@ -0,0 +1,114 @@ +package eu.kanade.tachiyomi.ui.browse.source.browse + +import android.graphics.Color +import android.view.View +import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.bumptech.glide.load.resource.bitmap.CenterCrop +import com.bumptech.glide.load.resource.bitmap.RoundedCorners +import com.bumptech.glide.request.RequestOptions +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.data.glide.GlideApp +import eu.kanade.tachiyomi.data.glide.toMangaThumbnail +import eu.kanade.tachiyomi.util.system.getResourceColor +import exh.metadata.EX_DATE_FORMAT +import exh.metadata.metadata.EHentaiSearchMetadata +import exh.metadata.metadata.base.RaisedSearchMetadata +import exh.util.SourceTagsUtil +import exh.util.SourceTagsUtil.Companion.getLocaleSourceUtil +import java.util.Date +import kotlinx.android.synthetic.main.source_enhanced_ehentai_list_item.date_posted +import kotlinx.android.synthetic.main.source_enhanced_ehentai_list_item.genre +import kotlinx.android.synthetic.main.source_enhanced_ehentai_list_item.language +import kotlinx.android.synthetic.main.source_enhanced_ehentai_list_item.rating_bar +import kotlinx.android.synthetic.main.source_enhanced_ehentai_list_item.thumbnail +import kotlinx.android.synthetic.main.source_enhanced_ehentai_list_item.title +import kotlinx.android.synthetic.main.source_enhanced_ehentai_list_item.uploader + +/** + * Class used to hold the displayed data of a manga in the catalogue, like the cover or the title. + * All the elements from the layout file "item_catalogue_list" are available in this class. + * + * @param view the inflated view for this holder. + * @param adapter the adapter handling this holder. + * @constructor creates a new catalogue holder. + */ +class SourceEnhancedEHentaiListHolder(private val view: View, adapter: FlexibleAdapter<*>) : + SourceHolder(view, adapter) { + + private val favoriteColor = view.context.getResourceColor(R.attr.colorOnSurface, 0.38f) + private val unfavoriteColor = view.context.getResourceColor(R.attr.colorOnSurface) + + /** + * Method called from [CatalogueAdapter.onBindViewHolder]. It updates the data for this + * holder with the given manga. + * + * @param manga the manga to bind. + */ + override fun onSetValues(manga: Manga) { + title.text = manga.title + title.setTextColor(if (manga.favorite) favoriteColor else unfavoriteColor) + + // Set alpha of thumbnail. + thumbnail.alpha = if (manga.favorite) 0.3f else 1.0f + + setImage(manga) + } + + fun onSetMetadataValues(manga: Manga, metadata: RaisedSearchMetadata) { + if (metadata !is EHentaiSearchMetadata) return + + if (metadata.uploader != null) { + uploader.text = metadata.uploader + } + + val pair = when (metadata.genre) { + "doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi) + "manga" -> Pair(SourceTagsUtil.MANGA_COLOR, R.string.manga) + "artistcg" -> Pair(SourceTagsUtil.ARTIST_CG_COLOR, R.string.artist_cg) + "gamecg" -> Pair(SourceTagsUtil.GAME_CG_COLOR, R.string.game_cg) + "western" -> Pair(SourceTagsUtil.WESTERN_COLOR, R.string.western) + "non-h" -> Pair(SourceTagsUtil.NON_H_COLOR, R.string.non_h) + "imageset" -> Pair(SourceTagsUtil.IMAGE_SET_COLOR, R.string.image_set) + "cosplay" -> Pair(SourceTagsUtil.COSPLAY_COLOR, R.string.cosplay) + "asianporn" -> Pair(SourceTagsUtil.ASIAN_PORN_COLOR, R.string.asian_porn) + "misc" -> Pair(SourceTagsUtil.MISC_COLOR, R.string.misc) + else -> Pair("", 0) + } + + if (pair.first.isNotBlank()) { + genre.setBackgroundColor(Color.parseColor(pair.first)) + genre.text = view.context.getString(pair.second) + } else genre.text = metadata.genre + + metadata.datePosted?.let { date_posted.text = EX_DATE_FORMAT.format(Date(it)) } + + metadata.averageRating?.let { rating_bar.rating = it.toFloat() } + + val locale = getLocaleSourceUtil(metadata.tags.firstOrNull { it.namespace == "language" }?.name) + val pageCount = metadata.length + + language.text = if (locale != null && pageCount != null) { + view.resources.getQuantityString(R.plurals.browse_language_and_pages, pageCount, pageCount, locale.toLanguageTag().toUpperCase()) + } else if (pageCount != null) { + view.resources.getQuantityString(R.plurals.num_pages, pageCount, pageCount) + } else locale?.toLanguageTag()?.toUpperCase() + } + + override fun setImage(manga: Manga) { + GlideApp.with(view.context).clear(thumbnail) + + if (!manga.thumbnail_url.isNullOrEmpty()) { + val radius = view.context.resources.getDimensionPixelSize(R.dimen.card_radius) + val requestOptions = RequestOptions().transform(CenterCrop(), RoundedCorners(radius)) + GlideApp.with(view.context) + .load(manga.toMangaThumbnail()) + .diskCacheStrategy(DiskCacheStrategy.DATA) + .apply(requestOptions) + .dontAnimate() + .placeholder(android.R.color.transparent) + .into(thumbnail) + } + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceItem.kt index 5ee0bb61a..a2b68483e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceItem.kt @@ -14,25 +14,26 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.preference.PreferenceValues.DisplayMode import eu.kanade.tachiyomi.widget.AutofitRecyclerView +import exh.metadata.metadata.base.RaisedSearchMetadata import kotlinx.android.synthetic.main.source_compact_grid_item.view.card import kotlinx.android.synthetic.main.source_compact_grid_item.view.gradient -class SourceItem(val manga: Manga, private val displayMode: Preference) : +class SourceItem(val manga: Manga, private val displayMode: Preference /* SY --> */, private val metadata: RaisedSearchMetadata? = null /* SY <-- */) : AbstractFlexibleItem() { override fun getLayoutRes(): Int { - return when (displayMode.get()) { + return /* SY --> */ if (metadata == null) /* SY <-- */ when (displayMode.get()) { DisplayMode.COMPACT_GRID -> R.layout.source_compact_grid_item DisplayMode.COMFORTABLE_GRID -> R.layout.source_comfortable_grid_item DisplayMode.LIST -> R.layout.source_list_item - } + } /* SY --> */ else R.layout.source_enhanced_ehentai_list_item /* SY <-- */ } override fun createViewHolder( view: View, adapter: FlexibleAdapter> ): SourceHolder { - return when (displayMode.get()) { + return /* SY --> */ if (metadata == null) /* SY <-- */ when (displayMode.get()) { DisplayMode.COMPACT_GRID -> { val parent = adapter.recyclerView as AutofitRecyclerView val coverHeight = parent.itemWidth / 3 * 4 @@ -59,7 +60,11 @@ class SourceItem(val manga: Manga, private val displayMode: Preference { SourceListHolder(view, adapter) } + // SY --> + } else { + SourceEnhancedEHentaiListHolder(view, adapter) } + // SY <-- } override fun bindViewHolder( @@ -69,6 +74,11 @@ class SourceItem(val manga: Manga, private val displayMode: Preference? ) { holder.onSetValues(manga) + // SY --> + if (metadata != null) { + (holder as? SourceEnhancedEHentaiListHolder)?.onSetMetadataValues(manga, metadata) + } + // SY <-- } override fun equals(other: Any?): Boolean { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoItemAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoItemAdapter.kt index 74e8a82e9..6b4a68204 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoItemAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoItemAdapter.kt @@ -20,7 +20,7 @@ import exh.isEhBasedSource import exh.isNamespaceSource import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL -import exh.util.SourceTagsUtil +import exh.util.SourceTagsUtil.Companion.getRaisedTags import exh.util.makeSearchChip import exh.util.setChipsExtended import kotlinx.coroutines.CoroutineScope @@ -128,11 +128,11 @@ class MangaInfoItemAdapter( .mapValues { values -> values.value.map { makeSearchChip(it.name, controller::performSearch, controller::performGlobalSearch, source.id, itemView.context, it.namespace, it.type) } } .map { NamespaceTagsItem(it.key!!, it.value) } } else { - val genre = manga.getGenres() + val genre = manga.getRaisedTags() if (!genre.isNullOrEmpty()) { - 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) } } + namespaceTags = genre + .groupBy { it.namespace } + .mapValues { values -> values.value.map { makeSearchChip(it.name, controller::performSearch, controller::performGlobalSearch, source.id, itemView.context, it.namespace) } } .map { NamespaceTagsItem(it.key, it.value) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/NamespaceTagsItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/NamespaceTagsItem.kt index 731f27422..21cca007a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/NamespaceTagsItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/NamespaceTagsItem.kt @@ -10,7 +10,7 @@ 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) : AbstractFlexibleItem() { +open class NamespaceTagsItem(val namespace: String?, val tags: List) : AbstractFlexibleItem() { override fun getLayoutRes(): Int { return R.layout.manga_info_genre_grouping @@ -22,7 +22,7 @@ open class NamespaceTagsItem(val namespace: String, val tags: List) : Abst override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { val namespaceChip = Chip(holder.itemView.context) - namespaceChip.text = namespace + namespaceChip.text = namespace ?: holder.itemView.context.getString(R.string.unknown) holder.namespaceChipGroup.addView(namespaceChip) tags.forEach { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt index 92fb557d7..b0c8b9e12 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt @@ -518,6 +518,13 @@ class SettingsEhController : SettingsController() { onChange { preferences.imageQuality().reconfigure() } }.dependency = PreferenceKeys.eh_enableExHentai + + switchPreference { + titleRes = R.string.pref_enhanced_e_hentai_view + summaryRes = R.string.pref_enhanced_e_hentai_view_summary + key = PreferenceKeys.enhancedEHentaiView + defaultValue = true + } } preferenceCategory { diff --git a/app/src/main/java/exh/ui/metadata/adapters/EHentaiDescriptionAdapter.kt b/app/src/main/java/exh/ui/metadata/adapters/EHentaiDescriptionAdapter.kt index 7f44576e8..0c32091b1 100644 --- a/app/src/main/java/exh/ui/metadata/adapters/EHentaiDescriptionAdapter.kt +++ b/app/src/main/java/exh/ui/metadata/adapters/EHentaiDescriptionAdapter.kt @@ -14,6 +14,7 @@ import exh.metadata.EX_DATE_FORMAT import exh.metadata.humanReadableByteCount import exh.metadata.metadata.EHentaiSearchMetadata import exh.ui.metadata.MetadataViewController +import exh.util.SourceTagsUtil import java.util.Date import kotlin.math.roundToInt import kotlinx.coroutines.CoroutineScope @@ -50,16 +51,16 @@ class EHentaiDescriptionAdapter( val genre = meta.genre if (genre != null) { val pair = when (genre) { - "doujinshi" -> Pair("#fc4e4e", R.string.doujinshi) - "manga" -> Pair("#e78c1a", R.string.manga) - "artistcg" -> Pair("#dde500", R.string.artist_cg) - "gamecg" -> Pair("#05bf0b", R.string.game_cg) - "western" -> Pair("#14e723", R.string.western) - "non-h" -> Pair("#08d7e2", R.string.non_h) - "imageset" -> Pair("#5f5fff", R.string.image_set) - "cosplay" -> Pair("#9755f5", R.string.cosplay) - "asianporn" -> Pair("#fe93ff", R.string.asian_porn) - "misc" -> Pair("#9e9e9e", R.string.misc) + "doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi) + "manga" -> Pair(SourceTagsUtil.MANGA_COLOR, R.string.manga) + "artistcg" -> Pair(SourceTagsUtil.ARTIST_CG_COLOR, R.string.artist_cg) + "gamecg" -> Pair(SourceTagsUtil.GAME_CG_COLOR, R.string.game_cg) + "western" -> Pair(SourceTagsUtil.WESTERN_COLOR, R.string.western) + "non-h" -> Pair(SourceTagsUtil.NON_H_COLOR, R.string.non_h) + "imageset" -> Pair(SourceTagsUtil.IMAGE_SET_COLOR, R.string.image_set) + "cosplay" -> Pair(SourceTagsUtil.COSPLAY_COLOR, R.string.cosplay) + "asianporn" -> Pair(SourceTagsUtil.ASIAN_PORN_COLOR, R.string.asian_porn) + "misc" -> Pair(SourceTagsUtil.MISC_COLOR, R.string.misc) else -> Pair("", 0) } @@ -80,7 +81,7 @@ class EHentaiDescriptionAdapter( binding.uploader.text = meta.uploader ?: itemView.context.getString(R.string.unknown) binding.size.text = humanReadableByteCount(meta.size ?: 0, true) - binding.pages.text = itemView.context.getString(R.string.num_pages, meta.length ?: 0) + binding.pages.text = itemView.resources.getQuantityString(R.plurals.num_pages, meta.length ?: 0, meta.length ?: 0) val language = meta.language ?: itemView.context.getString(R.string.unknown) binding.language.text = if (meta.translated == true) { itemView.context.getString(R.string.language_translated, language) diff --git a/app/src/main/java/exh/ui/metadata/adapters/HBrowseDescriptionAdapter.kt b/app/src/main/java/exh/ui/metadata/adapters/HBrowseDescriptionAdapter.kt index 95e99381d..4e22ff969 100644 --- a/app/src/main/java/exh/ui/metadata/adapters/HBrowseDescriptionAdapter.kt +++ b/app/src/main/java/exh/ui/metadata/adapters/HBrowseDescriptionAdapter.kt @@ -41,7 +41,7 @@ class HBrowseDescriptionAdapter( val meta = controller.presenter.meta if (meta == null || meta !is HBrowseSearchMetadata) return - binding.pages.text = itemView.context.getString(R.string.num_pages, meta.length ?: 0) + binding.pages.text = itemView.resources.getQuantityString(R.plurals.num_pages, meta.length ?: 0, meta.length ?: 0) binding.moreInfo.clicks() .onEach { diff --git a/app/src/main/java/exh/ui/metadata/adapters/HitomiDescriptionAdapter.kt b/app/src/main/java/exh/ui/metadata/adapters/HitomiDescriptionAdapter.kt index 7147cc53c..1b160d573 100644 --- a/app/src/main/java/exh/ui/metadata/adapters/HitomiDescriptionAdapter.kt +++ b/app/src/main/java/exh/ui/metadata/adapters/HitomiDescriptionAdapter.kt @@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaController import exh.metadata.EX_DATE_FORMAT import exh.metadata.metadata.HitomiSearchMetadata import exh.ui.metadata.MetadataViewController +import exh.util.SourceTagsUtil import java.util.Date import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -47,16 +48,16 @@ class HitomiDescriptionAdapter( val genre = meta.type if (genre != null) { val pair = when (genre) { - "doujinshi" -> Pair("#fc4e4e", R.string.doujinshi) - "manga" -> Pair("#e78c1a", R.string.manga) - "artist CG" -> Pair("#dde500", R.string.artist_cg) - "game CG" -> Pair("#05bf0b", R.string.game_cg) - "western" -> Pair("#14e723", R.string.western) - "non-H" -> Pair("#08d7e2", R.string.non_h) - "image Set" -> Pair("#5f5fff", R.string.image_set) - "cosplay" -> Pair("#9755f5", R.string.cosplay) - "asian Porn" -> Pair("#fe93ff", R.string.asian_porn) - "misc" -> Pair("#9e9e9e", R.string.misc) + "doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi) + "manga" -> Pair(SourceTagsUtil.MANGA_COLOR, R.string.manga) + "artist CG" -> Pair(SourceTagsUtil.ARTIST_CG_COLOR, R.string.artist_cg) + "game CG" -> Pair(SourceTagsUtil.GAME_CG_COLOR, R.string.game_cg) + "western" -> Pair(SourceTagsUtil.WESTERN_COLOR, R.string.western) + "non-H" -> Pair(SourceTagsUtil.NON_H_COLOR, R.string.non_h) + "image Set" -> Pair(SourceTagsUtil.IMAGE_SET_COLOR, R.string.image_set) + "cosplay" -> Pair(SourceTagsUtil.COSPLAY_COLOR, R.string.cosplay) + "asian Porn" -> Pair(SourceTagsUtil.ASIAN_PORN_COLOR, R.string.asian_porn) + "misc" -> Pair(SourceTagsUtil.MISC_COLOR, R.string.misc) else -> Pair("", 0) } diff --git a/app/src/main/java/exh/ui/metadata/adapters/NHentaiDescriptionAdapter.kt b/app/src/main/java/exh/ui/metadata/adapters/NHentaiDescriptionAdapter.kt index 83beec647..5db683c07 100644 --- a/app/src/main/java/exh/ui/metadata/adapters/NHentaiDescriptionAdapter.kt +++ b/app/src/main/java/exh/ui/metadata/adapters/NHentaiDescriptionAdapter.kt @@ -14,6 +14,7 @@ import eu.kanade.tachiyomi.util.system.getResourceColor import exh.metadata.EX_DATE_FORMAT import exh.metadata.metadata.NHentaiSearchMetadata import exh.ui.metadata.MetadataViewController +import exh.util.SourceTagsUtil import java.util.Date import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -54,16 +55,16 @@ class NHentaiDescriptionAdapter( if (category != null) { val pair = when (category) { - "doujinshi" -> Pair("#fc4e4e", R.string.doujinshi) - "manga" -> Pair("#e78c1a", R.string.manga) - "artistcg" -> Pair("#dde500", R.string.artist_cg) - "gamecg" -> Pair("#05bf0b", R.string.game_cg) - "western" -> Pair("#14e723", R.string.western) - "non-h" -> Pair("#08d7e2", R.string.non_h) - "imageset" -> Pair("#5f5fff", R.string.image_set) - "cosplay" -> Pair("#9755f5", R.string.cosplay) - "asianporn" -> Pair("#fe93ff", R.string.asian_porn) - "misc" -> Pair("#9e9e9e", R.string.misc) + "doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi) + "manga" -> Pair(SourceTagsUtil.MANGA_COLOR, R.string.manga) + "artistcg" -> Pair(SourceTagsUtil.ARTIST_CG_COLOR, R.string.artist_cg) + "gamecg" -> Pair(SourceTagsUtil.GAME_CG_COLOR, R.string.game_cg) + "western" -> Pair(SourceTagsUtil.WESTERN_COLOR, R.string.western) + "non-h" -> Pair(SourceTagsUtil.NON_H_COLOR, R.string.non_h) + "imageset" -> Pair(SourceTagsUtil.IMAGE_SET_COLOR, R.string.image_set) + "cosplay" -> Pair(SourceTagsUtil.COSPLAY_COLOR, R.string.cosplay) + "asianporn" -> Pair(SourceTagsUtil.ASIAN_PORN_COLOR, R.string.asian_porn) + "misc" -> Pair(SourceTagsUtil.MISC_COLOR, R.string.misc) else -> Pair("", 0) } @@ -85,7 +86,7 @@ class NHentaiDescriptionAdapter( binding.whenPosted.text = EX_DATE_FORMAT.format(Date((meta.uploadDate ?: 0) * 1000)) - binding.pages.text = itemView.context.getString(R.string.num_pages, meta.pageImageTypes.size) + binding.pages.text = itemView.resources.getQuantityString(R.plurals.num_pages, meta.pageImageTypes.size, meta.pageImageTypes.size) @SuppressLint("SetTextI18n") binding.id.text = "#" + (meta.nhId ?: 0) diff --git a/app/src/main/java/exh/ui/metadata/adapters/PervEdenDescriptionAdapter.kt b/app/src/main/java/exh/ui/metadata/adapters/PervEdenDescriptionAdapter.kt index 0d43c1bec..d8ec1a69a 100644 --- a/app/src/main/java/exh/ui/metadata/adapters/PervEdenDescriptionAdapter.kt +++ b/app/src/main/java/exh/ui/metadata/adapters/PervEdenDescriptionAdapter.kt @@ -11,6 +11,7 @@ import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.manga.MangaController import exh.metadata.metadata.PervEdenSearchMetadata import exh.ui.metadata.MetadataViewController +import exh.util.SourceTagsUtil import java.util.Locale import kotlin.math.roundToInt import kotlinx.coroutines.CoroutineScope @@ -47,11 +48,11 @@ class PervEdenDescriptionAdapter( val genre = meta.type if (genre != null) { val pair = when (genre) { - "Doujinshi" -> Pair("#fc4e4e", R.string.doujinshi) - "Japanese Manga" -> Pair("#e78c1a", R.string.manga) - "Korean Manhwa" -> Pair("#dde500", R.string.manhwa) - "Chinese Manhua" -> Pair("#05bf0b", R.string.manhua) - "Comic" -> Pair("#14e723", R.string.comic) + "Doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi) + "Japanese Manga" -> Pair(SourceTagsUtil.MANGA_COLOR, R.string.manga) + "Korean Manhwa" -> Pair(SourceTagsUtil.ARTIST_CG_COLOR, R.string.manhwa) + "Chinese Manhua" -> Pair(SourceTagsUtil.GAME_CG_COLOR, R.string.manhua) + "Comic" -> Pair(SourceTagsUtil.WESTERN_COLOR, R.string.comic) else -> Pair("", 0) } diff --git a/app/src/main/java/exh/ui/metadata/adapters/PururinDescriptionAdapter.kt b/app/src/main/java/exh/ui/metadata/adapters/PururinDescriptionAdapter.kt index 43262dc14..1c62c198c 100644 --- a/app/src/main/java/exh/ui/metadata/adapters/PururinDescriptionAdapter.kt +++ b/app/src/main/java/exh/ui/metadata/adapters/PururinDescriptionAdapter.kt @@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaController import exh.metadata.metadata.PururinSearchMetadata import exh.metadata.metadata.PururinSearchMetadata.Companion.TAG_NAMESPACE_CATEGORY import exh.ui.metadata.MetadataViewController +import exh.util.SourceTagsUtil import kotlin.math.roundToInt import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -47,12 +48,12 @@ class PururinDescriptionAdapter( val genre = meta.tags.find { it.namespace == TAG_NAMESPACE_CATEGORY } if (genre != null) { val pair = when (genre.name) { - "doujinshi" -> Pair("#fc4e4e", R.string.doujinshi) - "manga" -> Pair("#e78c1a", R.string.manga) - "artist-cg" -> Pair("#dde500", R.string.artist_cg) - "game-cg" -> Pair("#05bf0b", R.string.game_cg) - "artbook" -> Pair("#5f5fff", R.string.artbook) - "webtoon" -> Pair("#5f5fff", R.string.webtoon) + "doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi) + "manga" -> Pair(SourceTagsUtil.MANGA_COLOR, R.string.manga) + "artist-cg" -> Pair(SourceTagsUtil.ARTIST_CG_COLOR, R.string.artist_cg) + "game-cg" -> Pair(SourceTagsUtil.GAME_CG_COLOR, R.string.game_cg) + "artbook" -> Pair(SourceTagsUtil.IMAGE_SET_COLOR, R.string.artbook) + "webtoon" -> Pair(SourceTagsUtil.NON_H_COLOR, R.string.webtoon) else -> Pair("", 0) } @@ -64,7 +65,7 @@ class PururinDescriptionAdapter( binding.uploader.text = meta.uploaderDisp ?: meta.uploader ?: "" binding.size.text = meta.fileSize ?: itemView.context.getString(R.string.unknown) - binding.pages.text = itemView.context.getString(R.string.num_pages, meta.pages ?: 0) + binding.pages.text = itemView.resources.getQuantityString(R.plurals.num_pages, meta.pages ?: 0, meta.pages ?: 0) val ratingFloat = meta.averageRating?.toFloat() val name = when (((ratingFloat ?: 100F) * 2).roundToInt()) { diff --git a/app/src/main/java/exh/ui/metadata/adapters/TsuminoDescriptionAdapter.kt b/app/src/main/java/exh/ui/metadata/adapters/TsuminoDescriptionAdapter.kt index 384b38686..cde81acae 100644 --- a/app/src/main/java/exh/ui/metadata/adapters/TsuminoDescriptionAdapter.kt +++ b/app/src/main/java/exh/ui/metadata/adapters/TsuminoDescriptionAdapter.kt @@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.util.system.getResourceColor import exh.metadata.metadata.TsuminoSearchMetadata import exh.ui.metadata.MetadataViewController +import exh.util.SourceTagsUtil import java.util.Date import kotlin.math.roundToInt import kotlinx.coroutines.CoroutineScope @@ -48,11 +49,11 @@ class TsuminoDescriptionAdapter( val genre = meta.category if (genre != null) { val pair = when (genre) { - "Doujinshi" -> Pair("#fc4e4e", R.string.doujinshi) - "Manga" -> Pair("#e78c1a", R.string.manga) - "Artist CG" -> Pair("#dde500", R.string.artist_cg) - "Game CG" -> Pair("#05bf0b", R.string.game_cg) - "Video" -> Pair("#14e723", R.string.video) + "Doujinshi" -> Pair(SourceTagsUtil.DOUJINSHI_COLOR, R.string.doujinshi) + "Manga" -> Pair(SourceTagsUtil.MANGA_COLOR, R.string.manga) + "Artist CG" -> Pair(SourceTagsUtil.ARTIST_CG_COLOR, R.string.artist_cg) + "Game CG" -> Pair(SourceTagsUtil.GAME_CG_COLOR, R.string.game_cg) + "Video" -> Pair(SourceTagsUtil.WESTERN_COLOR, R.string.video) else -> Pair("", 0) } @@ -70,7 +71,7 @@ class TsuminoDescriptionAdapter( binding.whenPosted.text = TsuminoSearchMetadata.TSUMINO_DATE_FORMAT.format(Date(meta.uploadDate ?: 0)) binding.uploader.text = meta.uploader ?: itemView.context.getString(R.string.unknown) - binding.pages.text = itemView.context.getString(R.string.num_pages, meta.length ?: 0) + binding.pages.text = itemView.resources.getQuantityString(R.plurals.num_pages, meta.length ?: 0, meta.length ?: 0) val name = when (((meta.averageRating ?: 100F) * 2).roundToInt()) { 0 -> R.string.rating0 diff --git a/app/src/main/java/exh/util/SourceTagsUtil.kt b/app/src/main/java/exh/util/SourceTagsUtil.kt index b6a4bd957..48c5dc639 100644 --- a/app/src/main/java/exh/util/SourceTagsUtil.kt +++ b/app/src/main/java/exh/util/SourceTagsUtil.kt @@ -1,30 +1,31 @@ package exh.util +import eu.kanade.tachiyomi.data.database.models.Manga 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 import exh.TSUMINO_SOURCE_ID +import exh.metadata.metadata.base.RaisedTag +import java.util.Locale class SourceTagsUtil { 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 == NHENTAI_SOURCE_ID || sourceId == HITOMI_SOURCE_ID) { - val parsed = if (fullTag != null) parseTag(fullTag) else if (namespace != null && tag != null) Pair(namespace, tag) else null - if (parsed != null) { + val parsed = if (fullTag != null) parseTag(fullTag) else if (namespace != null && tag != null) RaisedTag(namespace, tag, TAG_TYPE_DEFAULT) else null + if (parsed?.namespace != null) { 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() - TSUMINO_SOURCE_ID -> parsed.second.substringBefore('|').trim() - else -> wrapTag(parsed.first, parsed.second.substringBefore('|').trim()) + HITOMI_SOURCE_ID -> wrapTagHitomi(parsed.namespace, parsed.name.substringBefore('|').trim()) + NHENTAI_SOURCE_ID -> wrapTagNHentai(parsed.namespace, parsed.name.substringBefore('|').trim()) + PURURIN_SOURCE_ID -> parsed.name.substringBefore('|').trim() + TSUMINO_SOURCE_ID -> parsed.name.substringBefore('|').trim() + else -> wrapTag(parsed.namespace, parsed.name.substringBefore('|').trim()) } } else null } else null } - fun parseTag(tag: String) = tag.substringBefore(':').trim() to tag.substringAfter(':').trim() - private fun wrapTag(namespace: String, tag: String) = if (tag.contains(' ')) { "$namespace:\"$tag$\"" } else { @@ -46,4 +47,40 @@ class SourceTagsUtil { } else { "$namespace:$tag" } + companion object { + fun Manga.getRaisedTags(): List? = this.getGenres()?.map { parseTag(it) } + + fun parseTag(tag: String) = RaisedTag(tag.substringBefore(':').trimOrNull(), (tag.substringAfter(':').trimOrNull() ?: tag), TAG_TYPE_DEFAULT) + + const val DOUJINSHI_COLOR = "#f44336" + const val MANGA_COLOR = "#ff9800" + const val ARTIST_CG_COLOR = "#fbc02d" + const val GAME_CG_COLOR = "#4caf50" + const val WESTERN_COLOR = "#8bc34a" + const val NON_H_COLOR = "#2196f3" + const val IMAGE_SET_COLOR = "#3f51b5" + const val COSPLAY_COLOR = "#9c27b0" + const val ASIAN_PORN_COLOR = "#9575cd" + const val MISC_COLOR = "#f06292" + + fun getLocaleSourceUtil(language: String?) = when (language) { + "english", "eng" -> Locale("en") + "chinese" -> Locale("zh") + "spanish" -> Locale("es") + "korean" -> Locale("ko") + "russian" -> Locale("ru") + "french" -> Locale("fr") + "portuguese" -> Locale("pt") + "thai" -> Locale("th") + "german" -> Locale("de") + "italian" -> Locale("it") + "vietnamese" -> Locale("vi") + "polish" -> Locale("pl") + "hungarian" -> Locale("hu") + "dutch" -> Locale("nl") + else -> null + } + + private const val TAG_TYPE_DEFAULT = 1 + } } diff --git a/app/src/main/res/layout/source_enhanced_ehentai_list_item.xml b/app/src/main/res/layout/source_enhanced_ehentai_list_item.xml new file mode 100644 index 000000000..09450e07d --- /dev/null +++ b/app/src/main/res/layout/source_enhanced_ehentai_list_item.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/menu/source_browse.xml b/app/src/main/res/menu/source_browse.xml index 584a7ecac..bf8c4337a 100644 --- a/app/src/main/res/menu/source_browse.xml +++ b/app/src/main/res/menu/source_browse.xml @@ -10,6 +10,7 @@ app:showAsAction="collapseActionView|ifRoom" /> 1280x 980x 780x + Enhanced E/ExHentai browse + Enable/Disable the enhanced browse menu made for E/ExHentai Favorites sync Disable favorites uploading Favorites are only downloaded from ExHentai. Any changes to favorites in the app will not be uploaded. Prevents accidental loss of favorites on ExHentai. Note that removals will still be downloaded (if you remove a favorites on ExHentai, it will be removed in the app as well). @@ -414,11 +416,21 @@ Parodies - %1$d pages + + %1$d page + %1$d pages + Tags Visible: %1$s %1$s (%2$s, %3$d) %1$s (%2$s) %1$s TR + + + %2$s, %1$d page + %2$s, %1$d pages + + + \ No newline at end of file