mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-22 01:40:07 +01:00
Revamp group name color generation.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package org.thoughtcrime.securesms.conversation
|
||||
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.whispersystems.signalservice.api.push.ServiceId
|
||||
|
||||
/**
|
||||
* Represents metadata about a conversation.
|
||||
@@ -15,7 +16,8 @@ data class ConversationData(
|
||||
val threadSize: Int,
|
||||
val messageRequestData: MessageRequestData,
|
||||
@get:JvmName("showUniversalExpireTimerMessage") val showUniversalExpireTimerMessage: Boolean,
|
||||
val unreadCount: Int
|
||||
val unreadCount: Int,
|
||||
val groupMemberAcis: List<ServiceId>
|
||||
) {
|
||||
|
||||
fun shouldJumpToMessage(): Boolean {
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.thoughtcrime.securesms.util.ConversationUtil;
|
||||
import org.thoughtcrime.securesms.util.MessageRecordUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.whispersystems.signalservice.api.push.ServiceId;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -110,6 +111,13 @@ public class ConversationRepository {
|
||||
messageRequestData = new ConversationData.MessageRequestData(isMessageRequestAccepted, isConversationHidden, recipientIsKnownOrHasGroupsInCommon, isGroup);
|
||||
}
|
||||
|
||||
List<ServiceId> groupMemberAcis;
|
||||
if (conversationRecipient.isPushV2Group()) {
|
||||
groupMemberAcis = conversationRecipient.getParticipantAcis();
|
||||
} else {
|
||||
groupMemberAcis = Collections.emptyList();
|
||||
}
|
||||
|
||||
if (SignalStore.settings().getUniversalExpireTimer() != 0 &&
|
||||
conversationRecipient.getExpiresInSeconds() == 0 &&
|
||||
!conversationRecipient.isGroup() &&
|
||||
@@ -119,7 +127,7 @@ public class ConversationRepository {
|
||||
showUniversalExpireTimerUpdate = true;
|
||||
}
|
||||
|
||||
return new ConversationData(conversationRecipient, threadId, lastSeen, lastSeenPosition, lastScrolledPosition, jumpToPosition, threadSize, messageRequestData, showUniversalExpireTimerUpdate, metadata.getUnreadCount());
|
||||
return new ConversationData(conversationRecipient, threadId, lastSeen, lastSeenPosition, lastScrolledPosition, jumpToPosition, threadSize, messageRequestData, showUniversalExpireTimerUpdate, metadata.getUnreadCount(), groupMemberAcis);
|
||||
}
|
||||
|
||||
public void markGiftBadgeRevealed(long messageId) {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package org.thoughtcrime.securesms.conversation.colors
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.content.ContextCompat
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.whispersystems.signalservice.api.push.ServiceId
|
||||
|
||||
/**
|
||||
* Helper class for all things ChatColors.
|
||||
@@ -18,8 +18,12 @@ import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
class Colorizer {
|
||||
|
||||
private var colorsHaveBeenSet = false
|
||||
|
||||
@Deprecated("Not needed for CFv2")
|
||||
private val groupSenderColors: MutableMap<RecipientId, NameColor> = mutableMapOf()
|
||||
|
||||
private val groupMembers: LinkedHashSet<ServiceId> = linkedSetOf()
|
||||
|
||||
@ColorInt
|
||||
fun getOutgoingBodyTextColor(context: Context): Int {
|
||||
return ContextCompat.getColor(context, R.color.conversation_outgoing_body_color)
|
||||
@@ -63,8 +67,26 @@ class Colorizer {
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
fun getIncomingGroupSenderColor(context: Context, recipient: Recipient): Int = groupSenderColors[recipient.id]?.getColor(context) ?: getDefaultColor(context, recipient.id)
|
||||
fun getIncomingGroupSenderColor(context: Context, recipient: Recipient): Int {
|
||||
return if (groupMembers.isEmpty()) {
|
||||
groupSenderColors[recipient.id]?.getColor(context) ?: getDefaultColor(context, recipient)
|
||||
} else {
|
||||
val memberPosition = groupMembers.indexOf(recipient.requireServiceId())
|
||||
|
||||
if (memberPosition >= 0) {
|
||||
val colorPosition = memberPosition % ChatColorsPalette.Names.all.size
|
||||
ChatColorsPalette.Names.all[colorPosition].getColor(context)
|
||||
} else {
|
||||
getDefaultColor(context, recipient)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onGroupMembershipChanged(serviceIds: List<ServiceId>) {
|
||||
groupMembers.addAll(serviceIds.sortedBy { it.toString() })
|
||||
}
|
||||
|
||||
@Deprecated("Not needed for CFv2", ReplaceWith("onGroupMembershipChanged"))
|
||||
fun onNameColorsChanged(nameColorMap: Map<RecipientId, NameColor>) {
|
||||
groupSenderColors.clear()
|
||||
groupSenderColors.putAll(nameColorMap)
|
||||
@@ -72,13 +94,13 @@ class Colorizer {
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
private fun getDefaultColor(context: Context, recipientId: RecipientId): Int {
|
||||
private fun getDefaultColor(context: Context, recipient: Recipient): Int {
|
||||
return if (colorsHaveBeenSet) {
|
||||
val color = ChatColorsPalette.Names.all[groupSenderColors.size % ChatColorsPalette.Names.all.size]
|
||||
groupSenderColors[recipientId] = color
|
||||
groupSenderColors[recipient.id] = color
|
||||
return color.getColor(context)
|
||||
} else {
|
||||
Color.TRANSPARENT
|
||||
getIncomingBodyTextColor(context, recipient.hasWallpaper())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@ package org.thoughtcrime.securesms.conversation.colors
|
||||
|
||||
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.
|
||||
@@ -18,35 +16,6 @@ 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.
|
||||
|
||||
@@ -766,6 +766,7 @@ class ConversationFragment :
|
||||
.doOnSuccess { state ->
|
||||
SignalLocalMetrics.ConversationOpen.onDataLoaded()
|
||||
conversationItemDecorations.setFirstUnreadCount(state.meta.unreadCount)
|
||||
colorizer.onGroupMembershipChanged(state.meta.groupMemberAcis)
|
||||
}
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnSuccess { state ->
|
||||
@@ -817,10 +818,9 @@ class ConversationFragment :
|
||||
.subscribeBy(onNext = this::presentScrollButtons)
|
||||
|
||||
disposables += viewModel
|
||||
.nameColorsMap
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.groupMemberServiceIds
|
||||
.subscribeBy(onNext = {
|
||||
colorizer.onNameColorsChanged(it)
|
||||
colorizer.onGroupMembershipChanged(it)
|
||||
adapter.updateNameColors()
|
||||
})
|
||||
|
||||
@@ -1059,8 +1059,6 @@ class ConversationFragment :
|
||||
}
|
||||
|
||||
composeText.setMessageSendType(MessageSendType.SignalMessageSendType)
|
||||
|
||||
colorizer.onNameColorsChanged(inputReadyState.groupNameColors)
|
||||
}
|
||||
|
||||
private fun presentIdentityRecordsState(identityRecordsState: IdentityRecordsState) {
|
||||
|
||||
@@ -44,8 +44,6 @@ import org.thoughtcrime.securesms.contactshare.Contact
|
||||
import org.thoughtcrime.securesms.contactshare.ContactUtil
|
||||
import org.thoughtcrime.securesms.conversation.ConversationMessage
|
||||
import org.thoughtcrime.securesms.conversation.MessageSendType
|
||||
import org.thoughtcrime.securesms.conversation.colors.GroupAuthorNameColorHelper
|
||||
import org.thoughtcrime.securesms.conversation.colors.NameColor
|
||||
import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart
|
||||
import org.thoughtcrime.securesms.conversation.v2.RequestReviewState.GroupReviewState
|
||||
import org.thoughtcrime.securesms.conversation.v2.RequestReviewState.IndividualReviewState
|
||||
@@ -159,16 +157,6 @@ class ConversationRepository(
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the name color-map for groups.
|
||||
*/
|
||||
fun getNameColorsMap(
|
||||
group: GroupRecord,
|
||||
groupAuthorNameColorHelper: GroupAuthorNameColorHelper
|
||||
): Map<RecipientId, NameColor> {
|
||||
return groupAuthorNameColorHelper.getColorMap(group)
|
||||
}
|
||||
|
||||
fun sendReactionRemoval(messageRecord: MessageRecord, oldRecord: ReactionRecord): Completable {
|
||||
return Completable.fromAction {
|
||||
MessageSender.sendReactionRemoval(
|
||||
|
||||
@@ -32,8 +32,6 @@ import org.thoughtcrime.securesms.components.reminder.Reminder
|
||||
import org.thoughtcrime.securesms.contactshare.Contact
|
||||
import org.thoughtcrime.securesms.conversation.ConversationMessage
|
||||
import org.thoughtcrime.securesms.conversation.ScheduledMessagesRepository
|
||||
import org.thoughtcrime.securesms.conversation.colors.GroupAuthorNameColorHelper
|
||||
import org.thoughtcrime.securesms.conversation.colors.NameColor
|
||||
import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectPart
|
||||
import org.thoughtcrime.securesms.conversation.v2.data.ConversationElementKey
|
||||
import org.thoughtcrime.securesms.database.DatabaseObserver
|
||||
@@ -68,6 +66,7 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import org.thoughtcrime.securesms.util.hasGiftBadge
|
||||
import org.thoughtcrime.securesms.util.rx.RxStore
|
||||
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper
|
||||
import org.whispersystems.signalservice.api.push.ServiceId
|
||||
import java.util.Optional
|
||||
import kotlin.time.Duration
|
||||
|
||||
@@ -84,7 +83,6 @@ class ConversationViewModel(
|
||||
) : ViewModel() {
|
||||
|
||||
private val disposables = CompositeDisposable()
|
||||
private val groupAuthorNameColorHelper = GroupAuthorNameColorHelper()
|
||||
|
||||
private val scrollButtonStateStore = RxStore(ConversationScrollButtonState()).addTo(disposables)
|
||||
val scrollButtonState: Flowable<ConversationScrollButtonState> = scrollButtonStateStore.stateFlowable
|
||||
@@ -107,12 +105,10 @@ class ConversationViewModel(
|
||||
|
||||
val pagingController = ProxyPagingController<ConversationElementKey>()
|
||||
|
||||
val nameColorsMap: Observable<Map<RecipientId, NameColor>> = recipientRepository
|
||||
val groupMemberServiceIds: Observable<List<ServiceId>> = recipientRepository
|
||||
.groupRecord
|
||||
.filter { it.isPresent }
|
||||
.map { it.get() }
|
||||
.distinctUntilChanged { previous, next -> previous.hasSameMembers(next) }
|
||||
.map { repository.getNameColorsMap(it, groupAuthorNameColorHelper) }
|
||||
.filter { it.isPresent && it.get().isV2Group }
|
||||
.map { it.get().requireV2GroupProperties().getMemberServiceIds() }
|
||||
.distinctUntilChanged()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
||||
@@ -217,7 +213,6 @@ 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,13 +5,10 @@
|
||||
|
||||
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.
|
||||
@@ -21,12 +18,10 @@ data class InputReadyState(
|
||||
val messageRequestState: MessageRequestState,
|
||||
val groupRecord: GroupRecord?,
|
||||
val isClientExpired: Boolean,
|
||||
val isUnauthorized: Boolean,
|
||||
val groupNameColors: Map<RecipientId, NameColor>
|
||||
val isUnauthorized: Boolean
|
||||
) {
|
||||
private val selfMemberLevel: GroupTable.MemberLevel? = groupRecord?.memberLevel(Recipient.self())
|
||||
|
||||
val isSignalConversation: Boolean = conversationRecipient.registered == RecipientTable.RegisteredState.REGISTERED && Recipient.self().isRegistered
|
||||
val isAnnouncementGroup: Boolean? = groupRecord?.isAnnouncementGroup
|
||||
val isActiveGroup: Boolean? = if (selfMemberLevel == null) null else selfMemberLevel != GroupTable.MemberLevel.NOT_A_MEMBER
|
||||
val isAdmin: Boolean? = selfMemberLevel?.equals(GroupTable.MemberLevel.ADMINISTRATOR)
|
||||
|
||||
Reference in New Issue
Block a user