fix #95: unicode normalization for search

This commit is contained in:
Josia Pietsch 2025-01-10 00:21:57 +01:00
parent ce5fade39a
commit c6d0477e3a
Signed by: jrpie
GPG key ID: E70B571D66986A2D
2 changed files with 22 additions and 8 deletions

View file

@ -1,6 +1,9 @@
package de.jrpie.android.launcher.apps
import android.annotation.SuppressLint
import android.content.Context
import android.icu.text.Normalizer2
import android.os.Build
import de.jrpie.android.launcher.actions.Action
import de.jrpie.android.launcher.actions.AppAction
import de.jrpie.android.launcher.actions.Gesture
@ -10,10 +13,12 @@ import kotlin.text.Regex.Companion.escape
class AppFilter(
var context: Context,
var search: String,
var query: String,
var favoritesVisibility: AppSetVisibility = AppSetVisibility.VISIBLE,
var hiddenVisibility: AppSetVisibility = AppSetVisibility.HIDDEN,
) {
@SuppressLint("NewApi")
operator fun invoke(apps: List<DetailedAppInfo>): List<DetailedAppInfo> {
var apps =
apps.sortedBy { app -> app.getCustomLabel(context).toString().lowercase(Locale.ROOT) }
@ -35,7 +40,7 @@ class AppFilter(
}
// normalize text for search
val allowedSpecialCharacters = search
val allowedSpecialCharacters = unicodeNormalize(query)
.lowercase(Locale.ROOT)
.toCharArray()
.distinct()
@ -45,20 +50,21 @@ class AppFilter(
val disallowedCharsRegex = "[^\\p{L}$allowedSpecialCharacters]".toRegex()
fun normalize(text: String): String {
return text.lowercase(Locale.ROOT).replace(disallowedCharsRegex, "")
return unicodeNormalize(text).replace(disallowedCharsRegex, "")
}
if (search.isEmpty()) {
if (query.isEmpty()) {
return apps
} else {
val r: MutableList<DetailedAppInfo> = ArrayList()
val appsSecondary: MutableList<DetailedAppInfo> = ArrayList()
val normalizedText: String = normalize(search)
val normalizedQuery: String = normalize(query)
for (item in apps) {
val itemLabel: String = normalize(item.getCustomLabel(context).toString())
if (itemLabel.startsWith(normalizedText)) {
if (itemLabel.startsWith(normalizedQuery)) {
r.add(item)
} else if (itemLabel.contains(normalizedText)) {
} else if (itemLabel.contains(normalizedQuery)) {
appsSecondary.add(item)
}
}
@ -77,5 +83,13 @@ class AppFilter(
EXCLUSIVE({ set, appInfo -> set.contains(appInfo.app) }),
;
}
private fun unicodeNormalize(s: String): String {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val normalizer = Normalizer2.getNFKDInstance()
return normalizer.normalize(s.lowercase(Locale.ROOT))
}
return s.lowercase(Locale.ROOT)
}
}
}

View file

@ -226,7 +226,7 @@ class AppsRecyclerAdapter(
* The function [setSearchString] is used to search elements within this [RecyclerView].
*/
fun setSearchString(search: String) {
appFilter.search = search
appFilter.query = search
updateAppsList(true)
}