Search algorithm improved
Language settings in default tag fixed
This commit is contained in:
@@ -14,7 +14,7 @@ android {
|
|||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 20
|
versionCode 20
|
||||||
versionName "2.11.1"
|
versionName "2.12"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|||||||
@@ -90,8 +90,8 @@ class Tags(tag: List<Tag?>?) : ArrayList<Tag>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeByArea(area: String) {
|
fun removeByArea(area: String, isNegative: Boolean? = null) {
|
||||||
filter { it.area == area }.forEach {
|
filter { it.area == area && (if(isNegative == null) true else (it.isNegative == isNegative)) }.forEach {
|
||||||
remove(it)
|
remove(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import android.widget.TextView
|
|||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.cardview.widget.CardView
|
import androidx.cardview.widget.CardView
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
@@ -45,6 +46,7 @@ import xyz.quaver.pupil.BuildConfig
|
|||||||
import xyz.quaver.pupil.Pupil
|
import xyz.quaver.pupil.Pupil
|
||||||
import xyz.quaver.pupil.R
|
import xyz.quaver.pupil.R
|
||||||
import xyz.quaver.pupil.adapters.GalleryBlockAdapter
|
import xyz.quaver.pupil.adapters.GalleryBlockAdapter
|
||||||
|
import xyz.quaver.pupil.types.SelectorSuggestion
|
||||||
import xyz.quaver.pupil.types.Tag
|
import xyz.quaver.pupil.types.Tag
|
||||||
import xyz.quaver.pupil.types.TagSuggestion
|
import xyz.quaver.pupil.types.TagSuggestion
|
||||||
import xyz.quaver.pupil.types.Tags
|
import xyz.quaver.pupil.types.Tags
|
||||||
@@ -755,7 +757,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
if (query.isEmpty() or query.endsWith(' ')) {
|
if (query.isEmpty() or query.endsWith(' ')) {
|
||||||
swapSuggestions(json.parse(serializer, favoritesFile.readText()).map {
|
swapSuggestions(json.parse(serializer, favoritesFile.readText()).map {
|
||||||
TagSuggestion(it.tag, -1, "", it.area ?: "tag")
|
TagSuggestion(it.tag, -1, "", it.area ?: "tag")
|
||||||
})
|
} + SelectorSuggestion())
|
||||||
|
|
||||||
return@setOnQueryChangeListener
|
return@setOnQueryChangeListener
|
||||||
}
|
}
|
||||||
@@ -782,83 +784,115 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setOnBindSuggestionCallback { suggestionView, leftIcon, textView, item, _ ->
|
setOnBindSuggestionCallback { suggestionView, leftIcon, textView, item, _ ->
|
||||||
val suggestion = item as TagSuggestion
|
if (item is SelectorSuggestion) {
|
||||||
val tag = "${suggestion.n}:${suggestion.s.replace(Regex("\\s"), "_")}"
|
var hasSelector = false
|
||||||
|
|
||||||
leftIcon.setImageDrawable(
|
with(suggestionView as LinearLayout) {
|
||||||
ResourcesCompat.getDrawable(
|
for (i in 0 until childCount) {
|
||||||
resources,
|
val child = getChildAt(i)
|
||||||
when(suggestion.n) {
|
if (child is ConstraintLayout) {
|
||||||
"female" -> R.drawable.ic_gender_female
|
child.visibility = View.VISIBLE
|
||||||
"male" -> R.drawable.ic_gender_male
|
hasSelector = true
|
||||||
"language" -> R.drawable.ic_translate
|
}
|
||||||
"group" -> R.drawable.ic_account_group
|
else
|
||||||
"character" -> R.drawable.ic_account_star
|
child.visibility = View.GONE
|
||||||
"series" -> R.drawable.ic_book_open
|
|
||||||
"artist" -> R.drawable.ic_brush
|
|
||||||
else -> R.drawable.ic_tag
|
|
||||||
},
|
|
||||||
null)
|
|
||||||
)
|
|
||||||
|
|
||||||
with(suggestionView.findViewById<ImageView>(R.id.right_icon)) {
|
|
||||||
|
|
||||||
if (Tags(json.parse(serializer, favoritesFile.readText())).contains(tag))
|
|
||||||
setImageResource(R.drawable.ic_star_filled)
|
|
||||||
else
|
|
||||||
setImageResource(R.drawable.ic_star_empty)
|
|
||||||
|
|
||||||
visibility = View.VISIBLE
|
|
||||||
rotation = 0f
|
|
||||||
isEnabled = true
|
|
||||||
|
|
||||||
setColorFilter(ContextCompat.getColor(context, R.color.material_orange_500))
|
|
||||||
|
|
||||||
isClickable = true
|
|
||||||
setOnClickListener {
|
|
||||||
val favorites = Tags(json.parse(serializer, favoritesFile.readText()))
|
|
||||||
|
|
||||||
if (favorites.contains(tag)) {
|
|
||||||
setImageResource(R.drawable.ic_star_empty)
|
|
||||||
favorites.remove(tag)
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
setImageDrawable(AnimatedVectorDrawableCompat.create(context,
|
|
||||||
R.drawable.avd_star
|
|
||||||
))
|
|
||||||
(drawable as Animatable).start()
|
|
||||||
|
|
||||||
favorites.add(tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
favoritesFile.writeText(json.stringify(favorites))
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (suggestion.t == -1) {
|
if (!hasSelector) {
|
||||||
textView.text = suggestion.s
|
val view = LayoutInflater.from(context)
|
||||||
} else {
|
.inflate(R.layout.item_selector_suggestion, suggestionView, false)
|
||||||
val text = "${suggestion.s}\n ${suggestion.t}"
|
|
||||||
|
|
||||||
val len = text.length
|
suggestionView.addView(view)
|
||||||
val left = suggestion.s.length
|
}
|
||||||
|
} else if(item is TagSuggestion) {
|
||||||
|
with(suggestionView as LinearLayout) {
|
||||||
|
for (i in 0 until childCount) {
|
||||||
|
val child = getChildAt(i)
|
||||||
|
if (child is ConstraintLayout) {
|
||||||
|
child.visibility = View.GONE
|
||||||
|
}
|
||||||
|
else
|
||||||
|
child.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val tag = "${item.n}:${item.s.replace(Regex("\\s"), "_")}"
|
||||||
|
|
||||||
textView.text = SpannableString(text).apply {
|
leftIcon.setImageDrawable(
|
||||||
val s = AlignmentSpan.Standard(Layout.Alignment.ALIGN_OPPOSITE)
|
ResourcesCompat.getDrawable(
|
||||||
setSpan(s, left, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
resources,
|
||||||
setSpan(SetLineOverlap(true), 1, len-2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
when(item.n) {
|
||||||
setSpan(SetLineOverlap(false), len-1, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
"female" -> R.drawable.ic_gender_female
|
||||||
|
"male" -> R.drawable.ic_gender_male
|
||||||
|
"language" -> R.drawable.ic_translate
|
||||||
|
"group" -> R.drawable.ic_account_group
|
||||||
|
"character" -> R.drawable.ic_account_star
|
||||||
|
"series" -> R.drawable.ic_book_open
|
||||||
|
"artist" -> R.drawable.ic_brush
|
||||||
|
else -> R.drawable.ic_tag
|
||||||
|
},
|
||||||
|
null)
|
||||||
|
)
|
||||||
|
|
||||||
|
with(suggestionView.findViewById<ImageView>(R.id.right_icon)) {
|
||||||
|
|
||||||
|
if (Tags(json.parse(serializer, favoritesFile.readText())).contains(tag))
|
||||||
|
setImageResource(R.drawable.ic_star_filled)
|
||||||
|
else
|
||||||
|
setImageResource(R.drawable.ic_star_empty)
|
||||||
|
|
||||||
|
rotation = 0f
|
||||||
|
isEnabled = true
|
||||||
|
|
||||||
|
setColorFilter(ContextCompat.getColor(context, R.color.material_orange_500))
|
||||||
|
|
||||||
|
isClickable = true
|
||||||
|
setOnClickListener {
|
||||||
|
val favorites = Tags(json.parse(serializer, favoritesFile.readText()))
|
||||||
|
|
||||||
|
if (favorites.contains(tag)) {
|
||||||
|
setImageResource(R.drawable.ic_star_empty)
|
||||||
|
favorites.remove(tag)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setImageDrawable(AnimatedVectorDrawableCompat.create(context,
|
||||||
|
R.drawable.avd_star
|
||||||
|
))
|
||||||
|
(drawable as Animatable).start()
|
||||||
|
|
||||||
|
favorites.add(tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
favoritesFile.writeText(json.stringify(favorites))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.t == -1) {
|
||||||
|
textView.text = item.s
|
||||||
|
} else {
|
||||||
|
val text = "${item.s}\n ${item.t}"
|
||||||
|
|
||||||
|
val len = text.length
|
||||||
|
val left = item.s.length
|
||||||
|
|
||||||
|
textView.text = SpannableString(text).apply {
|
||||||
|
val s = AlignmentSpan.Standard(Layout.Alignment.ALIGN_OPPOSITE)
|
||||||
|
setSpan(s, left, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
|
setSpan(SetLineOverlap(true), 1, len-2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
|
setSpan(SetLineOverlap(false), len-1, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setOnSearchListener(object : FloatingSearchView.OnSearchListener {
|
setOnSearchListener(object : FloatingSearchView.OnSearchListener {
|
||||||
override fun onSuggestionClicked(searchSuggestion: SearchSuggestion?) {
|
override fun onSuggestionClicked(searchSuggestion: SearchSuggestion?) {
|
||||||
val suggestion = searchSuggestion as TagSuggestion
|
if (searchSuggestion !is TagSuggestion)
|
||||||
|
return
|
||||||
|
|
||||||
with(searchInputView.text) {
|
with(searchInputView.text) {
|
||||||
delete(if (lastIndexOf(' ') == -1) 0 else lastIndexOf(' ')+1, length)
|
delete(if (lastIndexOf(' ') == -1) 0 else lastIndexOf(' ')+1, length)
|
||||||
append("${suggestion.n}:${suggestion.s.replace(Regex("\\s"), "_")} ")
|
append("${searchSuggestion.n}:${searchSuggestion.s.replace(Regex("\\s"), "_")} ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -872,7 +906,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
if (query.isEmpty() or query.endsWith(' '))
|
if (query.isEmpty() or query.endsWith(' '))
|
||||||
swapSuggestions(json.parse(serializer, favoritesFile.readText()).map {
|
swapSuggestions(json.parse(serializer, favoritesFile.readText()).map {
|
||||||
TagSuggestion(it.tag, -1, "", it.area ?: "tag")
|
TagSuggestion(it.tag, -1, "", it.area ?: "tag")
|
||||||
})
|
} + SelectorSuggestion())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFocusCleared() {
|
override fun onFocusCleared() {
|
||||||
|
|||||||
@@ -222,14 +222,14 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
addAll(languages.values)
|
addAll(languages.values)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if (tags.any { it.area == "language" }) {
|
if (tags.any { it.area == "language" && !it.isNegative }) {
|
||||||
val tag = languages[tags.first { it.area == "language" }.tag]
|
val tag = languages[tags.first { it.area == "language" }.tag]
|
||||||
if (tag != null) {
|
if (tag != null) {
|
||||||
setSelection(
|
setSelection(
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
(adapter as ArrayAdapter<String>).getPosition(tag)
|
(adapter as ArrayAdapter<String>).getPosition(tag)
|
||||||
)
|
)
|
||||||
tags.removeByArea("language")
|
tags.removeByArea("language", false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
34
app/src/main/res/layout/item_selector_suggestion.xml
Normal file
34
app/src/main/res/layout/item_selector_suggestion.xml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="@color/dark_gray"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toRightOf="parent">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style="?borderlessButtonStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="favorite" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style="?borderlessButtonStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="history" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
package xyz.quaver.hitomi
|
package xyz.quaver.hitomi
|
||||||
|
|
||||||
import kotlinx.coroutines.asCoroutineDispatcher
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
val searchDispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher()
|
|
||||||
fun doSearch(query: String) : List<Int> {
|
fun doSearch(query: String) : List<Int> {
|
||||||
|
val time = System.currentTimeMillis()
|
||||||
|
|
||||||
val terms = query
|
val terms = query
|
||||||
.trim()
|
.trim()
|
||||||
.replace(Regex("""^\?"""), "")
|
.replace(Regex("""^\?"""), "")
|
||||||
@@ -43,24 +42,24 @@ fun doSearch(query: String) : List<Int> {
|
|||||||
|
|
||||||
//positive results
|
//positive results
|
||||||
positiveTerms.map {
|
positiveTerms.map {
|
||||||
launch(searchDispatcher) {
|
async(Dispatchers.IO) {
|
||||||
val newResults = getGalleryIDsForQuery(it)
|
Pair(getGalleryIDsForQuery(it), true)
|
||||||
filterPositive(newResults.sorted())
|
}
|
||||||
|
}+negativeTerms.map {
|
||||||
|
async(Dispatchers.IO) {
|
||||||
|
Pair(getGalleryIDsForQuery(it), false)
|
||||||
}
|
}
|
||||||
}.forEach {
|
}.forEach {
|
||||||
it.join()
|
val (result, isPositive) = it.await()
|
||||||
}
|
|
||||||
|
|
||||||
//negative results
|
when {
|
||||||
negativeTerms.map {
|
isPositive -> filterPositive(result.sorted())
|
||||||
launch(searchDispatcher) {
|
else -> filterNegative(result.sorted())
|
||||||
filterNegative(getGalleryIDsForQuery(it).sorted())
|
|
||||||
}
|
}
|
||||||
}.forEach {
|
|
||||||
it.join()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return results
|
println("PUPIL/SEARCH TIME ${System.currentTimeMillis() - time}ms")
|
||||||
|
|
||||||
|
return results
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user