Skip to content

Commit 14bf321

Browse files
authored
Merge pull request #108 from LeanBitLab/perf/settings-container-filter-5764429429682301740
⚡ Optimize SettingsContainer filter performance
2 parents e89507f + 641dcf3 commit 14bf321

2 files changed

Lines changed: 70 additions & 6 deletions

File tree

app/src/main/java/helium314/keyboard/settings/SettingsContainer.kt

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,22 @@ class SettingsContainer(context: Context) {
3333
// show, but change will not do anything because another setting needs to be enabled first -> probably best
3434
fun filter(searchTerm: String): List<Setting> {
3535
val term = searchTerm.lowercase()
36-
val results = mutableSetOf<Setting>()
37-
list.forEach { setting -> if (setting.title.lowercase().startsWith(term)) results.add(setting) }
38-
list.forEach { setting -> if (setting.title.lowercase().split(' ').any { it.startsWith(term) }) results.add(setting) }
36+
val titleMatch = mutableListOf<Setting>()
37+
val titleWordMatch = mutableListOf<Setting>()
38+
val descriptionMatch = mutableListOf<Setting>()
39+
3940
list.forEach { setting ->
40-
if (setting.description?.lowercase()?.split(' ')?.any { it.startsWith(term) } == true)
41-
results.add(setting)
41+
val titleLower = setting.titleLower
42+
if (titleLower.startsWith(term)) {
43+
titleMatch.add(setting)
44+
} else if (setting.titleWords.any { it.startsWith(term) }) {
45+
titleWordMatch.add(setting)
46+
} else if (setting.descriptionWords?.any { it.startsWith(term) } == true) {
47+
descriptionMatch.add(setting)
48+
}
4249
}
43-
return results.toList()
50+
51+
return titleMatch + titleWordMatch + descriptionMatch
4452
}
4553
}
4654

@@ -53,7 +61,10 @@ class Setting(
5361
private val content: @Composable (Setting) -> Unit
5462
) {
5563
val title = context.getString(titleId)
64+
val titleLower = title.lowercase()
65+
val titleWords = titleLower.split(' ')
5666
val description = descriptionId?.let { context.getString(it) }
67+
val descriptionWords = description?.lowercase()?.split(' ')
5768

5869
@Composable
5970
fun Preference() {
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package helium314.keyboard.settings
2+
3+
import android.content.Context
4+
import androidx.test.core.app.ApplicationProvider
5+
import org.junit.Assert.assertEquals
6+
import org.junit.Assert.assertTrue
7+
import org.junit.Before
8+
import org.junit.Test
9+
import org.junit.runner.RunWith
10+
import org.robolectric.RobolectricTestRunner
11+
import org.robolectric.annotation.Config
12+
import kotlin.system.measureNanoTime
13+
14+
@RunWith(RobolectricTestRunner::class)
15+
@Config(sdk = [33])
16+
class SettingsContainerTest {
17+
18+
private lateinit var container: SettingsContainer
19+
20+
@Before
21+
fun setup() {
22+
val context = ApplicationProvider.getApplicationContext<Context>()
23+
container = SettingsContainer(context)
24+
}
25+
26+
@Test
27+
fun testFilterFunctionality() {
28+
// Just verify it doesn't crash and returns some results for empty or common strings
29+
val res = container.filter("a")
30+
assertTrue(res.isNotEmpty())
31+
}
32+
33+
@Test
34+
fun testFilterPerformance() {
35+
val searches = listOf("a", "b", "c", "theme", "color", "sound", "vib", "dictionary", "key", "layout")
36+
37+
// Warmup
38+
for (i in 1..10) {
39+
for (s in searches) {
40+
container.filter(s)
41+
}
42+
}
43+
44+
val time = measureNanoTime {
45+
for (i in 1..100) {
46+
for (s in searches) {
47+
container.filter(s)
48+
}
49+
}
50+
}
51+
println("Filter performance: ${time / 1_000_000} ms")
52+
}
53+
}

0 commit comments

Comments
 (0)