diff --git a/CHANGES.md b/CHANGES.md index 6b02cc2102..a5953dcb14 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,7 @@ Other changes: Bugfix πŸ›: - Scroll breadcrumbs to top when opened + - Render default room name when it starts with an emoji (#477) Translations πŸ—£: - diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/MatrixItem.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/MatrixItem.kt index b94335e526..c6d5665d0d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/MatrixItem.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/MatrixItem.kt @@ -20,6 +20,7 @@ import im.vector.matrix.android.api.session.group.model.GroupSummary import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.roomdirectory.PublicRoom import im.vector.matrix.android.api.session.user.model.User +import java.util.* sealed class MatrixItem( open val id: String, @@ -66,6 +67,41 @@ sealed class MatrixItem( is GroupItem -> '+' } + fun firstLetterOfDisplayName(): String { + return displayName + ?.takeIf { it.isNotBlank() } + ?.let { dn -> + var startIndex = 0 + val initial = dn[startIndex] + + if (initial in listOf('@', '#', '+') && dn.length > 1) { + startIndex++ + } + + var length = 1 + var first = dn[startIndex] + + // LEFT-TO-RIGHT MARK + if (dn.length >= 2 && 0x200e == first.toInt()) { + startIndex++ + first = dn[startIndex] + } + + // check if it’s the start of a surrogate pair + if (first.toInt() in 0xD800..0xDBFF && dn.length > startIndex + 1) { + val second = dn[startIndex + 1] + if (second.toInt() in 0xDC00..0xDFFF) { + length++ + } + } + + dn.substring(startIndex, startIndex + length) + } + ?.toUpperCase(Locale.ROOT) + ?: " " + } + + companion object { fun from(user: User) = UserItem(user.userId, user.displayName, user.avatarUrl) fun from(groupSummary: GroupSummary) = GroupItem(groupSummary.groupId, groupSummary.displayName, groupSummary.avatarUrl) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/StringUtils.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/StringUtils.kt index c27d66efed..f19bebe482 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/StringUtils.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/StringUtils.kt @@ -16,9 +16,7 @@ package im.vector.matrix.android.internal.util -import im.vector.matrix.android.api.MatrixPatterns import timber.log.Timber -import java.util.Locale /** * Convert a string to an UTF8 String @@ -51,11 +49,3 @@ fun convertFromUTF8(s: String): String { s } } - -// TODO Improve this -fun String?.firstLetterOfDisplayName(): String { - if (this.isNullOrEmpty()) return "" - val isUserId = MatrixPatterns.isUserId(this) - val firstLetterIndex = if (isUserId) 1 else 0 - return this[firstLetterIndex].toString().toUpperCase(Locale.ROOT) -} diff --git a/vector/src/main/java/im/vector/riotx/features/home/AvatarRenderer.kt b/vector/src/main/java/im/vector/riotx/features/home/AvatarRenderer.kt index 736cb6e4ff..ae02ce6519 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/AvatarRenderer.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/AvatarRenderer.kt @@ -28,7 +28,6 @@ import com.bumptech.glide.request.target.DrawableImageViewTarget import com.bumptech.glide.request.target.Target import im.vector.matrix.android.api.session.content.ContentUrlResolver import im.vector.matrix.android.api.util.MatrixItem -import im.vector.matrix.android.internal.util.firstLetterOfDisplayName import im.vector.riotx.core.di.ActiveSessionHolder import im.vector.riotx.core.glide.GlideApp import im.vector.riotx.core.glide.GlideRequest @@ -73,7 +72,7 @@ class AvatarRenderer @Inject constructor(private val activeSessionHolder: Active return if (matrixItem.displayName.isNullOrBlank()) { TextDrawable.builder().buildRound("", avatarColor) } else { - val firstLetter = matrixItem.displayName.firstLetterOfDisplayName() + val firstLetter = matrixItem.firstLetterOfDisplayName() TextDrawable.builder() .beginConfig() .bold() diff --git a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomViewModel.kt index f4cd81436c..221b078daa 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/createdirect/CreateDirectRoomViewModel.kt @@ -30,7 +30,7 @@ import com.squareup.inject.assisted.AssistedInject import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams import im.vector.matrix.android.api.session.user.model.User -import im.vector.matrix.android.internal.util.firstLetterOfDisplayName +import im.vector.matrix.android.api.util.MatrixItem import im.vector.matrix.rx.rx import im.vector.riotx.core.extensions.postLiveEvent import im.vector.riotx.core.platform.VectorViewModel @@ -142,7 +142,7 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted session.rx() .searchUsersDirectory(search, 50, emptySet()) .map { users -> - users.sortedBy { it.displayName.firstLetterOfDisplayName() } + users.sortedBy { MatrixItem.from(it).firstLetterOfDisplayName() } } } stream.toAsync { diff --git a/vector/src/main/java/im/vector/riotx/features/home/createdirect/KnownUsersController.kt b/vector/src/main/java/im/vector/riotx/features/home/createdirect/KnownUsersController.kt index 6a4fdd553b..437e617bd6 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/createdirect/KnownUsersController.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/createdirect/KnownUsersController.kt @@ -24,7 +24,6 @@ import com.airbnb.mvrx.Uninitialized import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.user.model.User import im.vector.matrix.android.api.util.MatrixItem -import im.vector.matrix.android.internal.util.firstLetterOfDisplayName import im.vector.riotx.R import im.vector.riotx.core.epoxy.EmptyItem_ import im.vector.riotx.core.epoxy.loadingItem @@ -87,7 +86,7 @@ class KnownUsersController @Inject constructor(private val session: Session, for (model in models) { if (model is CreateDirectRoomUserItem) { if (model.matrixItem.id == session.myUserId) continue - val currentFirstLetter = model.matrixItem.displayName.firstLetterOfDisplayName() + val currentFirstLetter = model.matrixItem.firstLetterOfDisplayName() val showLetter = !isFiltering && currentFirstLetter.isNotEmpty() && lastFirstLetter != currentFirstLetter lastFirstLetter = currentFirstLetter