Skip to content

Commit e89d668

Browse files
committed
Fix #615: 'Checklist - Order issue with char like É or È' by using a locale-aware Collator
1 parent 07071b7 commit e89d668

File tree

2 files changed

+81
-2
lines changed

2 files changed

+81
-2
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package com.simplemobiletools.notes.pro.helpers
2+
3+
import java.text.Collator
4+
5+
/**
6+
* Collator-based string comparator
7+
*
8+
* Adapted from AlphanumericComparator to support numerical collation.
9+
*/
10+
class CollatorBasedComparator: Comparator<String> {
11+
override fun compare(string1: String, string2: String): Int {
12+
val collator = getCollator()
13+
14+
var thisMarker = 0
15+
var thatMarker = 0
16+
17+
while (thisMarker < string1.length && thatMarker < string2.length) {
18+
val thisChunk = getChunk(string1, string1.length, thisMarker)
19+
thisMarker += thisChunk.length
20+
21+
val thatChunk = getChunk(string2, string2.length, thatMarker)
22+
thatMarker += thatChunk.length
23+
24+
val result = if (isDigit(thisChunk[0]) && isDigit(thatChunk[0])) {
25+
collateNumerically(thisChunk, thatChunk)
26+
} else {
27+
collator.compare(thisChunk, thatChunk)
28+
}
29+
30+
if (result != 0) {
31+
return coerceResult(result)
32+
}
33+
}
34+
35+
return coerceResult(string1.length - string2.length)
36+
}
37+
38+
private fun collateNumerically(string1: String, string2: String): Int {
39+
var result: Int
40+
result = string1.length - string2.length
41+
if (result == 0) {
42+
// equal length, the first different number counts
43+
for (i in string1.indices) {
44+
result = string1[i] - string2[i]
45+
if (result != 0) {
46+
break
47+
}
48+
}
49+
}
50+
return result
51+
}
52+
53+
private fun getChunk(string: String, length: Int, marker: Int): String {
54+
var current = marker
55+
var c = string[current]
56+
val chunk = StringBuilder(c.toString())
57+
current++
58+
val chunkOfDigits = isDigit(c)
59+
while (current < length) {
60+
c = string[current]
61+
if (isDigit(c) != chunkOfDigits) {
62+
break
63+
}
64+
chunk.append(c)
65+
current++
66+
}
67+
return chunk.toString()
68+
}
69+
70+
private fun isDigit(ch: Char) = ch in '0'..'9'
71+
private fun coerceResult(compareToResult: Int) = compareToResult.coerceIn(-1, 1)
72+
73+
private fun getCollator(): Collator {
74+
val collator = Collator.getInstance()
75+
collator.strength = Collator.PRIMARY
76+
collator.decomposition = Collator.CANONICAL_DECOMPOSITION
77+
return collator
78+
}
79+
}

app/src/main/kotlin/com/simplemobiletools/notes/pro/models/ChecklistItem.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package com.simplemobiletools.notes.pro.models
22

3-
import com.simplemobiletools.commons.helpers.AlphanumericComparator
43
import com.simplemobiletools.commons.helpers.SORT_BY_TITLE
54
import com.simplemobiletools.commons.helpers.SORT_DESCENDING
5+
import com.simplemobiletools.notes.pro.helpers.CollatorBasedComparator
66
import kotlinx.serialization.Serializable
77

88
@Serializable
@@ -19,7 +19,7 @@ data class ChecklistItem(
1919

2020
override fun compareTo(other: ChecklistItem): Int {
2121
var result = when {
22-
sorting and SORT_BY_TITLE != 0 -> AlphanumericComparator().compare(title.lowercase(), other.title.lowercase())
22+
sorting and SORT_BY_TITLE != 0 -> CollatorBasedComparator().compare(title, other.title)
2323
else -> dateCreated.compareTo(other.dateCreated)
2424
}
2525

0 commit comments

Comments
 (0)