From 33a8d86684bf74b6ac407382c7734b82c217e4be Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Thu, 2 Feb 2023 22:44:09 +0800 Subject: [PATCH] Refine EhTagDatabase (#750) * Refine EhTagDatabase * Fix warnings --- app/build.gradle | 2 +- .../hippo/ehviewer/client/EhTagDatabase.kt | 101 +++++++++++------- .../ehviewer/ui/scene/GalleryDetailScene.kt | 2 +- .../hippo/ehviewer/ui/scene/SearchBarScene.kt | 59 ++++------ app/src/main/res/values-zh-rCN/strings.xml | 2 +- app/src/main/res/values-zh-rTW/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- gradle.properties | 2 + 8 files changed, 93 insertions(+), 79 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 39b323978..c811d73f7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -35,7 +35,7 @@ android { targetSdkVersion 33 versionCode 180031 versionName "1.8.5.3" - resConfigs 'zh', 'zh-rCN', 'zh-rHK', 'zh-rTW', 'es', 'ja', 'ko', 'fr', 'de', 'th', 'nb-rNO' + resourceConfigurations += ['zh', 'zh-rCN', 'zh-rHK', 'zh-rTW', 'es', 'ja', 'ko', 'fr', 'de', 'th', 'nb-rNO'] } externalNativeBuild { diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhTagDatabase.kt b/app/src/main/java/com/hippo/ehviewer/client/EhTagDatabase.kt index 0476a2328..689b7aacc 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhTagDatabase.kt +++ b/app/src/main/java/com/hippo/ehviewer/client/EhTagDatabase.kt @@ -43,35 +43,62 @@ import java.nio.charset.StandardCharsets import java.security.MessageDigest import java.security.NoSuchAlgorithmException +private typealias TagGroup = Map +private typealias TagGroups = Map + object EhTagDatabase { - private lateinit var tags: JSONObject - private lateinit var tagList: ArrayList> + private const val NAMESPACE_PREFIX = "n" + private lateinit var tagGroups: TagGroups fun isInitialized(): Boolean { - return this::tags.isInitialized && this::tagList.isInitialized + return this::tagGroups.isInitialized } + private fun JSONObject.toTagGroups(): TagGroups = + keys().asSequence().associateWith { getJSONObject(it).toTagGroup() } + + private fun JSONObject.toTagGroup(): TagGroup = + keys().asSequence().associateWith { getString(it) } + private fun updateData(source: BufferedSource) { try { - val tmpTags = JSONObject(source.readString(StandardCharsets.UTF_8)) - val tmpTagList = arrayListOf>() - tmpTags.keys().forEachRemaining { prefix: String -> - val values = tmpTags.optJSONObject(prefix) - values?.let { - it.keys().forEachRemaining { tag: String -> - tmpTagList.add(Pair("$prefix:$tag", values.optString(tag))) - } - } - } - tags = tmpTags - tagList = tmpTagList + tagGroups = JSONObject(source.readString(StandardCharsets.UTF_8)).toTagGroups() } catch (e: JSONException) { e.printStackTrace() } } - fun getTranslation(prefix: String?, tag: String?): String? { - return tags.optJSONObject(prefix)?.optString(tag)?.trim()?.takeIf { it.isNotEmpty() } + fun getTranslation(prefix: String? = NAMESPACE_PREFIX, tag: String?): String? { + return tagGroups[prefix]?.get(tag)?.trim()?.takeIf { it.isNotEmpty() } + } + + private fun internalSuggestFlow( + tags: Map, + keyword: String, + translate: Boolean, + exactly: Boolean + ): Flow> = flow { + if (exactly) { + tags[keyword]?.let { + emit(Pair(it.takeIf { translate }, keyword)) + } + } else { + if (translate) { + tags.forEach { (tag, hint) -> + if (tag != keyword && + (tag.containsIgnoreSpace(keyword) || hint.containsIgnoreSpace(keyword)) + ) { + emit(Pair(hint, tag)) + } + } + } else { + tags.keys.forEach { tag -> + if (tag != keyword && tag.containsIgnoreSpace(keyword)) { + emit(Pair(null, tag)) + } + } + } + } } /* Construct a cold flow for tag database suggestions */ @@ -80,32 +107,32 @@ object EhTagDatabase { translate: Boolean, exactly: Boolean = false ): Flow> = flow { - tagList.forEach { - val tag = it.first - val hint = if (translate) it.second else null - val index = tag.indexOf(':') - if (tag.substring(index + 1).trim() == keyword) { - if (exactly) - emit(Pair(hint, tag)) - return@forEach + val keywordPrefix = keyword.substringBefore(':') + val keywordTag = keyword.drop(keywordPrefix.length + 1) + val prefix = namespaceToPrefix(keywordPrefix) ?: keywordPrefix + val tags = tagGroups[prefix.takeIf { keywordTag.isNotEmpty() && it != NAMESPACE_PREFIX }] + tags?.let { + internalSuggestFlow(it, keywordTag, translate, exactly).collect { (hint, tag) -> + emit(Pair(hint, "$prefix:$tag")) } - if (exactly) - return@forEach - val keywordMatches: Boolean = - if (index == -1 || index >= tag.length - 1 || keyword.length > 2) { - containsIgnoreSpace(tag, keyword) - } else { - containsIgnoreSpace(tag.substring(index + 1), keyword) + } ?: tagGroups.forEach { (prefix, tags) -> + if (prefix != NAMESPACE_PREFIX) { + internalSuggestFlow(tags, keyword, translate, exactly).collect { (hint, tag) -> + emit(Pair(hint, "$prefix:$tag")) + } + } else { + internalSuggestFlow(tags, keyword, translate, exactly).collect { (hint, tag) -> + emit(Pair(hint, "$tag:")) } - if (keywordMatches || containsIgnoreSpace(hint, keyword)) { - emit(Pair(hint, tag)) } } } - private fun containsIgnoreSpace(text: String?, key: String): Boolean { - return text != null && text.replace(" ", "").contains(key.replace(" ", "")) - } + private fun String.removeSpace(): String = replace(" ", "") + + private fun String.containsIgnoreSpace(other: String, ignoreCase: Boolean = true): Boolean = + removeSpace().contains(other.removeSpace(), ignoreCase) + private val NAMESPACE_TO_PREFIX = HashMap().also { it["artist"] = "a" diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.kt index 30849f63e..4d51959f2 100755 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.kt @@ -625,7 +625,7 @@ class GalleryDetailScene : CollapsingToolbarScene(), View.OnClickListener, Downl binding.content.tags.tags.addView(ll) var readableTagName: String? = null if (ehTags != null && ehTags.isInitialized()) { - readableTagName = ehTags.getTranslation("n", tg.groupName) + readableTagName = ehTags.getTranslation(tag = tg.groupName) } val tgName = inflater.inflate(R.layout.item_gallery_tag, ll, false) as TextView ll.addView(tgName) diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/SearchBarScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/SearchBarScene.kt index 0b1493ac2..65b93b285 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/SearchBarScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/SearchBarScene.kt @@ -2,7 +2,6 @@ package com.hippo.ehviewer.ui.scene import android.annotation.SuppressLint import android.os.Bundle -import android.text.TextUtils import android.view.Gravity import android.view.LayoutInflater import android.view.MenuItem @@ -170,24 +169,15 @@ abstract class SearchBarScene : BaseScene(), ToolBarScene { } private fun wrapTagKeyword(keyword: String): String { - var keyword = keyword - keyword = keyword.trim { it <= ' ' } - - val index1 = keyword.indexOf(':') - if (index1 == -1 || index1 >= keyword.length - 1) { - // Can't find :, or : is the last char - return keyword - } - if (keyword[index1 + 1] == '"') { - // The char after : is ", the word must be quoted - return keyword - } - val index2 = keyword.indexOf(' ') - return if (index2 <= index1) { - // Can't find space, or space is before : + return if (keyword.endsWith(':')) { keyword - } else keyword.substring(0, index1 + 1) + "\"" + keyword.substring(index1 + 1) + "$\"" - + } else if (keyword.contains(' ')) { + val tag = keyword.substringAfter(':') + val prefix = keyword.dropLast(tag.length) + "$prefix\"$tag$\" " + } else { + "$keyword$ " + } } interface SuggestionProvider { @@ -276,13 +266,11 @@ abstract class SearchBarScene : BaseScene(), ToolBarScene { override fun onClick() { val edittext = binding.searchview.editText edittext.let { - val text = it.text.toString() - var temp = wrapTagKeyword(mKeyword) + " " - if (text.contains(" ")) { - temp = text.substring(0, text.lastIndexOf(" ")) + " " + temp - } - it.setText(temp) - it.setSelection(temp.length) + val keywords = it.text.toString().substringBeforeLast(' ', "") + val keyword = wrapTagKeyword(mKeyword) + val newKeywords = if (keywords.isNotEmpty()) "$keywords $keyword" else keyword + it.setText(newKeywords) + it.setSelection(newKeywords.length) } } } @@ -340,18 +328,15 @@ abstract class SearchBarScene : BaseScene(), ToolBarScene { mSuggestionProvider?.run { providerSuggestions(text)?.forEach { emit(it) } } mSearchDatabase.getSuggestions(text, 128).forEach { emit(KeywordSuggestion(it)) } EhTagDatabase.takeIf { it.isInitialized() }?.run { - if (!TextUtils.isEmpty(text) && !text.endsWith(" ")) { - val s = text.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() - if (s.isNotEmpty()) { - val keyword = s[s.size - 1] - val translate = - Settings.getShowTagTranslations() && isTranslatable(requireContext()) - suggestFlow(keyword, translate, true).collect { - emit(TagSuggestion(it.first, it.second)) - } - suggestFlow(keyword, translate).collect { - emit(TagSuggestion(it.first, it.second)) - } + if (text.isNotEmpty() && !text.endsWith(' ')) { + val keyword = text.substringAfterLast(' ') + val translate = + Settings.getShowTagTranslations() && isTranslatable(requireContext()) + suggestFlow(keyword, translate, true).collect { + emit(TagSuggestion(it.first, it.second)) + } + suggestFlow(keyword, translate).collect { + emit(TagSuggestion(it.first, it.second)) } } } diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 7ddee3aeb..818326e8c 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -480,7 +480,7 @@ 原始档案 重采样档案 下载压缩包需要 H@H 客户端 - 资金:%s GP,%d Credits + 资金:%1$s GP,%2$d Credits 余额不足 隐私 不允许屏幕抓取 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 774daf4e9..3fbe8defb 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -529,7 +529,7 @@ 原始檔案 重新採樣檔案 下載壓縮檔需要 H@H 客戶端 - 資金:%s GP,%d Credits + 資金:%1$s GP,%2$d Credits 餘額不足 隱私 不允許螢幕擷取 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 75d8a5092..4037422d2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -550,7 +550,7 @@ Original archive Resample archive Need H@H client for archive download - Funds: %s GP, %d Credits + Funds: %1$s GP, %2$d Credits Insufficient Funds Privacy Prevent screenshots diff --git a/gradle.properties b/gradle.properties index 41f32a9eb..021ffb249 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,6 +21,8 @@ org.gradle.caching=true android.useAndroidX=true android.enableAppCompileTimeRClass=true android.experimental.enableNewResourceShrinker.preciseShrinking=true +kotlin.mpp.androidGradlePluginCompatibility.nowarn=true +kotlin.mpp.androidSourceSetLayoutVersion=2 # signing environment variable signStoreFile=keystore/androidkey.jks