Replace glyphs in group update messages.

This commit is contained in:
Sagar
2025-04-22 20:04:52 +05:30
committed by Cody Henthorne
parent 919f03522a
commit 9f40bfc645
15 changed files with 390 additions and 421 deletions

View File

@@ -7,6 +7,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.fonts.SignalSymbols.Glyph;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
@@ -93,7 +94,7 @@ public class InMemoryMessageRecord extends MessageRecord {
@Override
public @Nullable UpdateDescription getUpdateDisplayBody(@NonNull Context context, @Nullable Consumer<RecipientId> recipientClickHandler) {
return UpdateDescription.staticDescription(context.getString(R.string.ConversationUpdateItem_hidden_contact_message_to_add_back),
R.drawable.symbol_info_compact_16);
Glyph.INFO);
}
@Override
@@ -122,7 +123,7 @@ public class InMemoryMessageRecord extends MessageRecord {
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()));
return UpdateDescription.staticDescription(update, R.drawable.symbol_timer_compact_24);
return UpdateDescription.staticDescription(update, Glyph.TIMER);
}
@Override

View File

@@ -1,11 +1,6 @@
package org.thoughtcrime.securesms.database.model;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
@@ -18,12 +13,13 @@ import androidx.lifecycle.Transformations;
import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.fonts.SignalSymbols;
import org.thoughtcrime.securesms.fonts.SignalSymbols.Glyph;
import org.thoughtcrime.securesms.fonts.SignalSymbols.Weight;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.ContextUtil;
import org.thoughtcrime.securesms.util.SpanUtil;
import org.thoughtcrime.securesms.util.ThemeUtil;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
import java.util.List;
@@ -66,31 +62,25 @@ public final class LiveUpdateMessage {
}
private static @NonNull SpannableString toSpannable(@NonNull Context context, @NonNull UpdateDescription updateDescription, @NonNull Spannable string, @ColorInt int defaultTint, boolean adjustPosition) {
boolean isDarkTheme = ThemeUtil.isDarkTheme(context);
int drawableResource = updateDescription.getIconResource();
int tint = isDarkTheme ? updateDescription.getDarkTint() : updateDescription.getLightTint();
boolean isDarkTheme = ThemeUtil.isDarkTheme(context);
Glyph glyph = updateDescription.getGlyph();
int tint = isDarkTheme ? updateDescription.getDarkTint() : updateDescription.getLightTint();
if (tint == 0) {
tint = defaultTint;
}
if (drawableResource == 0) {
if (glyph == null) {
return new SpannableString(string);
} else {
Drawable drawable = ContextUtil.requireDrawable(context, drawableResource);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
drawable.setColorFilter(tint, PorterDuff.Mode.SRC_ATOP);
SpannableStringBuilder builder = new SpannableStringBuilder();
CharSequence glyphChar = SignalSymbols.getSpannedString(context, Weight.REGULAR, glyph, -1);
int insetTop = adjustPosition ? ViewUtil.dpToPx(2) : 0;
InsetDrawable insetDrawable = new InsetDrawable(drawable, 0, insetTop, 0, 0);
insetDrawable.setBounds(0, 0, drawable.getIntrinsicWidth(), insetDrawable.getIntrinsicHeight());
builder.append(glyphChar);
builder.append(" ");
builder.append(string);
Drawable spaceDrawable = new ColorDrawable(Color.TRANSPARENT);
spaceDrawable.setBounds(0, 0, ViewUtil.dpToPx(8), drawable.getIntrinsicHeight());
Spannable stringWithImage = new SpannableStringBuilder().append(SpanUtil.buildImageSpan(drawable)).append(SpanUtil.buildImageSpan(spaceDrawable)).append(string);
return new SpannableString(SpanUtil.color(tint, stringWithImage));
return new SpannableString(SpanUtil.color(tint, builder));
}
}
}

View File

@@ -29,6 +29,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
import androidx.compose.ui.text.AnnotatedString;
import androidx.core.content.ContextCompat;
import com.annimon.stream.Stream;
@@ -54,6 +55,9 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.SessionSwitchove
import org.thoughtcrime.securesms.database.model.databaseprotos.ThreadMergeEvent;
import org.thoughtcrime.securesms.emoji.EmojiSource;
import org.thoughtcrime.securesms.emoji.JumboEmoji;
import org.thoughtcrime.securesms.fonts.SignalSymbols;
import org.thoughtcrime.securesms.fonts.SignalSymbols.Glyph;
import org.thoughtcrime.securesms.fonts.SignalSymbols.Weight;
import org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.mms.Slide;
@@ -193,69 +197,69 @@ public abstract class MessageRecord extends DisplayRecord {
return getGv2ChangeDescription(context, getBody(), recipientClickHandler);
}
} 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), Glyph.GROUP);
} else if (isGroupUpdate()) {
return fromRecipient(getFromRecipient(), r -> GroupUtil.getNonV2GroupDescription(context, getBody()).toString(r), R.drawable.ic_update_group_16);
return fromRecipient(getFromRecipient(), r -> GroupUtil.getNonV2GroupDescription(context, getBody()).toString(r), Glyph.GROUP);
} else if (isGroupQuit() && isOutgoing()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_left_group), R.drawable.ic_update_group_leave_16);
return staticUpdateDescription(context.getString(R.string.MessageRecord_left_group), Glyph.LEAVE);
} else if (isGroupQuit()) {
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.ConversationItem_group_action_left, r.getDisplayName(context)), R.drawable.ic_update_group_leave_16);
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.ConversationItem_group_action_left, r.getDisplayName(context)), Glyph.LEAVE);
} else if (isIncomingAudioCall()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(R.string.MessageRecord_incoming_voice_call), getCallDateString(context)), R.drawable.ic_update_audio_call_incoming_16);
return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(R.string.MessageRecord_incoming_voice_call), getCallDateString(context)), Glyph.PHONE);
} else if (isIncomingVideoCall()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(R.string.MessageRecord_incoming_video_call), getCallDateString(context)), R.drawable.ic_update_video_call_incoming_16);
return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(R.string.MessageRecord_incoming_video_call), getCallDateString(context)), Glyph.VIDEO_CAMERA);
} else if (isOutgoingAudioCall()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(R.string.MessageRecord_outgoing_voice_call), getCallDateString(context)), R.drawable.ic_update_audio_call_outgoing_16);
return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(R.string.MessageRecord_outgoing_voice_call), getCallDateString(context)), Glyph.PHONE);
} else if (isOutgoingVideoCall()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(R.string.MessageRecord_outgoing_video_call), getCallDateString(context)), R.drawable.ic_update_video_call_outgoing_16);
return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(R.string.MessageRecord_outgoing_video_call), getCallDateString(context)), Glyph.VIDEO_CAMERA);
} else if (isMissedAudioCall()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(R.string.MessageRecord_missed_voice_call), getCallDateString(context)), R.drawable.ic_update_audio_call_missed_16, ContextCompat.getColor(context, R.color.core_red_shade), ContextCompat.getColor(context, R.color.core_red));
return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(R.string.MessageRecord_missed_voice_call), getCallDateString(context)), Glyph.PHONE, ContextCompat.getColor(context, R.color.core_red_shade), ContextCompat.getColor(context, R.color.core_red));
} else if (isMissedVideoCall()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(R.string.MessageRecord_missed_video_call), getCallDateString(context)), R.drawable.ic_update_video_call_missed_16, ContextCompat.getColor(context, R.color.core_red_shade), ContextCompat.getColor(context, R.color.core_red));
return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(R.string.MessageRecord_missed_video_call), getCallDateString(context)), Glyph.VIDEO_CAMERA, ContextCompat.getColor(context, R.color.core_red_shade), ContextCompat.getColor(context, R.color.core_red));
} else if (isGroupCall()) {
return getGroupCallUpdateDescription(context, getBody(), true);
} else if (isJoined()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_s_joined_signal, getFromRecipient().getDisplayName(context)), R.drawable.ic_update_group_add_16);
return staticUpdateDescription(context.getString(R.string.MessageRecord_s_joined_signal, getFromRecipient().getDisplayName(context)), Glyph.PERSON_PLUS);
} else if (isExpirationTimerUpdate()) {
int seconds = (int)(getExpiresIn() / 1000);
if (seconds <= 0) {
return isOutgoing() ? staticUpdateDescription(context.getString(R.string.MessageRecord_you_disabled_disappearing_messages), R.drawable.ic_update_timer_disabled_16)
: fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_s_disabled_disappearing_messages, r.getDisplayName(context)), R.drawable.ic_update_timer_disabled_16);
return isOutgoing() ? staticUpdateDescription(context.getString(R.string.MessageRecord_you_disabled_disappearing_messages), Glyph.TIMER_SLASH)
: fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_s_disabled_disappearing_messages, r.getDisplayName(context)), Glyph.TIMER_SLASH);
}
String time = ExpirationUtil.getExpirationDisplayValue(context, seconds);
return isOutgoing() ? staticUpdateDescription(context.getString(R.string.MessageRecord_you_set_disappearing_message_time_to_s, time), R.drawable.ic_update_timer_16)
: fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_s_set_disappearing_message_time_to_s, r.getDisplayName(context), time), R.drawable.ic_update_timer_16);
return isOutgoing() ? staticUpdateDescription(context.getString(R.string.MessageRecord_you_set_disappearing_message_time_to_s, time), Glyph.TIMER)
: fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_s_set_disappearing_message_time_to_s, r.getDisplayName(context), time), Glyph.TIMER);
} else if (isIdentityUpdate()) {
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_your_safety_number_with_s_has_changed, r.getDisplayName(context)), R.drawable.ic_update_safety_number_16);
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_your_safety_number_with_s_has_changed, r.getDisplayName(context)), Glyph.SAFETY_NUMBER);
} else if (isIdentityVerified()) {
if (isOutgoing()) return fromRecipient(getToRecipient(), r -> context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_verified, r.getDisplayName(context)), R.drawable.ic_safety_number_16);
else return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_verified_from_another_device, r.getDisplayName(context)), R.drawable.ic_safety_number_16);
if (isOutgoing()) return fromRecipient(getToRecipient(), r -> context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_verified, r.getDisplayName(context)), Glyph.SAFETY_NUMBER);
else return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_verified_from_another_device, r.getDisplayName(context)), Glyph.SAFETY_NUMBER);
} else if (isIdentityDefault()) {
if (isOutgoing()) return fromRecipient(getToRecipient(), r -> context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_unverified, r.getDisplayName(context)), R.drawable.ic_update_info_16);
else return fromRecipient(getFromRecipient(), 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_16);
if (isOutgoing()) return fromRecipient(getToRecipient(), r -> context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_unverified, r.getDisplayName(context)), Glyph.INFO);
else return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_you_marked_your_safety_number_with_s_unverified_from_another_device, r.getDisplayName(context)), Glyph.INFO);
} else if (isProfileChange()) {
return getProfileChangeDescription(context);
} else if (isChangeNumber()) {
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_s_changed_their_phone_number, r.getDisplayName(context)), R.drawable.ic_phone_16);
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_s_changed_their_phone_number, r.getDisplayName(context)), Glyph.PHONE);
} else if (isReleaseChannelDonationRequest()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_like_this_new_feature_help_support_signal_with_a_one_time_donation), 0);
return staticUpdateDescription(context.getString(R.string.MessageRecord_like_this_new_feature_help_support_signal_with_a_one_time_donation), null);
} else if (isEndSession()) {
if (isOutgoing()) return staticUpdateDescription(context.getString(R.string.SmsMessageRecord_secure_session_reset), R.drawable.ic_update_info_16);
else return fromRecipient(getFromRecipient(), r-> context.getString(R.string.SmsMessageRecord_secure_session_reset_s, r.getDisplayName(context)), R.drawable.ic_update_info_16);
if (isOutgoing()) return staticUpdateDescription(context.getString(R.string.SmsMessageRecord_secure_session_reset), Glyph.INFO);
else return fromRecipient(getFromRecipient(), r-> context.getString(R.string.SmsMessageRecord_secure_session_reset_s, r.getDisplayName(context)), Glyph.INFO);
} else if (isGroupV1MigrationEvent()) {
return getGroupMigrationEventDescription(context);
} else if (isChatSessionRefresh()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_chat_session_refreshed), R.drawable.ic_refresh_16);
return staticUpdateDescription(context.getString(R.string.MessageRecord_chat_session_refreshed), Glyph.REFRESH);
} else if (isBadDecryptType()) {
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_a_message_from_s_couldnt_be_delivered, r.getDisplayName(context)), R.drawable.ic_error_outline_14);
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_a_message_from_s_couldnt_be_delivered, r.getDisplayName(context)), Glyph.ERROR);
} else if (isThreadMergeEventType()) {
try {
ThreadMergeEvent event = ThreadMergeEvent.ADAPTER.decode(Base64.decodeOrThrow(getBody()));
if (event.previousE164.isEmpty()) {
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_your_message_history_with_s_and_another_chat_has_been_merged, r.getDisplayName(context)), R.drawable.ic_thread_merge_16);
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_your_message_history_with_s_and_another_chat_has_been_merged, r.getDisplayName(context)), Glyph.MERGE);
} else {
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_your_message_history_with_s_and_their_number_s_has_been_merged, r.getDisplayName(context), SignalE164Util.prettyPrint(event.previousE164)), R.drawable.ic_thread_merge_16);
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_your_message_history_with_s_and_their_number_s_has_been_merged, r.getDisplayName(context), SignalE164Util.prettyPrint(event.previousE164)), Glyph.MERGE);
}
} catch (IOException e) {
throw new AssertionError(e);
@@ -265,30 +269,30 @@ public abstract class MessageRecord extends DisplayRecord {
SessionSwitchoverEvent event = SessionSwitchoverEvent.ADAPTER.decode(Base64.decodeOrThrow(getBody()));
if (event.e164.isEmpty()) {
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_your_safety_number_with_s_has_changed, r.getDisplayName(context)), R.drawable.ic_update_safety_number_16);
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_your_safety_number_with_s_has_changed, r.getDisplayName(context)), Glyph.SAFETY_NUMBER);
} else {
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_s_belongs_to_s, SignalE164Util.prettyPrint(event.e164), r.getDisplayName(context)), R.drawable.ic_update_info_16);
return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_s_belongs_to_s, SignalE164Util.prettyPrint(event.e164), r.getDisplayName(context)), Glyph.INFO);
}
} catch (IOException e) {
throw new AssertionError(e);
}
} else if (isSmsExportType()) {
int messageResource = R.string.MessageRecord__you_can_no_longer_send_sms_messages_in_signal;
return fromRecipient(getFromRecipient(), r -> context.getString(messageResource, r.getDisplayName(context)), R.drawable.ic_update_info_16);
return fromRecipient(getFromRecipient(), r -> context.getString(messageResource, r.getDisplayName(context)), Glyph.INFO);
} else if (isPaymentsRequestToActivate()) {
return isOutgoing() ? fromRecipient(getToRecipient(), r -> context.getString(R.string.MessageRecord_you_sent_request, r.getShortDisplayName(context)), R.drawable.ic_card_activate_payments)
: fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_wants_you_to_activate_payments, r.getShortDisplayName(context)), R.drawable.ic_card_activate_payments);
return isOutgoing() ? fromRecipient(getToRecipient(), r -> context.getString(R.string.MessageRecord_you_sent_request, r.getShortDisplayName(context)), Glyph.ACTIVATE_PAYMENTS)
: fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_wants_you_to_activate_payments, r.getShortDisplayName(context)), Glyph.ACTIVATE_PAYMENTS);
} else if (isPaymentsActivated()) {
return isOutgoing() ? staticUpdateDescription(context.getString(R.string.MessageRecord_you_activated_payments), R.drawable.ic_card_activate_payments)
: fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_can_accept_payments, r.getShortDisplayName(context)), R.drawable.ic_card_activate_payments);
return isOutgoing() ? staticUpdateDescription(context.getString(R.string.MessageRecord_you_activated_payments), Glyph.ACTIVATE_PAYMENTS)
: fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_can_accept_payments, r.getShortDisplayName(context)), Glyph.ACTIVATE_PAYMENTS);
} else if (isReportedSpam()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_reported_as_spam), R.drawable.symbol_spam_16);
return staticUpdateDescription(context.getString(R.string.MessageRecord_reported_as_spam), Glyph.SPAM);
} else if (isMessageRequestAccepted()) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_you_accepted_the_message_request), R.drawable.symbol_thread_16);
return staticUpdateDescription(context.getString(R.string.MessageRecord_you_accepted_the_message_request), Glyph.THREAD);
} else if (isBlocked()) {
return staticUpdateDescription(context.getString(isGroupV2() ? R.string.MessageRecord_you_blocked_this_group : R.string.MessageRecord_you_blocked_this_person), R.drawable.symbol_block_16);
return staticUpdateDescription(context.getString(isGroupV2() ? R.string.MessageRecord_you_blocked_this_group : R.string.MessageRecord_you_blocked_this_person), Glyph.BLOCK);
} else if (isUnblocked()) {
return staticUpdateDescription(context.getString(isGroupV2() ? R.string.MessageRecord_you_unblocked_this_group : R.string.MessageRecord_you_unblocked_this_person) , R.drawable.symbol_thread_16);
return staticUpdateDescription(context.getString(isGroupV2() ? R.string.MessageRecord_you_unblocked_this_group : R.string.MessageRecord_you_unblocked_this_person) , Glyph.THREAD);
}
return null;
@@ -352,7 +356,7 @@ public abstract class MessageRecord extends DisplayRecord {
return getGv2ChangeDescription(context, decryptedGroupV2Context, recipientClickHandler);
} catch (IOException | IllegalArgumentException | IllegalStateException e) {
Log.w(TAG, "GV2 Message update detail could not be read", e);
return staticUpdateDescription(context.getString(R.string.MessageRecord_group_updated), R.drawable.ic_update_group_16);
return staticUpdateDescription(context.getString(R.string.MessageRecord_group_updated), Glyph.GROUP);
}
}
@@ -368,7 +372,7 @@ public abstract class MessageRecord extends DisplayRecord {
Log.w(TAG, "GV2 Update Description missing group change update!");
}
}
return staticUpdateDescription(context.getString(R.string.MessageRecord_group_updated), R.drawable.ic_update_group_16);
return staticUpdateDescription(context.getString(R.string.MessageRecord_group_updated), Glyph.GROUP);
}
public static @NonNull UpdateDescription getGv2ChangeDescription(@NonNull Context context, @NonNull DecryptedGroupV2Context decryptedGroupV2Context, @Nullable Consumer<RecipientId> recipientClickHandler) {
@@ -386,20 +390,20 @@ public abstract class MessageRecord extends DisplayRecord {
}
if (selfCreatedGroup(decryptedGroupV2Context.change)) {
newGroupDescriptions.add(staticUpdateDescription(context.getString(R.string.MessageRecord_invite_friends_to_this_group), 0));
newGroupDescriptions.add(staticUpdateDescription(context.getString(R.string.MessageRecord_invite_friends_to_this_group), null));
}
return concatWithNewLinesCapped(context, newGroupDescriptions);
}
} catch (IllegalArgumentException | IllegalStateException e) {
Log.w(TAG, "GV2 Message update detail could not be read", e);
return staticUpdateDescription(context.getString(R.string.MessageRecord_group_updated), R.drawable.ic_update_group_16);
return staticUpdateDescription(context.getString(R.string.MessageRecord_group_updated), Glyph.GROUP);
}
}
private static @NonNull UpdateDescription concatWithNewLinesCapped(@NonNull Context context, @NonNull List<UpdateDescription> updateDescriptions) {
if (updateDescriptions.size() > 100) {
// Arbitrary update description collapse cap, otherwise the long string can cause issues
return staticUpdateDescription(context.getString(R.string.MessageRecord_group_updated), R.drawable.ic_update_group_16);
return staticUpdateDescription(context.getString(R.string.MessageRecord_group_updated), Glyph.GROUP);
}
return UpdateDescription.concatWithNewLines(updateDescriptions);
}
@@ -437,25 +441,25 @@ public abstract class MessageRecord extends DisplayRecord {
protected static @NonNull UpdateDescription fromRecipient(@NonNull Recipient recipient,
@NonNull Function<Recipient, String> stringGenerator,
@DrawableRes int iconResource)
Glyph glyph)
{
return UpdateDescription.mentioning(Collections.singletonList(recipient.getAci().orElse(ACI.UNKNOWN)),
() -> new SpannableString(stringGenerator.apply(recipient.resolve())),
iconResource);
glyph);
}
protected static @NonNull UpdateDescription staticUpdateDescription(@NonNull String string,
@DrawableRes int iconResource)
Glyph glyph)
{
return UpdateDescription.staticDescription(string, iconResource);
return UpdateDescription.staticDescription(string, glyph);
}
protected static @NonNull UpdateDescription staticUpdateDescription(@NonNull String string,
@DrawableRes int iconResource,
Glyph glyph,
@ColorInt int lightTint,
@ColorInt int darkTint)
{
return UpdateDescription.staticDescription(string, iconResource, lightTint, darkTint);
return UpdateDescription.staticDescription(string, glyph, lightTint, darkTint);
}
private @NonNull UpdateDescription getProfileChangeDescription(@NonNull Context context) {
@@ -486,9 +490,9 @@ public abstract class MessageRecord extends DisplayRecord {
updateMessage = context.getString(R.string.MessageRecord_changed_their_profile_name_to, previousName, newName);
}
return staticUpdateDescription(updateMessage, R.drawable.ic_update_profile_16);
return staticUpdateDescription(updateMessage, Glyph.PERSON);
} else if (profileChangeDetails.deprecatedLearnedProfileName != null) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_started_this_chat, profileChangeDetails.deprecatedLearnedProfileName.previous), R.drawable.symbol_thread_16);
return staticUpdateDescription(context.getString(R.string.MessageRecord_started_this_chat, profileChangeDetails.deprecatedLearnedProfileName.previous), Glyph.THREAD);
} else if (profileChangeDetails.learnedProfileName != null) {
String previouslyKnownAs;
if (!Util.isEmpty(profileChangeDetails.learnedProfileName.e164)) {
@@ -498,31 +502,31 @@ public abstract class MessageRecord extends DisplayRecord {
}
if (!Util.isEmpty(previouslyKnownAs)) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_started_this_chat, previouslyKnownAs), R.drawable.symbol_thread_16);
return staticUpdateDescription(context.getString(R.string.MessageRecord_started_this_chat, previouslyKnownAs), Glyph.THREAD);
}
}
}
return staticUpdateDescription(context.getString(R.string.MessageRecord_changed_their_profile, getFromRecipient().getDisplayName(context)), R.drawable.ic_update_profile_16);
return staticUpdateDescription(context.getString(R.string.MessageRecord_changed_their_profile, getFromRecipient().getDisplayName(context)), Glyph.PERSON);
}
private UpdateDescription getGroupMigrationEventDescription(@NonNull Context context) {
if (Util.isEmpty(getBody())) {
return staticUpdateDescription(context.getString(R.string.MessageRecord_this_group_was_updated_to_a_new_group), R.drawable.ic_update_group_role_16);
return staticUpdateDescription(context.getString(R.string.MessageRecord_this_group_was_updated_to_a_new_group), Glyph.MEGAPHONE);
} else {
GroupMigrationMembershipChange change = getGroupV1MigrationMembershipChanges();
List<UpdateDescription> updates = new ArrayList<>(2);
if (change.getPending().size() == 1 && change.getPending().get(0).equals(Recipient.self().getId())) {
updates.add(staticUpdateDescription(context.getString(R.string.MessageRecord_you_couldnt_be_added_to_the_new_group_and_have_been_invited_to_join), R.drawable.ic_update_group_add_16));
updates.add(staticUpdateDescription(context.getString(R.string.MessageRecord_you_couldnt_be_added_to_the_new_group_and_have_been_invited_to_join), Glyph.PERSON_PLUS));
} else if (change.getPending().size() > 0) {
int count = change.getPending().size();
updates.add(staticUpdateDescription(context.getResources().getQuantityString(R.plurals.MessageRecord_members_couldnt_be_added_to_the_new_group_and_have_been_invited, count, count), R.drawable.ic_update_group_add_16));
updates.add(staticUpdateDescription(context.getResources().getQuantityString(R.plurals.MessageRecord_members_couldnt_be_added_to_the_new_group_and_have_been_invited, count, count), Glyph.PERSON_PLUS));
}
if (change.getDropped().size() > 0) {
int count = change.getDropped().size();
updates.add(staticUpdateDescription(context.getResources().getQuantityString(R.plurals.MessageRecord_members_couldnt_be_added_to_the_new_group_and_have_been_removed, count, count), R.drawable.ic_update_group_remove_16));
updates.add(staticUpdateDescription(context.getResources().getQuantityString(R.plurals.MessageRecord_members_couldnt_be_added_to_the_new_group_and_have_been_removed, count, count), Glyph.PERSON_MINUS));
}
return concatWithNewLinesCapped(context, updates);
@@ -540,7 +544,7 @@ public abstract class MessageRecord extends DisplayRecord {
UpdateDescription.SpannableFactory stringFactory = new GroupCallUpdateMessageFactory(context, joinedMembers, withTime, groupCallUpdateDetails);
return UpdateDescription.mentioning(joinedMembers, stringFactory, R.drawable.ic_video_16);
return UpdateDescription.mentioning(joinedMembers, stringFactory, Glyph.VIDEO_CAMERA);
}
public boolean isGroupV2DescriptionUpdate() {

View File

@@ -28,6 +28,8 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList;
import org.thoughtcrime.securesms.database.model.databaseprotos.CryptoValue;
import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge;
import org.thoughtcrime.securesms.database.model.databaseprotos.MessageExtras;
import org.thoughtcrime.securesms.fonts.SignalSymbols;
import org.thoughtcrime.securesms.fonts.SignalSymbols.Glyph;
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
@@ -250,20 +252,20 @@ public class MmsMessageRecord extends MessageRecord {
if (call.getDirection() == CallTable.Direction.OUTGOING) {
if (call.getType() == CallTable.Type.AUDIO_CALL) {
int updateString = R.string.MessageRecord_outgoing_voice_call;
return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(updateString), callDateString), R.drawable.ic_update_audio_call_outgoing_16);
return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(updateString), callDateString), Glyph.PHONE);
} else {
int updateString = R.string.MessageRecord_outgoing_video_call;
return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(updateString), callDateString), R.drawable.ic_update_video_call_outgoing_16);
return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(updateString), callDateString), Glyph.VIDEO_CAMERA);
}
} else {
boolean isVideoCall = call.getType() == CallTable.Type.VIDEO_CALL;
if (accepted || !call.isDisplayedAsMissedCallInUi()) {
int updateString = isVideoCall ? R.string.MessageRecord_incoming_video_call : R.string.MessageRecord_incoming_voice_call;
int icon = isVideoCall ? R.drawable.ic_update_video_call_incoming_16 : R.drawable.ic_update_audio_call_incoming_16;
Glyph icon = isVideoCall ? Glyph.VIDEO_CAMERA : Glyph.PHONE;
return staticUpdateDescription(context.getString(R.string.MessageRecord_call_message_with_date, context.getString(updateString), callDateString), icon);
} else {
int icon = isVideoCall ? R.drawable.ic_update_video_call_missed_16 : R.drawable.ic_update_audio_call_missed_16;
Glyph icon = isVideoCall ? Glyph.VIDEO_CAMERA : Glyph.PHONE;
int message;
if (call.getEvent() == CallTable.Event.MISSED_NOTIFICATION_PROFILE) {
message = isVideoCall ? R.string.MessageRecord_missed_video_call_notification_profile : R.string.MessageRecord_missed_voice_call_notification_profile;

View File

@@ -11,7 +11,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
import org.thoughtcrime.securesms.fonts.SignalSymbols;
import org.thoughtcrime.securesms.fonts.SignalSymbols.Glyph;
import org.whispersystems.signalservice.api.push.ServiceId;
import java.util.Collection;
@@ -33,14 +34,14 @@ public final class UpdateDescription {
private final Collection<ServiceId> mentioned;
private final SpannableFactory stringFactory;
private final Spannable staticString;
private final int lightIconResource;
private final Glyph glyph;
private final int lightTint;
private final int darkTint;
private UpdateDescription(@NonNull Collection<ServiceId> mentioned,
@Nullable SpannableFactory stringFactory,
@Nullable Spannable staticString,
@DrawableRes int iconResource,
@NonNull Glyph glyph,
@ColorInt int lightTint,
@ColorInt int darkTint)
{
@@ -50,7 +51,7 @@ public final class UpdateDescription {
this.mentioned = mentioned;
this.stringFactory = stringFactory;
this.staticString = staticString;
this.lightIconResource = iconResource;
this.glyph = glyph;
this.lightTint = lightTint;
this.darkTint = darkTint;
}
@@ -64,43 +65,43 @@ public final class UpdateDescription {
*/
public static UpdateDescription mentioning(@NonNull Collection<ServiceId> mentioned,
@NonNull SpannableFactory stringFactory,
@DrawableRes int iconResource)
Glyph glyph)
{
return new UpdateDescription(mentioned.stream().filter(ServiceId::isValid).collect(Collectors.toList()),
stringFactory,
null,
iconResource,
glyph,
0,
0);
}
/**
* Create an update description that's string value is fixed.
* Create an update description that's string value is fixed with a start glyph.
*/
public static UpdateDescription staticDescription(@NonNull String staticString,
@DrawableRes int iconResource)
Glyph glyph)
{
return new UpdateDescription(Collections.emptyList(), null, new SpannableString(staticString), iconResource, 0, 0);
return new UpdateDescription(Collections.emptyList(), null, new SpannableString(staticString), glyph, 0, 0);
}
/**
* Create an update description that's string value is fixed.
*/
public static UpdateDescription staticDescription(@NonNull Spannable staticString,
@DrawableRes int iconResource)
Glyph glyph)
{
return new UpdateDescription(Collections.emptyList(), null, staticString, iconResource, 0, 0);
return new UpdateDescription(Collections.emptyList(), null, staticString, glyph, 0, 0);
}
/**
* Create an update description that's string value is fixed with a specific tint color.
*/
public static UpdateDescription staticDescription(@NonNull String staticString,
@DrawableRes int iconResource,
Glyph glyph,
@ColorInt int lightTint,
@ColorInt int darkTint)
{
return new UpdateDescription(Collections.emptyList(), null, new SpannableString(staticString), iconResource, lightTint, darkTint);
return new UpdateDescription(Collections.emptyList(), null, new SpannableString(staticString), glyph, lightTint, darkTint);
}
public boolean isStringStatic() {
@@ -131,8 +132,8 @@ public final class UpdateDescription {
return mentioned;
}
public @DrawableRes int getIconResource() {
return lightIconResource;
public @Nullable Glyph getGlyph() {
return glyph;
}
public @ColorInt int getLightTint() {
@@ -154,7 +155,7 @@ public final class UpdateDescription {
if (allAreStatic(updateDescriptions)) {
return UpdateDescription.staticDescription(concatStaticLines(updateDescriptions),
updateDescriptions.get(0).getIconResource()
updateDescriptions.get(0).getGlyph()
);
}
@@ -166,7 +167,7 @@ public final class UpdateDescription {
return UpdateDescription.mentioning(allMentioned,
() -> concatLines(updateDescriptions),
updateDescriptions.get(0).getIconResource());
updateDescriptions.get(0).getGlyph());
}
private static boolean allAreStatic(@NonNull Collection<UpdateDescription> updateDescriptions) {