diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java index 76ae6f78b6..ce61fb8a7b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java @@ -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 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 displayBody; + private LiveData 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 liveUpdateMessage = LiveUpdateMessage.fromMessageDescription(updateDescription); - LiveData spannableStringMessage = toSpannable(loading(liveUpdateMessage)); + UpdateDescription updateDescription = Objects.requireNonNull(messageRecord.getUpdateDisplayBody(getContext())); + LiveData liveUpdateMessage = LiveUpdateMessage.fromMessageDescription(getContext(), updateDescription); + LiveData 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 loading(@NonNull LiveData string) { - return LiveDataUtil.until(string, LiveDataUtil.delay(250, getContext().getString(R.string.ConversationUpdateItem_loading))); - } - - private static LiveData toSpannable(LiveData loading) { - return Transformations.map(loading, source -> source == null ? null : new SpannableString(source)); + private @NonNull LiveData loading(@NonNull LiveData 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 displayBody) { + private void observeDisplayBody(@NonNull LifecycleOwner lifecycleOwner, @Nullable LiveData 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 { + private final class UpdateObserver implements Observer { @Override - public void onChanged(SpannableString update) { + public void onChanged(Spannable update) { setBodyText(update); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java index afc2481321..b6a73684f9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java @@ -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 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 emphasisAdded(@NonNull String string) { - return emphasisAdded(UpdateDescription.staticDescription(string)); + private static @NonNull LiveData emphasisAdded(@NonNull Context context, @NonNull String string) { + return emphasisAdded(context, UpdateDescription.staticDescription(string, 0, 0)); } - private static @NonNull LiveData emphasisAdded(@NonNull UpdateDescription description) { - return emphasisAdded(LiveUpdateMessage.fromMessageDescription(description)); + private static @NonNull LiveData emphasisAdded(@NonNull Context context, @NonNull UpdateDescription description) { + return emphasisAdded(LiveUpdateMessage.fromMessageDescription(context, description)); } - private static @NonNull LiveData emphasisAdded(@NonNull LiveData description) { + private static @NonNull LiveData emphasisAdded(@NonNull LiveData description) { return Transformations.map(description, sequence -> { SpannableString spannable = new SpannableString(sequence); spannable.setSpan(new StyleSpan(Typeface.ITALIC), diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageProducer.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageProducer.java index faa34e65d6..d6d9bb6395 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageProducer.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageProducer.java @@ -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 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 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 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 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 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 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 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 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); } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/LiveUpdateMessage.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/LiveUpdateMessage.java index ea6ee48eec..7094dee458 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/LiveUpdateMessage.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/LiveUpdateMessage.java @@ -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 fromMessageDescription(@NonNull UpdateDescription updateDescription) { + public static LiveData fromMessageDescription(@NonNull Context context, @NonNull UpdateDescription updateDescription) { if (updateDescription.isStringStatic()) { - return LiveDataUtil.just(updateDescription.getStaticString()); + return LiveDataUtil.just(toSpannable(context, updateDescription, updateDescription.getStaticString())); } List> 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 recipientToStringAsync(@NonNull RecipientId recipientId, - @NonNull Function createStringInBackground) + public static LiveData recipientToStringAsync(@NonNull RecipientId recipientId, + @NonNull Function 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); + } + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java index 833ab70fcb..aa3726bbfc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java @@ -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 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 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) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/UpdateDescription.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/UpdateDescription.java index 8d86a1d5f1..d07ac1db4b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/UpdateDescription.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/UpdateDescription.java @@ -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 mentioned; private final StringFactory stringFactory; private final String staticString; + private final int lightIconResource; + private final int darkIconResource; private UpdateDescription(@NonNull Collection 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 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 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 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 updateDescriptions) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.java b/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.java index 5e355866ac..8f603180a6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/DateUtils.java @@ -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); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/SpanUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/SpanUtil.java index 1b01da1ab2..a307137b85 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/SpanUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/SpanUtil.java @@ -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; + } } diff --git a/app/src/main/res/drawable/ic_update_audio_call_incoming_dark_16.xml b/app/src/main/res/drawable/ic_update_audio_call_incoming_dark_16.xml new file mode 100644 index 0000000000..d66707ef13 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_audio_call_incoming_dark_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_audio_call_incoming_light_16.xml b/app/src/main/res/drawable/ic_update_audio_call_incoming_light_16.xml new file mode 100644 index 0000000000..16e6eb6f15 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_audio_call_incoming_light_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_audio_call_missed_dark_16.xml b/app/src/main/res/drawable/ic_update_audio_call_missed_dark_16.xml new file mode 100644 index 0000000000..cd5b800dc7 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_audio_call_missed_dark_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_audio_call_missed_light_16.xml b/app/src/main/res/drawable/ic_update_audio_call_missed_light_16.xml new file mode 100644 index 0000000000..f58e9fbfc5 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_audio_call_missed_light_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_audio_call_outgoing_dark_16.xml b/app/src/main/res/drawable/ic_update_audio_call_outgoing_dark_16.xml new file mode 100644 index 0000000000..77bdf848d7 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_audio_call_outgoing_dark_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_audio_call_outgoing_light_16.xml b/app/src/main/res/drawable/ic_update_audio_call_outgoing_light_16.xml new file mode 100644 index 0000000000..24f27e148a --- /dev/null +++ b/app/src/main/res/drawable/ic_update_audio_call_outgoing_light_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_group_accept_dark_16.xml b/app/src/main/res/drawable/ic_update_group_accept_dark_16.xml new file mode 100644 index 0000000000..a6b9169837 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_group_accept_dark_16.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_group_accept_light_16.xml b/app/src/main/res/drawable/ic_update_group_accept_light_16.xml new file mode 100644 index 0000000000..fbaa99bcd0 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_group_accept_light_16.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_group_add_dark_16.xml b/app/src/main/res/drawable/ic_update_group_add_dark_16.xml new file mode 100644 index 0000000000..b89b1b231c --- /dev/null +++ b/app/src/main/res/drawable/ic_update_group_add_dark_16.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_update_group_add_light_16.xml b/app/src/main/res/drawable/ic_update_group_add_light_16.xml new file mode 100644 index 0000000000..a52e8ce289 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_group_add_light_16.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_update_group_avatar_dark_16.xml b/app/src/main/res/drawable/ic_update_group_avatar_dark_16.xml new file mode 100644 index 0000000000..5549da12d7 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_group_avatar_dark_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_group_avatar_light_16.xml b/app/src/main/res/drawable/ic_update_group_avatar_light_16.xml new file mode 100644 index 0000000000..7791e304e4 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_group_avatar_light_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_group_dark_16.xml b/app/src/main/res/drawable/ic_update_group_dark_16.xml new file mode 100644 index 0000000000..c7c92bfc1f --- /dev/null +++ b/app/src/main/res/drawable/ic_update_group_dark_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_group_decline_dark_16.xml b/app/src/main/res/drawable/ic_update_group_decline_dark_16.xml new file mode 100644 index 0000000000..87a3ba4934 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_group_decline_dark_16.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_update_group_decline_light_16.xml b/app/src/main/res/drawable/ic_update_group_decline_light_16.xml new file mode 100644 index 0000000000..ca6e7f0a0c --- /dev/null +++ b/app/src/main/res/drawable/ic_update_group_decline_light_16.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_update_group_leave_dark_16.xml b/app/src/main/res/drawable/ic_update_group_leave_dark_16.xml new file mode 100644 index 0000000000..63520985d9 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_group_leave_dark_16.xml @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/ic_update_group_leave_light_16.xml b/app/src/main/res/drawable/ic_update_group_leave_light_16.xml new file mode 100644 index 0000000000..31370fe93b --- /dev/null +++ b/app/src/main/res/drawable/ic_update_group_leave_light_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_group_light_16.xml b/app/src/main/res/drawable/ic_update_group_light_16.xml new file mode 100644 index 0000000000..f66846131d --- /dev/null +++ b/app/src/main/res/drawable/ic_update_group_light_16.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_group_name_dark_16.xml b/app/src/main/res/drawable/ic_update_group_name_dark_16.xml new file mode 100644 index 0000000000..1221def22b --- /dev/null +++ b/app/src/main/res/drawable/ic_update_group_name_dark_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_group_name_light_16.xml b/app/src/main/res/drawable/ic_update_group_name_light_16.xml new file mode 100644 index 0000000000..aa01d9fc5f --- /dev/null +++ b/app/src/main/res/drawable/ic_update_group_name_light_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_group_remove_dark_16.xml b/app/src/main/res/drawable/ic_update_group_remove_dark_16.xml new file mode 100644 index 0000000000..9bb465530c --- /dev/null +++ b/app/src/main/res/drawable/ic_update_group_remove_dark_16.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_update_group_remove_light_16.xml b/app/src/main/res/drawable/ic_update_group_remove_light_16.xml new file mode 100644 index 0000000000..5ac10df1e4 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_group_remove_light_16.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_update_group_role_dark_16.xml b/app/src/main/res/drawable/ic_update_group_role_dark_16.xml new file mode 100644 index 0000000000..9d15c214de --- /dev/null +++ b/app/src/main/res/drawable/ic_update_group_role_dark_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_group_role_light_16.xml b/app/src/main/res/drawable/ic_update_group_role_light_16.xml new file mode 100644 index 0000000000..1537537161 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_group_role_light_16.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable/ic_update_info_dark_16.xml b/app/src/main/res/drawable/ic_update_info_dark_16.xml new file mode 100644 index 0000000000..6822e13c4b --- /dev/null +++ b/app/src/main/res/drawable/ic_update_info_dark_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_info_light_16.xml b/app/src/main/res/drawable/ic_update_info_light_16.xml new file mode 100644 index 0000000000..124910101f --- /dev/null +++ b/app/src/main/res/drawable/ic_update_info_light_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_profile_dark_16.xml b/app/src/main/res/drawable/ic_update_profile_dark_16.xml new file mode 100644 index 0000000000..a999424516 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_profile_dark_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_profile_light_16.xml b/app/src/main/res/drawable/ic_update_profile_light_16.xml new file mode 100644 index 0000000000..f3299fb306 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_profile_light_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_safety_number_dark_16.xml b/app/src/main/res/drawable/ic_update_safety_number_dark_16.xml new file mode 100644 index 0000000000..f3d933a6bd --- /dev/null +++ b/app/src/main/res/drawable/ic_update_safety_number_dark_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_safety_number_light_16.xml b/app/src/main/res/drawable/ic_update_safety_number_light_16.xml new file mode 100644 index 0000000000..e48e26c822 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_safety_number_light_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_timer_dark_16.xml b/app/src/main/res/drawable/ic_update_timer_dark_16.xml new file mode 100644 index 0000000000..ccd550c180 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_timer_dark_16.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_timer_disabled_dark_16.xml b/app/src/main/res/drawable/ic_update_timer_disabled_dark_16.xml new file mode 100644 index 0000000000..6856b4c1f3 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_timer_disabled_dark_16.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_timer_disabled_light_16.xml b/app/src/main/res/drawable/ic_update_timer_disabled_light_16.xml new file mode 100644 index 0000000000..c0bf388d16 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_timer_disabled_light_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_timer_light_16.xml b/app/src/main/res/drawable/ic_update_timer_light_16.xml new file mode 100644 index 0000000000..1b87e2f9b5 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_timer_light_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_verified_dark_16.xml b/app/src/main/res/drawable/ic_update_verified_dark_16.xml new file mode 100644 index 0000000000..52556b19e2 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_verified_dark_16.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_verified_light_16.xml b/app/src/main/res/drawable/ic_update_verified_light_16.xml new file mode 100644 index 0000000000..df6d2d86fd --- /dev/null +++ b/app/src/main/res/drawable/ic_update_verified_light_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_video_call_incomg_light_16.xml b/app/src/main/res/drawable/ic_update_video_call_incomg_light_16.xml new file mode 100644 index 0000000000..3000011f19 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_video_call_incomg_light_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_video_call_incoming_dark_16.xml b/app/src/main/res/drawable/ic_update_video_call_incoming_dark_16.xml new file mode 100644 index 0000000000..7dd80e1681 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_video_call_incoming_dark_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_video_call_missed_dark_16.xml b/app/src/main/res/drawable/ic_update_video_call_missed_dark_16.xml new file mode 100644 index 0000000000..fb5255c29c --- /dev/null +++ b/app/src/main/res/drawable/ic_update_video_call_missed_dark_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_video_call_missed_light_16.xml b/app/src/main/res/drawable/ic_update_video_call_missed_light_16.xml new file mode 100644 index 0000000000..fe3dd7ba56 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_video_call_missed_light_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_video_call_outgoing_dark_16.xml b/app/src/main/res/drawable/ic_update_video_call_outgoing_dark_16.xml new file mode 100644 index 0000000000..27966c64dc --- /dev/null +++ b/app/src/main/res/drawable/ic_update_video_call_outgoing_dark_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_update_video_call_outgoing_light_16.xml b/app/src/main/res/drawable/ic_update_video_call_outgoing_light_16.xml new file mode 100644 index 0000000000..3ec9fa2f84 --- /dev/null +++ b/app/src/main/res/drawable/ic_update_video_call_outgoing_light_16.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/conversation_item_update.xml b/app/src/main/res/layout/conversation_item_update.xml index 944ffd4bae..85ae0fdcd7 100644 --- a/app/src/main/res/layout/conversation_item_update.xml +++ b/app/src/main/res/layout/conversation_item_update.xml @@ -1,5 +1,6 @@ - - - - - - - - - - - - - - - - - + android:textColor="?attr/conversation_item_update_text_color" + tools:text="Gwen Stacy set the disappearing message timer to 1 hour" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f2b3e487fa..953787449a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -937,13 +937,11 @@ You have left the group. You updated the group. The group was updated. - You called - Contact called - Missed call + You called • %1$s + Missed call • %1$s %s updated the group. - %s called you + %1$s called you • %2$s Called %s - Missed call from %s %s is on Signal! You disabled disappearing messages. %1$s disabled disappearing messages. diff --git a/app/src/test/java/org/thoughtcrime/securesms/database/model/UpdateDescriptionTest.java b/app/src/test/java/org/thoughtcrime/securesms/database/model/UpdateDescriptionTest.java index 224dab9f99..e083a286d7 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/database/model/UpdateDescriptionTest.java +++ b/app/src/test/java/org/thoughtcrime/securesms/database/model/UpdateDescriptionTest.java @@ -29,28 +29,28 @@ public final class UpdateDescriptionTest { @Test public void staticDescription_byGetStaticString() { - UpdateDescription description = UpdateDescription.staticDescription("update"); + UpdateDescription description = UpdateDescription.staticDescription("update", 0, 0); assertEquals("update", description.getStaticString()); } @Test public void staticDescription_has_empty_mentions() { - UpdateDescription description = UpdateDescription.staticDescription("update"); + UpdateDescription description = UpdateDescription.staticDescription("update", 0, 0); assertTrue(description.getMentioned().isEmpty()); } @Test public void staticDescription_byString() { - UpdateDescription description = UpdateDescription.staticDescription("update"); + UpdateDescription description = UpdateDescription.staticDescription("update", 0, 0); assertEquals("update", description.getString()); } @Test(expected = AssertionError.class) public void stringFactory_cannot_run_on_main_thread() { - UpdateDescription description = UpdateDescription.mentioning(Collections.singletonList(UUID.randomUUID()), () -> "update"); + UpdateDescription description = UpdateDescription.mentioning(Collections.singletonList(UUID.randomUUID()), () -> "update", 0, 0); setMainThread(true); @@ -59,7 +59,7 @@ public final class UpdateDescriptionTest { @Test(expected = UnsupportedOperationException.class) public void stringFactory_cannot_call_static_string() { - UpdateDescription description = UpdateDescription.mentioning(Collections.singletonList(UUID.randomUUID()), () -> "update"); + UpdateDescription description = UpdateDescription.mentioning(Collections.singletonList(UUID.randomUUID()), () -> "update", 0, 0); description.getStaticString(); } @@ -73,7 +73,7 @@ public final class UpdateDescriptionTest { return "update"; }; - UpdateDescription description = UpdateDescription.mentioning(Collections.singletonList(UUID.randomUUID()), stringFactory); + UpdateDescription description = UpdateDescription.mentioning(Collections.singletonList(UUID.randomUUID()), stringFactory, 0 , 0); assertEquals(0, factoryCalls.get()); @@ -89,7 +89,7 @@ public final class UpdateDescriptionTest { public void stringFactory_reevaluated_on_every_call() { AtomicInteger factoryCalls = new AtomicInteger(); UpdateDescription.StringFactory stringFactory = () -> "call" + factoryCalls.incrementAndGet(); - UpdateDescription description = UpdateDescription.mentioning(Collections.singletonList(UUID.randomUUID()), stringFactory); + UpdateDescription description = UpdateDescription.mentioning(Collections.singletonList(UUID.randomUUID()), stringFactory, 0, 0); setMainThread(false); @@ -100,8 +100,8 @@ public final class UpdateDescriptionTest { @Test public void concat_static_lines() { - UpdateDescription description1 = UpdateDescription.staticDescription("update1"); - UpdateDescription description2 = UpdateDescription.staticDescription("update2"); + UpdateDescription description1 = UpdateDescription.staticDescription("update1", 0, 0); + UpdateDescription description2 = UpdateDescription.staticDescription("update2", 0 , 0); UpdateDescription description = UpdateDescription.concatWithNewLines(Arrays.asList(description1, description2)); @@ -112,7 +112,7 @@ public final class UpdateDescriptionTest { @Test public void concat_single_does_not_make_new_object() { - UpdateDescription description = UpdateDescription.staticDescription("update1"); + UpdateDescription description = UpdateDescription.staticDescription("update1", 0 , 0); UpdateDescription concat = UpdateDescription.concatWithNewLines(Collections.singletonList(description)); @@ -125,8 +125,8 @@ public final class UpdateDescriptionTest { AtomicInteger factoryCalls2 = new AtomicInteger(); UpdateDescription.StringFactory stringFactory1 = () -> "update." + factoryCalls1.incrementAndGet(); UpdateDescription.StringFactory stringFactory2 = () -> "update." + factoryCalls2.incrementAndGet(); - UpdateDescription description1 = UpdateDescription.mentioning(Collections.singletonList(UUID.randomUUID()), stringFactory1); - UpdateDescription description2 = UpdateDescription.mentioning(Collections.singletonList(UUID.randomUUID()), stringFactory2); + UpdateDescription description1 = UpdateDescription.mentioning(Collections.singletonList(UUID.randomUUID()), stringFactory1, 0, 0); + UpdateDescription description2 = UpdateDescription.mentioning(Collections.singletonList(UUID.randomUUID()), stringFactory2, 0, 0); factoryCalls1.set(10); factoryCalls2.set(20); @@ -148,9 +148,9 @@ public final class UpdateDescriptionTest { AtomicInteger factoryCalls2 = new AtomicInteger(); UpdateDescription.StringFactory stringFactory1 = () -> "update." + factoryCalls1.incrementAndGet(); UpdateDescription.StringFactory stringFactory2 = () -> "update." + factoryCalls2.incrementAndGet(); - UpdateDescription description1 = UpdateDescription.mentioning(Collections.singletonList(UUID.randomUUID()), stringFactory1); - UpdateDescription description2 = UpdateDescription.staticDescription("static"); - UpdateDescription description3 = UpdateDescription.mentioning(Collections.singletonList(UUID.randomUUID()), stringFactory2); + UpdateDescription description1 = UpdateDescription.mentioning(Collections.singletonList(UUID.randomUUID()), stringFactory1, 0, 0); + UpdateDescription description2 = UpdateDescription.staticDescription("static", 0, 0); + UpdateDescription description3 = UpdateDescription.mentioning(Collections.singletonList(UUID.randomUUID()), stringFactory2, 0, 0); factoryCalls1.set(100); factoryCalls2.set(200);