Improve conversation update message stylings.

This commit is contained in:
Greyson Parrelli
2020-10-22 10:59:23 -04:00
committed by Cody Henthorne
parent 90f20c36c5
commit 9c97cd8816
53 changed files with 757 additions and 369 deletions

View File

@@ -1,23 +1,18 @@
package org.thoughtcrime.securesms.conversation;
import android.content.Context;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.text.Spannable;
import android.text.SpannableString;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.Transformations;
import org.thoughtcrime.securesms.BindableConversationItem;
import org.thoughtcrime.securesms.R;
@@ -30,10 +25,7 @@ import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.recipients.LiveRecipient;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.IdentityUtil;
import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
import org.whispersystems.libsignal.util.guava.Optional;
@@ -50,15 +42,12 @@ public final class ConversationUpdateItem extends LinearLayout
private Set<ConversationMessage> batchSelected;
private ImageView icon;
private TextView title;
private TextView body;
private TextView date;
private LiveRecipient sender;
private ConversationMessage conversationMessage;
private MessageRecord messageRecord;
private Locale locale;
private LiveData<SpannableString> displayBody;
private LiveData<Spannable> displayBody;
private final UpdateObserver updateObserver = new UpdateObserver();
private final SenderObserver senderObserver = new SenderObserver();
@@ -74,11 +63,7 @@ public final class ConversationUpdateItem extends LinearLayout
@Override
public void onFinishInflate() {
super.onFinishInflate();
this.icon = findViewById(R.id.conversation_update_icon);
this.title = findViewById(R.id.conversation_update_title);
this.body = findViewById(R.id.conversation_update_body);
this.date = findViewById(R.id.conversation_update_date);
this.body = findViewById(R.id.conversation_update_body);
this.setOnClickListener(new InternalClickListener(null));
}
@@ -117,22 +102,18 @@ public final class ConversationUpdateItem extends LinearLayout
observeSender(lifecycleOwner, messageRecord.getIndividualRecipient());
UpdateDescription updateDescription = Objects.requireNonNull(messageRecord.getUpdateDisplayBody(getContext()));
LiveData<String> liveUpdateMessage = LiveUpdateMessage.fromMessageDescription(updateDescription);
LiveData<SpannableString> spannableStringMessage = toSpannable(loading(liveUpdateMessage));
UpdateDescription updateDescription = Objects.requireNonNull(messageRecord.getUpdateDisplayBody(getContext()));
LiveData<Spannable> liveUpdateMessage = LiveUpdateMessage.fromMessageDescription(getContext(), updateDescription);
LiveData<Spannable> spannableMessage = loading(liveUpdateMessage);
present(conversationMessage);
observeDisplayBody(lifecycleOwner, spannableStringMessage);
observeDisplayBody(lifecycleOwner, spannableMessage);
}
/** After a short delay, if the main data hasn't shown yet, then a loading message is displayed. */
private @NonNull LiveData<String> loading(@NonNull LiveData<String> string) {
return LiveDataUtil.until(string, LiveDataUtil.delay(250, getContext().getString(R.string.ConversationUpdateItem_loading)));
}
private static LiveData<SpannableString> toSpannable(LiveData<String> loading) {
return Transformations.map(loading, source -> source == null ? null : new SpannableString(source));
private @NonNull LiveData<Spannable> loading(@NonNull LiveData<Spannable> string) {
return LiveDataUtil.until(string, LiveDataUtil.delay(250, new SpannableString(getContext().getString(R.string.ConversationUpdateItem_loading))));
}
@Override
@@ -152,7 +133,7 @@ public final class ConversationUpdateItem extends LinearLayout
}
}
private void observeDisplayBody(@NonNull LifecycleOwner lifecycleOwner, @Nullable LiveData<SpannableString> displayBody) {
private void observeDisplayBody(@NonNull LifecycleOwner lifecycleOwner, @Nullable LiveData<Spannable> displayBody) {
if (this.displayBody != displayBody) {
if (this.displayBody != null) {
this.displayBody.removeObserver(updateObserver);
@@ -176,98 +157,10 @@ public final class ConversationUpdateItem extends LinearLayout
}
private void present(ConversationMessage conversationMessage) {
MessageRecord messageRecord = conversationMessage.getMessageRecord();
if (messageRecord.isGroupAction()) setGroupRecord();
else if (messageRecord.isCallLog()) setCallRecord(messageRecord);
else if (messageRecord.isJoined()) setJoinedRecord();
else if (messageRecord.isExpirationTimerUpdate()) setTimerRecord(messageRecord);
else if (messageRecord.isEndSession()) setEndSessionRecord();
else if (messageRecord.isIdentityUpdate()) setIdentityRecord();
else if (messageRecord.isIdentityVerified() ||
messageRecord.isIdentityDefault()) setIdentityVerifyUpdate(messageRecord);
else if (messageRecord.isProfileChange()) setProfileNameChangeRecord();
else throw new AssertionError("Neither group nor log nor joined.");
if (batchSelected.contains(conversationMessage)) setSelected(true);
else setSelected(false);
}
private void setCallRecord(MessageRecord messageRecord) {
if (messageRecord.isIncomingCall()) icon.setImageResource(R.drawable.ic_call_received_grey600_24dp);
else if (messageRecord.isOutgoingCall()) icon.setImageResource(R.drawable.ic_call_made_grey600_24dp);
else icon.setImageResource(R.drawable.ic_call_missed_grey600_24dp);
date.setText(DateUtils.getExtendedRelativeTimeSpanString(getContext(), locale, messageRecord.getDateSent()));
title.setVisibility(GONE);
date.setVisibility(View.VISIBLE);
}
private void setTimerRecord(final MessageRecord messageRecord) {
if (messageRecord.getExpiresIn() > 0) {
icon.setImageDrawable(ContextCompat.getDrawable(getContext(), R.drawable.ic_timer_24));
} else {
icon.setImageDrawable(ContextCompat.getDrawable(getContext(), R.drawable.ic_timer_disabled_24));
}
icon.setColorFilter(getIconTintFilter());
title.setText(ExpirationUtil.getExpirationDisplayValue(getContext(), (int)(messageRecord.getExpiresIn() / 1000)));
title.setVisibility(VISIBLE);
date.setVisibility(GONE);
}
private ColorFilter getIconTintFilter() {
return new PorterDuffColorFilter(ThemeUtil.getThemedColor(getContext(), R.attr.icon_tint), PorterDuff.Mode.SRC_IN);
}
private void setIdentityRecord() {
icon.setImageDrawable(ThemeUtil.getThemedDrawable(getContext(), R.attr.safety_number_icon));
icon.setColorFilter(getIconTintFilter());
title.setVisibility(GONE);
date.setVisibility(GONE);
}
private void setIdentityVerifyUpdate(final MessageRecord messageRecord) {
if (messageRecord.isIdentityVerified()) icon.setImageResource(R.drawable.ic_check_white_24dp);
else icon.setImageResource(R.drawable.ic_info_outline_white_24);
icon.setColorFilter(getIconTintFilter());
title.setVisibility(GONE);
date.setVisibility(GONE);
}
private void setProfileNameChangeRecord() {
icon.setImageDrawable(ContextCompat.getDrawable(getContext(), R.drawable.ic_profile_outline_20));
icon.setColorFilter(getIconTintFilter());
title.setVisibility(GONE);
date.setVisibility(GONE);
}
private void setGroupRecord() {
icon.setImageDrawable(ThemeUtil.getThemedDrawable(getContext(), R.attr.menu_group_icon));
icon.clearColorFilter();
title.setVisibility(GONE);
date.setVisibility(GONE);
}
private void setJoinedRecord() {
icon.setImageResource(R.drawable.ic_favorite_grey600_24dp);
icon.clearColorFilter();
title.setVisibility(GONE);
date.setVisibility(GONE);
}
private void setEndSessionRecord() {
icon.setImageResource(R.drawable.ic_refresh_white_24dp);
icon.setColorFilter(getIconTintFilter());
}
@Override
public void setOnClickListener(View.OnClickListener l) {
super.setOnClickListener(new InternalClickListener(l));
@@ -281,10 +174,10 @@ public final class ConversationUpdateItem extends LinearLayout
}
}
private final class UpdateObserver implements Observer<SpannableString> {
private final class UpdateObserver implements Observer<Spannable> {
@Override
public void onChanged(SpannableString update) {
public void onChanged(Spannable update) {
setBodyText(update);
}
}

View File

@@ -29,6 +29,7 @@ import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
@@ -423,65 +424,65 @@ public final class ConversationListItem extends RelativeLayout
private static @NonNull LiveData<SpannableString> getThreadDisplayBody(@NonNull Context context, @NonNull ThreadRecord thread) {
if (!thread.isMessageRequestAccepted()) {
return emphasisAdded(context.getString(R.string.ThreadRecord_message_request));
return emphasisAdded(context, context.getString(R.string.ThreadRecord_message_request));
} else if (SmsDatabase.Types.isGroupUpdate(thread.getType())) {
if (thread.getRecipient().isPushV2Group()) {
return emphasisAdded(MessageRecord.getGv2ChangeDescription(context, thread.getBody()));
return emphasisAdded(context, MessageRecord.getGv2ChangeDescription(context, thread.getBody()));
} else {
return emphasisAdded(context.getString(R.string.ThreadRecord_group_updated));
return emphasisAdded(context, context.getString(R.string.ThreadRecord_group_updated));
}
} else if (SmsDatabase.Types.isGroupQuit(thread.getType())) {
return emphasisAdded(context.getString(R.string.ThreadRecord_left_the_group));
return emphasisAdded(context, context.getString(R.string.ThreadRecord_left_the_group));
} else if (SmsDatabase.Types.isKeyExchangeType(thread.getType())) {
return emphasisAdded(context.getString(R.string.ConversationListItem_key_exchange_message));
return emphasisAdded(context, context.getString(R.string.ConversationListItem_key_exchange_message));
} else if (SmsDatabase.Types.isFailedDecryptType(thread.getType())) {
return emphasisAdded(context.getString(R.string.MessageDisplayHelper_bad_encrypted_message));
return emphasisAdded(context, context.getString(R.string.MessageDisplayHelper_bad_encrypted_message));
} else if (SmsDatabase.Types.isNoRemoteSessionType(thread.getType())) {
return emphasisAdded(context.getString(R.string.MessageDisplayHelper_message_encrypted_for_non_existing_session));
return emphasisAdded(context, context.getString(R.string.MessageDisplayHelper_message_encrypted_for_non_existing_session));
} else if (SmsDatabase.Types.isEndSessionType(thread.getType())) {
return emphasisAdded(context.getString(R.string.ThreadRecord_secure_session_reset));
return emphasisAdded(context, context.getString(R.string.ThreadRecord_secure_session_reset));
} else if (MmsSmsColumns.Types.isLegacyType(thread.getType())) {
return emphasisAdded(context.getString(R.string.MessageRecord_message_encrypted_with_a_legacy_protocol_version_that_is_no_longer_supported));
return emphasisAdded(context, context.getString(R.string.MessageRecord_message_encrypted_with_a_legacy_protocol_version_that_is_no_longer_supported));
} else if (MmsSmsColumns.Types.isDraftMessageType(thread.getType())) {
String draftText = context.getString(R.string.ThreadRecord_draft);
return emphasisAdded(draftText + " " + thread.getBody());
return emphasisAdded(context, draftText + " " + thread.getBody());
} else if (SmsDatabase.Types.isOutgoingCall(thread.getType())) {
return emphasisAdded(context.getString(R.string.ThreadRecord_called));
return emphasisAdded(context, context.getString(R.string.ThreadRecord_called));
} else if (SmsDatabase.Types.isIncomingCall(thread.getType())) {
return emphasisAdded(context.getString(R.string.ThreadRecord_called_you));
return emphasisAdded(context, context.getString(R.string.ThreadRecord_called_you));
} else if (SmsDatabase.Types.isMissedCall(thread.getType())) {
return emphasisAdded(context.getString(R.string.ThreadRecord_missed_call));
return emphasisAdded(context, context.getString(R.string.ThreadRecord_missed_call));
} else if (SmsDatabase.Types.isJoinedType(thread.getType())) {
return emphasisAdded(recipientToStringAsync(thread.getRecipient().getId(), r -> context.getString(R.string.ThreadRecord_s_is_on_signal, r.getDisplayName(context))));
return emphasisAdded(recipientToStringAsync(thread.getRecipient().getId(), r -> new SpannableString(context.getString(R.string.ThreadRecord_s_is_on_signal, r.getDisplayName(context)))));
} else if (SmsDatabase.Types.isExpirationTimerUpdate(thread.getType())) {
int seconds = (int)(thread.getExpiresIn() / 1000);
if (seconds <= 0) {
return emphasisAdded(context.getString(R.string.ThreadRecord_disappearing_messages_disabled));
return emphasisAdded(context, context.getString(R.string.ThreadRecord_disappearing_messages_disabled));
}
String time = ExpirationUtil.getExpirationDisplayValue(context, seconds);
return emphasisAdded(context.getString(R.string.ThreadRecord_disappearing_message_time_updated_to_s, time));
return emphasisAdded(context, context.getString(R.string.ThreadRecord_disappearing_message_time_updated_to_s, time));
} else if (SmsDatabase.Types.isIdentityUpdate(thread.getType())) {
return emphasisAdded(recipientToStringAsync(thread.getRecipient().getId(), r -> {
if (r.isGroup()) {
return context.getString(R.string.ThreadRecord_safety_number_changed);
return new SpannableString(context.getString(R.string.ThreadRecord_safety_number_changed));
} else {
return context.getString(R.string.ThreadRecord_your_safety_number_with_s_has_changed, r.getDisplayName(context));
return new SpannableString(context.getString(R.string.ThreadRecord_your_safety_number_with_s_has_changed, r.getDisplayName(context)));
}
}));
} else if (SmsDatabase.Types.isIdentityVerified(thread.getType())) {
return emphasisAdded(context.getString(R.string.ThreadRecord_you_marked_verified));
return emphasisAdded(context, context.getString(R.string.ThreadRecord_you_marked_verified));
} else if (SmsDatabase.Types.isIdentityDefault(thread.getType())) {
return emphasisAdded(context.getString(R.string.ThreadRecord_you_marked_unverified));
return emphasisAdded(context, context.getString(R.string.ThreadRecord_you_marked_unverified));
} else if (SmsDatabase.Types.isUnsupportedMessageType(thread.getType())) {
return emphasisAdded(context.getString(R.string.ThreadRecord_message_could_not_be_processed));
return emphasisAdded(context, context.getString(R.string.ThreadRecord_message_could_not_be_processed));
} else if (SmsDatabase.Types.isProfileChange(thread.getType())) {
return emphasisAdded("");
return emphasisAdded(context, "");
} else {
ThreadDatabase.Extra extra = thread.getExtra();
if (extra != null && extra.isViewOnce()) {
return emphasisAdded(getViewOnceDescription(context, thread.getContentType()));
return emphasisAdded(context, getViewOnceDescription(context, thread.getContentType()));
} else if (extra != null && extra.isRemoteDelete()) {
return emphasisAdded(context.getString(thread.isOutgoing() ? R.string.ThreadRecord_you_deleted_this_message : R.string.ThreadRecord_this_message_was_deleted));
return emphasisAdded(context, context.getString(thread.isOutgoing() ? R.string.ThreadRecord_you_deleted_this_message : R.string.ThreadRecord_this_message_was_deleted));
} else {
return LiveDataUtil.just(new SpannableString(removeNewlines(thread.getBody())));
}
@@ -500,15 +501,15 @@ public final class ConversationListItem extends RelativeLayout
}
}
private static @NonNull LiveData<SpannableString> emphasisAdded(@NonNull String string) {
return emphasisAdded(UpdateDescription.staticDescription(string));
private static @NonNull LiveData<SpannableString> emphasisAdded(@NonNull Context context, @NonNull String string) {
return emphasisAdded(context, UpdateDescription.staticDescription(string, 0, 0));
}
private static @NonNull LiveData<SpannableString> emphasisAdded(@NonNull UpdateDescription description) {
return emphasisAdded(LiveUpdateMessage.fromMessageDescription(description));
private static @NonNull LiveData<SpannableString> emphasisAdded(@NonNull Context context, @NonNull UpdateDescription description) {
return emphasisAdded(LiveUpdateMessage.fromMessageDescription(context, description));
}
private static @NonNull LiveData<SpannableString> emphasisAdded(@NonNull LiveData<String> description) {
private static @NonNull LiveData<SpannableString> emphasisAdded(@NonNull LiveData<Spannable> description) {
return Transformations.map(description, sequence -> {
SpannableString spannable = new SpannableString(sequence);
spannable.setSpan(new StyleSpan(Typeface.ITALIC),

View File

@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.database.model;
import android.content.Context;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
@@ -63,22 +64,22 @@ final class GroupsV2UpdateMessageProducer {
UpdateDescription describeNewGroup(@NonNull DecryptedGroup group, @NonNull DecryptedGroupChange decryptedGroupChange) {
Optional<DecryptedPendingMember> selfPending = DecryptedGroupUtil.findPendingByUuid(group.getPendingMembersList(), selfUuid);
if (selfPending.isPresent()) {
return updateDescription(selfPending.get().getAddedByUuid(), inviteBy -> context.getString(R.string.MessageRecord_s_invited_you_to_the_group, inviteBy));
return updateDescription(selfPending.get().getAddedByUuid(), inviteBy -> context.getString(R.string.MessageRecord_s_invited_you_to_the_group, inviteBy), R.drawable.ic_update_group_add_light_16, R.drawable.ic_update_group_add_dark_16);
}
ByteString foundingMemberUuid = decryptedGroupChange.getEditor();
if (!foundingMemberUuid.isEmpty()) {
if (selfUuidBytes.equals(foundingMemberUuid)) {
return updateDescription(context.getString(R.string.MessageRecord_you_created_the_group));
return updateDescription(context.getString(R.string.MessageRecord_you_created_the_group), R.drawable.ic_update_group_light_16, R.drawable.ic_update_group_dark_16);
} else {
return updateDescription(foundingMemberUuid, creator -> context.getString(R.string.MessageRecord_s_added_you, creator));
return updateDescription(foundingMemberUuid, creator -> context.getString(R.string.MessageRecord_s_added_you, creator), R.drawable.ic_update_group_add_light_16, R.drawable.ic_update_group_add_dark_16);
}
}
if (DecryptedGroupUtil.findMemberByUuid(group.getMembersList(), selfUuid).isPresent()) {
return updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group));
return updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group), R.drawable.ic_update_group_add_light_16, R.drawable.ic_update_group_add_dark_16);
} else {
return updateDescription(context.getString(R.string.MessageRecord_group_updated));
return updateDescription(context.getString(R.string.MessageRecord_group_updated), R.drawable.ic_update_group_light_16, R.drawable.ic_update_group_dark_16);
}
}
@@ -146,14 +147,14 @@ final class GroupsV2UpdateMessageProducer {
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_updated_group)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_updated_group), R.drawable.ic_update_group_light_16, R.drawable.ic_update_group_dark_16));
} else {
updates.add(updateDescription(change.getEditor(), (editor) -> context.getString(R.string.MessageRecord_s_updated_group, editor)));
updates.add(updateDescription(change.getEditor(), (editor) -> context.getString(R.string.MessageRecord_s_updated_group, editor), R.drawable.ic_update_group_light_16, R.drawable.ic_update_group_dark_16));
}
}
private void describeUnknownEditorUnknownChange(@NonNull List<UpdateDescription> updates) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_was_updated)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_was_updated), R.drawable.ic_update_group_light_16, R.drawable.ic_update_group_dark_16));
}
private void describeMemberAdditions(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
@@ -164,18 +165,18 @@ final class GroupsV2UpdateMessageProducer {
if (editorIsYou) {
if (newMemberIsYou) {
updates.add(0, updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group_via_the_group_link)));
updates.add(0, updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group_via_the_group_link), R.drawable.ic_update_group_accept_light_16, R.drawable.ic_update_group_accept_dark_16));
} else {
updates.add(updateDescription(member.getUuid(), added -> context.getString(R.string.MessageRecord_you_added_s, added)));
updates.add(updateDescription(member.getUuid(), added -> context.getString(R.string.MessageRecord_you_added_s, added), R.drawable.ic_update_group_add_light_16, R.drawable.ic_update_group_add_dark_16));
}
} else {
if (newMemberIsYou) {
updates.add(0, updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_added_you, editor)));
updates.add(0, updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_added_you, editor), R.drawable.ic_update_group_add_light_16, R.drawable.ic_update_group_add_dark_16));
} else {
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)));
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_light_16, R.drawable.ic_update_group_accept_dark_16));
} else {
updates.add(updateDescription(change.getEditor(), member.getUuid(), (editor, newMember) -> context.getString(R.string.MessageRecord_s_added_s, editor, newMember)));
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_light_16, R.drawable.ic_update_group_add_dark_16));
}
}
}
@@ -187,9 +188,9 @@ final class GroupsV2UpdateMessageProducer {
boolean newMemberIsYou = member.getUuid().equals(selfUuidBytes);
if (newMemberIsYou) {
updates.add(0, updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group)));
updates.add(0, updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group), R.drawable.ic_update_group_add_light_16, R.drawable.ic_update_group_add_dark_16));
} else {
updates.add(updateDescription(member.getUuid(), newMember -> context.getString(R.string.MessageRecord_s_joined_the_group, newMember)));
updates.add(updateDescription(member.getUuid(), newMember -> context.getString(R.string.MessageRecord_s_joined_the_group, newMember), R.drawable.ic_update_group_add_light_16, R.drawable.ic_update_group_add_dark_16));
}
}
}
@@ -202,18 +203,18 @@ final class GroupsV2UpdateMessageProducer {
if (editorIsYou) {
if (removedMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_left_the_group)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_left_the_group), R.drawable.ic_update_group_leave_light_16, R.drawable.ic_update_group_leave_dark_16));
} else {
updates.add(updateDescription(member, removedMember -> context.getString(R.string.MessageRecord_you_removed_s, removedMember)));
updates.add(updateDescription(member, removedMember -> context.getString(R.string.MessageRecord_you_removed_s, removedMember), R.drawable.ic_update_group_remove_light_16, R.drawable.ic_update_group_remove_dark_16));
}
} else {
if (removedMemberIsYou) {
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_removed_you_from_the_group, editor)));
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_removed_you_from_the_group, editor), R.drawable.ic_update_group_remove_light_16, R.drawable.ic_update_group_remove_dark_16));
} else {
if (member.equals(change.getEditor())) {
updates.add(updateDescription(member, leavingMember -> context.getString(R.string.MessageRecord_s_left_the_group, leavingMember)));
updates.add(updateDescription(member, leavingMember -> context.getString(R.string.MessageRecord_s_left_the_group, leavingMember), R.drawable.ic_update_group_leave_light_16, R.drawable.ic_update_group_leave_dark_16));
} else {
updates.add(updateDescription(change.getEditor(), member, (editor, removedMember) -> context.getString(R.string.MessageRecord_s_removed_s, editor, removedMember)));
updates.add(updateDescription(change.getEditor(), member, (editor, removedMember) -> context.getString(R.string.MessageRecord_s_removed_s, editor, removedMember), R.drawable.ic_update_group_remove_light_16, R.drawable.ic_update_group_remove_dark_16));
}
}
}
@@ -225,9 +226,9 @@ final class GroupsV2UpdateMessageProducer {
boolean removedMemberIsYou = member.equals(selfUuidBytes);
if (removedMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_are_no_longer_in_the_group)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_are_no_longer_in_the_group), R.drawable.ic_update_group_leave_light_16, R.drawable.ic_update_group_leave_dark_16));
} else {
updates.add(updateDescription(member, oldMember -> context.getString(R.string.MessageRecord_s_is_no_longer_in_the_group, oldMember)));
updates.add(updateDescription(member, oldMember -> context.getString(R.string.MessageRecord_s_is_no_longer_in_the_group, oldMember), R.drawable.ic_update_group_leave_light_16, R.drawable.ic_update_group_leave_dark_16));
}
}
}
@@ -239,23 +240,23 @@ final class GroupsV2UpdateMessageProducer {
boolean changedMemberIsYou = roleChange.getUuid().equals(selfUuidBytes);
if (roleChange.getRole() == Member.Role.ADMINISTRATOR) {
if (editorIsYou) {
updates.add(updateDescription(roleChange.getUuid(), newAdmin -> context.getString(R.string.MessageRecord_you_made_s_an_admin, newAdmin)));
updates.add(updateDescription(roleChange.getUuid(), newAdmin -> context.getString(R.string.MessageRecord_you_made_s_an_admin, newAdmin), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
} else {
if (changedMemberIsYou) {
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_made_you_an_admin, editor)));
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_made_you_an_admin, editor), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
} else {
updates.add(updateDescription(change.getEditor(), roleChange.getUuid(), (editor, newAdmin) -> context.getString(R.string.MessageRecord_s_made_s_an_admin, editor, newAdmin)));
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_light_16, R.drawable.ic_update_group_role_dark_16));
}
}
} else {
if (editorIsYou) {
updates.add(updateDescription(roleChange.getUuid(), oldAdmin -> context.getString(R.string.MessageRecord_you_revoked_admin_privileges_from_s, oldAdmin)));
updates.add(updateDescription(roleChange.getUuid(), oldAdmin -> context.getString(R.string.MessageRecord_you_revoked_admin_privileges_from_s, oldAdmin), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
} else {
if (changedMemberIsYou) {
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_revoked_your_admin_privileges, editor)));
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_revoked_your_admin_privileges, editor), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
} else {
updates.add(updateDescription(change.getEditor(), roleChange.getUuid(), (editor, oldAdmin) -> context.getString(R.string.MessageRecord_s_revoked_admin_privileges_from_s, editor, oldAdmin)));
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_light_16, R.drawable.ic_update_group_role_dark_16));
}
}
}
@@ -268,15 +269,15 @@ final class GroupsV2UpdateMessageProducer {
if (roleChange.getRole() == Member.Role.ADMINISTRATOR) {
if (changedMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_are_now_an_admin)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_are_now_an_admin), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
} else {
updates.add(updateDescription(roleChange.getUuid(), newAdmin -> context.getString(R.string.MessageRecord_s_is_now_an_admin, newAdmin)));
updates.add(updateDescription(roleChange.getUuid(), newAdmin -> context.getString(R.string.MessageRecord_s_is_now_an_admin, newAdmin), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
}
} else {
if (changedMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_are_no_longer_an_admin)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_are_no_longer_an_admin), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
} else {
updates.add(updateDescription(roleChange.getUuid(), oldAdmin -> context.getString(R.string.MessageRecord_s_is_no_longer_an_admin, oldAdmin)));
updates.add(updateDescription(roleChange.getUuid(), oldAdmin -> context.getString(R.string.MessageRecord_s_is_no_longer_an_admin, oldAdmin), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
}
}
}
@@ -290,10 +291,10 @@ final class GroupsV2UpdateMessageProducer {
boolean newMemberIsYou = invitee.getUuid().equals(selfUuidBytes);
if (newMemberIsYou) {
updates.add(0, updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_invited_you_to_the_group, editor)));
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_light_16, R.drawable.ic_update_group_add_dark_16));
} else {
if (editorIsYou) {
updates.add(updateDescription(invitee.getUuid(), newInvitee -> context.getString(R.string.MessageRecord_you_invited_s_to_the_group, newInvitee)));
updates.add(updateDescription(invitee.getUuid(), newInvitee -> context.getString(R.string.MessageRecord_you_invited_s_to_the_group, newInvitee), R.drawable.ic_update_group_add_light_16, R.drawable.ic_update_group_add_dark_16));
} else {
notYouInviteCount++;
}
@@ -302,7 +303,7 @@ final class GroupsV2UpdateMessageProducer {
if (notYouInviteCount > 0) {
final int notYouInviteCountFinalCopy = notYouInviteCount;
updates.add(updateDescription(change.getEditor(), editor -> context.getResources().getQuantityString(R.plurals.MessageRecord_s_invited_members, notYouInviteCountFinalCopy, editor, notYouInviteCountFinalCopy)));
updates.add(updateDescription(change.getEditor(), editor -> context.getResources().getQuantityString(R.plurals.MessageRecord_s_invited_members, notYouInviteCountFinalCopy, editor, notYouInviteCountFinalCopy), R.drawable.ic_update_group_add_light_16, R.drawable.ic_update_group_add_dark_16));
}
}
@@ -316,9 +317,9 @@ final class GroupsV2UpdateMessageProducer {
UUID uuid = UuidUtil.fromByteStringOrUnknown(invitee.getAddedByUuid());
if (UuidUtil.UNKNOWN_UUID.equals(uuid)) {
updates.add(0, updateDescription(context.getString(R.string.MessageRecord_you_were_invited_to_the_group)));
updates.add(0, updateDescription(context.getString(R.string.MessageRecord_you_were_invited_to_the_group), R.drawable.ic_update_group_add_light_16, R.drawable.ic_update_group_add_dark_16));
} else {
updates.add(0, updateDescription(invitee.getAddedByUuid(), editor -> context.getString(R.string.MessageRecord_s_invited_you_to_the_group, editor)));
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_light_16, R.drawable.ic_update_group_add_dark_16));
}
} else {
notYouInviteCount++;
@@ -326,7 +327,7 @@ final class GroupsV2UpdateMessageProducer {
}
if (notYouInviteCount > 0) {
updates.add(updateDescription(context.getResources().getQuantityString(R.plurals.MessageRecord_d_people_were_invited_to_the_group, notYouInviteCount, notYouInviteCount)));
updates.add(updateDescription(context.getResources().getQuantityString(R.plurals.MessageRecord_d_people_were_invited_to_the_group, notYouInviteCount, notYouInviteCount), R.drawable.ic_update_group_add_light_16, R.drawable.ic_update_group_add_dark_16));
}
}
@@ -338,12 +339,12 @@ final class GroupsV2UpdateMessageProducer {
boolean decline = invitee.getUuid().equals(change.getEditor());
if (decline) {
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_declined_the_invitation_to_the_group)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_declined_the_invitation_to_the_group), R.drawable.ic_update_group_decline_light_16, R.drawable.ic_update_group_decline_dark_16));
} else {
updates.add(updateDescription(context.getString(R.string.MessageRecord_someone_declined_an_invitation_to_the_group)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_someone_declined_an_invitation_to_the_group), R.drawable.ic_update_group_decline_light_16, R.drawable.ic_update_group_decline_dark_16));
}
} 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)));
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_light_16, R.drawable.ic_update_group_decline_dark_16));
} else {
notDeclineCount++;
}
@@ -351,10 +352,10 @@ final class GroupsV2UpdateMessageProducer {
if (notDeclineCount > 0) {
if (editorIsYou) {
updates.add(updateDescription(context.getResources().getQuantityString(R.plurals.MessageRecord_you_revoked_invites, notDeclineCount, notDeclineCount)));
updates.add(updateDescription(context.getResources().getQuantityString(R.plurals.MessageRecord_you_revoked_invites, notDeclineCount, notDeclineCount), R.drawable.ic_update_group_decline_light_16, R.drawable.ic_update_group_decline_dark_16));
} else {
final int notDeclineCountFinalCopy = notDeclineCount;
updates.add(updateDescription(change.getEditor(), editor -> context.getResources().getQuantityString(R.plurals.MessageRecord_s_revoked_invites, notDeclineCountFinalCopy, editor, notDeclineCountFinalCopy)));
updates.add(updateDescription(change.getEditor(), editor -> context.getResources().getQuantityString(R.plurals.MessageRecord_s_revoked_invites, notDeclineCountFinalCopy, editor, notDeclineCountFinalCopy), R.drawable.ic_update_group_decline_light_16, R.drawable.ic_update_group_decline_dark_16));
}
}
}
@@ -366,14 +367,14 @@ final class GroupsV2UpdateMessageProducer {
boolean inviteeWasYou = invitee.getUuid().equals(selfUuidBytes);
if (inviteeWasYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_an_admin_revoked_your_invitation_to_the_group)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_an_admin_revoked_your_invitation_to_the_group), R.drawable.ic_update_group_decline_light_16, R.drawable.ic_update_group_decline_dark_16));
} else {
notDeclineCount++;
}
}
if (notDeclineCount > 0) {
updates.add(updateDescription(context.getResources().getQuantityString(R.plurals.MessageRecord_d_invitations_were_revoked, notDeclineCount, notDeclineCount)));
updates.add(updateDescription(context.getResources().getQuantityString(R.plurals.MessageRecord_d_invitations_were_revoked, notDeclineCount, notDeclineCount), R.drawable.ic_update_group_decline_light_16, R.drawable.ic_update_group_decline_dark_16));
}
}
@@ -386,18 +387,18 @@ final class GroupsV2UpdateMessageProducer {
if (editorIsYou) {
if (newMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_accepted_invite)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_accepted_invite), R.drawable.ic_update_group_accept_light_16, R.drawable.ic_update_group_accept_dark_16));
} else {
updates.add(updateDescription(uuid, newPromotedMember -> context.getString(R.string.MessageRecord_you_added_invited_member_s, newPromotedMember)));
updates.add(updateDescription(uuid, newPromotedMember -> context.getString(R.string.MessageRecord_you_added_invited_member_s, newPromotedMember), R.drawable.ic_update_group_add_light_16, R.drawable.ic_update_group_add_dark_16));
}
} else {
if (newMemberIsYou) {
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_added_you, editor)));
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_added_you, editor), R.drawable.ic_update_group_add_light_16, R.drawable.ic_update_group_add_light_16));
} else {
if (uuid.equals(change.getEditor())) {
updates.add(updateDescription(uuid, newAcceptedMember -> context.getString(R.string.MessageRecord_s_accepted_invite, newAcceptedMember)));
updates.add(updateDescription(uuid, newAcceptedMember -> context.getString(R.string.MessageRecord_s_accepted_invite, newAcceptedMember), R.drawable.ic_update_group_accept_light_16, R.drawable.ic_update_group_accept_dark_16));
} else {
updates.add(updateDescription(change.getEditor(), uuid, (editor, newAcceptedMember) -> context.getString(R.string.MessageRecord_s_added_invited_member_s, editor, newAcceptedMember)));
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_light_16, R.drawable.ic_update_group_add_dark_16));
}
}
}
@@ -410,9 +411,9 @@ final class GroupsV2UpdateMessageProducer {
boolean newMemberIsYou = uuid.equals(selfUuidBytes);
if (newMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_joined_the_group), R.drawable.ic_update_group_add_light_16, R.drawable.ic_update_group_add_dark_16));
} else {
updates.add(updateDescription(uuid, newMemberName -> context.getString(R.string.MessageRecord_s_joined_the_group, newMemberName)));
updates.add(updateDescription(uuid, newMemberName -> context.getString(R.string.MessageRecord_s_joined_the_group, newMemberName), R.drawable.ic_update_group_add_light_16, R.drawable.ic_update_group_add_dark_16));
}
}
}
@@ -423,16 +424,16 @@ final class GroupsV2UpdateMessageProducer {
if (change.hasNewTitle()) {
String newTitle = StringUtil.isolateBidi(change.getNewTitle().getValue());
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_the_group_name_to_s, newTitle)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_the_group_name_to_s, newTitle), R.drawable.ic_update_group_name_light_16, R.drawable.ic_update_group_name_dark_16));
} else {
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_changed_the_group_name_to_s, editor, newTitle)));
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_light_16, R.drawable.ic_update_group_name_dark_16));
}
}
}
private void describeUnknownEditorNewTitle(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
if (change.hasNewTitle()) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_name_has_changed_to_s, StringUtil.isolateBidi(change.getNewTitle().getValue()))));
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_name_has_changed_to_s, StringUtil.isolateBidi(change.getNewTitle().getValue())), R.drawable.ic_update_group_name_light_16, R.drawable.ic_update_group_name_dark_16));
}
}
@@ -441,16 +442,16 @@ final class GroupsV2UpdateMessageProducer {
if (change.hasNewAvatar()) {
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_the_group_avatar)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_the_group_avatar), R.drawable.ic_update_group_avatar_light_16, R.drawable.ic_update_group_avatar_dark_16));
} else {
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_changed_the_group_avatar, editor)));
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_changed_the_group_avatar, editor), R.drawable.ic_update_group_avatar_light_16, R.drawable.ic_update_group_avatar_dark_16));
}
}
}
private void describeUnknownEditorNewAvatar(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
if (change.hasNewAvatar()) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_group_avatar_has_been_changed)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_group_avatar_has_been_changed), R.drawable.ic_update_group_avatar_light_16, R.drawable.ic_update_group_avatar_dark_16));
}
}
@@ -460,9 +461,9 @@ final class GroupsV2UpdateMessageProducer {
if (change.hasNewTimer()) {
String time = ExpirationUtil.getExpirationDisplayValue(context, change.getNewTimer().getDuration());
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_set_disappearing_message_time_to_s, time)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_set_disappearing_message_time_to_s, time), R.drawable.ic_update_timer_light_16, R.drawable.ic_update_timer_dark_16));
} else {
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_set_disappearing_message_time_to_s, editor, time)));
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_light_16, R.drawable.ic_update_timer_dark_16));
}
}
}
@@ -470,7 +471,7 @@ final class GroupsV2UpdateMessageProducer {
private void describeUnknownEditorNewTimer(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
if (change.hasNewTimer()) {
String time = ExpirationUtil.getExpirationDisplayValue(context, change.getNewTimer().getDuration());
updates.add(updateDescription(context.getString(R.string.MessageRecord_disappearing_message_time_set_to_s, time)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_disappearing_message_time_set_to_s, time), R.drawable.ic_update_timer_light_16, R.drawable.ic_update_timer_dark_16));
}
}
@@ -480,9 +481,9 @@ final class GroupsV2UpdateMessageProducer {
if (change.getNewAttributeAccess() != AccessControl.AccessRequired.UNKNOWN) {
String accessLevel = GV2AccessLevelUtil.toString(context, change.getNewAttributeAccess());
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_who_can_edit_group_info_to_s, accessLevel)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_who_can_edit_group_info_to_s, accessLevel), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
} else {
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_changed_who_can_edit_group_info_to_s, editor, accessLevel)));
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_light_16, R.drawable.ic_update_group_role_dark_16));
}
}
}
@@ -490,7 +491,7 @@ final class GroupsV2UpdateMessageProducer {
private void describeUnknownEditorNewAttributeAccess(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
if (change.getNewAttributeAccess() != AccessControl.AccessRequired.UNKNOWN) {
String accessLevel = GV2AccessLevelUtil.toString(context, change.getNewAttributeAccess());
updates.add(updateDescription(context.getString(R.string.MessageRecord_who_can_edit_group_info_has_been_changed_to_s, accessLevel)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_who_can_edit_group_info_has_been_changed_to_s, accessLevel), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
}
}
@@ -500,9 +501,9 @@ final class GroupsV2UpdateMessageProducer {
if (change.getNewMemberAccess() != AccessControl.AccessRequired.UNKNOWN) {
String accessLevel = GV2AccessLevelUtil.toString(context, change.getNewMemberAccess());
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_who_can_edit_group_membership_to_s, accessLevel)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_changed_who_can_edit_group_membership_to_s, accessLevel), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
} else {
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_changed_who_can_edit_group_membership_to_s, editor, accessLevel)));
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_light_16, R.drawable.ic_update_group_role_dark_16));
}
}
}
@@ -510,7 +511,7 @@ final class GroupsV2UpdateMessageProducer {
private void describeUnknownEditorNewMembershipAccess(@NonNull DecryptedGroupChange change, @NonNull List<UpdateDescription> updates) {
if (change.getNewMemberAccess() != AccessControl.AccessRequired.UNKNOWN) {
String accessLevel = GV2AccessLevelUtil.toString(context, change.getNewMemberAccess());
updates.add(updateDescription(context.getString(R.string.MessageRecord_who_can_edit_group_membership_has_been_changed_to_s, accessLevel)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_who_can_edit_group_membership_has_been_changed_to_s, accessLevel), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
}
}
@@ -532,15 +533,15 @@ final class GroupsV2UpdateMessageProducer {
groupLinkEnabled = true;
if (editorIsYou) {
if (previousAccessControl == AccessControl.AccessRequired.ADMINISTRATOR) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_off_admin_approval_for_the_group_link)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_off_admin_approval_for_the_group_link), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
} else {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_on_the_group_link_with_admin_approval_off)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_on_the_group_link_with_admin_approval_off), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
}
} else {
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)));
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_light_16, R.drawable.ic_update_group_role_dark_16));
} else {
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_turned_on_the_group_link_with_admin_approval_off, editor)));
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_light_16, R.drawable.ic_update_group_role_dark_16));
}
}
break;
@@ -548,32 +549,32 @@ final class GroupsV2UpdateMessageProducer {
groupLinkEnabled = true;
if (editorIsYou) {
if (previousAccessControl == AccessControl.AccessRequired.ANY) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_on_admin_approval_for_the_group_link)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_on_admin_approval_for_the_group_link), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
} else {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_on_the_group_link_with_admin_approval_on)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_on_the_group_link_with_admin_approval_on), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
}
} else {
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)));
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_light_16, R.drawable.ic_update_group_role_dark_16));
} else {
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_turned_on_the_group_link_with_admin_approval_on, editor)));
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_light_16, R.drawable.ic_update_group_role_dark_16));
}
}
break;
case UNSATISFIABLE:
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_off_the_group_link)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_turned_off_the_group_link), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
} else {
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_turned_off_the_group_link, editor)));
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_turned_off_the_group_link, editor), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
}
break;
}
if (!groupLinkEnabled && change.getNewInviteLinkPassword().size() > 0) {
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_reset_the_group_link)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_reset_the_group_link), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
} else {
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_reset_the_group_link, editor)));
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_reset_the_group_link, editor), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
}
}
}
@@ -591,25 +592,25 @@ final class GroupsV2UpdateMessageProducer {
switch (change.getNewInviteLinkAccess()) {
case ANY:
if (previousAccessControl == AccessControl.AccessRequired.ADMINISTRATOR) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_admin_approval_for_the_group_link_has_been_turned_off)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_admin_approval_for_the_group_link_has_been_turned_off), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
} else {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_link_has_been_turned_on_with_admin_approval_off)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_link_has_been_turned_on_with_admin_approval_off), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
}
break;
case ADMINISTRATOR:
if (previousAccessControl == AccessControl.AccessRequired.ANY) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_admin_approval_for_the_group_link_has_been_turned_on)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_admin_approval_for_the_group_link_has_been_turned_on), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
} else {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_link_has_been_turned_on_with_admin_approval_on)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_link_has_been_turned_on_with_admin_approval_on), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
}
break;
case UNSATISFIABLE:
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_link_has_been_turned_off)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_link_has_been_turned_off), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
break;
}
if (change.getNewInviteLinkPassword().size() > 0) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_link_has_been_reset)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_the_group_link_has_been_reset), R.drawable.ic_update_group_role_light_16, R.drawable.ic_update_group_role_dark_16));
}
}
@@ -618,9 +619,9 @@ final class GroupsV2UpdateMessageProducer {
boolean requestingMemberIsYou = member.getUuid().equals(selfUuidBytes);
if (requestingMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_sent_a_request_to_join_the_group)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_sent_a_request_to_join_the_group), R.drawable.ic_update_group_light_16, R.drawable.ic_update_group_dark_16));
} else {
updates.add(updateDescription(member.getUuid(), requesting -> context.getString(R.string.MessageRecord_s_requested_to_join_via_the_group_link, requesting)));
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_light_16, R.drawable.ic_update_group_dark_16));
}
}
}
@@ -630,14 +631,14 @@ final class GroupsV2UpdateMessageProducer {
boolean requestingMemberIsYou = requestingMember.getUuid().equals(selfUuidBytes);
if (requestingMemberIsYou) {
updates.add(updateDescription(change.getEditor(), editor -> context.getString(R.string.MessageRecord_s_approved_your_request_to_join_the_group, editor)));
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_light_16, R.drawable.ic_update_group_accept_dark_16));
} else {
boolean editorIsYou = change.getEditor().equals(selfUuidBytes);
if (editorIsYou) {
updates.add(updateDescription(requestingMember.getUuid(), requesting -> context.getString(R.string.MessageRecord_you_approved_a_request_to_join_the_group_from_s, requesting)));
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_light_16, R.drawable.ic_update_group_accept_dark_16));
} 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)));
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_light_16, R.drawable.ic_update_group_accept_dark_16));
}
}
}
@@ -648,9 +649,9 @@ final class GroupsV2UpdateMessageProducer {
boolean requestingMemberIsYou = requestingMember.getUuid().equals(selfUuidBytes);
if (requestingMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_your_request_to_join_the_group_has_been_approved)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_your_request_to_join_the_group_has_been_approved), R.drawable.ic_update_group_accept_light_16, R.drawable.ic_update_group_accept_dark_16));
} else {
updates.add(updateDescription(requestingMember.getUuid(), requesting -> context.getString(R.string.MessageRecord_a_request_to_join_the_group_from_s_has_been_approved, requesting)));
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_light_16, R.drawable.ic_update_group_accept_dark_16));
}
}
}
@@ -663,17 +664,17 @@ final class GroupsV2UpdateMessageProducer {
if (requestingMemberIsYou) {
if (editorIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_canceled_your_request_to_join_the_group)));
updates.add(updateDescription(context.getString(R.string.MessageRecord_you_canceled_your_request_to_join_the_group), R.drawable.ic_update_group_decline_light_16, R.drawable.ic_update_group_decline_dark_16));
} else {
updates.add(updateDescription(context.getString(R.string.MessageRecord_your_request_to_join_the_group_has_been_denied_by_an_admin)));
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_light_16, R.drawable.ic_update_group_decline_dark_16));
}
} else {
boolean editorIsCanceledMember = change.getEditor().equals(requestingMember);
if (editorIsCanceledMember) {
updates.add(updateDescription(requestingMember, editorRequesting -> context.getString(R.string.MessageRecord_s_canceled_their_request_to_join_the_group, editorRequesting)));
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_light_16, R.drawable.ic_update_group_decline_dark_16));
} 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)));
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_light_16, R.drawable.ic_update_group_decline_dark_16));
}
}
}
@@ -684,9 +685,9 @@ final class GroupsV2UpdateMessageProducer {
boolean requestingMemberIsYou = requestingMember.equals(selfUuidBytes);
if (requestingMemberIsYou) {
updates.add(updateDescription(context.getString(R.string.MessageRecord_your_request_to_join_the_group_has_been_denied_by_an_admin)));
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_light_16, R.drawable.ic_update_group_decline_dark_16));
} else {
updates.add(updateDescription(requestingMember, requesting -> context.getString(R.string.MessageRecord_a_request_to_join_the_group_from_s_has_been_denied, requesting)));
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_light_16, R.drawable.ic_update_group_decline_dark_16));
}
}
}
@@ -709,20 +710,32 @@ final class GroupsV2UpdateMessageProducer {
String create(String arg1, String arg2);
}
private static UpdateDescription updateDescription(@NonNull String string) {
return UpdateDescription.staticDescription(string);
private static UpdateDescription updateDescription(@NonNull String string,
@DrawableRes int lightIconResource,
@DrawableRes int darkIconResource)
{
return UpdateDescription.staticDescription(string, lightIconResource, darkIconResource);
}
private UpdateDescription updateDescription(@NonNull ByteString uuid1Bytes, @NonNull StringFactory1Arg stringFactory) {
private UpdateDescription updateDescription(@NonNull ByteString uuid1Bytes,
@NonNull StringFactory1Arg stringFactory,
@DrawableRes int lightIconResource,
@DrawableRes int darkIconResource)
{
UUID uuid1 = UuidUtil.fromByteStringOrUnknown(uuid1Bytes);
return UpdateDescription.mentioning(Collections.singletonList(uuid1), () -> stringFactory.create(descriptionStrategy.describe(uuid1)));
return UpdateDescription.mentioning(Collections.singletonList(uuid1), () -> stringFactory.create(descriptionStrategy.describe(uuid1)), lightIconResource, darkIconResource);
}
private UpdateDescription updateDescription(@NonNull ByteString uuid1Bytes, @NonNull ByteString uuid2Bytes, @NonNull StringFactory2Args stringFactory) {
private UpdateDescription updateDescription(@NonNull ByteString uuid1Bytes,
@NonNull ByteString uuid2Bytes,
@NonNull StringFactory2Args stringFactory,
@DrawableRes int lightIconResource,
@DrawableRes int darkIconResource)
{
UUID uuid1 = UuidUtil.fromByteStringOrUnknown(uuid1Bytes);
UUID uuid2 = UuidUtil.fromByteStringOrUnknown(uuid2Bytes);
return UpdateDescription.mentioning(Arrays.asList(uuid1, uuid2), () -> stringFactory.create(descriptionStrategy.describe(uuid1), descriptionStrategy.describe(uuid2)));
return UpdateDescription.mentioning(Arrays.asList(uuid1, uuid2), () -> stringFactory.create(descriptionStrategy.describe(uuid1), descriptionStrategy.describe(uuid2)), lightIconResource, darkIconResource);
}
}

View File

@@ -1,13 +1,24 @@
package org.thoughtcrime.securesms.database.model;
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import androidx.annotation.AnyThread;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LiveData;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.SpanUtil;
import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
import org.whispersystems.libsignal.util.guava.Function;
@@ -20,9 +31,9 @@ public final class LiveUpdateMessage {
* recreates the string asynchronously when they change.
*/
@AnyThread
public static LiveData<String> fromMessageDescription(@NonNull UpdateDescription updateDescription) {
public static LiveData<Spannable> fromMessageDescription(@NonNull Context context, @NonNull UpdateDescription updateDescription) {
if (updateDescription.isStringStatic()) {
return LiveDataUtil.just(updateDescription.getStaticString());
return LiveDataUtil.just(toSpannable(context, updateDescription, updateDescription.getStaticString()));
}
List<LiveData<Recipient>> allMentionedRecipients = Stream.of(updateDescription.getMentioned())
@@ -32,16 +43,30 @@ public final class LiveUpdateMessage {
LiveData<?> mentionedRecipientChangeStream = allMentionedRecipients.isEmpty() ? LiveDataUtil.just(new Object())
: LiveDataUtil.merge(allMentionedRecipients);
return LiveDataUtil.mapAsync(mentionedRecipientChangeStream, event -> updateDescription.getString());
return LiveDataUtil.mapAsync(mentionedRecipientChangeStream, event -> toSpannable(context, updateDescription, updateDescription.getString()));
}
/**
* Observes a single recipient and recreates the string asynchronously when they change.
*/
public static LiveData<String> recipientToStringAsync(@NonNull RecipientId recipientId,
@NonNull Function<Recipient, String> createStringInBackground)
public static LiveData<Spannable> recipientToStringAsync(@NonNull RecipientId recipientId,
@NonNull Function<Recipient, Spannable> createStringInBackground)
{
return LiveDataUtil.mapAsync(Recipient.live(recipientId).getLiveData(), createStringInBackground);
}
private static @NonNull Spannable toSpannable(@NonNull Context context, @NonNull UpdateDescription updateDescription, @NonNull String string) {
boolean isDarkTheme = ThemeUtil.isDarkTheme(context);
int drawableResource = isDarkTheme ? updateDescription.getDarkIconResource() : updateDescription.getLightIconResource();
if (drawableResource == 0) {
return new SpannableString(string);
} else {
Drawable drawable = ContextCompat.getDrawable(context, drawableResource);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
drawable.setColorFilter(ThemeUtil.getThemedColor(context, R.attr.conversation_item_update_text_color), PorterDuff.Mode.SRC_ATOP);
return new SpannableStringBuilder().append(SpanUtil.buildImageSpan(drawable)).append(" ").append(string);
}
}
}

View File

@@ -22,6 +22,7 @@ import android.text.SpannableString;
import android.text.style.RelativeSizeSpan;
import android.text.style.StyleSpan;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -38,6 +39,7 @@ import org.thoughtcrime.securesms.profiles.ProfileName;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.DateUtils;
import org.thoughtcrime.securesms.util.ExpirationUtil;
import org.thoughtcrime.securesms.util.GroupUtil;
import org.thoughtcrime.securesms.util.StringUtil;
@@ -48,6 +50,7 @@ import org.whispersystems.signalservice.api.util.UuidUtil;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
/**
@@ -127,43 +130,43 @@ public abstract class MessageRecord extends DisplayRecord {
if (isGroupUpdate() && isGroupV2()) {
return getGv2ChangeDescription(context, getBody());
} else if (isGroupUpdate() && isOutgoing()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_you_updated_group));
return staticUpdateDescription(context.getString(R.string.MessageRecord_you_updated_group), R.drawable.ic_update_group_light_16, R.drawable.ic_update_group_dark_16);
} else if (isGroupUpdate()) {
return fromRecipient(getIndividualRecipient(), r -> GroupUtil.getNonV2GroupDescription(context, getBody()).toString(r));
return fromRecipient(getIndividualRecipient(), r -> GroupUtil.getNonV2GroupDescription(context, getBody()).toString(r), R.drawable.ic_update_group_light_16, R.drawable.ic_update_group_dark_16);
} else if (isGroupQuit() && isOutgoing()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_left_group));
return staticUpdateDescription(context.getString(R.string.MessageRecord_left_group), R.drawable.ic_update_group_leave_light_16, R.drawable.ic_update_group_leave_dark_16);
} else if (isGroupQuit()) {
return fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.ConversationItem_group_action_left, r.getDisplayName(context)));
return fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.ConversationItem_group_action_left, r.getDisplayName(context)), R.drawable.ic_update_group_leave_light_16, R.drawable.ic_update_group_leave_dark_16);
} else if (isIncomingCall()) {
return fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_s_called_you, r.getDisplayName(context)));
return fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_s_called_you_date, r.getDisplayName(context), getCallDateString()), R.drawable.ic_update_audio_call_incoming_light_16, R.drawable.ic_update_audio_call_incoming_dark_16);
} else if (isOutgoingCall()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_you_called));
return staticUpdateDescription(context.getString(R.string.MessageRecord_you_called_date, getCallDateString()), R.drawable.ic_update_audio_call_outgoing_light_16, R.drawable.ic_update_audio_call_outgoing_dark_16);
} else if (isMissedCall()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_missed_call));
return staticUpdateDescription(context.getString(R.string.MessageRecord_missed_call_date, getCallDateString()), R.drawable.ic_update_audio_call_missed_light_16, R.drawable.ic_update_audio_call_missed_dark_16);
} else if (isJoined()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_s_joined_signal, getIndividualRecipient().getDisplayName(context)));
return staticUpdateDescription(context.getString(R.string.MessageRecord_s_joined_signal, getIndividualRecipient().getDisplayName(context)), R.drawable.ic_update_group_add_light_16, R.drawable.ic_update_group_add_dark_16);
} else if (isExpirationTimerUpdate()) {
int seconds = (int)(getExpiresIn() / 1000);
if (seconds <= 0) {
return isOutgoing() ? staticUpdateDescription(context.getString(R.string.MessageRecord_you_disabled_disappearing_messages))
: fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_s_disabled_disappearing_messages, r.getDisplayName(context)));
return isOutgoing() ? staticUpdateDescription(context.getString(R.string.MessageRecord_you_disabled_disappearing_messages), R.drawable.ic_update_timer_disabled_light_16, R.drawable.ic_update_timer_disabled_dark_16)
: fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_s_disabled_disappearing_messages, r.getDisplayName(context)), R.drawable.ic_update_timer_disabled_light_16, R.drawable.ic_update_timer_disabled_dark_16);
}
String time = ExpirationUtil.getExpirationDisplayValue(context, seconds);
return isOutgoing() ? staticUpdateDescription(context.getString(R.string.MessageRecord_you_set_disappearing_message_time_to_s, time))
: fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_s_set_disappearing_message_time_to_s, r.getDisplayName(context), time));
return isOutgoing() ? staticUpdateDescription(context.getString(R.string.MessageRecord_you_set_disappearing_message_time_to_s, time), R.drawable.ic_update_timer_light_16, R.drawable.ic_update_timer_dark_16)
: fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_s_set_disappearing_message_time_to_s, r.getDisplayName(context), time), R.drawable.ic_update_timer_light_16, R.drawable.ic_update_timer_dark_16);
} else if (isIdentityUpdate()) {
return fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_your_safety_number_with_s_has_changed, r.getDisplayName(context)));
return fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_your_safety_number_with_s_has_changed, r.getDisplayName(context)), R.drawable.ic_update_safety_number_light_16, R.drawable.ic_update_safety_number_dark_16);
} else if (isIdentityVerified()) {
if (isOutgoing()) return fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_verified, r.getDisplayName(context)));
else return fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_verified_from_another_device, r.getDisplayName(context)));
if (isOutgoing()) return fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_verified, r.getDisplayName(context)), R.drawable.ic_update_verified_light_16, R.drawable.ic_update_verified_dark_16);
else return fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_verified_from_another_device, r.getDisplayName(context)), R.drawable.ic_update_verified_light_16, R.drawable.ic_update_verified_dark_16);
} else if (isIdentityDefault()) {
if (isOutgoing()) return fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_unverified, r.getDisplayName(context)));
else return fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_unverified_from_another_device, r.getDisplayName(context)));
if (isOutgoing()) return fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_unverified, r.getDisplayName(context)), R.drawable.ic_update_info_light_16, R.drawable.ic_update_info_dark_16);
else return fromRecipient(getIndividualRecipient(), r -> context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_unverified_from_another_device, r.getDisplayName(context)), R.drawable.ic_update_info_light_16, R.drawable.ic_update_info_dark_16);
} else if (isProfileChange()) {
return staticUpdateDescription(getProfileChangeDescription(context));
return staticUpdateDescription(getProfileChangeDescription(context), R.drawable.ic_update_profile_light_16, R.drawable.ic_update_profile_dark_16);
} else if (isEndSession()) {
if (isOutgoing()) return staticUpdateDescription(context.getString(R.string.SmsMessageRecord_secure_session_reset));
else return fromRecipient(getIndividualRecipient(), r-> context.getString(R.string.SmsMessageRecord_secure_session_reset_s, r.getDisplayName(context)));
if (isOutgoing()) return staticUpdateDescription(context.getString(R.string.SmsMessageRecord_secure_session_reset), R.drawable.ic_update_info_light_16, R.drawable.ic_update_info_dark_16);
else return fromRecipient(getIndividualRecipient(), r-> context.getString(R.string.SmsMessageRecord_secure_session_reset_s, r.getDisplayName(context)), R.drawable.ic_update_info_light_16, R.drawable.ic_update_info_dark_16);
}
return null;
@@ -183,7 +186,7 @@ public abstract class MessageRecord extends DisplayRecord {
}
} catch (IOException e) {
Log.w(TAG, "GV2 Message update detail could not be read", e);
return staticUpdateDescription(context.getString(R.string.MessageRecord_group_updated));
return staticUpdateDescription(context.getString(R.string.MessageRecord_group_updated), R.drawable.ic_update_group_light_16, R.drawable.ic_update_group_dark_16);
}
}
@@ -210,12 +213,26 @@ public abstract class MessageRecord extends DisplayRecord {
}
}
private static @NonNull UpdateDescription fromRecipient(@NonNull Recipient recipient, @NonNull Function<Recipient, String> stringFunction) {
return UpdateDescription.mentioning(Collections.singletonList(recipient.getUuid().or(UuidUtil.UNKNOWN_UUID)), () -> stringFunction.apply(recipient.resolve()));
private @NonNull String getCallDateString() {
return DateUtils.getBriefExactTimeString(Locale.getDefault(), getDateSent());
}
private static @NonNull UpdateDescription staticUpdateDescription(@NonNull String string) {
return UpdateDescription.staticDescription(string);
private static @NonNull UpdateDescription fromRecipient(@NonNull Recipient recipient,
@NonNull Function<Recipient, String> stringGenerator,
@DrawableRes int lightIconResource,
@DrawableRes int darkIconResource)
{
return UpdateDescription.mentioning(Collections.singletonList(recipient.getUuid().or(UuidUtil.UNKNOWN_UUID)),
() -> stringGenerator.apply(recipient.resolve()),
lightIconResource,
darkIconResource);
}
private static @NonNull UpdateDescription staticUpdateDescription(@NonNull String string,
@DrawableRes int lightIconResource,
@DrawableRes int darkIconResource)
{
return UpdateDescription.staticDescription(string, lightIconResource, darkIconResource);
}
private @NonNull String getProfileChangeDescription(@NonNull Context context) {

View File

@@ -1,6 +1,7 @@
package org.thoughtcrime.securesms.database.model;
import androidx.annotation.AnyThread;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
@@ -28,17 +29,23 @@ public final class UpdateDescription {
private final Collection<UUID> mentioned;
private final StringFactory stringFactory;
private final String staticString;
private final int lightIconResource;
private final int darkIconResource;
private UpdateDescription(@NonNull Collection<UUID> mentioned,
@Nullable StringFactory stringFactory,
@Nullable String staticString)
@Nullable String staticString,
@DrawableRes int lightIconResource,
@DrawableRes int darkIconResource)
{
if (staticString == null && stringFactory == null) {
throw new AssertionError();
}
this.mentioned = mentioned;
this.stringFactory = stringFactory;
this.staticString = staticString;
this.mentioned = mentioned;
this.stringFactory = stringFactory;
this.staticString = staticString;
this.lightIconResource = lightIconResource;
this.darkIconResource = darkIconResource;
}
/**
@@ -49,18 +56,25 @@ public final class UpdateDescription {
* @param stringFactory The background method for generating the string.
*/
public static UpdateDescription mentioning(@NonNull Collection<UUID> mentioned,
@NonNull StringFactory stringFactory)
@NonNull StringFactory stringFactory,
@DrawableRes int lightIconResource,
@DrawableRes int darkIconResource)
{
return new UpdateDescription(UuidUtil.filterKnown(mentioned),
stringFactory,
null);
null,
lightIconResource,
darkIconResource);
}
/**
* Create an update description that's string value is fixed.
*/
public static UpdateDescription staticDescription(@NonNull String staticString) {
return new UpdateDescription(Collections.emptyList(), null, staticString);
public static UpdateDescription staticDescription(@NonNull String staticString,
@DrawableRes int lightIconResource,
@DrawableRes int darkIconResource)
{
return new UpdateDescription(Collections.emptyList(), null, staticString, lightIconResource, darkIconResource);
}
public boolean isStringStatic() {
@@ -93,6 +107,14 @@ public final class UpdateDescription {
return mentioned;
}
public @DrawableRes int getLightIconResource() {
return lightIconResource;
}
public @DrawableRes int getDarkIconResource() {
return darkIconResource;
}
public static UpdateDescription concatWithNewLines(@NonNull List<UpdateDescription> updateDescriptions) {
if (updateDescriptions.size() == 0) {
throw new AssertionError();
@@ -103,7 +125,9 @@ public final class UpdateDescription {
}
if (allAreStatic(updateDescriptions)) {
return UpdateDescription.staticDescription(concatStaticLines(updateDescriptions));
return UpdateDescription.staticDescription(concatStaticLines(updateDescriptions),
updateDescriptions.get(0).getLightIconResource(),
updateDescriptions.get(0).getDarkIconResource());
}
Set<UUID> allMentioned = new HashSet<>();
@@ -112,7 +136,10 @@ public final class UpdateDescription {
allMentioned.addAll(updateDescription.getMentioned());
}
return UpdateDescription.mentioning(allMentioned, () -> concatLines(updateDescriptions));
return UpdateDescription.mentioning(allMentioned,
() -> concatLines(updateDescriptions),
updateDescriptions.get(0).getLightIconResource(),
updateDescriptions.get(0).getDarkIconResource());
}
private static boolean allAreStatic(@NonNull Collection<UpdateDescription> updateDescriptions) {

View File

@@ -27,6 +27,7 @@ import androidx.annotation.Nullable;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.logging.Log;
import java.text.DateFormatSymbols;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
@@ -40,8 +41,9 @@ import java.util.concurrent.TimeUnit;
public class DateUtils extends android.text.format.DateUtils {
@SuppressWarnings("unused")
private static final String TAG = DateUtils.class.getSimpleName();
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd");
private static final String TAG = DateUtils.class.getSimpleName();
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd");
private static final SimpleDateFormat BRIEF_EXACT_FORMAT = new SimpleDateFormat();
private static boolean isWithin(final long millis, final long span, final TimeUnit unit) {
return System.currentTimeMillis() - millis <= unit.toMillis(span);
@@ -172,6 +174,16 @@ public class DateUtils extends android.text.format.DateUtils {
return getExtendedRelativeTimeSpanString(context, locale, t1).equals(getExtendedRelativeTimeSpanString(context, locale, t2));
}
public static String getBriefExactTimeString(@NonNull Locale locale, long timestamp) {
SimpleDateFormat format = new SimpleDateFormat(getLocalizedPattern("MMM dd, hh:mm a", locale), locale);
DateFormatSymbols symbols = new DateFormatSymbols(locale);
symbols.setAmPmStrings(new String[] { "am", "pm"});
format.setDateFormatSymbols(symbols);
return format.format(timestamp);
}
private static String getLocalizedPattern(String template, Locale locale) {
return DateFormat.getBestDateTimePattern(locale, template);
}

View File

@@ -1,11 +1,14 @@
package org.thoughtcrime.securesms.util;
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.DynamicDrawableSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.ImageSpan;
import android.text.style.RelativeSizeSpan;
@@ -14,6 +17,9 @@ import android.text.style.URLSpan;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import org.thoughtcrime.securesms.R;
public class SpanUtil {
@@ -50,4 +56,12 @@ public class SpanUtil {
spannable.setSpan(new ForegroundColorSpan(color), 0, sequence.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return spannable;
}
public static CharSequence buildImageSpan(@NonNull Drawable drawable) {
SpannableString imageSpan = new SpannableString(" ");
imageSpan.setSpan(new ImageSpan(drawable, DynamicDrawableSpan.ALIGN_CENTER), 0, imageSpan.length(), 0);
return imageSpan;
}
}