Make names in group update descriptions tappable.
This commit is contained in:
parent
3b17a41415
commit
e2cb535f3f
17 changed files with 378 additions and 215 deletions
|
@ -60,6 +60,10 @@ public interface BindableConversationItem extends Unbindable, GiphyMp4Playable,
|
||||||
// Intentionally Blank.
|
// Intentionally Blank.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default void updateSelectedState() {
|
||||||
|
// Intentionall Blank.
|
||||||
|
}
|
||||||
|
|
||||||
interface EventListener {
|
interface EventListener {
|
||||||
void onQuoteClicked(MmsMessageRecord messageRecord);
|
void onQuoteClicked(MmsMessageRecord messageRecord);
|
||||||
void onLinkPreviewClicked(@NonNull LinkPreview linkPreview);
|
void onLinkPreviewClicked(@NonNull LinkPreview linkPreview);
|
||||||
|
@ -95,6 +99,7 @@ public interface BindableConversationItem extends Unbindable, GiphyMp4Playable,
|
||||||
void onCallToAction(@NonNull String action);
|
void onCallToAction(@NonNull String action);
|
||||||
void onDonateClicked();
|
void onDonateClicked();
|
||||||
void onBlockJoinRequest(@NonNull Recipient recipient);
|
void onBlockJoinRequest(@NonNull Recipient recipient);
|
||||||
|
void onRecipientNameClicked(@NonNull RecipientId target);
|
||||||
|
|
||||||
/** @return true if handled, false if you want to let the normal url handling continue */
|
/** @return true if handled, false if you want to let the normal url handling continue */
|
||||||
boolean onUrlClicked(@NonNull String url);
|
boolean onUrlClicked(@NonNull String url);
|
||||||
|
|
|
@ -104,6 +104,7 @@ public class ConversationAdapter
|
||||||
|
|
||||||
private static final int PAYLOAD_TIMESTAMP = 0;
|
private static final int PAYLOAD_TIMESTAMP = 0;
|
||||||
public static final int PAYLOAD_NAME_COLORS = 1;
|
public static final int PAYLOAD_NAME_COLORS = 1;
|
||||||
|
public static final int PAYLOAD_SELECTED = 2;
|
||||||
|
|
||||||
private final ItemClickListener clickListener;
|
private final ItemClickListener clickListener;
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
@ -229,7 +230,7 @@ public class ConversationAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean containsValidPayload(@NonNull List<Object> payloads) {
|
private boolean containsValidPayload(@NonNull List<Object> payloads) {
|
||||||
return payloads.contains(PAYLOAD_TIMESTAMP) || payloads.contains(PAYLOAD_NAME_COLORS);
|
return payloads.contains(PAYLOAD_TIMESTAMP) || payloads.contains(PAYLOAD_NAME_COLORS) || payloads.contains(PAYLOAD_SELECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -250,6 +251,10 @@ public class ConversationAdapter
|
||||||
conversationViewHolder.getBindable().updateContactNameColor();
|
conversationViewHolder.getBindable().updateContactNameColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (payloads.contains(PAYLOAD_SELECTED)) {
|
||||||
|
conversationViewHolder.getBindable().updateSelectedState();
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -522,6 +527,7 @@ public class ConversationAdapter
|
||||||
|
|
||||||
public void removeFromSelection(@NonNull Set<MultiselectPart> parts) {
|
public void removeFromSelection(@NonNull Set<MultiselectPart> parts) {
|
||||||
selected.removeAll(parts);
|
selected.removeAll(parts);
|
||||||
|
updateSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -529,6 +535,7 @@ public class ConversationAdapter
|
||||||
*/
|
*/
|
||||||
void clearSelection() {
|
void clearSelection() {
|
||||||
selected.clear();
|
selected.clear();
|
||||||
|
updateSelected();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -540,6 +547,11 @@ public class ConversationAdapter
|
||||||
} else {
|
} else {
|
||||||
selected.add(multiselectPart);
|
selected.add(multiselectPart);
|
||||||
}
|
}
|
||||||
|
updateSelected();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSelected() {
|
||||||
|
notifyItemRangeChanged(0, getItemCount(), PAYLOAD_SELECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -106,7 +106,7 @@ class ConversationDataSource implements PagedDataSource<MessageId, ConversationM
|
||||||
reactionHelper.add(record);
|
reactionHelper.add(record);
|
||||||
attachmentHelper.add(record);
|
attachmentHelper.add(record);
|
||||||
|
|
||||||
UpdateDescription description = record.getUpdateDisplayBody(context);
|
UpdateDescription description = record.getUpdateDisplayBody(context, null);
|
||||||
if (description != null) {
|
if (description != null) {
|
||||||
referencedIds.addAll(description.getMentioned());
|
referencedIds.addAll(description.getMentioned());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1928,6 +1928,13 @@ public class ConversationFragment extends LoggingFragment implements Multiselect
|
||||||
.setPositiveButton(R.string.ConversationFragment__block_request_button, (d, w) -> handleBlockJoinRequest(recipient))
|
.setPositiveButton(R.string.ConversationFragment__block_request_button, (d, w) -> handleBlockJoinRequest(recipient))
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRecipientNameClicked(@NonNull RecipientId target) {
|
||||||
|
if (getParentFragment() == null) return;
|
||||||
|
|
||||||
|
RecipientBottomSheetDialogFragment.create(target, recipient.get().getGroupId().orElse(null)).show(getParentFragmentManager(), "BOTTOM");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshList() {
|
public void refreshList() {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Context;
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
import android.text.Spannable;
|
import android.text.Spannable;
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
|
import android.text.method.LinkMovementMethod;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -107,6 +108,9 @@ public final class ConversationUpdateItem extends FrameLayout
|
||||||
this.donateButtonStub = ViewUtil.findStubById(this, R.id.conversation_update_donate_action);
|
this.donateButtonStub = ViewUtil.findStubById(this, R.id.conversation_update_donate_action);
|
||||||
this.background = findViewById(R.id.conversation_update_background);
|
this.background = findViewById(R.id.conversation_update_background);
|
||||||
|
|
||||||
|
body.setOnClickListener(v -> performClick());
|
||||||
|
body.setOnLongClickListener(v -> performLongClick());
|
||||||
|
|
||||||
this.setOnClickListener(new InternalClickListener(null));
|
this.setOnClickListener(new InternalClickListener(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +183,7 @@ public final class ConversationUpdateItem extends FrameLayout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateDescription updateDescription = Objects.requireNonNull(messageRecord.getUpdateDisplayBody(getContext()));
|
UpdateDescription updateDescription = Objects.requireNonNull(messageRecord.getUpdateDisplayBody(getContext(), eventListener::onRecipientNameClicked));
|
||||||
LiveData<SpannableString> liveUpdateMessage = LiveUpdateMessage.fromMessageDescription(getContext(), updateDescription, textColor, true);
|
LiveData<SpannableString> liveUpdateMessage = LiveUpdateMessage.fromMessageDescription(getContext(), updateDescription, textColor, true);
|
||||||
LiveData<SpannableString> spannableMessage = loading(liveUpdateMessage);
|
LiveData<SpannableString> spannableMessage = loading(liveUpdateMessage);
|
||||||
|
|
||||||
|
@ -190,6 +194,17 @@ public final class ConversationUpdateItem extends FrameLayout
|
||||||
presentBackground(shouldCollapse(messageRecord, previousMessageRecord),
|
presentBackground(shouldCollapse(messageRecord, previousMessageRecord),
|
||||||
shouldCollapse(messageRecord, nextMessageRecord),
|
shouldCollapse(messageRecord, nextMessageRecord),
|
||||||
hasWallpaper);
|
hasWallpaper);
|
||||||
|
|
||||||
|
updateSelectedState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateSelectedState() {
|
||||||
|
if (batchSelected.size() > 0) {
|
||||||
|
body.setMovementMethod(null);
|
||||||
|
} else {
|
||||||
|
body.setMovementMethod(LinkMovementMethod.getInstance());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean shouldCollapse(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> candidate)
|
private static boolean shouldCollapse(@NonNull MessageRecord current, @NonNull Optional<MessageRecord> candidate)
|
||||||
|
|
|
@ -73,7 +73,7 @@ abstract class ConversationListDataSource implements PagedDataSource<Long, Conve
|
||||||
if (!record.getRecipient().isPushV2Group()) {
|
if (!record.getRecipient().isPushV2Group()) {
|
||||||
needsResolve.add(record.getRecipient().getId());
|
needsResolve.add(record.getRecipient().getId());
|
||||||
} else if (SmsDatabase.Types.isGroupUpdate(record.getType())) {
|
} else if (SmsDatabase.Types.isGroupUpdate(record.getType())) {
|
||||||
UpdateDescription description = MessageRecord.getGv2ChangeDescription(ApplicationDependencies.getApplication(), record.getBody());
|
UpdateDescription description = MessageRecord.getGv2ChangeDescription(ApplicationDependencies.getApplication(), record.getBody(), null);
|
||||||
needsResolve.addAll(description.getMentioned().stream().map(sid -> RecipientId.from(sid, null)).collect(Collectors.toList()));
|
needsResolve.addAll(description.getMentioned().stream().map(sid -> RecipientId.from(sid, null)).collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -466,7 +466,7 @@ public final class ConversationListItem extends ConstraintLayout
|
||||||
return emphasisAdded(context, context.getString(R.string.ThreadRecord_message_request), defaultTint);
|
return emphasisAdded(context, context.getString(R.string.ThreadRecord_message_request), defaultTint);
|
||||||
} else if (SmsDatabase.Types.isGroupUpdate(thread.getType())) {
|
} else if (SmsDatabase.Types.isGroupUpdate(thread.getType())) {
|
||||||
if (thread.getRecipient().isPushV2Group()) {
|
if (thread.getRecipient().isPushV2Group()) {
|
||||||
return emphasisAdded(context, MessageRecord.getGv2ChangeDescription(context, thread.getBody()), defaultTint);
|
return emphasisAdded(context, MessageRecord.getGv2ChangeDescription(context, thread.getBody(), null), defaultTint);
|
||||||
} else {
|
} else {
|
||||||
return emphasisAdded(context, context.getString(R.string.ThreadRecord_group_updated), R.drawable.ic_update_group_16, defaultTint);
|
return emphasisAdded(context, context.getString(R.string.ThreadRecord_group_updated), R.drawable.ic_update_group_16, defaultTint);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package org.thoughtcrime.securesms.database.model;
|
package org.thoughtcrime.securesms.database.model;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.SpannableString;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
@ -21,7 +23,7 @@ import java.util.Objects;
|
||||||
/**
|
/**
|
||||||
* Create a group call update message based on time and joined members.
|
* Create a group call update message based on time and joined members.
|
||||||
*/
|
*/
|
||||||
public class GroupCallUpdateMessageFactory implements UpdateDescription.StringFactory {
|
public class GroupCallUpdateMessageFactory implements UpdateDescription.SpannableFactory {
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final List<ServiceId> joinedMembers;
|
private final List<ServiceId> joinedMembers;
|
||||||
private final boolean withTime;
|
private final boolean withTime;
|
||||||
|
@ -46,7 +48,11 @@ public class GroupCallUpdateMessageFactory implements UpdateDescription.StringFa
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull String create() {
|
public @NonNull Spannable create() {
|
||||||
|
return new SpannableString(createString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NonNull String createString() {
|
||||||
String time = DateUtils.getTimeString(context, Locale.getDefault(), groupCallUpdateDetails.getStartedCallTimestamp());
|
String time = DateUtils.getTimeString(context, Locale.getDefault(), groupCallUpdateDetails.getStartedCallTimestamp());
|
||||||
|
|
||||||
switch (joinedMembers.size()) {
|
switch (joinedMembers.size()) {
|
||||||
|
|
|
@ -1,10 +1,18 @@
|
||||||
package org.thoughtcrime.securesms.database.model;
|
package org.thoughtcrime.securesms.database.model;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.text.PrecomputedText;
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.SpannableString;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.annotation.DrawableRes;
|
import androidx.annotation.DrawableRes;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.PluralsRes;
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
|
|
||||||
|
@ -21,12 +29,16 @@ import org.signal.storageservice.protos.groups.local.DecryptedRequestingMember;
|
||||||
import org.signal.storageservice.protos.groups.local.EnabledState;
|
import org.signal.storageservice.protos.groups.local.EnabledState;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.groups.GV2AccessLevelUtil;
|
import org.thoughtcrime.securesms.groups.GV2AccessLevelUtil;
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.util.ExpirationUtil;
|
import org.thoughtcrime.securesms.util.ExpirationUtil;
|
||||||
import org.signal.core.util.StringUtil;
|
import org.signal.core.util.StringUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.SpanUtil;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
|
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
|
||||||
import org.whispersystems.signalservice.api.push.ServiceId;
|
import org.whispersystems.signalservice.api.push.ServiceId;
|
||||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -35,25 +47,21 @@ import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
final class GroupsV2UpdateMessageProducer {
|
final class GroupsV2UpdateMessageProducer {
|
||||||
|
|
||||||
@NonNull private final Context context;
|
@NonNull private final Context context;
|
||||||
@NonNull private final DescribeMemberStrategy descriptionStrategy;
|
|
||||||
@NonNull private final UUID selfUuid;
|
@NonNull private final UUID selfUuid;
|
||||||
@NonNull private final ByteString selfUuidBytes;
|
@NonNull private final ByteString selfUuidBytes;
|
||||||
|
@Nullable private final Consumer<RecipientId> recipientClickHandler;
|
||||||
|
|
||||||
/**
|
GroupsV2UpdateMessageProducer(@NonNull Context context, @NonNull UUID selfUuid, @Nullable Consumer<RecipientId> recipientClickHandler) {
|
||||||
* @param descriptionStrategy Strategy for member description.
|
|
||||||
*/
|
|
||||||
GroupsV2UpdateMessageProducer(@NonNull Context context,
|
|
||||||
@NonNull DescribeMemberStrategy descriptionStrategy,
|
|
||||||
@NonNull UUID selfUuid) {
|
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.descriptionStrategy = descriptionStrategy;
|
|
||||||
this.selfUuid = selfUuid;
|
this.selfUuid = selfUuid;
|
||||||
this.selfUuidBytes = UuidUtil.toByteString(selfUuid);
|
this.selfUuidBytes = UuidUtil.toByteString(selfUuid);
|
||||||
|
this.recipientClickHandler = recipientClickHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -68,7 +76,7 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
UpdateDescription describeNewGroup(@NonNull DecryptedGroup group, @NonNull DecryptedGroupChange decryptedGroupChange) {
|
UpdateDescription describeNewGroup(@NonNull DecryptedGroup group, @NonNull DecryptedGroupChange decryptedGroupChange) {
|
||||||
Optional<DecryptedPendingMember> selfPending = DecryptedGroupUtil.findPendingByUuid(group.getPendingMembersList(), selfUuid);
|
Optional<DecryptedPendingMember> selfPending = DecryptedGroupUtil.findPendingByUuid(group.getPendingMembersList(), selfUuid);
|
||||||
if (selfPending.isPresent()) {
|
if (selfPending.isPresent()) {
|
||||||
return updateDescription(selfPending.get().getAddedByUuid(), inviteBy -> context.getString(R.string.MessageRecord_s_invited_you_to_the_group, inviteBy), R.drawable.ic_update_group_add_16);
|
return updateDescription(R.string.MessageRecord_s_invited_you_to_the_group, selfPending.get().getAddedByUuid(), R.drawable.ic_update_group_add_16);
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteString foundingMemberUuid = decryptedGroupChange.getEditor();
|
ByteString foundingMemberUuid = decryptedGroupChange.getEditor();
|
||||||
|
@ -76,7 +84,7 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (selfUuidBytes.equals(foundingMemberUuid)) {
|
if (selfUuidBytes.equals(foundingMemberUuid)) {
|
||||||
return updateDescription(context.getString(R.string.MessageRecord_you_created_the_group), R.drawable.ic_update_group_16);
|
return updateDescription(context.getString(R.string.MessageRecord_you_created_the_group), R.drawable.ic_update_group_16);
|
||||||
} else {
|
} else {
|
||||||
return updateDescription(foundingMemberUuid, creator -> context.getString(R.string.MessageRecord_s_added_you, creator), R.drawable.ic_update_group_add_16);
|
return updateDescription(R.string.MessageRecord_s_added_you, foundingMemberUuid, R.drawable.ic_update_group_add_16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +165,7 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (editorIsYou) {
|
if (editorIsYou) {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_updated_group), R.drawable.ic_update_group_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_updated_group), R.drawable.ic_update_group_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(change.getEditor(), (editor) -> context.getString(R.string.MessageRecord_s_updated_group, editor), R.drawable.ic_update_group_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_updated_group, change.getEditor(), R.drawable.ic_update_group_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,16 +183,16 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (newMemberIsYou) {
|
if (newMemberIsYou) {
|
||||||
updates.add(0, updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group_via_the_group_link), R.drawable.ic_update_group_accept_16));
|
updates.add(0, updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group_via_the_group_link), R.drawable.ic_update_group_accept_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(member.getUuid(), added -> context.getString(R.string.MessageRecord_you_added_s, added), R.drawable.ic_update_group_add_16));
|
updates.add(updateDescription(R.string.MessageRecord_you_added_s, member.getUuid(), R.drawable.ic_update_group_add_16));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (newMemberIsYou) {
|
if (newMemberIsYou) {
|
||||||
updates.add(0, updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_added_you, editor), R.drawable.ic_update_group_add_16));
|
updates.add(0, updateDescription(R.string.MessageRecord_s_added_you, change.getEditor(), R.drawable.ic_update_group_add_16));
|
||||||
} else {
|
} else {
|
||||||
if (member.getUuid().equals(change.getEditor())) {
|
if (member.getUuid().equals(change.getEditor())) {
|
||||||
updates.add(updateDescription(member.getUuid(), newMember -> context.getString(R.string.MessageRecord_s_joined_the_group_via_the_group_link, newMember), R.drawable.ic_update_group_accept_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_joined_the_group_via_the_group_link, member.getUuid(), R.drawable.ic_update_group_accept_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(change.getEditor(), member.getUuid(), (editor, newMember) -> context.getString(R.string.MessageRecord_s_added_s, editor, newMember), R.drawable.ic_update_group_add_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_added_s, change.getEditor(), member.getUuid(), R.drawable.ic_update_group_add_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,7 +206,7 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (newMemberIsYou) {
|
if (newMemberIsYou) {
|
||||||
updates.add(0, updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group), R.drawable.ic_update_group_add_16));
|
updates.add(0, updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group), R.drawable.ic_update_group_add_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(member.getUuid(), newMember -> context.getString(R.string.MessageRecord_s_joined_the_group, newMember), R.drawable.ic_update_group_add_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_joined_the_group, member.getUuid(), R.drawable.ic_update_group_add_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -213,16 +221,16 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (removedMemberIsYou) {
|
if (removedMemberIsYou) {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_left_the_group), R.drawable.ic_update_group_leave_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_left_the_group), R.drawable.ic_update_group_leave_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(member, removedMember -> context.getString(R.string.MessageRecord_you_removed_s, removedMember), R.drawable.ic_update_group_remove_16));
|
updates.add(updateDescription(R.string.MessageRecord_you_removed_s, member, R.drawable.ic_update_group_remove_16));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (removedMemberIsYou) {
|
if (removedMemberIsYou) {
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_removed_you_from_the_group, editor), R.drawable.ic_update_group_remove_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_removed_you_from_the_group, change.getEditor(), R.drawable.ic_update_group_remove_16));
|
||||||
} else {
|
} else {
|
||||||
if (member.equals(change.getEditor())) {
|
if (member.equals(change.getEditor())) {
|
||||||
updates.add(updateDescription(member, leavingMember -> context.getString(R.string.MessageRecord_s_left_the_group, leavingMember), R.drawable.ic_update_group_leave_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_left_the_group, member, R.drawable.ic_update_group_leave_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(change.getEditor(), member, (editor, removedMember) -> context.getString(R.string.MessageRecord_s_removed_s, editor, removedMember), R.drawable.ic_update_group_remove_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_removed_s, change.getEditor(), member, R.drawable.ic_update_group_remove_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,7 +244,7 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (removedMemberIsYou) {
|
if (removedMemberIsYou) {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_are_no_longer_in_the_group), R.drawable.ic_update_group_leave_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_are_no_longer_in_the_group), R.drawable.ic_update_group_leave_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(member, oldMember -> context.getString(R.string.MessageRecord_s_is_no_longer_in_the_group, oldMember), R.drawable.ic_update_group_leave_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_is_no_longer_in_the_group, member, R.drawable.ic_update_group_leave_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -248,23 +256,23 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
boolean changedMemberIsYou = roleChange.getUuid().equals(selfUuidBytes);
|
boolean changedMemberIsYou = roleChange.getUuid().equals(selfUuidBytes);
|
||||||
if (roleChange.getRole() == Member.Role.ADMINISTRATOR) {
|
if (roleChange.getRole() == Member.Role.ADMINISTRATOR) {
|
||||||
if (editorIsYou) {
|
if (editorIsYou) {
|
||||||
updates.add(updateDescription(roleChange.getUuid(), newAdmin -> context.getString(R.string.MessageRecord_you_made_s_an_admin, newAdmin), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(R.string.MessageRecord_you_made_s_an_admin, roleChange.getUuid(), R.drawable.ic_update_group_role_16));
|
||||||
} else {
|
} else {
|
||||||
if (changedMemberIsYou) {
|
if (changedMemberIsYou) {
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_made_you_an_admin, editor), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_made_you_an_admin, change.getEditor(), R.drawable.ic_update_group_role_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(change.getEditor(), roleChange.getUuid(), (editor, newAdmin) -> context.getString(R.string.MessageRecord_s_made_s_an_admin, editor, newAdmin), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_made_s_an_admin, change.getEditor(), roleChange.getUuid(), R.drawable.ic_update_group_role_16));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (editorIsYou) {
|
if (editorIsYou) {
|
||||||
updates.add(updateDescription(roleChange.getUuid(), oldAdmin -> context.getString(R.string.MessageRecord_you_revoked_admin_privileges_from_s, oldAdmin), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(R.string.MessageRecord_you_revoked_admin_privileges_from_s, roleChange.getUuid(), R.drawable.ic_update_group_role_16));
|
||||||
} else {
|
} else {
|
||||||
if (changedMemberIsYou) {
|
if (changedMemberIsYou) {
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_revoked_your_admin_privileges, editor), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_revoked_your_admin_privileges, change.getEditor(), R.drawable.ic_update_group_role_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(change.getEditor(), roleChange.getUuid(), (editor, oldAdmin) -> context.getString(R.string.MessageRecord_s_revoked_admin_privileges_from_s, editor, oldAdmin), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_revoked_admin_privileges_from_s, change.getEditor(), roleChange.getUuid(), R.drawable.ic_update_group_role_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -279,13 +287,13 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (changedMemberIsYou) {
|
if (changedMemberIsYou) {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_are_now_an_admin), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_are_now_an_admin), R.drawable.ic_update_group_role_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(roleChange.getUuid(), newAdmin -> context.getString(R.string.MessageRecord_s_is_now_an_admin, newAdmin), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_is_now_an_admin, roleChange.getUuid(), R.drawable.ic_update_group_role_16));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (changedMemberIsYou) {
|
if (changedMemberIsYou) {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_are_no_longer_an_admin), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_are_no_longer_an_admin), R.drawable.ic_update_group_role_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(roleChange.getUuid(), oldAdmin -> context.getString(R.string.MessageRecord_s_is_no_longer_an_admin, oldAdmin), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_is_no_longer_an_admin, roleChange.getUuid(), R.drawable.ic_update_group_role_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -299,10 +307,10 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
boolean newMemberIsYou = invitee.getUuid().equals(selfUuidBytes);
|
boolean newMemberIsYou = invitee.getUuid().equals(selfUuidBytes);
|
||||||
|
|
||||||
if (newMemberIsYou) {
|
if (newMemberIsYou) {
|
||||||
updates.add(0, updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_invited_you_to_the_group, editor), R.drawable.ic_update_group_add_16));
|
updates.add(0, updateDescription(R.string.MessageRecord_s_invited_you_to_the_group, change.getEditor(), R.drawable.ic_update_group_add_16));
|
||||||
} else {
|
} else {
|
||||||
if (editorIsYou) {
|
if (editorIsYou) {
|
||||||
updates.add(updateDescription(invitee.getUuid(), newInvitee -> context.getString(R.string.MessageRecord_you_invited_s_to_the_group, newInvitee), R.drawable.ic_update_group_add_16));
|
updates.add(updateDescription(R.string.MessageRecord_you_invited_s_to_the_group, invitee.getUuid(), R.drawable.ic_update_group_add_16));
|
||||||
} else {
|
} else {
|
||||||
notYouInviteCount++;
|
notYouInviteCount++;
|
||||||
}
|
}
|
||||||
|
@ -311,7 +319,7 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
|
|
||||||
if (notYouInviteCount > 0) {
|
if (notYouInviteCount > 0) {
|
||||||
final int notYouInviteCountFinalCopy = notYouInviteCount;
|
final int notYouInviteCountFinalCopy = notYouInviteCount;
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getResources().getQuantityString(R.plurals.MessageRecord_s_invited_members, notYouInviteCountFinalCopy, editor, notYouInviteCountFinalCopy), R.drawable.ic_update_group_add_16));
|
updates.add(updateDescription(R.plurals.MessageRecord_s_invited_members, notYouInviteCountFinalCopy, change.getEditor(), notYouInviteCountFinalCopy, R.drawable.ic_update_group_add_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +335,7 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (UuidUtil.UNKNOWN_UUID.equals(uuid)) {
|
if (UuidUtil.UNKNOWN_UUID.equals(uuid)) {
|
||||||
updates.add(0, updateDescription(context.getString(R.string.MessageRecord_you_were_invited_to_the_group), R.drawable.ic_update_group_add_16));
|
updates.add(0, updateDescription(context.getString(R.string.MessageRecord_you_were_invited_to_the_group), R.drawable.ic_update_group_add_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(0, updateDescription(invitee.getAddedByUuid(), editor -> context.getString(R.string.MessageRecord_s_invited_you_to_the_group, editor), R.drawable.ic_update_group_add_16));
|
updates.add(0, updateDescription(R.string.MessageRecord_s_invited_you_to_the_group, invitee.getAddedByUuid(), R.drawable.ic_update_group_add_16));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
notYouInviteCount++;
|
notYouInviteCount++;
|
||||||
|
@ -352,7 +360,7 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_someone_declined_an_invitation_to_the_group), R.drawable.ic_update_group_decline_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_someone_declined_an_invitation_to_the_group), R.drawable.ic_update_group_decline_16));
|
||||||
}
|
}
|
||||||
} else if (invitee.getUuid().equals(selfUuidBytes)) {
|
} else if (invitee.getUuid().equals(selfUuidBytes)) {
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_revoked_your_invitation_to_the_group, editor), R.drawable.ic_update_group_decline_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_revoked_your_invitation_to_the_group, change.getEditor(), R.drawable.ic_update_group_decline_16));
|
||||||
} else {
|
} else {
|
||||||
notDeclineCount++;
|
notDeclineCount++;
|
||||||
}
|
}
|
||||||
|
@ -363,7 +371,7 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
updates.add(updateDescription(context.getResources().getQuantityString(R.plurals.MessageRecord_you_revoked_invites, notDeclineCount, notDeclineCount), R.drawable.ic_update_group_decline_16));
|
updates.add(updateDescription(context.getResources().getQuantityString(R.plurals.MessageRecord_you_revoked_invites, notDeclineCount, notDeclineCount), R.drawable.ic_update_group_decline_16));
|
||||||
} else {
|
} else {
|
||||||
final int notDeclineCountFinalCopy = notDeclineCount;
|
final int notDeclineCountFinalCopy = notDeclineCount;
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getResources().getQuantityString(R.plurals.MessageRecord_s_revoked_invites, notDeclineCountFinalCopy, editor, notDeclineCountFinalCopy), R.drawable.ic_update_group_decline_16));
|
updates.add(updateDescription(R.plurals.MessageRecord_s_revoked_invites, notDeclineCountFinalCopy, change.getEditor(), notDeclineCountFinalCopy, R.drawable.ic_update_group_decline_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -397,16 +405,16 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (newMemberIsYou) {
|
if (newMemberIsYou) {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_accepted_invite), R.drawable.ic_update_group_accept_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_accepted_invite), R.drawable.ic_update_group_accept_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(uuid, newPromotedMember -> context.getString(R.string.MessageRecord_you_added_invited_member_s, newPromotedMember), R.drawable.ic_update_group_add_16));
|
updates.add(updateDescription(R.string.MessageRecord_you_added_invited_member_s, uuid, R.drawable.ic_update_group_add_16));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (newMemberIsYou) {
|
if (newMemberIsYou) {
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_added_you, editor), R.drawable.ic_update_group_add_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_added_you, change.getEditor(), R.drawable.ic_update_group_add_16));
|
||||||
} else {
|
} else {
|
||||||
if (uuid.equals(change.getEditor())) {
|
if (uuid.equals(change.getEditor())) {
|
||||||
updates.add(updateDescription(uuid, newAcceptedMember -> context.getString(R.string.MessageRecord_s_accepted_invite, newAcceptedMember), R.drawable.ic_update_group_accept_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_accepted_invite, uuid, R.drawable.ic_update_group_accept_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(change.getEditor(), uuid, (editor, newAcceptedMember) -> context.getString(R.string.MessageRecord_s_added_invited_member_s, editor, newAcceptedMember), R.drawable.ic_update_group_add_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_added_invited_member_s, change.getEditor(), uuid, R.drawable.ic_update_group_add_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -421,7 +429,7 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (newMemberIsYou) {
|
if (newMemberIsYou) {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group), R.drawable.ic_update_group_add_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group), R.drawable.ic_update_group_add_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(uuid, newMemberName -> context.getString(R.string.MessageRecord_s_joined_the_group, newMemberName), R.drawable.ic_update_group_add_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_joined_the_group, uuid, R.drawable.ic_update_group_add_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -434,7 +442,7 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (editorIsYou) {
|
if (editorIsYou) {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_the_group_name_to_s, newTitle), R.drawable.ic_update_group_name_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_the_group_name_to_s, newTitle), R.drawable.ic_update_group_name_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_changed_the_group_name_to_s, editor, newTitle), R.drawable.ic_update_group_name_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_changed_the_group_name_to_s, change.getEditor(), newTitle, R.drawable.ic_update_group_name_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -446,7 +454,7 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (editorIsYou) {
|
if (editorIsYou) {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_the_group_description), R.drawable.ic_update_group_name_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_the_group_description), R.drawable.ic_update_group_name_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_changed_the_group_description, editor), R.drawable.ic_update_group_name_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_changed_the_group_description, change.getEditor(), R.drawable.ic_update_group_name_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -470,7 +478,7 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (editorIsYou) {
|
if (editorIsYou) {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_the_group_avatar), R.drawable.ic_update_group_avatar_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_the_group_avatar), R.drawable.ic_update_group_avatar_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_changed_the_group_avatar, editor), R.drawable.ic_update_group_avatar_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_changed_the_group_avatar, change.getEditor(), R.drawable.ic_update_group_avatar_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -489,7 +497,7 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (editorIsYou) {
|
if (editorIsYou) {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_set_disappearing_message_time_to_s, time), R.drawable.ic_update_timer_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_set_disappearing_message_time_to_s, time), R.drawable.ic_update_timer_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_set_disappearing_message_time_to_s, editor, time), R.drawable.ic_update_timer_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_set_disappearing_message_time_to_s, change.getEditor(), time, R.drawable.ic_update_timer_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -509,7 +517,7 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (editorIsYou) {
|
if (editorIsYou) {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_who_can_edit_group_info_to_s, accessLevel), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_who_can_edit_group_info_to_s, accessLevel), R.drawable.ic_update_group_role_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_changed_who_can_edit_group_info_to_s, editor, accessLevel), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_changed_who_can_edit_group_info_to_s, change.getEditor(), accessLevel, R.drawable.ic_update_group_role_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -529,7 +537,7 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (editorIsYou) {
|
if (editorIsYou) {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_who_can_edit_group_membership_to_s, accessLevel), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_who_can_edit_group_membership_to_s, accessLevel), R.drawable.ic_update_group_role_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_changed_who_can_edit_group_membership_to_s, editor, accessLevel), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_changed_who_can_edit_group_membership_to_s, change.getEditor(), accessLevel, R.drawable.ic_update_group_role_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -565,9 +573,9 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (previousAccessControl == AccessControl.AccessRequired.ADMINISTRATOR) {
|
if (previousAccessControl == AccessControl.AccessRequired.ADMINISTRATOR) {
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_turned_off_admin_approval_for_the_group_link, editor), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_turned_off_admin_approval_for_the_group_link, change.getEditor(), R.drawable.ic_update_group_role_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_turned_on_the_group_link_with_admin_approval_off, editor), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_turned_on_the_group_link_with_admin_approval_off, change.getEditor(), R.drawable.ic_update_group_role_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -581,9 +589,9 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (previousAccessControl == AccessControl.AccessRequired.ANY) {
|
if (previousAccessControl == AccessControl.AccessRequired.ANY) {
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_turned_on_admin_approval_for_the_group_link, editor), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_turned_on_admin_approval_for_the_group_link, change.getEditor(), R.drawable.ic_update_group_role_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_turned_on_the_group_link_with_admin_approval_on, editor), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_turned_on_the_group_link_with_admin_approval_on, change.getEditor(), R.drawable.ic_update_group_role_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -591,7 +599,7 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (editorIsYou) {
|
if (editorIsYou) {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_off_the_group_link), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_off_the_group_link), R.drawable.ic_update_group_role_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_turned_off_the_group_link, editor), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_turned_off_the_group_link, change.getEditor(), R.drawable.ic_update_group_role_16));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -600,7 +608,7 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (editorIsYou) {
|
if (editorIsYou) {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_reset_the_group_link), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_reset_the_group_link), R.drawable.ic_update_group_role_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_reset_the_group_link, editor), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_reset_the_group_link, change.getEditor(), R.drawable.ic_update_group_role_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -650,12 +658,13 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_sent_a_request_to_join_the_group), R.drawable.ic_update_group_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_sent_a_request_to_join_the_group), R.drawable.ic_update_group_16));
|
||||||
} else {
|
} else {
|
||||||
if (deleteRequestingUuids.contains(member.getUuid())) {
|
if (deleteRequestingUuids.contains(member.getUuid())) {
|
||||||
updates.add(updateDescription(member.getUuid(), requesting -> context.getResources().getQuantityString(R.plurals.MessageRecord_s_requested_and_cancelled_their_request_to_join_via_the_group_link,
|
updates.add(updateDescription(R.plurals.MessageRecord_s_requested_and_cancelled_their_request_to_join_via_the_group_link,
|
||||||
change.getDeleteRequestingMembersCount(),
|
change.getDeleteRequestingMembersCount(),
|
||||||
requesting,
|
member.getUuid(),
|
||||||
change.getDeleteRequestingMembersCount()), R.drawable.ic_update_group_16));
|
change.getDeleteRequestingMembersCount(),
|
||||||
|
R.drawable.ic_update_group_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(member.getUuid(), requesting -> context.getString(R.string.MessageRecord_s_requested_to_join_via_the_group_link, requesting), R.drawable.ic_update_group_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_requested_to_join_via_the_group_link, member.getUuid(), R.drawable.ic_update_group_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -666,14 +675,14 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
boolean requestingMemberIsYou = requestingMember.getUuid().equals(selfUuidBytes);
|
boolean requestingMemberIsYou = requestingMember.getUuid().equals(selfUuidBytes);
|
||||||
|
|
||||||
if (requestingMemberIsYou) {
|
if (requestingMemberIsYou) {
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_approved_your_request_to_join_the_group, editor), R.drawable.ic_update_group_accept_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_approved_your_request_to_join_the_group, change.getEditor(), R.drawable.ic_update_group_accept_16));
|
||||||
} else {
|
} else {
|
||||||
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
|
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
|
||||||
|
|
||||||
if (editorIsYou) {
|
if (editorIsYou) {
|
||||||
updates.add(updateDescription(requestingMember.getUuid(), requesting -> context.getString(R.string.MessageRecord_you_approved_a_request_to_join_the_group_from_s, requesting), R.drawable.ic_update_group_accept_16));
|
updates.add(updateDescription(R.string.MessageRecord_you_approved_a_request_to_join_the_group_from_s, requestingMember.getUuid(), R.drawable.ic_update_group_accept_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(change.getEditor(), requestingMember.getUuid(), (editor, requesting) -> context.getString(R.string.MessageRecord_s_approved_a_request_to_join_the_group_from_s, editor, requesting), R.drawable.ic_update_group_accept_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_approved_a_request_to_join_the_group_from_s, change.getEditor(), requestingMember.getUuid(), R.drawable.ic_update_group_accept_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -686,7 +695,7 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (requestingMemberIsYou) {
|
if (requestingMemberIsYou) {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_your_request_to_join_the_group_has_been_approved), R.drawable.ic_update_group_accept_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_your_request_to_join_the_group_has_been_approved), R.drawable.ic_update_group_accept_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(requestingMember.getUuid(), requesting -> context.getString(R.string.MessageRecord_a_request_to_join_the_group_from_s_has_been_approved, requesting), R.drawable.ic_update_group_accept_16));
|
updates.add(updateDescription(R.string.MessageRecord_a_request_to_join_the_group_from_s_has_been_approved, requestingMember.getUuid(), R.drawable.ic_update_group_accept_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -713,9 +722,9 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
boolean editorIsCanceledMember = change.getEditor().equals(requestingMember);
|
boolean editorIsCanceledMember = change.getEditor().equals(requestingMember);
|
||||||
|
|
||||||
if (editorIsCanceledMember) {
|
if (editorIsCanceledMember) {
|
||||||
updates.add(updateDescription(requestingMember, editorRequesting -> context.getString(R.string.MessageRecord_s_canceled_their_request_to_join_the_group, editorRequesting), R.drawable.ic_update_group_decline_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_canceled_their_request_to_join_the_group, requestingMember, R.drawable.ic_update_group_decline_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(change.getEditor(), requestingMember, (editor, requesting) -> context.getString(R.string.MessageRecord_s_denied_a_request_to_join_the_group_from_s, editor, requesting), R.drawable.ic_update_group_decline_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_denied_a_request_to_join_the_group_from_s, change.getEditor(), requestingMember, R.drawable.ic_update_group_decline_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -728,7 +737,7 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (requestingMemberIsYou) {
|
if (requestingMemberIsYou) {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_your_request_to_join_the_group_has_been_denied_by_an_admin), R.drawable.ic_update_group_decline_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_your_request_to_join_the_group_has_been_denied_by_an_admin), R.drawable.ic_update_group_decline_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(requestingMember, requesting -> context.getString(R.string.MessageRecord_a_request_to_join_the_group_from_s_has_been_denied, requesting), R.drawable.ic_update_group_decline_16));
|
updates.add(updateDescription(R.string.MessageRecord_a_request_to_join_the_group_from_s_has_been_denied, requestingMember, R.drawable.ic_update_group_decline_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -740,13 +749,13 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
if (editorIsYou) {
|
if (editorIsYou) {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_allow_only_admins_to_send), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_allow_only_admins_to_send), R.drawable.ic_update_group_role_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_allow_only_admins_to_send, editor), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_allow_only_admins_to_send, change.getEditor(), R.drawable.ic_update_group_role_16));
|
||||||
}
|
}
|
||||||
} else if (change.getNewIsAnnouncementGroup() == EnabledState.DISABLED) {
|
} else if (change.getNewIsAnnouncementGroup() == EnabledState.DISABLED) {
|
||||||
if (editorIsYou) {
|
if (editorIsYou) {
|
||||||
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_allow_all_members_to_send), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_allow_all_members_to_send), R.drawable.ic_update_group_role_16));
|
||||||
} else {
|
} else {
|
||||||
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_allow_all_members_to_send, editor), R.drawable.ic_update_group_role_16));
|
updates.add(updateDescription(R.string.MessageRecord_s_allow_all_members_to_send, change.getEditor(), R.drawable.ic_update_group_role_16));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -759,46 +768,130 @@ final class GroupsV2UpdateMessageProducer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DescribeMemberStrategy {
|
private static UpdateDescription updateDescription(@NonNull String string, @DrawableRes int iconResource) {
|
||||||
|
|
||||||
/**
|
|
||||||
* Map a ServiceId to a string that describes the group member.
|
|
||||||
*/
|
|
||||||
@NonNull
|
|
||||||
String describe(@NonNull ServiceId serviceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private interface StringFactory1Arg {
|
|
||||||
String create(String arg1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private interface StringFactory2Args {
|
|
||||||
String create(String arg1, String arg2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static UpdateDescription updateDescription(@NonNull String string,
|
|
||||||
@DrawableRes int iconResource)
|
|
||||||
{
|
|
||||||
return UpdateDescription.staticDescription(string, iconResource);
|
return UpdateDescription.staticDescription(string, iconResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
private UpdateDescription updateDescription(@NonNull ByteString uuid1Bytes,
|
private UpdateDescription updateDescription(@StringRes int stringRes,
|
||||||
@NonNull StringFactory1Arg stringFactory,
|
@NonNull ByteString uuid1Bytes,
|
||||||
@DrawableRes int iconResource)
|
@DrawableRes int iconResource)
|
||||||
{
|
{
|
||||||
ServiceId serviceId = ServiceId.fromByteStringOrUnknown(uuid1Bytes);
|
ServiceId serviceId = ServiceId.fromByteStringOrUnknown(uuid1Bytes);
|
||||||
|
RecipientId recipientId = RecipientId.from(serviceId, null);
|
||||||
|
|
||||||
return UpdateDescription.mentioning(Collections.singletonList(serviceId), () -> stringFactory.create(descriptionStrategy.describe(serviceId)), iconResource);
|
return UpdateDescription.mentioning(
|
||||||
|
Collections.singletonList(serviceId),
|
||||||
|
() -> {
|
||||||
|
List<RecipientId> recipientIdList = Collections.singletonList(recipientId);
|
||||||
|
String templateString = context.getString(stringRes, makePlaceholders(recipientIdList, null));
|
||||||
|
|
||||||
|
return makeRecipientsClickable(context, templateString, recipientIdList, recipientClickHandler);
|
||||||
|
},
|
||||||
|
iconResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
private UpdateDescription updateDescription(@NonNull ByteString uuid1Bytes,
|
private UpdateDescription updateDescription(@StringRes int stringRes,
|
||||||
|
@NonNull ByteString uuid1Bytes,
|
||||||
@NonNull ByteString uuid2Bytes,
|
@NonNull ByteString uuid2Bytes,
|
||||||
@NonNull StringFactory2Args stringFactory,
|
|
||||||
@DrawableRes int iconResource)
|
@DrawableRes int iconResource)
|
||||||
{
|
{
|
||||||
ServiceId sid1 = ServiceId.fromByteStringOrUnknown(uuid1Bytes);
|
ServiceId sid1 = ServiceId.fromByteStringOrUnknown(uuid1Bytes);
|
||||||
ServiceId sid2 = ServiceId.fromByteStringOrUnknown(uuid2Bytes);
|
ServiceId sid2 = ServiceId.fromByteStringOrUnknown(uuid2Bytes);
|
||||||
|
|
||||||
return UpdateDescription.mentioning(Arrays.asList(sid1, sid2), () -> stringFactory.create(descriptionStrategy.describe(sid1), descriptionStrategy.describe(sid2)), iconResource);
|
RecipientId recipientId1 = RecipientId.from(sid1, null);
|
||||||
|
RecipientId recipientId2 = RecipientId.from(sid2, null);
|
||||||
|
|
||||||
|
return UpdateDescription.mentioning(
|
||||||
|
Arrays.asList(sid1, sid2),
|
||||||
|
() -> {
|
||||||
|
List<RecipientId> recipientIdList = Arrays.asList(recipientId1, recipientId2);
|
||||||
|
String templateString = context.getString(stringRes, makePlaceholders(recipientIdList, null));
|
||||||
|
|
||||||
|
return makeRecipientsClickable(context, templateString, recipientIdList, recipientClickHandler);
|
||||||
|
},
|
||||||
|
iconResource
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private UpdateDescription updateDescription(@StringRes int stringRes,
|
||||||
|
@NonNull ByteString uuid1Bytes,
|
||||||
|
@NonNull Object formatArg,
|
||||||
|
@DrawableRes int iconResource)
|
||||||
|
{
|
||||||
|
ServiceId serviceId = ServiceId.fromByteStringOrUnknown(uuid1Bytes);
|
||||||
|
RecipientId recipientId = RecipientId.from(serviceId, null);
|
||||||
|
|
||||||
|
return UpdateDescription.mentioning(
|
||||||
|
Collections.singletonList(serviceId),
|
||||||
|
() -> {
|
||||||
|
List<RecipientId> recipientIdList = Collections.singletonList(recipientId);
|
||||||
|
String templateString = context.getString(stringRes, makePlaceholders(recipientIdList, Collections.singletonList(formatArg)));
|
||||||
|
|
||||||
|
return makeRecipientsClickable(context, templateString, recipientIdList, recipientClickHandler);
|
||||||
|
},
|
||||||
|
iconResource
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private UpdateDescription updateDescription(@PluralsRes int stringRes,
|
||||||
|
int quantity,
|
||||||
|
@NonNull ByteString uuid1Bytes,
|
||||||
|
@NonNull Object formatArg,
|
||||||
|
@DrawableRes int iconResource)
|
||||||
|
{
|
||||||
|
ServiceId serviceId = ServiceId.fromByteStringOrUnknown(uuid1Bytes);
|
||||||
|
RecipientId recipientId = RecipientId.from(serviceId, null);
|
||||||
|
|
||||||
|
return UpdateDescription.mentioning(
|
||||||
|
Collections.singletonList(serviceId),
|
||||||
|
() -> {
|
||||||
|
List<RecipientId> recipientIdList = Collections.singletonList(recipientId);
|
||||||
|
String templateString = context.getResources().getQuantityString(stringRes, quantity, makePlaceholders(recipientIdList, Collections.singletonList(formatArg)));
|
||||||
|
|
||||||
|
return makeRecipientsClickable(context, templateString, recipientIdList, recipientClickHandler);
|
||||||
|
},
|
||||||
|
iconResource
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NonNull Object[] makePlaceholders(@NonNull List<RecipientId> recipientIds, @Nullable List<Object> formatArgs) {
|
||||||
|
List<String> placeholders = recipientIds.stream().map(GroupsV2UpdateMessageProducer::makePlaceholder).collect(Collectors.toList());
|
||||||
|
List<Object> args = new LinkedList<>(placeholders);
|
||||||
|
|
||||||
|
if (formatArgs != null) {
|
||||||
|
args.addAll(formatArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return args.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NonNull Spannable makeRecipientsClickable(@NonNull Context context, @NonNull String template, @NonNull List<RecipientId> recipientIds, @Nullable Consumer<RecipientId> clickHandler) {
|
||||||
|
SpannableStringBuilder builder = new SpannableStringBuilder();
|
||||||
|
int startIndex = 0;
|
||||||
|
|
||||||
|
for (RecipientId recipientId : recipientIds) {
|
||||||
|
String placeholder = makePlaceholder(recipientId);
|
||||||
|
int placeHolderStart = template.indexOf(placeholder);
|
||||||
|
String beforeChunk = template.substring(startIndex, placeHolderStart);
|
||||||
|
|
||||||
|
builder.append(beforeChunk);
|
||||||
|
builder.append(SpanUtil.clickable(Recipient.resolved(recipientId).getDisplayName(context), 0, v -> {
|
||||||
|
if (!recipientId.isUnknown() && clickHandler != null) {
|
||||||
|
clickHandler.accept(recipientId);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
startIndex = placeHolderStart + placeholder.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startIndex < template.length()) {
|
||||||
|
builder.append(template.substring(startIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NonNull String makePlaceholder(@NonNull RecipientId recipientId) {
|
||||||
|
return "{{SPAN_PLACEHOLDER_" + recipientId + "}}";
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,9 +9,11 @@ import androidx.annotation.StringRes;
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.thoughtcrime.securesms.util.ExpirationUtil;
|
import org.thoughtcrime.securesms.util.ExpirationUtil;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In memory message record for use in temporary conversation messages.
|
* In memory message record for use in temporary conversation messages.
|
||||||
|
@ -90,7 +92,7 @@ public class InMemoryMessageRecord extends MessageRecord {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable UpdateDescription getUpdateDisplayBody(@NonNull Context context) {
|
public @Nullable UpdateDescription getUpdateDisplayBody(@NonNull Context context, @Nullable Consumer<RecipientId> recipientClickHandler) {
|
||||||
return UpdateDescription.staticDescription(context.getString(isGroup ? R.string.ConversationUpdateItem_no_contacts_in_this_group_review_requests_carefully
|
return UpdateDescription.staticDescription(context.getString(isGroup ? R.string.ConversationUpdateItem_no_contacts_in_this_group_review_requests_carefully
|
||||||
: R.string.ConversationUpdateItem_no_groups_in_common_review_requests_carefully),
|
: R.string.ConversationUpdateItem_no_groups_in_common_review_requests_carefully),
|
||||||
R.drawable.ic_update_info_16);
|
R.drawable.ic_update_info_16);
|
||||||
|
@ -127,7 +129,7 @@ public class InMemoryMessageRecord extends MessageRecord {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable UpdateDescription getUpdateDisplayBody(@NonNull Context context) {
|
public @Nullable UpdateDescription getUpdateDisplayBody(@NonNull Context context, @Nullable Consumer<RecipientId> recipientClickHandler) {
|
||||||
String update = context.getString(R.string.ConversationUpdateItem_the_disappearing_message_time_will_be_set_to_s_when_you_message_them,
|
String update = context.getString(R.string.ConversationUpdateItem_the_disappearing_message_time_will_be_set_to_s_when_you_message_them,
|
||||||
ExpirationUtil.getExpirationDisplayValue(context, SignalStore.settings().getUniversalExpireTimer()));
|
ExpirationUtil.getExpirationDisplayValue(context, SignalStore.settings().getUniversalExpireTimer()));
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import android.text.Spannable;
|
||||||
import android.text.SpannableString;
|
import android.text.SpannableString;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
|
|
||||||
import androidx.annotation.AnyThread;
|
|
||||||
import androidx.annotation.ColorInt;
|
import androidx.annotation.ColorInt;
|
||||||
import androidx.annotation.MainThread;
|
import androidx.annotation.MainThread;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
@ -43,7 +42,7 @@ public final class LiveUpdateMessage {
|
||||||
boolean adjustPosition)
|
boolean adjustPosition)
|
||||||
{
|
{
|
||||||
if (updateDescription.isStringStatic()) {
|
if (updateDescription.isStringStatic()) {
|
||||||
return LiveDataUtil.just(toSpannable(context, updateDescription, updateDescription.getStaticString(), defaultTint, adjustPosition));
|
return LiveDataUtil.just(toSpannable(context, updateDescription, updateDescription.getStaticSpannable(), defaultTint, adjustPosition));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<LiveData<Recipient>> allMentionedRecipients = Stream.of(updateDescription.getMentioned())
|
List<LiveData<Recipient>> allMentionedRecipients = Stream.of(updateDescription.getMentioned())
|
||||||
|
@ -53,7 +52,7 @@ public final class LiveUpdateMessage {
|
||||||
LiveData<?> mentionedRecipientChangeStream = allMentionedRecipients.isEmpty() ? LiveDataUtil.just(new Object())
|
LiveData<?> mentionedRecipientChangeStream = allMentionedRecipients.isEmpty() ? LiveDataUtil.just(new Object())
|
||||||
: LiveDataUtil.merge(allMentionedRecipients);
|
: LiveDataUtil.merge(allMentionedRecipients);
|
||||||
|
|
||||||
return Transformations.map(mentionedRecipientChangeStream, event -> toSpannable(context, updateDescription, updateDescription.getString(), defaultTint, adjustPosition));
|
return Transformations.map(mentionedRecipientChangeStream, event -> toSpannable(context, updateDescription, updateDescription.getSpannable(), defaultTint, adjustPosition));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,7 +65,7 @@ public final class LiveUpdateMessage {
|
||||||
return Transformations.map(Recipient.live(recipientId).getLiveDataResolved(), createStringInBackground::apply);
|
return Transformations.map(Recipient.live(recipientId).getLiveDataResolved(), createStringInBackground::apply);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @NonNull SpannableString toSpannable(@NonNull Context context, @NonNull UpdateDescription updateDescription, @NonNull String string, @ColorInt int defaultTint, boolean adjustPosition) {
|
private static @NonNull SpannableString toSpannable(@NonNull Context context, @NonNull UpdateDescription updateDescription, @NonNull Spannable string, @ColorInt int defaultTint, boolean adjustPosition) {
|
||||||
boolean isDarkTheme = ThemeUtil.isDarkTheme(context);
|
boolean isDarkTheme = ThemeUtil.isDarkTheme(context);
|
||||||
int drawableResource = updateDescription.getIconResource();
|
int drawableResource = updateDescription.getIconResource();
|
||||||
int tint = isDarkTheme ? updateDescription.getDarkTint() : updateDescription.getLightTint();
|
int tint = isDarkTheme ? updateDescription.getDarkTint() : updateDescription.getLightTint();
|
||||||
|
|
|
@ -71,6 +71,7 @@ import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -143,21 +144,27 @@ public abstract class MessageRecord extends DisplayRecord {
|
||||||
return MmsSmsColumns.Types.isLegacyType(type);
|
return MmsSmsColumns.Types.isLegacyType(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public SpannableString getDisplayBody(@NonNull Context context) {
|
public SpannableString getDisplayBody(@NonNull Context context) {
|
||||||
UpdateDescription updateDisplayBody = getUpdateDisplayBody(context);
|
return getDisplayBody(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
public SpannableString getDisplayBody(@NonNull Context context, @Nullable Consumer<RecipientId> recipientClickHandler) {
|
||||||
|
UpdateDescription updateDisplayBody = getUpdateDisplayBody(context, recipientClickHandler);
|
||||||
|
|
||||||
if (updateDisplayBody != null) {
|
if (updateDisplayBody != null) {
|
||||||
return new SpannableString(updateDisplayBody.getString());
|
return new SpannableString(updateDisplayBody.getSpannable());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SpannableString(getBody());
|
return new SpannableString(getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable UpdateDescription getUpdateDisplayBody(@NonNull Context context) {
|
public @Nullable UpdateDescription getUpdateDisplayBody(@NonNull Context context, @Nullable Consumer<RecipientId> recipientClickHandler) {
|
||||||
if (isGroupUpdate() && isGroupV2()) {
|
if (isGroupUpdate() && isGroupV2()) {
|
||||||
return getGv2ChangeDescription(context, getBody());
|
return getGv2ChangeDescription(context, getBody(), recipientClickHandler);
|
||||||
} else if (isGroupUpdate() && isOutgoing()) {
|
} else if (isGroupUpdate() && isOutgoing()) {
|
||||||
return staticUpdateDescription(context.getString(R.string.MessageRecord_you_updated_group), R.drawable.ic_update_group_16);
|
return staticUpdateDescription(context.getString(R.string.MessageRecord_you_updated_group), R.drawable.ic_update_group_16);
|
||||||
} else if (isGroupUpdate()) {
|
} else if (isGroupUpdate()) {
|
||||||
|
@ -222,7 +229,7 @@ public abstract class MessageRecord extends DisplayRecord {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDisplayBodyEmpty(@NonNull Context context) {
|
public boolean isDisplayBodyEmpty(@NonNull Context context) {
|
||||||
return getUpdateDisplayBody(context) == null && getBody().isEmpty();
|
return getUpdateDisplayBody(context, null) == null && getBody().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSelfCreatedGroup() {
|
public boolean isSelfCreatedGroup() {
|
||||||
|
@ -259,12 +266,11 @@ public abstract class MessageRecord extends DisplayRecord {
|
||||||
change.getEditor().equals(UuidUtil.toByteString(SignalStore.account().requireAci().uuid()));
|
change.getEditor().equals(UuidUtil.toByteString(SignalStore.account().requireAci().uuid()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull UpdateDescription getGv2ChangeDescription(@NonNull Context context, @NonNull String body) {
|
public static @NonNull UpdateDescription getGv2ChangeDescription(@NonNull Context context, @NonNull String body, @Nullable Consumer<RecipientId> recipientClickHandler) {
|
||||||
try {
|
try {
|
||||||
ShortStringDescriptionStrategy descriptionStrategy = new ShortStringDescriptionStrategy(context);
|
|
||||||
byte[] decoded = Base64.decode(body);
|
byte[] decoded = Base64.decode(body);
|
||||||
DecryptedGroupV2Context decryptedGroupV2Context = DecryptedGroupV2Context.parseFrom(decoded);
|
DecryptedGroupV2Context decryptedGroupV2Context = DecryptedGroupV2Context.parseFrom(decoded);
|
||||||
GroupsV2UpdateMessageProducer updateMessageProducer = new GroupsV2UpdateMessageProducer(context, descriptionStrategy, SignalStore.account().requireAci().uuid());
|
GroupsV2UpdateMessageProducer updateMessageProducer = new GroupsV2UpdateMessageProducer(context, SignalStore.account().requireAci().uuid(), recipientClickHandler);
|
||||||
|
|
||||||
if (decryptedGroupV2Context.hasChange() && (decryptedGroupV2Context.getGroupState().getRevision() != 0 || decryptedGroupV2Context.hasPreviousGroupState())) {
|
if (decryptedGroupV2Context.hasChange() && (decryptedGroupV2Context.getGroupState().getRevision() != 0 || decryptedGroupV2Context.hasPreviousGroupState())) {
|
||||||
return UpdateDescription.concatWithNewLines(updateMessageProducer.describeChanges(decryptedGroupV2Context.getPreviousGroupState(), decryptedGroupV2Context.getChange()));
|
return UpdateDescription.concatWithNewLines(updateMessageProducer.describeChanges(decryptedGroupV2Context.getPreviousGroupState(), decryptedGroupV2Context.getChange()));
|
||||||
|
@ -318,7 +324,7 @@ public abstract class MessageRecord extends DisplayRecord {
|
||||||
@DrawableRes int iconResource)
|
@DrawableRes int iconResource)
|
||||||
{
|
{
|
||||||
return UpdateDescription.mentioning(Collections.singletonList(recipient.getServiceId().orElse(ServiceId.UNKNOWN)),
|
return UpdateDescription.mentioning(Collections.singletonList(recipient.getServiceId().orElse(ServiceId.UNKNOWN)),
|
||||||
() -> stringGenerator.apply(recipient.resolve()),
|
() -> new SpannableString(stringGenerator.apply(recipient.resolve())),
|
||||||
iconResource);
|
iconResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,7 +397,7 @@ public abstract class MessageRecord extends DisplayRecord {
|
||||||
.map(ServiceId::from)
|
.map(ServiceId::from)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
UpdateDescription.StringFactory stringFactory = new GroupCallUpdateMessageFactory(context, joinedMembers, withTime, groupCallUpdateDetails);
|
UpdateDescription.SpannableFactory stringFactory = new GroupCallUpdateMessageFactory(context, joinedMembers, withTime, groupCallUpdateDetails);
|
||||||
|
|
||||||
return UpdateDescription.mentioning(joinedMembers, stringFactory, R.drawable.ic_video_16);
|
return UpdateDescription.mentioning(joinedMembers, stringFactory, R.drawable.ic_video_16);
|
||||||
}
|
}
|
||||||
|
@ -460,26 +466,6 @@ public abstract class MessageRecord extends DisplayRecord {
|
||||||
throw new AssertionError("Attempting to modify a message with no change");
|
throw new AssertionError("Attempting to modify a message with no change");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Describes a UUID by it's corresponding recipient's {@link Recipient#getDisplayName(Context)}.
|
|
||||||
*/
|
|
||||||
private static class ShortStringDescriptionStrategy implements GroupsV2UpdateMessageProducer.DescribeMemberStrategy {
|
|
||||||
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
ShortStringDescriptionStrategy(@NonNull Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String describe(@NonNull ServiceId serviceId) {
|
|
||||||
if (serviceId.isUnknown()) {
|
|
||||||
return context.getString(R.string.MessageRecord_unknown);
|
|
||||||
}
|
|
||||||
return Recipient.resolved(RecipientId.from(serviceId, null)).getDisplayName(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getId() {
|
public long getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
package org.thoughtcrime.securesms.database.model;
|
package org.thoughtcrime.securesms.database.model;
|
||||||
|
|
||||||
|
import android.text.Spannable;
|
||||||
|
import android.text.SpannableString;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
|
||||||
import androidx.annotation.AnyThread;
|
import androidx.annotation.AnyThread;
|
||||||
import androidx.annotation.ColorInt;
|
import androidx.annotation.ColorInt;
|
||||||
import androidx.annotation.DrawableRes;
|
import androidx.annotation.DrawableRes;
|
||||||
|
@ -7,7 +11,6 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.WorkerThread;
|
import androidx.annotation.WorkerThread;
|
||||||
|
|
||||||
import org.signal.core.util.ThreadUtil;
|
|
||||||
import org.whispersystems.signalservice.api.push.ServiceId;
|
import org.whispersystems.signalservice.api.push.ServiceId;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -21,20 +24,20 @@ import java.util.Set;
|
||||||
*/
|
*/
|
||||||
public final class UpdateDescription {
|
public final class UpdateDescription {
|
||||||
|
|
||||||
public interface StringFactory {
|
public interface SpannableFactory {
|
||||||
String create();
|
Spannable create();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Collection<ServiceId> mentioned;
|
private final Collection<ServiceId> mentioned;
|
||||||
private final StringFactory stringFactory;
|
private final SpannableFactory stringFactory;
|
||||||
private final String staticString;
|
private final Spannable staticString;
|
||||||
private final int lightIconResource;
|
private final int lightIconResource;
|
||||||
private final int lightTint;
|
private final int lightTint;
|
||||||
private final int darkTint;
|
private final int darkTint;
|
||||||
|
|
||||||
private UpdateDescription(@NonNull Collection<ServiceId> mentioned,
|
private UpdateDescription(@NonNull Collection<ServiceId> mentioned,
|
||||||
@Nullable StringFactory stringFactory,
|
@Nullable SpannableFactory stringFactory,
|
||||||
@Nullable String staticString,
|
@Nullable Spannable staticString,
|
||||||
@DrawableRes int iconResource,
|
@DrawableRes int iconResource,
|
||||||
@ColorInt int lightTint,
|
@ColorInt int lightTint,
|
||||||
@ColorInt int darkTint)
|
@ColorInt int darkTint)
|
||||||
|
@ -58,7 +61,7 @@ public final class UpdateDescription {
|
||||||
* @param stringFactory The background method for generating the string.
|
* @param stringFactory The background method for generating the string.
|
||||||
*/
|
*/
|
||||||
public static UpdateDescription mentioning(@NonNull Collection<ServiceId> mentioned,
|
public static UpdateDescription mentioning(@NonNull Collection<ServiceId> mentioned,
|
||||||
@NonNull StringFactory stringFactory,
|
@NonNull SpannableFactory stringFactory,
|
||||||
@DrawableRes int iconResource)
|
@DrawableRes int iconResource)
|
||||||
{
|
{
|
||||||
return new UpdateDescription(ServiceId.filterKnown(mentioned),
|
return new UpdateDescription(ServiceId.filterKnown(mentioned),
|
||||||
|
@ -74,6 +77,15 @@ public final class UpdateDescription {
|
||||||
*/
|
*/
|
||||||
public static UpdateDescription staticDescription(@NonNull String staticString,
|
public static UpdateDescription staticDescription(@NonNull String staticString,
|
||||||
@DrawableRes int iconResource)
|
@DrawableRes int iconResource)
|
||||||
|
{
|
||||||
|
return new UpdateDescription(Collections.emptyList(), null, new SpannableString(staticString), iconResource, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an update description that's string value is fixed.
|
||||||
|
*/
|
||||||
|
public static UpdateDescription staticDescription(@NonNull Spannable staticString,
|
||||||
|
@DrawableRes int iconResource)
|
||||||
{
|
{
|
||||||
return new UpdateDescription(Collections.emptyList(), null, staticString, iconResource, 0, 0);
|
return new UpdateDescription(Collections.emptyList(), null, staticString, iconResource, 0, 0);
|
||||||
}
|
}
|
||||||
|
@ -86,7 +98,7 @@ public final class UpdateDescription {
|
||||||
@ColorInt int lightTint,
|
@ColorInt int lightTint,
|
||||||
@ColorInt int darkTint)
|
@ColorInt int darkTint)
|
||||||
{
|
{
|
||||||
return new UpdateDescription(Collections.emptyList(), null, staticString, iconResource, lightTint, darkTint);
|
return new UpdateDescription(Collections.emptyList(), null, new SpannableString(staticString), iconResource, lightTint, darkTint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isStringStatic() {
|
public boolean isStringStatic() {
|
||||||
|
@ -94,7 +106,7 @@ public final class UpdateDescription {
|
||||||
}
|
}
|
||||||
|
|
||||||
@AnyThread
|
@AnyThread
|
||||||
public @NonNull String getStaticString() {
|
public @NonNull Spannable getStaticSpannable() {
|
||||||
if (staticString == null) {
|
if (staticString == null) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
@ -103,7 +115,7 @@ public final class UpdateDescription {
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
public @NonNull String getString() {
|
public @NonNull Spannable getSpannable() {
|
||||||
if (staticString != null) {
|
if (staticString != null) {
|
||||||
return staticString;
|
return staticString;
|
||||||
}
|
}
|
||||||
|
@ -166,26 +178,26 @@ public final class UpdateDescription {
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
private static String concatLines(@NonNull List<UpdateDescription> updateDescriptions) {
|
private static Spannable concatLines(@NonNull List<UpdateDescription> updateDescriptions) {
|
||||||
StringBuilder result = new StringBuilder();
|
SpannableStringBuilder result = new SpannableStringBuilder();
|
||||||
|
|
||||||
for (int i = 0; i < updateDescriptions.size(); i++) {
|
for (int i = 0; i < updateDescriptions.size(); i++) {
|
||||||
if (i > 0) result.append('\n');
|
if (i > 0) result.append('\n');
|
||||||
result.append(updateDescriptions.get(i).getString());
|
result.append(updateDescriptions.get(i).getSpannable());
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.toString();
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@AnyThread
|
@AnyThread
|
||||||
private static String concatStaticLines(@NonNull List<UpdateDescription> updateDescriptions) {
|
private static Spannable concatStaticLines(@NonNull List<UpdateDescription> updateDescriptions) {
|
||||||
StringBuilder result = new StringBuilder();
|
SpannableStringBuilder result = new SpannableStringBuilder();
|
||||||
|
|
||||||
for (int i = 0; i < updateDescriptions.size(); i++) {
|
for (int i = 0; i < updateDescriptions.size(); i++) {
|
||||||
if (i > 0) result.append('\n');
|
if (i > 0) result.append('\n');
|
||||||
result.append(updateDescriptions.get(i).getStaticString());
|
result.append(updateDescriptions.get(i).getStaticSpannable());
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.toString();
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -185,7 +185,7 @@ class MessageNotification(threadRecipient: Recipient, record: MessageRecord) : N
|
||||||
} else if (record.isMms && !record.isMmsNotification && (record as MmsMessageRecord).slideDeck.slides.isNotEmpty()) {
|
} else if (record.isMms && !record.isMmsNotification && (record as MmsMessageRecord).slideDeck.slides.isNotEmpty()) {
|
||||||
ThreadBodyUtil.getFormattedBodyFor(context, record)
|
ThreadBodyUtil.getFormattedBodyFor(context, record)
|
||||||
} else if (record.isGroupCall) {
|
} else if (record.isGroupCall) {
|
||||||
MessageRecord.getGroupCallUpdateDescription(context, record.body, false).string
|
MessageRecord.getGroupCallUpdateDescription(context, record.body, false).spannable
|
||||||
} else {
|
} else {
|
||||||
MentionUtil.updateBodyWithDisplayNames(context, record)
|
MentionUtil.updateBodyWithDisplayNames(context, record)
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,10 @@ public class RecipientId implements Parcelable, Comparable<RecipientId>, Databas
|
||||||
@AnyThread
|
@AnyThread
|
||||||
@SuppressLint("WrongThread")
|
@SuppressLint("WrongThread")
|
||||||
private static @NonNull RecipientId from(@Nullable ServiceId serviceId, @Nullable String e164, boolean highTrust) {
|
private static @NonNull RecipientId from(@Nullable ServiceId serviceId, @Nullable String e164, boolean highTrust) {
|
||||||
|
if (serviceId != null && serviceId.isUnknown()) {
|
||||||
|
return RecipientId.UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
RecipientId recipientId = RecipientIdCache.INSTANCE.get(serviceId, e164);
|
RecipientId recipientId = RecipientIdCache.INSTANCE.get(serviceId, e164);
|
||||||
|
|
||||||
if (recipientId == null) {
|
if (recipientId == null) {
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
package org.thoughtcrime.securesms.database.model;
|
package org.thoughtcrime.securesms.database.model;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
|
import android.text.Spannable;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
import com.annimon.stream.Stream;
|
import com.annimon.stream.Stream;
|
||||||
import com.google.common.collect.ImmutableMap;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
|
@ -19,18 +19,18 @@ import org.mockito.junit.MockitoJUnit;
|
||||||
import org.mockito.junit.MockitoRule;
|
import org.mockito.junit.MockitoRule;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
import org.signal.core.util.ThreadUtil;
|
|
||||||
import org.signal.storageservice.protos.groups.AccessControl;
|
import org.signal.storageservice.protos.groups.AccessControl;
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedMember;
|
import org.signal.storageservice.protos.groups.local.DecryptedMember;
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedPendingMember;
|
import org.signal.storageservice.protos.groups.local.DecryptedPendingMember;
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||||
import org.whispersystems.signalservice.api.push.ServiceId;
|
import org.whispersystems.signalservice.api.push.ServiceId;
|
||||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@ -38,8 +38,11 @@ import static org.hamcrest.CoreMatchers.is;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
import static org.thoughtcrime.securesms.groups.v2.ChangeBuilder.changeBy;
|
import static org.thoughtcrime.securesms.groups.v2.ChangeBuilder.changeBy;
|
||||||
import static org.thoughtcrime.securesms.groups.v2.ChangeBuilder.changeByUnknown;
|
import static org.thoughtcrime.securesms.groups.v2.ChangeBuilder.changeByUnknown;
|
||||||
import static org.signal.core.util.StringUtil.isolateBidi;
|
import static org.signal.core.util.StringUtil.isolateBidi;
|
||||||
|
@ -60,18 +63,38 @@ public final class GroupsV2UpdateMessageProducerTest {
|
||||||
public MockitoRule rule = MockitoJUnit.rule();
|
public MockitoRule rule = MockitoJUnit.rule();
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
public MockedStatic<ThreadUtil> threadUtilMockedStatic;
|
public MockedStatic<Recipient> recipientMockedStatic;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
public MockedStatic<RecipientId> recipientIdMockedStatic;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
you = UUID.randomUUID();
|
you = UUID.randomUUID();
|
||||||
alice = UUID.randomUUID();
|
alice = UUID.randomUUID();
|
||||||
bob = UUID.randomUUID();
|
bob = UUID.randomUUID();
|
||||||
GroupsV2UpdateMessageProducer.DescribeMemberStrategy describeMember = createDescriber(ImmutableMap.of(alice, "Alice", bob, "Bob"));
|
|
||||||
producer = new GroupsV2UpdateMessageProducer(ApplicationProvider.getApplicationContext(), describeMember, you);
|
|
||||||
|
|
||||||
threadUtilMockedStatic.when(ThreadUtil::assertMainThread).thenCallRealMethod();
|
recipientIdMockedStatic.when(() -> RecipientId.from(anyLong())).thenCallRealMethod();
|
||||||
threadUtilMockedStatic.when(ThreadUtil::assertNotMainThread).thenCallRealMethod();
|
|
||||||
|
RecipientId aliceId = RecipientId.from(1);
|
||||||
|
RecipientId bobId = RecipientId.from(2);
|
||||||
|
|
||||||
|
Recipient aliceRecipient = recipientWithName(aliceId, "Alice");
|
||||||
|
Recipient bobRecipient = recipientWithName(bobId, "Bob");
|
||||||
|
|
||||||
|
producer = new GroupsV2UpdateMessageProducer(ApplicationProvider.getApplicationContext(), you, null);
|
||||||
|
|
||||||
|
recipientIdMockedStatic.when(() -> RecipientId.from(ServiceId.from(alice), null)).thenReturn(aliceId);
|
||||||
|
recipientIdMockedStatic.when(() -> RecipientId.from(ServiceId.from(bob), null)).thenReturn(bobId);
|
||||||
|
recipientMockedStatic.when(() -> Recipient.resolved(aliceId)).thenReturn(aliceRecipient);
|
||||||
|
recipientMockedStatic.when(() -> Recipient.resolved(bobId)).thenReturn(bobRecipient);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Recipient recipientWithName(RecipientId id, String name) {
|
||||||
|
Recipient recipient = mock(Recipient.class);
|
||||||
|
when(recipient.getId()).thenReturn(id);
|
||||||
|
when(recipient.getDisplayName(any())).thenReturn(name);
|
||||||
|
return recipient;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1335,9 +1358,9 @@ public final class GroupsV2UpdateMessageProducerTest {
|
||||||
private @NonNull List<String> describeChange(@Nullable DecryptedGroup previousGroupState,
|
private @NonNull List<String> describeChange(@Nullable DecryptedGroup previousGroupState,
|
||||||
@NonNull DecryptedGroupChange change)
|
@NonNull DecryptedGroupChange change)
|
||||||
{
|
{
|
||||||
threadUtilMockedStatic.when(ThreadUtil::isMainThread).thenReturn(false);
|
|
||||||
return Stream.of(producer.describeChanges(previousGroupState, change))
|
return Stream.of(producer.describeChanges(previousGroupState, change))
|
||||||
.map(UpdateDescription::getString)
|
.map(UpdateDescription::getSpannable)
|
||||||
|
.map(Spannable::toString)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1346,8 +1369,7 @@ public final class GroupsV2UpdateMessageProducerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private @NonNull String describeNewGroup(@NonNull DecryptedGroup group, @NonNull DecryptedGroupChange groupChange) {
|
private @NonNull String describeNewGroup(@NonNull DecryptedGroup group, @NonNull DecryptedGroupChange groupChange) {
|
||||||
threadUtilMockedStatic.when(ThreadUtil::isMainThread).thenReturn(false);
|
return producer.describeNewGroup(group, groupChange).getSpannable().toString();
|
||||||
return producer.describeNewGroup(group, groupChange).getString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GroupStateBuilder newGroupBy(UUID foundingMember, int revision) {
|
private static GroupStateBuilder newGroupBy(UUID foundingMember, int revision) {
|
||||||
|
@ -1399,12 +1421,4 @@ public final class GroupsV2UpdateMessageProducerTest {
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @NonNull GroupsV2UpdateMessageProducer.DescribeMemberStrategy createDescriber(@NonNull Map<UUID, String> map) {
|
|
||||||
return serviceId -> {
|
|
||||||
String name = map.get(serviceId.uuid());
|
|
||||||
assertNotNull(name);
|
|
||||||
return name;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
package org.thoughtcrime.securesms.database.model;
|
package org.thoughtcrime.securesms.database.model;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.text.SpannableString;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
import org.whispersystems.signalservice.api.push.ServiceId;
|
import org.whispersystems.signalservice.api.push.ServiceId;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -13,13 +19,15 @@ import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertSame;
|
import static org.junit.Assert.assertSame;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(manifest = Config.NONE, application = Application.class)
|
||||||
public final class UpdateDescriptionTest {
|
public final class UpdateDescriptionTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void staticDescription_byGetStaticString() {
|
public void staticDescription_byGetStaticString() {
|
||||||
UpdateDescription description = UpdateDescription.staticDescription("update", 0);
|
UpdateDescription description = UpdateDescription.staticDescription("update", 0);
|
||||||
|
|
||||||
assertEquals("update", description.getStaticString());
|
assertEquals("update", description.getStaticSpannable().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -33,30 +41,30 @@ public final class UpdateDescriptionTest {
|
||||||
public void staticDescription_byString() {
|
public void staticDescription_byString() {
|
||||||
UpdateDescription description = UpdateDescription.staticDescription("update", 0);
|
UpdateDescription description = UpdateDescription.staticDescription("update", 0);
|
||||||
|
|
||||||
assertEquals("update", description.getString());
|
assertEquals("update", description.getSpannable().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = UnsupportedOperationException.class)
|
@Test(expected = UnsupportedOperationException.class)
|
||||||
public void stringFactory_cannot_call_static_string() {
|
public void stringFactory_cannot_call_static_string() {
|
||||||
UpdateDescription description = UpdateDescription.mentioning(Collections.singletonList(ServiceId.from(UUID.randomUUID())), () -> "update", 0);
|
UpdateDescription description = UpdateDescription.mentioning(Collections.singletonList(ServiceId.from(UUID.randomUUID())), () -> new SpannableString("update"), 0);
|
||||||
|
|
||||||
description.getStaticString();
|
description.getStaticSpannable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void stringFactory_not_evaluated_until_getString() {
|
public void stringFactory_not_evaluated_until_getString() {
|
||||||
AtomicInteger factoryCalls = new AtomicInteger();
|
AtomicInteger factoryCalls = new AtomicInteger();
|
||||||
|
|
||||||
UpdateDescription.StringFactory stringFactory = () -> {
|
UpdateDescription.SpannableFactory stringFactory = () -> {
|
||||||
factoryCalls.incrementAndGet();
|
factoryCalls.incrementAndGet();
|
||||||
return "update";
|
return new SpannableString("update");
|
||||||
};
|
};
|
||||||
|
|
||||||
UpdateDescription description = UpdateDescription.mentioning(Collections.singletonList(ServiceId.from(UUID.randomUUID())), stringFactory, 0);
|
UpdateDescription description = UpdateDescription.mentioning(Collections.singletonList(ServiceId.from(UUID.randomUUID())), stringFactory, 0);
|
||||||
|
|
||||||
assertEquals(0, factoryCalls.get());
|
assertEquals(0, factoryCalls.get());
|
||||||
|
|
||||||
String string = description.getString();
|
String string = description.getSpannable().toString();
|
||||||
|
|
||||||
assertEquals("update", string);
|
assertEquals("update", string);
|
||||||
assertEquals(1, factoryCalls.get());
|
assertEquals(1, factoryCalls.get());
|
||||||
|
@ -65,12 +73,12 @@ public final class UpdateDescriptionTest {
|
||||||
@Test
|
@Test
|
||||||
public void stringFactory_reevaluated_on_every_call() {
|
public void stringFactory_reevaluated_on_every_call() {
|
||||||
AtomicInteger factoryCalls = new AtomicInteger();
|
AtomicInteger factoryCalls = new AtomicInteger();
|
||||||
UpdateDescription.StringFactory stringFactory = () -> "call" + factoryCalls.incrementAndGet();
|
UpdateDescription.SpannableFactory stringFactory = () -> new SpannableString( "call" + factoryCalls.incrementAndGet());
|
||||||
UpdateDescription description = UpdateDescription.mentioning(Collections.singletonList(ServiceId.from(UUID.randomUUID())), stringFactory, 0);
|
UpdateDescription description = UpdateDescription.mentioning(Collections.singletonList(ServiceId.from(UUID.randomUUID())), stringFactory, 0);
|
||||||
|
|
||||||
assertEquals("call1", description.getString());
|
assertEquals("call1", description.getSpannable().toString());
|
||||||
assertEquals("call2", description.getString());
|
assertEquals("call2", description.getSpannable().toString());
|
||||||
assertEquals("call3", description.getString());
|
assertEquals("call3", description.getSpannable().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -81,8 +89,8 @@ public final class UpdateDescriptionTest {
|
||||||
UpdateDescription description = UpdateDescription.concatWithNewLines(Arrays.asList(description1, description2));
|
UpdateDescription description = UpdateDescription.concatWithNewLines(Arrays.asList(description1, description2));
|
||||||
|
|
||||||
assertTrue(description.isStringStatic());
|
assertTrue(description.isStringStatic());
|
||||||
assertEquals("update1\nupdate2", description.getStaticString());
|
assertEquals("update1\nupdate2", description.getStaticSpannable().toString());
|
||||||
assertEquals("update1\nupdate2", description.getString());
|
assertEquals("update1\nupdate2", description.getSpannable().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -98,8 +106,8 @@ public final class UpdateDescriptionTest {
|
||||||
public void concat_dynamic_lines() {
|
public void concat_dynamic_lines() {
|
||||||
AtomicInteger factoryCalls1 = new AtomicInteger();
|
AtomicInteger factoryCalls1 = new AtomicInteger();
|
||||||
AtomicInteger factoryCalls2 = new AtomicInteger();
|
AtomicInteger factoryCalls2 = new AtomicInteger();
|
||||||
UpdateDescription.StringFactory stringFactory1 = () -> "update." + factoryCalls1.incrementAndGet();
|
UpdateDescription.SpannableFactory stringFactory1 = () -> new SpannableString("update." + factoryCalls1.incrementAndGet());
|
||||||
UpdateDescription.StringFactory stringFactory2 = () -> "update." + factoryCalls2.incrementAndGet();
|
UpdateDescription.SpannableFactory stringFactory2 = () -> new SpannableString("update." + factoryCalls2.incrementAndGet());
|
||||||
UpdateDescription description1 = UpdateDescription.mentioning(Collections.singletonList(ServiceId.from(UUID.randomUUID())), stringFactory1, 0);
|
UpdateDescription description1 = UpdateDescription.mentioning(Collections.singletonList(ServiceId.from(UUID.randomUUID())), stringFactory1, 0);
|
||||||
UpdateDescription description2 = UpdateDescription.mentioning(Collections.singletonList(ServiceId.from(UUID.randomUUID())), stringFactory2, 0);
|
UpdateDescription description2 = UpdateDescription.mentioning(Collections.singletonList(ServiceId.from(UUID.randomUUID())), stringFactory2, 0);
|
||||||
|
|
||||||
|
@ -110,17 +118,17 @@ public final class UpdateDescriptionTest {
|
||||||
|
|
||||||
assertFalse(description.isStringStatic());
|
assertFalse(description.isStringStatic());
|
||||||
|
|
||||||
assertEquals("update.11\nupdate.21", description.getString());
|
assertEquals("update.11\nupdate.21", description.getSpannable().toString());
|
||||||
assertEquals("update.12\nupdate.22", description.getString());
|
assertEquals("update.12\nupdate.22", description.getSpannable().toString());
|
||||||
assertEquals("update.13\nupdate.23", description.getString());
|
assertEquals("update.13\nupdate.23", description.getSpannable().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void concat_dynamic_lines_and_static_lines() {
|
public void concat_dynamic_lines_and_static_lines() {
|
||||||
AtomicInteger factoryCalls1 = new AtomicInteger();
|
AtomicInteger factoryCalls1 = new AtomicInteger();
|
||||||
AtomicInteger factoryCalls2 = new AtomicInteger();
|
AtomicInteger factoryCalls2 = new AtomicInteger();
|
||||||
UpdateDescription.StringFactory stringFactory1 = () -> "update." + factoryCalls1.incrementAndGet();
|
UpdateDescription.SpannableFactory stringFactory1 = () -> new SpannableString("update." + factoryCalls1.incrementAndGet());
|
||||||
UpdateDescription.StringFactory stringFactory2 = () -> "update." + factoryCalls2.incrementAndGet();
|
UpdateDescription.SpannableFactory stringFactory2 = () -> new SpannableString("update." + factoryCalls2.incrementAndGet());
|
||||||
UpdateDescription description1 = UpdateDescription.mentioning(Collections.singletonList(ServiceId.from(UUID.randomUUID())), stringFactory1, 0);
|
UpdateDescription description1 = UpdateDescription.mentioning(Collections.singletonList(ServiceId.from(UUID.randomUUID())), stringFactory1, 0);
|
||||||
UpdateDescription description2 = UpdateDescription.staticDescription("static", 0);
|
UpdateDescription description2 = UpdateDescription.staticDescription("static", 0);
|
||||||
UpdateDescription description3 = UpdateDescription.mentioning(Collections.singletonList(ServiceId.from(UUID.randomUUID())), stringFactory2, 0);
|
UpdateDescription description3 = UpdateDescription.mentioning(Collections.singletonList(ServiceId.from(UUID.randomUUID())), stringFactory2, 0);
|
||||||
|
@ -132,8 +140,8 @@ public final class UpdateDescriptionTest {
|
||||||
|
|
||||||
assertFalse(description.isStringStatic());
|
assertFalse(description.isStringStatic());
|
||||||
|
|
||||||
assertEquals("update.101\nstatic\nupdate.201", description.getString());
|
assertEquals("update.101\nstatic\nupdate.201", description.getSpannable().toString());
|
||||||
assertEquals("update.102\nstatic\nupdate.202", description.getString());
|
assertEquals("update.102\nstatic\nupdate.202", description.getSpannable().toString());
|
||||||
assertEquals("update.103\nstatic\nupdate.203", description.getString());
|
assertEquals("update.103\nstatic\nupdate.203", description.getSpannable().toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue