diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java index e8c04fb86..038d0d167 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java @@ -1,5 +1,6 @@ package eu.siacs.conversations.ui.adapter; +import android.content.res.ColorStateList; import android.graphics.Typeface; import android.util.Pair; import android.view.LayoutInflater; @@ -8,6 +9,7 @@ import android.view.ViewGroup; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; +import androidx.core.widget.ImageViewCompat; import androidx.databinding.DataBindingUtil; import androidx.recyclerview.widget.RecyclerView; @@ -68,15 +70,47 @@ public class ConversationAdapter } if (conversation == ConversationFragment.getConversation(activity)) { - viewHolder.binding.frame.setBackgroundResource(R.drawable.background_selected_item_conversation); - //viewHolder.binding.frame.setBackgroundColor(MaterialColors.getColor(viewHolder.binding.frame, com.google.android.material.R.attr.colorSurfaceDim)); + viewHolder.binding.frame.setBackgroundResource( + R.drawable.background_selected_item_conversation); + // viewHolder.binding.frame.setBackgroundColor(MaterialColors.getColor(viewHolder.binding.frame, com.google.android.material.R.attr.colorSurfaceDim)); } else { - viewHolder.binding.frame.setBackgroundColor(MaterialColors.getColor(viewHolder.binding.frame, com.google.android.material.R.attr.colorSurface)); + viewHolder.binding.frame.setBackgroundColor( + MaterialColors.getColor( + viewHolder.binding.frame, + com.google.android.material.R.attr.colorSurface)); } final Message message = conversation.getLatestMessage(); + final int status = message.getStatus(); final int unreadCount = conversation.unreadCount(); final boolean isRead = conversation.isRead(); + final @DrawableRes Integer messageStatusDrawable = + MessageAdapter.getMessageStatusAsDrawable(message, status); + if (messageStatusDrawable == null) { + if (status <= Message.STATUS_RECEIVED) { + viewHolder.binding.messageStatus.setVisibility(View.GONE); + } else { + viewHolder.binding.messageStatus.setVisibility(View.INVISIBLE); + } + } else { + viewHolder.binding.messageStatus.setImageResource(messageStatusDrawable); + if (status == Message.STATUS_SEND_DISPLAYED) { + ImageViewCompat.setImageTintList( + viewHolder.binding.messageStatus, + ColorStateList.valueOf( + MaterialColors.getColor( + viewHolder.binding.messageStatus, + com.google.android.material.R.attr.colorPrimary))); + } else { + ImageViewCompat.setImageTintList( + viewHolder.binding.messageStatus, + ColorStateList.valueOf( + MaterialColors.getColor( + viewHolder.binding.messageStatus, + com.google.android.material.R.attr.colorControlNormal))); + } + viewHolder.binding.messageStatus.setVisibility(View.VISIBLE); + } final Conversation.Draft draft = isRead ? conversation.getDraft() : null; if (unreadCount > 0) { viewHolder.binding.unreadCount.setVisibility(View.VISIBLE); @@ -143,7 +177,7 @@ public class ConversationAdapter viewHolder.binding.senderName.setTypeface(null, Typeface.BOLD); } } - if (message.getStatus() == Message.STATUS_RECEIVED) { + if (status == Message.STATUS_RECEIVED) { if (conversation.getMode() == Conversation.MODE_MULTI) { viewHolder.binding.senderName.setVisibility(View.VISIBLE); viewHolder.binding.senderName.setText( @@ -171,21 +205,25 @@ public class ConversationAdapter if (ongoingCall.isPresent()) { viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE); - viewHolder.binding.notificationStatus.setImageResource(R.drawable.ic_phone_in_talk_24dp); + viewHolder.binding.notificationStatus.setImageResource( + R.drawable.ic_phone_in_talk_24dp); } else { final long muted_till = conversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0); if (muted_till == Long.MAX_VALUE) { viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE); - viewHolder.binding.notificationStatus.setImageResource(R.drawable.ic_notifications_off_24dp); + viewHolder.binding.notificationStatus.setImageResource( + R.drawable.ic_notifications_off_24dp); } else if (muted_till >= System.currentTimeMillis()) { viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE); - viewHolder.binding.notificationStatus.setImageResource(R.drawable.ic_notifications_paused_24dp); + viewHolder.binding.notificationStatus.setImageResource( + R.drawable.ic_notifications_paused_24dp); } else if (conversation.alwaysNotify()) { viewHolder.binding.notificationStatus.setVisibility(View.GONE); } else { viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE); - viewHolder.binding.notificationStatus.setImageResource(R.drawable.ic_notifications_none_24dp); + viewHolder.binding.notificationStatus.setImageResource( + R.drawable.ic_notifications_none_24dp); } } diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java index fbb38d5d8..c0f1c3fa6 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -29,19 +29,16 @@ import androidx.annotation.AttrRes; import androidx.annotation.ColorInt; import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.core.widget.ImageViewCompat; import com.google.android.material.button.MaterialButton; import com.google.android.material.color.MaterialColors; +import com.google.common.base.Joiner; import com.google.common.base.Strings; - -import java.net.URI; -import java.util.List; -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import com.google.common.collect.ImmutableList; import eu.siacs.conversations.AppSettings; import eu.siacs.conversations.Config; @@ -80,6 +77,12 @@ import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.mam.MamReference; +import java.net.URI; +import java.util.List; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + public class MessageAdapter extends ArrayAdapter { public static final String DATE_SEPARATOR_BODY = "DATE_SEPARATOR"; @@ -97,7 +100,8 @@ public class MessageAdapter extends ArrayAdapter { private boolean colorfulChatBubbles = false; private final boolean mForceNames; - public MessageAdapter(final XmppActivity activity, final List messages, final boolean forceNames) { + public MessageAdapter( + final XmppActivity activity, final List messages, final boolean forceNames) { super(activity, 0, messages); this.audioPlayer = new AudioPlayer(this); this.activity = activity; @@ -136,8 +140,7 @@ public class MessageAdapter extends ArrayAdapter { return activity; } - public void setOnContactPictureLongClicked( - OnContactPictureLongClicked listener) { + public void setOnContactPictureLongClicked(OnContactPictureLongClicked listener) { this.mOnContactPictureLongClickedListener = listener; } @@ -167,93 +170,72 @@ public class MessageAdapter extends ArrayAdapter { return this.getItemViewType(getItem(position)); } - private void displayStatus(ViewHolder viewHolder, Message message, int type, final BubbleColor bubbleColor) { - String filesize = null; - String info = null; - boolean error = false; + private void displayStatus( + final ViewHolder viewHolder, + final Message message, + final int type, + final BubbleColor bubbleColor) { + final int mergedStatus = message.getMergedStatus(); + final boolean error; if (viewHolder.indicatorReceived != null) { viewHolder.indicatorReceived.setVisibility(View.GONE); } - - if (viewHolder.edit_indicator != null) { - if (message.edited()) { - viewHolder.edit_indicator.setVisibility(View.VISIBLE); - setImageTint(viewHolder.edit_indicator, bubbleColor); - } else { - viewHolder.edit_indicator.setVisibility(View.GONE); - } - } final Transferable transferable = message.getTransferable(); - boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI - && message.getMergedStatus() <= Message.STATUS_RECEIVED; - if (message.isFileOrImage() || transferable != null || MessageUtils.unInitiatedButKnownSize(message)) { - FileParams params = message.getFileParams(); - filesize = params.size != null ? UIHelper.filesizeToString(params.size) : null; - if (transferable != null && (transferable.getStatus() == Transferable.STATUS_FAILED || transferable.getStatus() == Transferable.STATUS_CANCELLED)) { + final boolean multiReceived = + message.getConversation().getMode() == Conversation.MODE_MULTI + && mergedStatus <= Message.STATUS_RECEIVED; + final String fileSize; + if (message.isFileOrImage() + || transferable != null + || MessageUtils.unInitiatedButKnownSize(message)) { + final FileParams params = message.getFileParams(); + fileSize = params.size != null ? UIHelper.filesizeToString(params.size) : null; + if (message.getStatus() == Message.STATUS_SEND_FAILED + || (transferable != null + && (transferable.getStatus() == Transferable.STATUS_FAILED + || transferable.getStatus() + == Transferable.STATUS_CANCELLED))) { error = true; + } else { + error = message.getStatus() == Message.STATUS_SEND_FAILED; + } + } else { + fileSize = null; + error = message.getStatus() == Message.STATUS_SEND_FAILED; + } + if (type == SENT) { + final @DrawableRes Integer receivedIndicator = getMessageStatusAsDrawable(message, mergedStatus); + if (receivedIndicator == null) { + viewHolder.indicatorReceived.setVisibility(View.INVISIBLE); + } else { + viewHolder.indicatorReceived.setImageResource(receivedIndicator); + if (mergedStatus == Message.STATUS_SEND_FAILED) { + setImageTintError(viewHolder.indicatorReceived); + } else { + setImageTint(viewHolder.indicatorReceived, bubbleColor); + } + viewHolder.indicatorReceived.setVisibility(View.VISIBLE); } } - switch (message.getMergedStatus()) { - case Message.STATUS_WAITING: - info = getContext().getString(R.string.waiting); - break; - case Message.STATUS_UNSEND: - if (transferable != null) { - info = getContext().getString(R.string.sending_file, transferable.getProgress()); - } else { - info = getContext().getString(R.string.sending); - } - break; - case Message.STATUS_OFFERED: - info = getContext().getString(R.string.offering); - break; - case Message.STATUS_SEND_RECEIVED: - case Message.STATUS_SEND_DISPLAYED: - setImageTint(viewHolder.indicatorReceived, bubbleColor); - viewHolder.indicatorReceived.setVisibility(View.VISIBLE); - break; - case Message.STATUS_SEND_FAILED: - final String errorMessage = message.getErrorMessage(); - if (Message.ERROR_MESSAGE_CANCELLED.equals(errorMessage)) { - info = getContext().getString(R.string.cancelled); - } else if (errorMessage != null) { - final String[] errorParts = errorMessage.split("\\u001f", 2); - if (errorParts.length == 2) { - switch (errorParts[0]) { - case "file-too-large": - info = getContext().getString(R.string.file_too_large); - break; - default: - info = getContext().getString(R.string.send_failed); - break; - } - } else { - info = getContext().getString(R.string.send_failed); - } - } else { - info = getContext().getString(R.string.send_failed); - } - error = true; - break; - default: - if (mForceNames || multiReceived) { - info = UIHelper.getMessageDisplayName(message); - } - break; - } + final var additionalStatusInfo = getAdditionalStatusInfo(message, mergedStatus); + if (error && type == SENT) { - viewHolder.time.setTextColor(MaterialColors.getColor(viewHolder.time, com.google.android.material.R.attr.colorError)); + viewHolder.time.setTextColor( + MaterialColors.getColor( + viewHolder.time, com.google.android.material.R.attr.colorError)); } else { - setTextColor(viewHolder.time,bubbleColor); + setTextColor(viewHolder.time, bubbleColor); } if (message.getEncryption() == Message.ENCRYPTION_NONE) { viewHolder.indicator.setVisibility(View.GONE); } else { boolean verified = false; if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) { - final FingerprintStatus status = message.getConversation() - .getAccount().getAxolotlService().getFingerprintTrust( - message.getFingerprint()); + final FingerprintStatus status = + message.getConversation() + .getAccount() + .getAxolotlService() + .getFingerprintTrust(message.getFingerprint()); if (status != null && status.isVerified()) { verified = true; } @@ -263,51 +245,124 @@ public class MessageAdapter extends ArrayAdapter { } else { viewHolder.indicator.setImageResource(R.drawable.ic_lock_24dp); } - setImageTint(viewHolder.indicator, bubbleColor); + if (error && type == SENT) { + setImageTintError(viewHolder.indicator); + } else { + setImageTint(viewHolder.indicator, bubbleColor); + } viewHolder.indicator.setVisibility(View.VISIBLE); } - final String formattedTime = UIHelper.readableTimeDifferenceFull(getContext(), message.getMergedTimeSent()); - final String bodyLanguage = message.getBodyLanguage(); - final String bodyLanguageInfo = bodyLanguage == null ? "" : String.format(" \u00B7 %s", bodyLanguage.toUpperCase(Locale.US)); - if (message.getStatus() <= Message.STATUS_RECEIVED) { - if ((filesize != null) && (info != null)) { - viewHolder.time.setText(formattedTime + " \u00B7 " + filesize + " \u00B7 " + info + bodyLanguageInfo); - } else if ((filesize == null) && (info != null)) { - viewHolder.time.setText(formattedTime + " \u00B7 " + info + bodyLanguageInfo); - } else if ((filesize != null) && (info == null)) { - viewHolder.time.setText(formattedTime + " \u00B7 " + filesize + bodyLanguageInfo); - } else { - viewHolder.time.setText(formattedTime + bodyLanguageInfo); - } - } else { - if ((filesize != null) && (info != null)) { - viewHolder.time.setText(filesize + " \u00B7 " + info + bodyLanguageInfo); - } else if ((filesize == null) && (info != null)) { - if (error) { - viewHolder.time.setText(info + " \u00B7 " + formattedTime + bodyLanguageInfo); + if (viewHolder.edit_indicator != null) { + if (message.edited()) { + viewHolder.edit_indicator.setVisibility(View.VISIBLE); + if (error && type == SENT) { + setImageTintError(viewHolder.edit_indicator); } else { - viewHolder.time.setText(info); + setImageTint(viewHolder.edit_indicator, bubbleColor); } - } else if ((filesize != null) && (info == null)) { - viewHolder.time.setText(filesize + " \u00B7 " + formattedTime + bodyLanguageInfo); } else { - viewHolder.time.setText(formattedTime + bodyLanguageInfo); + viewHolder.edit_indicator.setVisibility(View.GONE); } } + + final String formattedTime = + UIHelper.readableTimeDifferenceFull(getContext(), message.getMergedTimeSent()); + final String bodyLanguage = message.getBodyLanguage(); + final ImmutableList.Builder timeInfoBuilder = new ImmutableList.Builder<>(); + if (message.getStatus() <= Message.STATUS_RECEIVED) { + timeInfoBuilder.add(formattedTime); + if (fileSize != null) { + timeInfoBuilder.add(fileSize); + } + if (mForceNames || multiReceived) { + final String displayName = UIHelper.getMessageDisplayName(message); + if (displayName != null) { + timeInfoBuilder.add(displayName); + } + } + if (bodyLanguage != null) { + timeInfoBuilder.add(bodyLanguage.toUpperCase(Locale.US)); + } + } else { + if (bodyLanguage != null) { + timeInfoBuilder.add(bodyLanguage.toUpperCase(Locale.US)); + } + if (fileSize != null) { + timeInfoBuilder.add(fileSize); + } + // for space reasons we display only 'additional status info' (send progress or concrete failure reason) or the time + if (additionalStatusInfo != null) { + timeInfoBuilder.add(additionalStatusInfo); + } else { + timeInfoBuilder.add(formattedTime); + } + } + final var timeInfo = timeInfoBuilder.build(); + viewHolder.time.setText(Joiner.on(" \u00B7 ").join(timeInfo)); } - private void displayInfoMessage(ViewHolder viewHolder, CharSequence text, final BubbleColor bubbleColor) { + public static @DrawableRes Integer getMessageStatusAsDrawable(final Message message, final int status) { + final var transferable = message.getTransferable(); + return switch (status) { + case Message.STATUS_WAITING -> R.drawable.ic_more_horiz_24dp; + case Message.STATUS_UNSEND -> transferable == null + ? null + : R.drawable.ic_upload_24dp; + case Message.STATUS_SEND -> R.drawable.ic_done_24dp; + case Message.STATUS_SEND_RECEIVED, Message.STATUS_SEND_DISPLAYED -> R + .drawable + .ic_done_all_24dp; + case Message.STATUS_SEND_FAILED -> { + final String errorMessage = message.getErrorMessage(); + if (Message.ERROR_MESSAGE_CANCELLED.equals(errorMessage)) { + yield R.drawable.ic_cancel_24dp; + } else { + yield R.drawable.ic_error_24dp; + } + } + case Message.STATUS_OFFERED -> R.drawable.ic_p2p_24dp; + default -> null; + }; + } + + @Nullable + private String getAdditionalStatusInfo(final Message message, final int mergedStatus) { + final String additionalStatusInfo; + if (mergedStatus == Message.STATUS_SEND_FAILED) { + final String errorMessage = Strings.nullToEmpty(message.getErrorMessage()); + final String[] errorParts = errorMessage.split("\\u001f", 2); + if (errorParts.length == 2 && errorParts[0].equals("file-too-large")) { + additionalStatusInfo = getContext().getString(R.string.file_too_large); + } else { + additionalStatusInfo = null; + } + } else if (mergedStatus == Message.STATUS_UNSEND) { + final var transferable = message.getTransferable(); + if (transferable == null) { + return null; + } + return getContext().getString(R.string.sending_file, transferable.getProgress()); + } else { + additionalStatusInfo = null; + } + return additionalStatusInfo; + } + + private void displayInfoMessage( + ViewHolder viewHolder, CharSequence text, final BubbleColor bubbleColor) { viewHolder.download_button.setVisibility(View.GONE); viewHolder.audioPlayer.setVisibility(View.GONE); viewHolder.image.setVisibility(View.GONE); viewHolder.messageBody.setVisibility(View.VISIBLE); viewHolder.messageBody.setText(text); - viewHolder.messageBody.setTextColor(bubbleToOnSurfaceVariant(viewHolder.messageBody,bubbleColor)); + viewHolder.messageBody.setTextColor( + bubbleToOnSurfaceVariant(viewHolder.messageBody, bubbleColor)); viewHolder.messageBody.setTextIsSelectable(false); } - private void displayEmojiMessage(final ViewHolder viewHolder, final String body, final BubbleColor bubbleColor) { + private void displayEmojiMessage( + final ViewHolder viewHolder, final String body, final BubbleColor bubbleColor) { viewHolder.download_button.setVisibility(View.GONE); viewHolder.audioPlayer.setVisibility(View.GONE); viewHolder.image.setVisibility(View.GONE); @@ -315,14 +370,21 @@ public class MessageAdapter extends ArrayAdapter { setTextColor(viewHolder.messageBody, bubbleColor); final Spannable span = new SpannableString(body); float size = Emoticons.isEmoji(body) ? 3.0f : 2.0f; - span.setSpan(new RelativeSizeSpan(size), 0, body.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + span.setSpan( + new RelativeSizeSpan(size), 0, body.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); viewHolder.messageBody.setText(span); } - private void applyQuoteSpan(final TextView textView, SpannableStringBuilder body, int start, int end, final BubbleColor bubbleColor) { + private void applyQuoteSpan( + final TextView textView, + SpannableStringBuilder body, + int start, + int end, + final BubbleColor bubbleColor) { if (start > 1 && !"\n\n".equals(body.subSequence(start - 2, start).toString())) { body.insert(start++, "\n"); - body.setSpan(new DividerSpan(false), start - 2, start, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + body.setSpan( + new DividerSpan(false), start - 2, start, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); end++; } if (end < body.length() - 1 && !"\n\n".equals(body.subSequence(end, end + 2).toString())) { @@ -330,14 +392,21 @@ public class MessageAdapter extends ArrayAdapter { body.setSpan(new DividerSpan(false), end, end + 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); - body.setSpan(new QuoteSpan(bubbleToOnSurfaceVariant(textView, bubbleColor), metrics), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + body.setSpan( + new QuoteSpan(bubbleToOnSurfaceVariant(textView, bubbleColor), metrics), + start, + end, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } /** - * Applies QuoteSpan to group of lines which starts with > or » characters. - * Appends likebreaks and applies DividerSpan to them to show a padding between quote and text. + * Applies QuoteSpan to group of lines which starts with > or » characters. Appends likebreaks + * and applies DividerSpan to them to show a padding between quote and text. */ - private boolean handleTextQuotes(final TextView textView, final SpannableStringBuilder body, final BubbleColor bubbleColor) { + private boolean handleTextQuotes( + final TextView textView, + final SpannableStringBuilder body, + final BubbleColor bubbleColor) { boolean startsWithQuote = false; int quoteDepth = 1; while (QuoteHelper.bodyContainsQuoteStart(body) && quoteDepth <= Config.QUOTE_MAX_DEPTH) { @@ -388,7 +457,11 @@ public class MessageAdapter extends ArrayAdapter { return startsWithQuote; } - private void displayTextMessage(final ViewHolder viewHolder, final Message message, final BubbleColor bubbleColor, int type) { + private void displayTextMessage( + final ViewHolder viewHolder, + final Message message, + final BubbleColor bubbleColor, + int type) { viewHolder.download_button.setVisibility(View.GONE); viewHolder.image.setVisibility(View.GONE); viewHolder.audioPlayer.setVisibility(View.GONE); @@ -407,7 +480,8 @@ public class MessageAdapter extends ArrayAdapter { body = new SpannableStringBuilder(body, 0, Config.MAX_DISPLAY_MESSAGE_CHARS); body.append("\u2026"); } - Message.MergeSeparator[] mergeSeparators = body.getSpans(0, body.length(), Message.MergeSeparator.class); + Message.MergeSeparator[] mergeSeparators = + body.getSpans(0, body.length(), Message.MergeSeparator.class); for (Message.MergeSeparator mergeSeparator : mergeSeparators) { int start = body.getSpanStart(mergeSeparator); int end = body.getSpanEnd(mergeSeparator); @@ -416,7 +490,10 @@ public class MessageAdapter extends ArrayAdapter { boolean startsWithQuote = handleTextQuotes(viewHolder.messageBody, body, bubbleColor); if (!message.isPrivateMessage()) { if (hasMeCommand) { - body.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), 0, nick.length(), + body.setSpan( + new StyleSpan(Typeface.BOLD_ITALIC), + 0, + nick.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } else { @@ -425,37 +502,66 @@ public class MessageAdapter extends ArrayAdapter { privateMarker = activity.getString(R.string.private_message); } else { Jid cp = message.getCounterpart(); - privateMarker = activity.getString(R.string.private_message_to, Strings.nullToEmpty(cp == null ? null : cp.getResource())); + privateMarker = + activity.getString( + R.string.private_message_to, + Strings.nullToEmpty(cp == null ? null : cp.getResource())); } body.insert(0, privateMarker); int privateMarkerIndex = privateMarker.length(); if (startsWithQuote) { body.insert(privateMarkerIndex, "\n\n"); - body.setSpan(new DividerSpan(false), privateMarkerIndex, privateMarkerIndex + 2, + body.setSpan( + new DividerSpan(false), + privateMarkerIndex, + privateMarkerIndex + 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } else { body.insert(privateMarkerIndex, " "); } - body.setSpan(new ForegroundColorSpan(bubbleToOnSurfaceVariant(viewHolder.messageBody, bubbleColor)), 0, privateMarkerIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - body.setSpan(new StyleSpan(Typeface.BOLD), 0, privateMarkerIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + body.setSpan( + new ForegroundColorSpan( + bubbleToOnSurfaceVariant(viewHolder.messageBody, bubbleColor)), + 0, + privateMarkerIndex, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + body.setSpan( + new StyleSpan(Typeface.BOLD), + 0, + privateMarkerIndex, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); if (hasMeCommand) { - body.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), privateMarkerIndex + 1, - privateMarkerIndex + 1 + nick.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + body.setSpan( + new StyleSpan(Typeface.BOLD_ITALIC), + privateMarkerIndex + 1, + privateMarkerIndex + 1 + nick.length(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } - if (message.getConversation().getMode() == Conversation.MODE_MULTI && message.getStatus() == Message.STATUS_RECEIVED) { + if (message.getConversation().getMode() == Conversation.MODE_MULTI + && message.getStatus() == Message.STATUS_RECEIVED) { if (message.getConversation() instanceof Conversation conversation) { - Pattern pattern = NotificationService.generateNickHighlightPattern(conversation.getMucOptions().getActualNick()); + Pattern pattern = + NotificationService.generateNickHighlightPattern( + conversation.getMucOptions().getActualNick()); Matcher matcher = pattern.matcher(body); while (matcher.find()) { - body.setSpan(new StyleSpan(Typeface.BOLD), matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + body.setSpan( + new StyleSpan(Typeface.BOLD), + matcher.start(), + matcher.end(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } } Matcher matcher = Emoticons.getEmojiPattern(body).matcher(body); while (matcher.find()) { if (matcher.start() < matcher.end()) { - body.setSpan(new RelativeSizeSpan(1.2f), matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + body.setSpan( + new RelativeSizeSpan(1.2f), + matcher.start(), + matcher.end(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } @@ -473,7 +579,11 @@ public class MessageAdapter extends ArrayAdapter { } } - private void displayDownloadableMessage(ViewHolder viewHolder, final Message message, String text, final BubbleColor bubbleColor) { + private void displayDownloadableMessage( + ViewHolder viewHolder, + final Message message, + String text, + final BubbleColor bubbleColor) { toggleWhisperInfo(viewHolder, message, bubbleColor); viewHolder.image.setVisibility(View.GONE); viewHolder.audioPlayer.setVisibility(View.GONE); @@ -482,22 +592,28 @@ public class MessageAdapter extends ArrayAdapter { final var attachment = Attachment.of(message); final @DrawableRes int imageResource = MediaAdapter.getImageDrawable(attachment); viewHolder.download_button.setIconResource(imageResource); - viewHolder.download_button.setOnClickListener(v -> ConversationFragment.downloadFile(activity, message)); + viewHolder.download_button.setOnClickListener( + v -> ConversationFragment.downloadFile(activity, message)); } - private void displayOpenableMessage(ViewHolder viewHolder, final Message message, final BubbleColor bubbleColor) { + private void displayOpenableMessage( + ViewHolder viewHolder, final Message message, final BubbleColor bubbleColor) { toggleWhisperInfo(viewHolder, message, bubbleColor); viewHolder.image.setVisibility(View.GONE); viewHolder.audioPlayer.setVisibility(View.GONE); viewHolder.download_button.setVisibility(View.VISIBLE); - viewHolder.download_button.setText(activity.getString(R.string.open_x_file, UIHelper.getFileDescriptionString(activity, message))); + viewHolder.download_button.setText( + activity.getString( + R.string.open_x_file, + UIHelper.getFileDescriptionString(activity, message))); final var attachment = Attachment.of(message); final @DrawableRes int imageResource = MediaAdapter.getImageDrawable(attachment); viewHolder.download_button.setIconResource(imageResource); viewHolder.download_button.setOnClickListener(v -> openDownloadable(message)); } - private void displayLocationMessage(ViewHolder viewHolder, final Message message, final BubbleColor bubbleColor) { + private void displayLocationMessage( + ViewHolder viewHolder, final Message message, final BubbleColor bubbleColor) { toggleWhisperInfo(viewHolder, message, bubbleColor); viewHolder.image.setVisibility(View.GONE); viewHolder.audioPlayer.setVisibility(View.GONE); @@ -509,7 +625,8 @@ public class MessageAdapter extends ArrayAdapter { viewHolder.download_button.setOnClickListener(v -> showLocation(message)); } - private void displayAudioMessage(ViewHolder viewHolder, Message message, final BubbleColor bubbleColor) { + private void displayAudioMessage( + ViewHolder viewHolder, Message message, final BubbleColor bubbleColor) { toggleWhisperInfo(viewHolder, message, bubbleColor); viewHolder.image.setVisibility(View.GONE); viewHolder.download_button.setVisibility(View.GONE); @@ -519,7 +636,8 @@ public class MessageAdapter extends ArrayAdapter { this.audioPlayer.init(audioPlayer, message); } - private void displayMediaPreviewMessage(ViewHolder viewHolder, final Message message, final BubbleColor bubbleColor) { + private void displayMediaPreviewMessage( + ViewHolder viewHolder, final Message message, final BubbleColor bubbleColor) { toggleWhisperInfo(viewHolder, message, bubbleColor); viewHolder.download_button.setVisibility(View.GONE); viewHolder.audioPlayer.setVisibility(View.GONE); @@ -541,25 +659,39 @@ public class MessageAdapter extends ArrayAdapter { scaledW = (int) target; scaledH = (int) (params.height / ((double) params.width / target)); } - final LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(scaledW, scaledH); + final LinearLayout.LayoutParams layoutParams = + new LinearLayout.LayoutParams(scaledW, scaledH); layoutParams.setMargins(0, (int) (metrics.density * 4), 0, (int) (metrics.density * 4)); viewHolder.image.setLayoutParams(layoutParams); activity.loadBitmap(message, viewHolder.image); viewHolder.image.setOnClickListener(v -> openDownloadable(message)); } - private void toggleWhisperInfo(ViewHolder viewHolder, final Message message, final BubbleColor bubbleColor) { + private void toggleWhisperInfo( + ViewHolder viewHolder, final Message message, final BubbleColor bubbleColor) { if (message.isPrivateMessage()) { final String privateMarker; if (message.getStatus() <= Message.STATUS_RECEIVED) { privateMarker = activity.getString(R.string.private_message); } else { Jid cp = message.getCounterpart(); - privateMarker = activity.getString(R.string.private_message_to, Strings.nullToEmpty(cp == null ? null : cp.getResource())); + privateMarker = + activity.getString( + R.string.private_message_to, + Strings.nullToEmpty(cp == null ? null : cp.getResource())); } final SpannableString body = new SpannableString(privateMarker); - body.setSpan(new ForegroundColorSpan(bubbleToOnSurfaceVariant(viewHolder.messageBody, bubbleColor)), 0, privateMarker.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - body.setSpan(new StyleSpan(Typeface.BOLD), 0, privateMarker.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + body.setSpan( + new ForegroundColorSpan( + bubbleToOnSurfaceVariant(viewHolder.messageBody, bubbleColor)), + 0, + privateMarker.length(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + body.setSpan( + new StyleSpan(Typeface.BOLD), + 0, + privateMarker.length(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); viewHolder.messageBody.setText(body); viewHolder.messageBody.setVisibility(View.VISIBLE); } else { @@ -577,11 +709,19 @@ public class MessageAdapter extends ArrayAdapter { timestamp = System.currentTimeMillis(); } conversation.messagesLoaded.set(true); - MessageArchiveService.Query query = activity.xmppConnectionService.getMessageArchiveService().query(conversation, new MamReference(0), timestamp, false); + MessageArchiveService.Query query = + activity.xmppConnectionService + .getMessageArchiveService() + .query(conversation, new MamReference(0), timestamp, false); if (query != null) { - Toast.makeText(activity, R.string.fetching_history_from_server, Toast.LENGTH_LONG).show(); + Toast.makeText(activity, R.string.fetching_history_from_server, Toast.LENGTH_LONG) + .show(); } else { - Toast.makeText(activity, R.string.not_fetching_history_retention_period, Toast.LENGTH_SHORT).show(); + Toast.makeText( + activity, + R.string.not_fetching_history_retention_period, + Toast.LENGTH_SHORT) + .show(); } } @@ -589,7 +729,8 @@ public class MessageAdapter extends ArrayAdapter { public View getView(final int position, View view, final @NonNull ViewGroup parent) { final Message message = getItem(position); final boolean omemoEncryption = message.getEncryption() == Message.ENCRYPTION_AXOLOTL; - final boolean isInValidSession = message.isValidInSession() && (!omemoEncryption || message.isTrusted()); + final boolean isInValidSession = + message.isValidInSession() && (!omemoEncryption || message.isTrusted()); final Conversational conversation = message.getConversation(); final Account account = conversation.getAccount(); final int type = getItemViewType(position); @@ -598,18 +739,24 @@ public class MessageAdapter extends ArrayAdapter { viewHolder = new ViewHolder(); switch (type) { case DATE_SEPARATOR: - view = activity.getLayoutInflater().inflate(R.layout.item_message_date_bubble, parent, false); + view = + activity.getLayoutInflater() + .inflate(R.layout.item_message_date_bubble, parent, false); viewHolder.status_message = view.findViewById(R.id.message_body); viewHolder.message_box = view.findViewById(R.id.message_box); break; case RTP_SESSION: - view = activity.getLayoutInflater().inflate(R.layout.item_message_rtp_session, parent, false); + view = + activity.getLayoutInflater() + .inflate(R.layout.item_message_rtp_session, parent, false); viewHolder.status_message = view.findViewById(R.id.message_body); viewHolder.message_box = view.findViewById(R.id.message_box); viewHolder.indicatorReceived = view.findViewById(R.id.indicator_received); break; case SENT: - view = activity.getLayoutInflater().inflate(R.layout.item_message_sent, parent, false); + view = + activity.getLayoutInflater() + .inflate(R.layout.item_message_sent, parent, false); viewHolder.message_box = view.findViewById(R.id.message_box); viewHolder.contact_picture = view.findViewById(R.id.message_photo); viewHolder.download_button = view.findViewById(R.id.download_button); @@ -622,7 +769,9 @@ public class MessageAdapter extends ArrayAdapter { viewHolder.audioPlayer = view.findViewById(R.id.audio_player); break; case RECEIVED: - view = activity.getLayoutInflater().inflate(R.layout.item_message_received, parent, false); + view = + activity.getLayoutInflater() + .inflate(R.layout.item_message_received, parent, false); viewHolder.message_box = view.findViewById(R.id.message_box); viewHolder.contact_picture = view.findViewById(R.id.message_photo); viewHolder.download_button = view.findViewById(R.id.download_button); @@ -636,7 +785,9 @@ public class MessageAdapter extends ArrayAdapter { viewHolder.audioPlayer = view.findViewById(R.id.audio_player); break; case STATUS: - view = activity.getLayoutInflater().inflate(R.layout.item_message_status, parent, false); + view = + activity.getLayoutInflater() + .inflate(R.layout.item_message_status, parent, false); viewHolder.contact_picture = view.findViewById(R.id.message_photo); viewHolder.status_message = view.findViewById(R.id.status_message); viewHolder.load_more_messages = view.findViewById(R.id.load_more_messages); @@ -670,13 +821,17 @@ public class MessageAdapter extends ArrayAdapter { } else if (UIHelper.yesterday(message.getTimeSent())) { viewHolder.status_message.setText(R.string.yesterday); } else { - viewHolder.status_message.setText(DateUtils.formatDateTime(activity, message.getTimeSent(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR)); + viewHolder.status_message.setText( + DateUtils.formatDateTime( + activity, + message.getTimeSent(), + DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR)); } if (colorfulBackground) { - setBackgroundTint(viewHolder.message_box,BubbleColor.PRIMARY); + setBackgroundTint(viewHolder.message_box, BubbleColor.PRIMARY); setTextColor(viewHolder.status_message, BubbleColor.PRIMARY); } else { - setBackgroundTint(viewHolder.message_box,BubbleColor.SURFACE); + setBackgroundTint(viewHolder.message_box, BubbleColor.SURFACE); setTextColor(viewHolder.status_message, BubbleColor.SURFACE); } return view; @@ -686,36 +841,56 @@ public class MessageAdapter extends ArrayAdapter { final long duration = rtpSessionStatus.duration; if (received) { if (duration > 0) { - viewHolder.status_message.setText(activity.getString(R.string.incoming_call_duration_timestamp, TimeFrameUtils.resolve(activity, duration), UIHelper.readableTimeDifferenceFull(activity, message.getTimeSent()))); + viewHolder.status_message.setText( + activity.getString( + R.string.incoming_call_duration_timestamp, + TimeFrameUtils.resolve(activity, duration), + UIHelper.readableTimeDifferenceFull( + activity, message.getTimeSent()))); } else if (rtpSessionStatus.successful) { viewHolder.status_message.setText(R.string.incoming_call); } else { - viewHolder.status_message.setText(activity.getString(R.string.missed_call_timestamp, UIHelper.readableTimeDifferenceFull(activity, message.getTimeSent()))); + viewHolder.status_message.setText( + activity.getString( + R.string.missed_call_timestamp, + UIHelper.readableTimeDifferenceFull( + activity, message.getTimeSent()))); } } else { if (duration > 0) { - viewHolder.status_message.setText(activity.getString(R.string.outgoing_call_duration_timestamp, TimeFrameUtils.resolve(activity, duration), UIHelper.readableTimeDifferenceFull(activity, message.getTimeSent()))); + viewHolder.status_message.setText( + activity.getString( + R.string.outgoing_call_duration_timestamp, + TimeFrameUtils.resolve(activity, duration), + UIHelper.readableTimeDifferenceFull( + activity, message.getTimeSent()))); } else { - viewHolder.status_message.setText(activity.getString(R.string.outgoing_call_timestamp, UIHelper.readableTimeDifferenceFull(activity, message.getTimeSent()))); + viewHolder.status_message.setText( + activity.getString( + R.string.outgoing_call_timestamp, + UIHelper.readableTimeDifferenceFull( + activity, message.getTimeSent()))); } } if (colorfulBackground) { - setBackgroundTint(viewHolder.message_box,BubbleColor.SECONDARY); + setBackgroundTint(viewHolder.message_box, BubbleColor.SECONDARY); setTextColor(viewHolder.status_message, BubbleColor.SECONDARY); setImageTint(viewHolder.indicatorReceived, BubbleColor.SECONDARY); } else { - setBackgroundTint(viewHolder.message_box,BubbleColor.SURFACE); + setBackgroundTint(viewHolder.message_box, BubbleColor.SURFACE); setTextColor(viewHolder.status_message, BubbleColor.SURFACE); setImageTint(viewHolder.indicatorReceived, BubbleColor.SURFACE); } - viewHolder.indicatorReceived.setImageResource(RtpSessionStatus.getDrawable(received, rtpSessionStatus.successful)); + viewHolder.indicatorReceived.setImageResource( + RtpSessionStatus.getDrawable(received, rtpSessionStatus.successful)); return view; } else if (type == STATUS) { if ("LOAD_MORE".equals(message.getBody())) { viewHolder.status_message.setVisibility(View.GONE); viewHolder.contact_picture.setVisibility(View.GONE); viewHolder.load_more_messages.setVisibility(View.VISIBLE); - viewHolder.load_more_messages.setOnClickListener(v -> loadMoreMessages((Conversation) message.getConversation())); + viewHolder.load_more_messages.setOnClickListener( + v -> loadMoreMessages((Conversation) message.getConversation())); } else { viewHolder.status_message.setVisibility(View.VISIBLE); viewHolder.load_more_messages.setVisibility(View.GONE); @@ -723,10 +898,15 @@ public class MessageAdapter extends ArrayAdapter { boolean showAvatar; if (conversation.getMode() == Conversation.MODE_SINGLE) { showAvatar = true; - AvatarWorkerTask.loadAvatar(message, viewHolder.contact_picture, R.dimen.avatar_on_status_message); - } else if (message.getCounterpart() != null || message.getTrueCounterpart() != null || (message.getCounterparts() != null && message.getCounterparts().size() > 0)) { + AvatarWorkerTask.loadAvatar( + message, viewHolder.contact_picture, R.dimen.avatar_on_status_message); + } else if (message.getCounterpart() != null + || message.getTrueCounterpart() != null + || (message.getCounterparts() != null + && message.getCounterparts().size() > 0)) { showAvatar = true; - AvatarWorkerTask.loadAvatar(message, viewHolder.contact_picture, R.dimen.avatar_on_status_message); + AvatarWorkerTask.loadAvatar( + message, viewHolder.contact_picture, R.dimen.avatar_on_status_message); } else { showAvatar = false; } @@ -744,34 +924,58 @@ public class MessageAdapter extends ArrayAdapter { resetClickListener(viewHolder.message_box, viewHolder.messageBody); - viewHolder.contact_picture.setOnClickListener(v -> { - if (MessageAdapter.this.mOnContactPictureClickedListener != null) { - MessageAdapter.this.mOnContactPictureClickedListener - .onContactPictureClicked(message); - } - - }); - viewHolder.contact_picture.setOnLongClickListener(v -> { - if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) { - MessageAdapter.this.mOnContactPictureLongClickedListener - .onContactPictureLongClicked(v, message); - return true; - } else { - return false; - } - }); + viewHolder.contact_picture.setOnClickListener( + v -> { + if (MessageAdapter.this.mOnContactPictureClickedListener != null) { + MessageAdapter.this.mOnContactPictureClickedListener + .onContactPictureClicked(message); + } + }); + viewHolder.contact_picture.setOnLongClickListener( + v -> { + if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) { + MessageAdapter.this.mOnContactPictureLongClickedListener + .onContactPictureLongClicked(v, message); + return true; + } else { + return false; + } + }); final Transferable transferable = message.getTransferable(); final boolean unInitiatedButKnownSize = MessageUtils.unInitiatedButKnownSize(message); - if (unInitiatedButKnownSize || message.isDeleted() || (transferable != null && transferable.getStatus() != Transferable.STATUS_UPLOADING)) { - if (unInitiatedButKnownSize || transferable != null && transferable.getStatus() == Transferable.STATUS_OFFER) { - displayDownloadableMessage(viewHolder, message, activity.getString(R.string.download_x_file, UIHelper.getFileDescriptionString(activity, message)), bubbleColor); - } else if (transferable != null && transferable.getStatus() == Transferable.STATUS_OFFER_CHECK_FILESIZE) { - displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)), bubbleColor); + if (unInitiatedButKnownSize + || message.isDeleted() + || (transferable != null + && transferable.getStatus() != Transferable.STATUS_UPLOADING)) { + if (unInitiatedButKnownSize + || transferable != null + && transferable.getStatus() == Transferable.STATUS_OFFER) { + displayDownloadableMessage( + viewHolder, + message, + activity.getString( + R.string.download_x_file, + UIHelper.getFileDescriptionString(activity, message)), + bubbleColor); + } else if (transferable != null + && transferable.getStatus() == Transferable.STATUS_OFFER_CHECK_FILESIZE) { + displayDownloadableMessage( + viewHolder, + message, + activity.getString( + R.string.check_x_filesize, + UIHelper.getFileDescriptionString(activity, message)), + bubbleColor); } else { - displayInfoMessage(viewHolder, UIHelper.getMessagePreview(activity, message).first, bubbleColor); + displayInfoMessage( + viewHolder, + UIHelper.getMessagePreview(activity, message).first, + bubbleColor); } - } else if (message.isFileOrImage() && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) { + } else if (message.isFileOrImage() + && message.getEncryption() != Message.ENCRYPTION_PGP + && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) { if (message.getFileParams().width > 0 && message.getFileParams().height > 0) { displayMediaPreviewMessage(viewHolder, message, bubbleColor); } else if (message.getFileParams().runtime > 0) { @@ -781,22 +985,33 @@ public class MessageAdapter extends ArrayAdapter { } } else if (message.getEncryption() == Message.ENCRYPTION_PGP) { if (account.isPgpDecryptionServiceConnected()) { - if (conversation instanceof Conversation && !account.hasPendingPgpIntent((Conversation) conversation)) { - displayInfoMessage(viewHolder, activity.getString(R.string.message_decrypting), bubbleColor); + if (conversation instanceof Conversation + && !account.hasPendingPgpIntent((Conversation) conversation)) { + displayInfoMessage( + viewHolder, + activity.getString(R.string.message_decrypting), + bubbleColor); } else { - displayInfoMessage(viewHolder, activity.getString(R.string.pgp_message), bubbleColor); + displayInfoMessage( + viewHolder, activity.getString(R.string.pgp_message), bubbleColor); } } else { - displayInfoMessage(viewHolder, activity.getString(R.string.install_openkeychain), bubbleColor); + displayInfoMessage( + viewHolder, activity.getString(R.string.install_openkeychain), bubbleColor); viewHolder.message_box.setOnClickListener(this::promptOpenKeychainInstall); viewHolder.messageBody.setOnClickListener(this::promptOpenKeychainInstall); } } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) { - displayInfoMessage(viewHolder, activity.getString(R.string.decryption_failed), bubbleColor); + displayInfoMessage( + viewHolder, activity.getString(R.string.decryption_failed), bubbleColor); } else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE) { - displayInfoMessage(viewHolder, activity.getString(R.string.not_encrypted_for_this_device), bubbleColor); + displayInfoMessage( + viewHolder, + activity.getString(R.string.not_encrypted_for_this_device), + bubbleColor); } else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL_FAILED) { - displayInfoMessage(viewHolder, activity.getString(R.string.omemo_decryption_failed), bubbleColor); + displayInfoMessage( + viewHolder, activity.getString(R.string.omemo_decryption_failed), bubbleColor); } else { if (message.isGeoUri()) { displayLocationMessage(viewHolder, message, bubbleColor); @@ -805,16 +1020,20 @@ public class MessageAdapter extends ArrayAdapter { } else if (message.treatAsDownloadable()) { try { final URI uri = new URI(message.getBody()); - displayDownloadableMessage(viewHolder, + displayDownloadableMessage( + viewHolder, message, - activity.getString(R.string.check_x_filesize_on_host, + activity.getString( + R.string.check_x_filesize_on_host, UIHelper.getFileDescriptionString(activity, message), uri.getHost()), bubbleColor); } catch (Exception e) { - displayDownloadableMessage(viewHolder, + displayDownloadableMessage( + viewHolder, message, - activity.getString(R.string.check_x_filesize, + activity.getString( + R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)), bubbleColor); } @@ -835,7 +1054,8 @@ public class MessageAdapter extends ArrayAdapter { if (omemoEncryption && !message.isTrusted()) { viewHolder.encryption.setText(R.string.not_trusted); } else { - viewHolder.encryption.setText(CryptoHelper.encryptionTypeToText(message.getEncryption())); + viewHolder.encryption.setText( + CryptoHelper.encryptionTypeToText(message.getEncryption())); } } } @@ -866,12 +1086,19 @@ public class MessageAdapter extends ArrayAdapter { } public void openDownloadable(Message message) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU && ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU + && ContextCompat.checkSelfPermission( + activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) + != PackageManager.PERMISSION_GRANTED) { ConversationFragment.registerPendingMessage(activity, message); - ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, ConversationsActivity.REQUEST_OPEN_MESSAGE); + ActivityCompat.requestPermissions( + activity, + new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, + ConversationsActivity.REQUEST_OPEN_MESSAGE); return; } - final DownloadableFile file = activity.xmppConnectionService.getFileBackend().getFile(message); + final DownloadableFile file = + activity.xmppConnectionService.getFileBackend().getFile(message); ViewUtil.view(activity, file); } @@ -882,7 +1109,11 @@ public class MessageAdapter extends ArrayAdapter { return; } } - Toast.makeText(activity, R.string.no_application_found_to_display_location, Toast.LENGTH_SHORT).show(); + Toast.makeText( + activity, + R.string.no_application_found_to_display_location, + Toast.LENGTH_SHORT) + .show(); } public void updatePreferences() { @@ -890,7 +1121,6 @@ public class MessageAdapter extends ArrayAdapter { this.colorfulChatBubbles = appSettings.isColorfulChatBubbles(); } - public void setHighlightedTerm(List terms) { this.highlightedTerm = terms == null ? null : StylingHelper.filterHighlightedWords(terms); } @@ -907,46 +1137,60 @@ public class MessageAdapter extends ArrayAdapter { view.setBackgroundTintList(bubbleToColorStateList(view, bubbleColor)); } - private static ColorStateList bubbleToColorStateList(final View view, final BubbleColor bubbleColor) { - final @AttrRes int colorAttributeResId = switch (bubbleColor) { - case SURFACE -> com.google.android.material.R.attr.colorSurfaceContainerHigh; - case PRIMARY -> com.google.android.material.R.attr.colorPrimaryContainer; - case SECONDARY -> com.google.android.material.R.attr.colorSecondaryContainer; - case TERTIARY -> com.google.android.material.R.attr.colorTertiaryContainer; - case WARNING -> com.google.android.material.R.attr.colorErrorContainer; - }; - return ColorStateList.valueOf(MaterialColors.getColor(view,colorAttributeResId)); + private static ColorStateList bubbleToColorStateList( + final View view, final BubbleColor bubbleColor) { + final @AttrRes int colorAttributeResId = + switch (bubbleColor) { + case SURFACE -> com.google.android.material.R.attr.colorSurfaceContainerHigh; + case PRIMARY -> com.google.android.material.R.attr.colorPrimaryContainer; + case SECONDARY -> com.google.android.material.R.attr.colorSecondaryContainer; + case TERTIARY -> com.google.android.material.R.attr.colorTertiaryContainer; + case WARNING -> com.google.android.material.R.attr.colorErrorContainer; + }; + return ColorStateList.valueOf(MaterialColors.getColor(view, colorAttributeResId)); } public static void setImageTint(final ImageView imageView, final BubbleColor bubbleColor) { - ImageViewCompat.setImageTintList(imageView,bubbleToOnSurfaceColorStateList(imageView, bubbleColor)); + ImageViewCompat.setImageTintList( + imageView, bubbleToOnSurfaceColorStateList(imageView, bubbleColor)); + } + + public static void setImageTintError(final ImageView imageView) { + ImageViewCompat.setImageTintList( + imageView, + ColorStateList.valueOf( + MaterialColors.getColor( + imageView, com.google.android.material.R.attr.colorError))); } public static void setTextColor(final TextView textView, final BubbleColor bubbleColor) { textView.setTextColor(bubbleToOnSurfaceColor(textView, bubbleColor)); } - private static @ColorInt int bubbleToOnSurfaceVariant(final View view, final BubbleColor bubbleColor) { + private static @ColorInt int bubbleToOnSurfaceVariant( + final View view, final BubbleColor bubbleColor) { final @AttrRes int colorAttributeResId; if (bubbleColor == BubbleColor.SURFACE) { colorAttributeResId = com.google.android.material.R.attr.colorOnSurfaceVariant; } else { colorAttributeResId = bubbleToOnSurface(bubbleColor); } - return MaterialColors.getColor(view,colorAttributeResId); + return MaterialColors.getColor(view, colorAttributeResId); } - private static @ColorInt int bubbleToOnSurfaceColor(final View view, final BubbleColor bubbleColor) { + private static @ColorInt int bubbleToOnSurfaceColor( + final View view, final BubbleColor bubbleColor) { return MaterialColors.getColor(view, bubbleToOnSurface(bubbleColor)); } - public static ColorStateList bubbleToOnSurfaceColorStateList(final View view, final BubbleColor bubbleColor) { + public static ColorStateList bubbleToOnSurfaceColorStateList( + final View view, final BubbleColor bubbleColor) { return ColorStateList.valueOf(bubbleToOnSurfaceColor(view, bubbleColor)); } private static @AttrRes int bubbleToOnSurface(final BubbleColor bubbleColor) { return switch (bubbleColor) { - case SURFACE -> com.google.android.material.R.attr.colorOnSurface; + case SURFACE -> com.google.android.material.R.attr.colorOnSurface; case PRIMARY -> com.google.android.material.R.attr.colorOnPrimaryContainer; case SECONDARY -> com.google.android.material.R.attr.colorOnSecondaryContainer; case TERTIARY -> com.google.android.material.R.attr.colorOnTertiaryContainer; @@ -954,8 +1198,12 @@ public class MessageAdapter extends ArrayAdapter { }; } - public enum BubbleColor { - SURFACE, PRIMARY, SECONDARY, TERTIARY, WARNING + public enum BubbleColor { + SURFACE, + PRIMARY, + SECONDARY, + TERTIARY, + WARNING } private static class ViewHolder { diff --git a/src/main/res/drawable/ic_more_horiz_24dp.xml b/src/main/res/drawable/ic_more_horiz_24dp.xml new file mode 100644 index 000000000..8914d90f2 --- /dev/null +++ b/src/main/res/drawable/ic_more_horiz_24dp.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/src/main/res/drawable/ic_p2p_24dp.xml b/src/main/res/drawable/ic_p2p_24dp.xml new file mode 100644 index 000000000..eba9e038d --- /dev/null +++ b/src/main/res/drawable/ic_p2p_24dp.xml @@ -0,0 +1,10 @@ + + + diff --git a/src/main/res/drawable/ic_upload_24dp.xml b/src/main/res/drawable/ic_upload_24dp.xml new file mode 100644 index 000000000..37179a8ce --- /dev/null +++ b/src/main/res/drawable/ic_upload_24dp.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/src/main/res/layout/item_conversation.xml b/src/main/res/layout/item_conversation.xml index ef64db6ae..e0ab2fecd 100644 --- a/src/main/res/layout/item_conversation.xml +++ b/src/main/res/layout/item_conversation.xml @@ -74,7 +74,7 @@ android:layout_height="wrap_content" android:layout_alignWithParentIfMissing="true" android:layout_centerVertical="true" - android:layout_toStartOf="@+id/notification_status" + android:layout_toStartOf="@+id/message_status" android:layout_toEndOf="@id/sender_name" android:orientation="horizontal"> @@ -98,6 +98,18 @@ tools:text="Hi. What’s up?" /> + + + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> + android:layout_marginVertical="1sp" + android:orientation="horizontal"> + android:textColor="?colorOnTertiaryContainer" + tools:text="@string/not_trusted" /> + android:textColor="?colorOnTertiaryContainer" + tools:text="10:42" /> diff --git a/src/main/res/layout/item_message_sent.xml b/src/main/res/layout/item_message_sent.xml index 4d6464259..78b8b1b98 100644 --- a/src/main/res/layout/item_message_sent.xml +++ b/src/main/res/layout/item_message_sent.xml @@ -56,15 +56,15 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end" - android:orientation="horizontal" - android:paddingBottom="2dp"> + android:layout_marginVertical="1sp" + android:orientation="horizontal"> @@ -86,6 +87,7 @@ android:layout_width="12sp" android:layout_height="12sp" android:layout_gravity="center_vertical" + android:layout_marginStart="4sp" android:gravity="center_vertical" android:src="@drawable/ic_edit_24dp" app:tint="?colorOnSecondaryContainer" /> @@ -95,6 +97,7 @@ android:layout_width="16sp" android:layout_height="16sp" android:layout_gravity="center_vertical" + android:layout_marginStart="4sp" android:gravity="center_vertical" android:src="@drawable/ic_done_24dp" app:tint="?colorOnSecondaryContainer" />