mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-09-14 04:37:34 +01:00
Use locale-aware collation when sorting recipients in message details.
This commit is contained in:
committed by
Cody Henthorne
parent
4b795a8aa6
commit
bef261e9dd
@@ -29,6 +29,18 @@ object GroupMemberOrder {
|
||||
getDisplayName: (T) -> String
|
||||
): Comparator<T> = compareBy<T> { !isSelf(it) }
|
||||
.thenBy { !isAdmin(it) }
|
||||
.thenBy { !hasDisplayName(it) }
|
||||
.then(displayNameComparator(hasDisplayName, getDisplayName))
|
||||
|
||||
/**
|
||||
* Creates a [Comparator] for just the display-name portion of the canonical order: members with a user-set display name
|
||||
* first, then alphabetically by display name using locale-aware collation.
|
||||
*
|
||||
* Useful for member lists that have no notion of self-first or admin-first ordering.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun <T> displayNameComparator(
|
||||
hasDisplayName: (T) -> Boolean,
|
||||
getDisplayName: (T) -> String
|
||||
): Comparator<T> = compareBy<T> { !hasDisplayName(it) }
|
||||
.thenBy { collator.getCollationKey(getDisplayName(it)) }
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.conversation.ConversationMessage;
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupMemberOrder;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
@@ -12,9 +13,9 @@ import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public final class MessageDetails {
|
||||
private static final Comparator<RecipientDeliveryStatus> HAS_DISPLAY_NAME = (r1, r2) -> Boolean.compare(r2.getRecipient().hasAUserSetDisplayName(AppDependencies.getApplication()), r1.getRecipient().hasAUserSetDisplayName(AppDependencies.getApplication()));
|
||||
private static final Comparator<RecipientDeliveryStatus> ALPHABETICAL = (r1, r2) -> r1.getRecipient().getDisplayName(AppDependencies.getApplication()).compareToIgnoreCase(r2.getRecipient().getDisplayName(AppDependencies.getApplication()));
|
||||
private static final Comparator<RecipientDeliveryStatus> RECIPIENT_COMPARATOR = HAS_DISPLAY_NAME.thenComparing(ALPHABETICAL);
|
||||
private static final Comparator<RecipientDeliveryStatus> RECIPIENT_COMPARATOR = GroupMemberOrder.<RecipientDeliveryStatus>displayNameComparator(r -> r.getRecipient().hasAUserSetDisplayName(AppDependencies.getApplication()),
|
||||
r -> r.getRecipient().getDisplayName(AppDependencies.getApplication()))
|
||||
.thenComparing(r -> r.getRecipient().getId());
|
||||
|
||||
private final ConversationMessage conversationMessage;
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2026 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.groups.ui
|
||||
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.util.Locale
|
||||
|
||||
class GroupMemberOrderTest {
|
||||
|
||||
private lateinit var previousLocale: Locale
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
previousLocale = Locale.getDefault()
|
||||
Locale.setDefault(Locale.GERMANY)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
Locale.setDefault(previousLocale)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `displayNameComparator - accented names sort alongside their unaccented equivalents`() {
|
||||
val names = listOf("Zeta", "Ärzte", "Anna", "Émile", "Bernd", "Ökonom", "Oskar")
|
||||
|
||||
val sorted = names.sortedWith(GroupMemberOrder.displayNameComparator({ true }, { it }))
|
||||
|
||||
assertThat(sorted).isEqualTo(listOf("Anna", "Ärzte", "Bernd", "Émile", "Ökonom", "Oskar", "Zeta"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `comparator - self first, then admins, then named members alphabetically`() {
|
||||
val self = Member("Zoe", isSelf = true)
|
||||
val admin = Member("Yannick", isAdmin = true)
|
||||
val bernd = Member("Bernd")
|
||||
val arzte = Member("Ärzte")
|
||||
val unnamed = Member("+15551234567", hasDisplayName = false)
|
||||
|
||||
val sorted = listOf(bernd, unnamed, arzte, admin, self).sortedWith(MEMBER_ORDER)
|
||||
|
||||
assertThat(sorted).isEqualTo(listOf(self, admin, arzte, bernd, unnamed))
|
||||
}
|
||||
|
||||
private data class Member(
|
||||
val name: String,
|
||||
val isSelf: Boolean = false,
|
||||
val isAdmin: Boolean = false,
|
||||
val hasDisplayName: Boolean = true
|
||||
)
|
||||
|
||||
companion object {
|
||||
private val MEMBER_ORDER = GroupMemberOrder.comparator<Member>({ it.isSelf }, { it.isAdmin }, { it.hasDisplayName }, { it.name })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user