diff --git a/app/src/main/kotlin/com/simplemobiletools/notes/pro/helpers/CollatorBasedComparator.kt b/app/src/main/kotlin/com/simplemobiletools/notes/pro/helpers/CollatorBasedComparator.kt new file mode 100644 index 00000000..74febf15 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/notes/pro/helpers/CollatorBasedComparator.kt @@ -0,0 +1,79 @@ +package com.simplemobiletools.notes.pro.helpers + +import java.text.Collator + +/** + * Collator-based string comparator + * + * Adapted from AlphanumericComparator to support numerical collation. + */ +class CollatorBasedComparator: Comparator { + override fun compare(string1: String, string2: String): Int { + val collator = getCollator() + + var thisMarker = 0 + var thatMarker = 0 + + while (thisMarker < string1.length && thatMarker < string2.length) { + val thisChunk = getChunk(string1, string1.length, thisMarker) + thisMarker += thisChunk.length + + val thatChunk = getChunk(string2, string2.length, thatMarker) + thatMarker += thatChunk.length + + val result = if (isDigit(thisChunk[0]) && isDigit(thatChunk[0])) { + collateNumerically(thisChunk, thatChunk) + } else { + collator.compare(thisChunk, thatChunk) + } + + if (result != 0) { + return coerceResult(result) + } + } + + return coerceResult(string1.length - string2.length) + } + + private fun collateNumerically(string1: String, string2: String): Int { + var result: Int + result = string1.length - string2.length + if (result == 0) { + // equal length, the first different number counts + for (i in string1.indices) { + result = string1[i] - string2[i] + if (result != 0) { + break + } + } + } + return result + } + + private fun getChunk(string: String, length: Int, marker: Int): String { + var current = marker + var c = string[current] + val chunk = StringBuilder(c.toString()) + current++ + val chunkOfDigits = isDigit(c) + while (current < length) { + c = string[current] + if (isDigit(c) != chunkOfDigits) { + break + } + chunk.append(c) + current++ + } + return chunk.toString() + } + + private fun isDigit(ch: Char) = ch in '0'..'9' + private fun coerceResult(compareToResult: Int) = compareToResult.coerceIn(-1, 1) + + private fun getCollator(): Collator { + val collator = Collator.getInstance() + collator.strength = Collator.PRIMARY + collator.decomposition = Collator.CANONICAL_DECOMPOSITION + return collator + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/notes/pro/models/ChecklistItem.kt b/app/src/main/kotlin/com/simplemobiletools/notes/pro/models/ChecklistItem.kt index 5f9bc70f..fb2437ce 100644 --- a/app/src/main/kotlin/com/simplemobiletools/notes/pro/models/ChecklistItem.kt +++ b/app/src/main/kotlin/com/simplemobiletools/notes/pro/models/ChecklistItem.kt @@ -1,8 +1,8 @@ package com.simplemobiletools.notes.pro.models -import com.simplemobiletools.commons.helpers.AlphanumericComparator import com.simplemobiletools.commons.helpers.SORT_BY_TITLE import com.simplemobiletools.commons.helpers.SORT_DESCENDING +import com.simplemobiletools.notes.pro.helpers.CollatorBasedComparator import kotlinx.serialization.Serializable @Serializable @@ -19,7 +19,7 @@ data class ChecklistItem( override fun compareTo(other: ChecklistItem): Int { var result = when { - sorting and SORT_BY_TITLE != 0 -> AlphanumericComparator().compare(title.lowercase(), other.title.lowercase()) + sorting and SORT_BY_TITLE != 0 -> CollatorBasedComparator().compare(title, other.title) else -> dateCreated.compareTo(other.dateCreated) }