Suggestion
This commit is contained in:
@@ -22,9 +22,10 @@ import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.core.content.ContextCompat
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import xyz.quaver.floatingsearchview.databinding.SearchSuggestionItemBinding
|
||||
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.sources.hitomi.Hitomi
|
||||
import xyz.quaver.pupil.sources.hitomi.Hiyobi
|
||||
|
||||
data class SearchResult(
|
||||
val id: String,
|
||||
@@ -56,15 +57,24 @@ data class SearchResult(
|
||||
enum class DefaultSortMode {
|
||||
DEFAULT
|
||||
}
|
||||
interface Source<Query_SortMode : Enum<Query_SortMode>> {
|
||||
val name: String
|
||||
val iconResID: Int
|
||||
val availableSortMode: Array<Query_SortMode>
|
||||
|
||||
suspend fun query(query: String, range: IntRange, sortMode: Enum<*>) : Pair<Channel<SearchResult>, Int>
|
||||
@Parcelize
|
||||
class DefaultSearchSuggestion(override val body: String) : SearchSuggestion
|
||||
|
||||
abstract class Source<Query_SortMode: Enum<Query_SortMode>, Suggestion: SearchSuggestion> {
|
||||
abstract val name: String
|
||||
abstract val iconResID: Int
|
||||
abstract val availableSortMode: Array<Query_SortMode>
|
||||
|
||||
abstract suspend fun search(query: String, range: IntRange, sortMode: Enum<*>) : Pair<Channel<SearchResult>, Int>
|
||||
abstract suspend fun suggestion(query: String) : List<Suggestion>
|
||||
|
||||
open fun onSuggestionBind(binding: SearchSuggestionItemBinding, item: Suggestion) {
|
||||
binding.leftIcon.setImageResource(R.drawable.tag)
|
||||
}
|
||||
}
|
||||
|
||||
val sources = mutableMapOf<String, Source<*>>()
|
||||
val sources = mutableMapOf<String, Source<*, SearchSuggestion>>()
|
||||
val sourceIcons = mutableMapOf<String, Drawable?>()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@@ -73,8 +83,8 @@ fun initSources(context: Context) {
|
||||
listOf(
|
||||
Hitomi(),
|
||||
Hiyobi()
|
||||
).forEach {
|
||||
sources[it.name] = it
|
||||
).forEach {
|
||||
sources[it.name] = it as Source<*, SearchSuggestion>
|
||||
sourceIcons[it.name] = ContextCompat.getDrawable(context, it.iconResID)
|
||||
}
|
||||
}
|
||||
@@ -16,26 +16,45 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.quaver.pupil.sources.hitomi
|
||||
package xyz.quaver.pupil.sources
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import xyz.quaver.floatingsearchview.databinding.SearchSuggestionItemBinding
|
||||
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
|
||||
import xyz.quaver.hitomi.*
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.sources.SearchResult
|
||||
import xyz.quaver.pupil.sources.SearchResult.ExtraType
|
||||
import xyz.quaver.pupil.sources.Source
|
||||
import xyz.quaver.pupil.util.translations
|
||||
import xyz.quaver.pupil.util.wordCapitalize
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
class Hitomi : Source<Hitomi.SortMode> {
|
||||
class Hitomi : Source<Hitomi.SortMode, Hitomi.TagSuggestion>() {
|
||||
|
||||
enum class SortMode {
|
||||
NEWEST,
|
||||
POPULAR
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class TagSuggestion(val s: String, val t: Int, val u: String, val n: String) :
|
||||
SearchSuggestion {
|
||||
constructor(s: Suggestion) : this(s.s, s.t, s.u, s.n)
|
||||
|
||||
@IgnoredOnParcel
|
||||
override val body =
|
||||
if (translations[s] != null)
|
||||
"${translations[s]} ($s)"
|
||||
else
|
||||
s
|
||||
}
|
||||
|
||||
override val name: String = "hitomi.la"
|
||||
override val iconResID: Int = R.drawable.hitomi
|
||||
override val availableSortMode: Array<SortMode> = SortMode.values()
|
||||
@@ -44,7 +63,7 @@ class Hitomi : Source<Hitomi.SortMode> {
|
||||
var cachedSortMode: SortMode? = null
|
||||
val cache = mutableListOf<Int>()
|
||||
|
||||
override suspend fun query(query: String, range: IntRange, sortMode: Enum<*>): Pair<Channel<SearchResult>, Int> {
|
||||
override suspend fun search(query: String, range: IntRange, sortMode: Enum<*>): Pair<Channel<SearchResult>, Int> {
|
||||
if (cachedQuery != query || cachedSortMode != sortMode || cache.isEmpty()) {
|
||||
cachedQuery = null
|
||||
cache.clear()
|
||||
@@ -79,6 +98,44 @@ class Hitomi : Source<Hitomi.SortMode> {
|
||||
return Pair(channel, cache.size)
|
||||
}
|
||||
|
||||
override suspend fun suggestion(query: String) : List<TagSuggestion> {
|
||||
return getSuggestionsForQuery(query.takeLastWhile { !it.isWhitespace() }).map {
|
||||
TagSuggestion(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSuggestionBind(binding: SearchSuggestionItemBinding, item: TagSuggestion) {
|
||||
binding.leftIcon.setImageResource(
|
||||
when(item.n) {
|
||||
"female" -> R.drawable.gender_female
|
||||
"male" -> R.drawable.gender_male
|
||||
"language" -> R.drawable.translate
|
||||
"group" -> R.drawable.account_group
|
||||
"character" -> R.drawable.account_star
|
||||
"series" -> R.drawable.book_open
|
||||
"artist" -> R.drawable.brush
|
||||
else -> R.drawable.tag
|
||||
}
|
||||
)
|
||||
|
||||
if (item.t > 0) {
|
||||
with (binding.root) {
|
||||
val count = findViewById<TextView>(R.id.count)
|
||||
if (count == null)
|
||||
addView(
|
||||
LayoutInflater.from(context).inflate(R.layout.suggestion_count, binding.root, false)
|
||||
.apply {
|
||||
this as TextView
|
||||
|
||||
text = item.t.toString()
|
||||
}, 2
|
||||
)
|
||||
else
|
||||
count.text = item.t.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val languageMap = mapOf(
|
||||
"indonesian" to "Bahasa Indonesia",
|
||||
@@ -16,27 +16,30 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package xyz.quaver.pupil.sources.hitomi
|
||||
package xyz.quaver.pupil.sources
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import androidx.core.content.ContextCompat
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.launch
|
||||
import xyz.quaver.hitomi.galleryblockdir
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Request
|
||||
import xyz.quaver.floatingsearchview.databinding.SearchSuggestionItemBinding
|
||||
import xyz.quaver.floatingsearchview.suggestions.model.SearchSuggestion
|
||||
import xyz.quaver.hiyobi.*
|
||||
import xyz.quaver.pupil.R
|
||||
import xyz.quaver.pupil.sources.DefaultSortMode
|
||||
import xyz.quaver.pupil.sources.SearchResult
|
||||
import xyz.quaver.pupil.sources.Source
|
||||
import xyz.quaver.pupil.client
|
||||
import xyz.quaver.pupil.util.wordCapitalize
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
class Hiyobi : Source<DefaultSortMode> {
|
||||
class Hiyobi : Source<DefaultSortMode, DefaultSearchSuggestion>() {
|
||||
|
||||
override val name: String = "hiyobi.me"
|
||||
override val iconResID: Int = R.drawable.ic_hiyobi
|
||||
override val availableSortMode: Array<DefaultSortMode> = DefaultSortMode.values()
|
||||
|
||||
override suspend fun query(query: String, range: IntRange, sortMode: Enum<*>): Pair<Channel<SearchResult>, Int> {
|
||||
override suspend fun search(query: String, range: IntRange, sortMode: Enum<*>): Pair<Channel<SearchResult>, Int> {
|
||||
val channel = Channel<SearchResult>()
|
||||
|
||||
val (results, total) = if (query.isEmpty())
|
||||
@@ -55,7 +58,58 @@ class Hiyobi : Source<DefaultSortMode> {
|
||||
return Pair(channel, total)
|
||||
}
|
||||
|
||||
override suspend fun suggestion(query: String): List<DefaultSearchSuggestion> {
|
||||
val result = mutableSetOf<String>()
|
||||
|
||||
for (tag in allTags.await()) {
|
||||
if (result.size >= 10)
|
||||
break
|
||||
|
||||
val lowQuery = query.toLowerCase(Locale.ROOT)
|
||||
|
||||
if (tag.contains(lowQuery, true))
|
||||
result.add(tag)
|
||||
}
|
||||
|
||||
return result.map { DefaultSearchSuggestion(it) }
|
||||
}
|
||||
|
||||
override fun onSuggestionBind(binding: SearchSuggestionItemBinding, item: DefaultSearchSuggestion) {
|
||||
val split = item.body.split(':', limit = 2)
|
||||
|
||||
if (split.size != 2)
|
||||
return
|
||||
|
||||
binding.leftIcon.setImageResource(
|
||||
when(split.first()) {
|
||||
"female" -> R.drawable.gender_female
|
||||
"male" -> R.drawable.gender_male
|
||||
"language" -> R.drawable.translate
|
||||
"group" -> R.drawable.account_group
|
||||
"character" -> R.drawable.account_star
|
||||
"series" -> R.drawable.book_open
|
||||
"artist" -> R.drawable.brush
|
||||
else -> R.drawable.tag
|
||||
}
|
||||
)
|
||||
|
||||
binding.body.text = split.last()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun downloadAllTags(): Deferred<List<String>> = CoroutineScope(Dispatchers.IO).async {
|
||||
Json.decodeFromString(kotlin.runCatching {
|
||||
client.newCall(Request.Builder().url("https://api.hiyobi.me/auto.json").build()).execute().also { if (it.code() != 200) throw IOException() }.body()?.use { it.string() }
|
||||
}.getOrNull() ?: "[]")
|
||||
}
|
||||
|
||||
private var _allTags: Deferred<List<String>>? = null
|
||||
|
||||
val allTags: Deferred<List<String>>
|
||||
get() = if (_allTags == null || (_allTags!!.isCompleted && runBlocking { _allTags!!.await() }.isEmpty())) downloadAllTags().also {
|
||||
_allTags = it
|
||||
} else _allTags!!
|
||||
|
||||
fun transform(galleryBlock: GalleryBlock): SearchResult =
|
||||
SearchResult(
|
||||
galleryBlock.id,
|
||||
Reference in New Issue
Block a user