Add transliteration support for non-latin scripts

This commit is contained in:
MM20
2025-12-15 23:00:40 +01:00
parent c17373e9e0
commit 67167de2da
35 changed files with 443 additions and 72 deletions

View File

@@ -6,7 +6,6 @@ import android.content.pm.LauncherActivityInfo
import android.content.pm.LauncherApps
import android.graphics.drawable.Drawable
import androidx.core.content.getSystemService
import de.mm20.launcher2.ktx.romanize
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import java.text.Collator
@@ -30,6 +29,6 @@ class AppShortcutConfigActivity(
val label1 = label
val label2 = other.label
return Collator.getInstance().apply { strength = Collator.SECONDARY }
.compare(label1.romanize(), label2.romanize())
.compare(label1, label2)
}
}

View File

@@ -9,7 +9,6 @@ import android.os.Looper
import android.os.Process
import android.os.UserHandle
import androidx.core.content.getSystemService
import de.mm20.launcher2.ktx.normalize
import de.mm20.launcher2.permissions.PermissionGroup
import de.mm20.launcher2.permissions.PermissionsManager
import de.mm20.launcher2.preferences.search.ShortcutSearchSettings
@@ -17,6 +16,7 @@ import de.mm20.launcher2.profiles.ProfileManager
import de.mm20.launcher2.search.AppShortcut
import de.mm20.launcher2.search.ResultScore
import de.mm20.launcher2.search.SearchableRepository
import de.mm20.launcher2.search.StringNormalizer
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toImmutableList
@@ -34,8 +34,6 @@ import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.withContext
import org.apache.commons.text.similarity.FuzzyScore
import java.util.Locale
interface AppShortcutRepository : SearchableRepository<AppShortcut> {
@@ -57,6 +55,7 @@ internal class AppShortcutRepositoryImpl(
private val permissionsManager: PermissionsManager,
private val settings: ShortcutSearchSettings,
private val profileManager: ProfileManager,
private val stringNormalizer: StringNormalizer,
) : AppShortcutRepository {
private val scope = CoroutineScope(Dispatchers.Default + Job())
@@ -120,6 +119,8 @@ internal class AppShortcutRepositoryImpl(
return flowOf(persistentListOf())
}
val normalizedQuery = stringNormalizer.normalize(query)
return combine(
listOf(
settings.enabled,
@@ -146,9 +147,14 @@ internal class AppShortcutRepositoryImpl(
)
val shortcuts = launcherApps.getShortcuts(shortcutQuery, Process.myUserHandle())
?.mapNotNull {
val score = ResultScore(
query = query,
primaryFields = listOfNotNull(it.longLabel?.toString(), it.shortLabel?.toString())
val score = ResultScore.from(
query = normalizedQuery,
primaryFields = listOfNotNull(
it.longLabel?.toString()
?.let { stringNormalizer.normalize(it) },
it.shortLabel?.toString()
?.let { stringNormalizer.normalize(it) },
)
)
if (score.score < 0.8f) return@mapNotNull null
LauncherShortcut(
@@ -227,13 +233,4 @@ internal class AppShortcutRepositoryImpl(
}
return results.sorted()
}
private fun matches(label: String, query: String): Boolean {
val normalizedLabel = label.normalize()
val normalizedQuery = query.normalize()
if (normalizedLabel.contains(normalizedQuery)) return true
val fuzzyScore = FuzzyScore(Locale.getDefault())
return fuzzyScore.fuzzyScore(normalizedLabel, normalizedQuery) >= query.length * 1.5
}
}

View File

@@ -8,7 +8,7 @@ import org.koin.core.qualifier.named
import org.koin.dsl.module
val appShortcutsModule = module {
factory<AppShortcutRepository> { AppShortcutRepositoryImpl(androidContext(), get(), get(), get()) }
factory<AppShortcutRepository> { AppShortcutRepositoryImpl(androidContext(), get(), get(), get(), get()) }
factory<SearchableRepository<AppShortcut>>(named<AppShortcut>()) { get<AppShortcutRepository>() }
factory<SearchableDeserializer>(named(LauncherShortcut.Domain)) { LauncherShortcutDeserializer(androidContext()) }
factory<SearchableDeserializer>(named(LegacyShortcut.Domain)) { LegacyShortcutDeserializer(androidContext()) }