mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-28 21:57:17 +00:00
Improve group name coloring performance.
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
package org.thoughtcrime.securesms.conversation.colors
|
||||
|
||||
import androidx.annotation.NonNull
|
||||
import org.thoughtcrime.securesms.database.GroupTable
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.GroupRecord
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.whispersystems.signalservice.api.push.ServiceId
|
||||
|
||||
/**
|
||||
* Class to assist managing the colors of author names in the UI in groups.
|
||||
@@ -17,11 +18,40 @@ class GroupAuthorNameColorHelper {
|
||||
|
||||
/** Needed so that we have a full history of current *and* past members (so colors don't change when someone leaves) */
|
||||
private val fullMemberCache: MutableMap<GroupId, Set<Recipient>> = mutableMapOf()
|
||||
private val fullMemberServiceIdsCache: MutableMap<GroupId, Set<ServiceId>> = mutableMapOf()
|
||||
|
||||
/**
|
||||
* Given a [GroupRecord], returns a map of member -> name color.
|
||||
*/
|
||||
fun getColorMap(groupRecord: GroupRecord): Map<RecipientId, NameColor> {
|
||||
if (!groupRecord.isV2Group) {
|
||||
return getColorMap(groupRecord.id)
|
||||
}
|
||||
|
||||
val cachedServiceIds: Set<ServiceId> = fullMemberServiceIdsCache[groupRecord.id] ?: setOf()
|
||||
val allIds: Set<ServiceId> = cachedServiceIds + groupRecord.decryptedMemberServiceIds.toSet()
|
||||
|
||||
fullMemberServiceIdsCache[groupRecord.id] = allIds
|
||||
|
||||
val selfId = Recipient.self().requireServiceId()
|
||||
val members: List<ServiceId> = allIds
|
||||
.filter { it != selfId }
|
||||
.sortedBy { it.toString() }
|
||||
|
||||
val allColors: List<NameColor> = ChatColorsPalette.Names.all
|
||||
|
||||
val colors: MutableMap<RecipientId, NameColor> = HashMap()
|
||||
for (i in members.indices) {
|
||||
colors[RecipientId.from(members[i])] = allColors[i % allColors.size]
|
||||
}
|
||||
|
||||
return colors.toMap()
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a [GroupId], returns a map of member -> name color.
|
||||
*/
|
||||
fun getColorMap(@NonNull groupId: GroupId): Map<RecipientId, NameColor> {
|
||||
fun getColorMap(groupId: GroupId): Map<RecipientId, NameColor> {
|
||||
val dbMembers: Set<Recipient> = SignalDatabase
|
||||
.groups
|
||||
.getGroupMembers(groupId, GroupTable.MemberSet.FULL_MEMBERS_INCLUDING_SELF)
|
||||
|
||||
@@ -1060,6 +1060,8 @@ class ConversationFragment :
|
||||
}
|
||||
|
||||
composeText.setMessageSendType(MessageSendType.SignalMessageSendType)
|
||||
|
||||
colorizer.onNameColorsChanged(inputReadyState.groupNameColors)
|
||||
}
|
||||
|
||||
private fun presentIdentityRecordsState(identityRecordsState: IdentityRecordsState) {
|
||||
|
||||
@@ -18,7 +18,6 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.core.Completable
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import io.reactivex.rxjava3.core.Maybe
|
||||
import io.reactivex.rxjava3.core.Observable
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.core.SingleEmitter
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
@@ -164,19 +163,10 @@ class ConversationRepository(
|
||||
* Generates the name color-map for groups.
|
||||
*/
|
||||
fun getNameColorsMap(
|
||||
recipient: Recipient,
|
||||
group: GroupRecord,
|
||||
groupAuthorNameColorHelper: GroupAuthorNameColorHelper
|
||||
): Observable<Map<RecipientId, NameColor>> {
|
||||
return Recipient.observable(recipient.id)
|
||||
.distinctUntilChanged { a, b -> a.participantIds == b.participantIds }
|
||||
.map {
|
||||
if (it.groupId.isPresent) {
|
||||
groupAuthorNameColorHelper.getColorMap(it.requireGroupId())
|
||||
} else {
|
||||
emptyMap()
|
||||
}
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
): Map<RecipientId, NameColor> {
|
||||
return groupAuthorNameColorHelper.getColorMap(group)
|
||||
}
|
||||
|
||||
fun sendReactionRemoval(messageRecord: MessageRecord, oldRecord: ReactionRecord): Completable {
|
||||
|
||||
@@ -104,10 +104,14 @@ class ConversationViewModel(
|
||||
|
||||
val pagingController = ProxyPagingController<ConversationElementKey>()
|
||||
|
||||
val nameColorsMap: Observable<Map<RecipientId, NameColor>> = recipient
|
||||
.filter { it.isGroup }
|
||||
.flatMap { repository.getNameColorsMap(it, groupAuthorNameColorHelper) }
|
||||
val nameColorsMap: Observable<Map<RecipientId, NameColor>> = recipientRepository
|
||||
.groupRecord
|
||||
.filter { it.isPresent }
|
||||
.map { it.get() }
|
||||
.distinctUntilChanged { previous, next -> previous.hasSameMembers(next) }
|
||||
.map { repository.getNameColorsMap(it, groupAuthorNameColorHelper) }
|
||||
.distinctUntilChanged()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
||||
@Volatile
|
||||
var recipientSnapshot: Recipient? = null
|
||||
@@ -210,6 +214,7 @@ class ConversationViewModel(
|
||||
conversationRecipient = recipient,
|
||||
messageRequestState = messageRequestRepository.getMessageRequestState(recipient, threadId),
|
||||
groupRecord = groupRecord.orNull(),
|
||||
groupNameColors = groupRecord.map { repository.getNameColorsMap(it, groupAuthorNameColorHelper) }.orElse(emptyMap()),
|
||||
isClientExpired = SignalStore.misc().isClientDeprecated,
|
||||
isUnauthorized = TextSecurePreferences.isUnauthorizedReceived(ApplicationDependencies.getApplication())
|
||||
)
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
|
||||
package org.thoughtcrime.securesms.conversation.v2
|
||||
|
||||
import org.thoughtcrime.securesms.conversation.colors.NameColor
|
||||
import org.thoughtcrime.securesms.database.GroupTable
|
||||
import org.thoughtcrime.securesms.database.RecipientTable
|
||||
import org.thoughtcrime.securesms.database.model.GroupRecord
|
||||
import org.thoughtcrime.securesms.messagerequests.MessageRequestState
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
|
||||
/**
|
||||
* Information necessary for rendering compose input.
|
||||
@@ -19,7 +21,8 @@ data class InputReadyState(
|
||||
val messageRequestState: MessageRequestState,
|
||||
val groupRecord: GroupRecord?,
|
||||
val isClientExpired: Boolean,
|
||||
val isUnauthorized: Boolean
|
||||
val isUnauthorized: Boolean,
|
||||
val groupNameColors: Map<RecipientId, NameColor>
|
||||
) {
|
||||
private val selfMemberLevel: GroupTable.MemberLevel? = groupRecord?.memberLevel(Recipient.self())
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@ import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil
|
||||
import org.whispersystems.signalservice.api.push.DistributionId
|
||||
import java.lang.AssertionError
|
||||
import org.whispersystems.signalservice.api.push.ServiceId
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil
|
||||
import java.util.Optional
|
||||
|
||||
class GroupRecord(
|
||||
@@ -44,6 +45,22 @@ class GroupRecord(
|
||||
}
|
||||
}
|
||||
|
||||
/** Valid for v2 groups only */
|
||||
val decryptedMemberServiceIds: List<ServiceId> by lazy {
|
||||
if (isV2Group) {
|
||||
requireV2GroupProperties()
|
||||
.decryptedGroup
|
||||
.membersList
|
||||
.asSequence()
|
||||
.map { DecryptedGroupUtil.toUuid(it) }
|
||||
.filterNot { it == UuidUtil.UNKNOWN_UUID }
|
||||
.map { ServiceId.from(it) }
|
||||
.toList()
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
/** V1 members that were lost during the V1->V2 migration */
|
||||
val unmigratedV1Members: List<RecipientId> by lazy {
|
||||
if (serializedUnmigratedV1Members.isNullOrEmpty()) {
|
||||
@@ -183,4 +200,12 @@ class GroupRecord(
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun hasSameMembers(other: GroupRecord): Boolean {
|
||||
if (!isV2Group || !other.isV2Group) {
|
||||
return false
|
||||
}
|
||||
|
||||
return decryptedMemberServiceIds == other.decryptedMemberServiceIds
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user