diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/NameUtil.kt b/app/src/main/java/org/thoughtcrime/securesms/util/NameUtil.kt index 18d8cf67a..517e48b55 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/NameUtil.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/NameUtil.kt @@ -1,9 +1,14 @@ package org.thoughtcrime.securesms.util -import android.text.TextUtils +import org.signal.core.util.CharacterIterable import java.util.regex.Pattern object NameUtil { + + /** + * \p{L} is letter, \p{Nd} is digit, \p{S} is whitespace/separator + * https://www.regular-expressions.info/unicode.html#category + */ private val PATTERN = Pattern.compile("[^\\p{L}\\p{Nd}\\p{S}]+") /** @@ -11,24 +16,20 @@ object NameUtil { */ @JvmStatic fun getAbbreviation(name: String): String? { - val parts = name.split(" ").toTypedArray() - val builder = StringBuilder() - var count = 0 - var i = 0 + val parts = name + .split(" ") + .map { it.trim() } + .map { PATTERN.matcher(it).replaceFirst("") } + .filter { it.isNotEmpty() } - while (i < parts.size && count < 2) { - val cleaned = PATTERN.matcher(parts[i]).replaceFirst("") - if (!TextUtils.isEmpty(cleaned)) { - builder.appendCodePoint(cleaned.codePointAt(0)) - count++ - } - i++ - } - - return if (builder.isEmpty()) { - null - } else { - builder.toString() + return when { + parts.isEmpty() -> null + parts.size == 1 -> parts[0].firstGrapheme() + else -> "${parts[0].firstGrapheme()}${parts[1].firstGrapheme()}" } } + + private fun String.firstGrapheme(): String { + return CharacterIterable(this).first() + } } diff --git a/app/src/test/java/org/thoughtcrime/securesms/util/NameUtil_getAbbreviation.kt b/app/src/test/java/org/thoughtcrime/securesms/util/NameUtil_getAbbreviation.kt new file mode 100644 index 000000000..8f0c13bec --- /dev/null +++ b/app/src/test/java/org/thoughtcrime/securesms/util/NameUtil_getAbbreviation.kt @@ -0,0 +1,39 @@ +package org.thoughtcrime.securesms.util + +import android.app.Application +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.ParameterizedRobolectricTestRunner +import org.robolectric.annotation.Config + +@RunWith(value = ParameterizedRobolectricTestRunner::class) +@Config(manifest = Config.NONE, application = Application::class) +class NameUtil_getAbbreviation( + private val name: String, + private val expected: String? +) { + + @Test + fun test_getAbbreviation() { + val actual: String? = NameUtil.getAbbreviation(name) + assertEquals(expected, actual) + } + + companion object { + @JvmStatic + @ParameterizedRobolectricTestRunner.Parameters(name = "{index}: getAbbreviation({0})={1}") + fun params() = listOf( + arrayOf("Gwen Stacy", "GS"), + arrayOf("Gwen", "G"), + arrayOf("gwen stacy", "gs"), + arrayOf("Mary Jane Watson", "MJ"), + arrayOf("Mary-Jane Watson", "MW"), + arrayOf("αlpha Ωmega", "αΩ"), + arrayOf("љabc ђ123", "љђ"), + // Works on device, but for whatever reason doesn't work in robolectric +// arrayOf("Bob \uD83C\uDDE8\uD83C\uDDFF", "B\uD83C\uDDE8\uD83C\uDDFF"), + arrayOf("", null), + ) + } +}