Move algorithms to separate file

This commit is contained in:
Jan Koll 2025-06-27 11:48:30 +02:00
parent 51cc55af98
commit 3917e34239
2 changed files with 63 additions and 61 deletions

View file

@ -8,9 +8,10 @@ import de.jrpie.android.launcher.actions.AppAction
import de.jrpie.android.launcher.actions.Gesture import de.jrpie.android.launcher.actions.Gesture
import de.jrpie.android.launcher.actions.ShortcutAction import de.jrpie.android.launcher.actions.ShortcutAction
import de.jrpie.android.launcher.preferences.LauncherPreferences import de.jrpie.android.launcher.preferences.LauncherPreferences
import de.jrpie.android.launcher.util.countOccurrences
import de.jrpie.android.launcher.util.isSubsequent
import java.util.Locale import java.util.Locale
import kotlin.text.Regex.Companion.escape import kotlin.text.Regex.Companion.escape
import kotlin.text.iterator
class AppFilter( class AppFilter(
var context: Context, var context: Context,
@ -63,74 +64,40 @@ class AppFilter(
if (query.isEmpty()) { if (query.isEmpty()) {
return apps return apps
} else {
val r: MutableSet<AbstractDetailedAppInfo> = hashSetOf()
val normalizedQuery: String = normalize(query)
val subsequentResult: MutableList<AbstractDetailedAppInfo> = mutableListOf()
val occurrences: MutableMap<AbstractDetailedAppInfo, Int> = mutableMapOf()
for (item in apps) {
val itemLabel: String = normalize(item.getCustomLabel(context))
if (itemLabel.startsWith(normalizedQuery)) {
r.add(item)
} else if (itemLabel.contains(normalizedQuery)) {
r.add(item)
}
if (LauncherPreferences.functionality().searchFuzzy()) {
if (isSubsequent(itemLabel, normalizedQuery)) {
subsequentResult.add(item)
}
occurrences[item] = countOccurrences(itemLabel, normalizedQuery)
}
}
if (LauncherPreferences.functionality().searchFuzzy() && r.size != 1) {
if (subsequentResult.isNotEmpty()) {
r.addAll(subsequentResult)
} else {
val maxOccurrences = occurrences.values.maxOrNull()
if (maxOccurrences == 0) return apps
val result = occurrences.filter { it.value == maxOccurrences }
r.addAll(result.keys)
}
}
return r.toList()
} }
} val r: MutableSet<AbstractDetailedAppInfo> = hashSetOf()
val normalizedQuery: String = normalize(query)
val subsequentResult: MutableList<AbstractDetailedAppInfo> = mutableListOf()
val occurrences: MutableMap<AbstractDetailedAppInfo, Int> = mutableMapOf()
for (item in apps) {
val itemLabel: String = normalize(item.getCustomLabel(context))
/** if (itemLabel.startsWith(normalizedQuery)) {
* Returns true if `search` is a subsequence of `text`. r.add(item)
* A subsequence means all characters in `search` appear in `text` } else if (itemLabel.contains(normalizedQuery)) {
* in the same order, but not necessarily contiguously. r.add(item)
*/ }
fun isSubsequent(text: String, search: String): Boolean { if (LauncherPreferences.functionality().searchFuzzy()) {
var i = 0 if (isSubsequent(itemLabel, normalizedQuery)) {
for (char in text) { subsequentResult.add(item)
if (char != search[i]) continue }
i++ occurrences[item] = countOccurrences(itemLabel, normalizedQuery)
if (i == search.length) {
return true
} }
} }
return false if (LauncherPreferences.functionality().searchFuzzy() && r.size != 1) {
} if (subsequentResult.isNotEmpty()) {
r.addAll(subsequentResult)
/** } else {
* Returns the amount of characters from `search` that occur inside `text`. val maxOccurrences = occurrences.values.maxOrNull()
* If `text` contains the same character multiple times, it is only counted if (maxOccurrences == 0) return apps
* as often as it occurs in `search`. val result = occurrences.filter { it.value == maxOccurrences }
*/ r.addAll(result.keys)
fun countOccurrences(text: String, search: String): Int {
val foundCharacters = mutableListOf<Char>()
var mutText = text
for (char in search) {
if (mutText.contains(char)) {
foundCharacters.add(char)
mutText = mutText.replaceFirst(char.toString(), "")
} }
} }
return foundCharacters.size return r.toList()
} }
companion object { companion object {
enum class AppSetVisibility( enum class AppSetVisibility(
val predicate: (set: Set<AbstractAppInfo>, AbstractDetailedAppInfo) -> Boolean val predicate: (set: Set<AbstractAppInfo>, AbstractDetailedAppInfo) -> Boolean

View file

@ -0,0 +1,35 @@
package de.jrpie.android.launcher.util
/**
* Returns true if `search` is a subsequence of `text`.
* A subsequence means all characters in `search` appear in `text`
* in the same order, but not necessarily contiguously.
*/
fun isSubsequent(text: String, search: String): Boolean {
var i = 0
for (char in text) {
if (char != search[i]) continue
i++
if (i == search.length) {
return true
}
}
return false
}
/**
* Returns the amount of characters from `search` that occur inside `text`.
* If `text` contains the same character multiple times, it is only counted
* as often as it occurs in `search`.
*/
fun countOccurrences(text: String, search: String): Int {
val foundCharacters = mutableListOf<Char>()
var mutText = text
for (char in search) {
if (mutText.contains(char)) {
foundCharacters.add(char)
mutText = mutText.replaceFirst(char.toString(), "")
}
}
return foundCharacters.size
}