From 8c78febaa836db5201611d47bda2c6e260961ee7 Mon Sep 17 00:00:00 2001 From: kosyak Date: Wed, 22 May 2024 20:30:58 +0200 Subject: [PATCH] improve replies on file or image messages --- .../siacs/conversations/entities/Message.java | 35 +++++++--- .../ui/ConversationFragment.java | 34 +++++++++- .../ui/adapter/MessageAdapter.java | 60 ++++++++++++++-- .../conversations/utils/StringUtils.java | 4 ++ src/main/res/layout/fragment_conversation.xml | 29 +++++++- src/main/res/layout/message_content.xml | 68 +++++++++++++++++++ 6 files changed, 213 insertions(+), 17 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index 80201ba20..54adad23f 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -1,10 +1,13 @@ package eu.siacs.conversations.entities; import android.content.ContentValues; +import android.content.Context; import android.database.Cursor; +import android.graphics.Bitmap; import android.graphics.Color; import android.text.SpannableStringBuilder; import android.text.TextUtils; +import android.text.style.ImageSpan; import android.util.Log; import androidx.annotation.Nullable; @@ -16,6 +19,7 @@ import com.google.common.primitives.Longs; import org.json.JSONException; +import java.io.FileNotFoundException; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -27,10 +31,12 @@ import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import eu.siacs.conversations.Config; +import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.crypto.axolotl.FingerprintStatus; import eu.siacs.conversations.http.URL; import eu.siacs.conversations.services.AvatarService; +import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.ui.util.PresenceSelector; import eu.siacs.conversations.ui.util.QuoteHelper; import eu.siacs.conversations.utils.CryptoHelper; @@ -38,6 +44,7 @@ import eu.siacs.conversations.utils.Emoticons; import eu.siacs.conversations.utils.GeoHelper; import eu.siacs.conversations.utils.MessageUtils; import eu.siacs.conversations.utils.MimeUtils; +import eu.siacs.conversations.utils.StringUtils; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Tag; @@ -839,9 +846,17 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable } public SpannableStringBuilder getBodyForDisplaying() { + return getBodyForDisplaying(false); + } + + public SpannableStringBuilder getBodyForDisplaying(boolean omitReplyText) { if (replyMessage != null) { + if (omitReplyText) { + return new SpannableStringBuilder(MessageUtils.filterLtrRtl(removeReplyFallback(this).toString()).trim()); + } + try { - return new SpannableStringBuilder(MessageUtils.filterLtrRtl(getReplyText(replyMessage) + "\n" + removeReplyFallback(this, replyMessage)).trim()); + return new SpannableStringBuilder(MessageUtils.filterLtrRtl(getReplyText(replyMessage) + "\n" + removeReplyFallback(this)).trim()); } catch (IndexOutOfBoundsException e) { return new SpannableStringBuilder(MessageUtils.filterLtrRtl(this.body).trim()); } @@ -850,16 +865,20 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable } } - public SpannableStringBuilder getBodyForReplyPreview() { - try { - return new SpannableStringBuilder(MessageUtils.filterLtrRtl(getReplyText(this).toString()).trim()); - } catch (IndexOutOfBoundsException e) { - return new SpannableStringBuilder(MessageUtils.filterLtrRtl(this.body).trim()); + public SpannableStringBuilder getBodyForReplyPreview(XmppConnectionService xmppConnectionService) { + if (isFileOrImage()) { + return SpannableStringBuilder.valueOf(StringUtils.capitalize(UIHelper.getFileDescriptionString(xmppConnectionService, this))); + } else { + try { + return new SpannableStringBuilder(MessageUtils.filterLtrRtl(getReplyText(this).toString()).trim()); + } catch (IndexOutOfBoundsException e) { + return new SpannableStringBuilder(MessageUtils.filterLtrRtl(this.body).trim()); + } } } private StringBuilder getReplyText(Message message) { - StringBuilder reply = removeReplyFallback(message, message.replyMessage); + StringBuilder reply = removeReplyFallback(message); reply.insert(0, '>'); for (int i=0;i replyFallback = message.getFallbacks("urn:xmpp:reply:0"); diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 747e2f48c..e5fef0d2e 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -38,6 +38,7 @@ import android.os.Vibrator; import android.preference.PreferenceManager; import android.provider.MediaStore; import android.text.Editable; +import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.format.DateUtils; @@ -62,6 +63,7 @@ import android.widget.AbsListView.OnScrollListener; import android.widget.AdapterView; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.CheckBox; +import android.widget.LinearLayout; import android.widget.ListView; import android.widget.PopupMenu; import android.widget.TextView.OnEditorActionListener; @@ -73,6 +75,7 @@ import androidx.annotation.NonNull; import androidx.annotation.StringRes; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.content.res.AppCompatResources; +import androidx.core.content.ContextCompat; import androidx.core.view.inputmethod.InputConnectionCompat; import androidx.core.view.inputmethod.InputContentInfoCompat; import androidx.databinding.DataBindingUtil; @@ -126,6 +129,7 @@ import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.ui.adapter.CommandAdapter; import eu.siacs.conversations.ui.adapter.MediaPreviewAdapter; import eu.siacs.conversations.ui.adapter.MessageAdapter; +import eu.siacs.conversations.ui.text.QuoteSpan; import eu.siacs.conversations.ui.util.ActivityResult; import eu.siacs.conversations.ui.util.Attachment; import eu.siacs.conversations.ui.util.ConversationMenuConfigurator; @@ -1572,9 +1576,33 @@ public class ConversationFragment extends XmppFragment return; } - SpannableStringBuilder body = message.getBodyForReplyPreview(); - if (message.isFileOrImage() || message.isOOb()) body.append(" 🖼️"); - messageListAdapter.handleTextQuotes(body, activity.isDarkTheme(), true, message); + SpannableStringBuilder body = message.getBodyForReplyPreview(activity.xmppConnectionService); + + if (message.isFileOrImage() && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) { + if (message.getFileParams().width > 0 && message.getFileParams().height > 0) { + binding.contextPreviewImage.setVisibility(View.VISIBLE); + binding.contextPreviewDoc.setVisibility(View.GONE); + binding.contextPreviewAudio.setVisibility(View.GONE); + activity.loadBitmap(message, binding.contextPreviewImage); + } else if (message.getFileParams().runtime > 0) { + binding.contextPreviewImage.setVisibility(View.GONE); + binding.contextPreviewDoc.setVisibility(View.GONE); + binding.contextPreviewAudio.setVisibility(View.VISIBLE); + } else { + binding.contextPreviewImage.setVisibility(View.GONE); + binding.contextPreviewDoc.setVisibility(View.VISIBLE); + binding.contextPreviewAudio.setVisibility(View.GONE); + } + } else if (message.isOOb()) { + messageListAdapter.handleTextQuotes(body, activity.isDarkTheme(), true, message); + binding.contextPreviewImage.setVisibility(View.GONE); + binding.contextPreviewDoc.setVisibility(View.GONE); + body.append(" 🖼️"); + } else { + messageListAdapter.handleTextQuotes(body, activity.isDarkTheme(), true, message); + binding.contextPreviewImage.setVisibility(View.GONE); + binding.contextPreviewDoc.setVisibility(View.GONE); + } binding.contextPreviewText.setText(body); binding.contextPreviewAuthor.setText(message.getAvatarName()); binding.contextPreview.setVisibility(View.VISIBLE); 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 f86bab3c9..eeabf8629 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -314,7 +314,7 @@ public class MessageAdapter extends ArrayAdapter implements DraggableLi return this.getItemViewType(getItem(position)); } - private int getMessageTextColor(boolean onDark, boolean primary) { + public int getMessageTextColor(boolean onDark, boolean primary) { if (onDark) { return ContextCompat.getColor(activity, primary ? R.color.white : R.color.white70); } else { @@ -520,8 +520,8 @@ public class MessageAdapter extends ArrayAdapter implements DraggableLi int color = this.getMessageTextColor(darkBackground, false); DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); - body.setSpan(new QuoteSpan(color, highlightReply ? ContextCompat.getColor(activity, R.color.blue_a100) : -1, metrics), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - if (highlightReply) { + body.setSpan(new QuoteSpan(color, highlightReply && start == 0 ? ContextCompat.getColor(activity, R.color.blue_a100) : -1, metrics), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + if (highlightReply && start == 0) { body.setSpan(new ReplyClickableSpan(new WeakReference(replyClickListener), new WeakReference(message)), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } @@ -633,15 +633,22 @@ public class MessageAdapter extends ArrayAdapter implements DraggableLi if (message.getBody() != null) { final String nick = UIHelper.getMessageDisplayName(message); - SpannableStringBuilder body = message.getBodyForDisplaying(); + + Message replyMessage = message.getReplyMessage(); + Boolean fileOrImageReply = replyMessage != null && replyMessage.isFileOrImage(); + + SpannableStringBuilder body = message.getBodyForDisplaying(fileOrImageReply); + boolean hasMeCommand = message.hasMeCommand(); if (hasMeCommand) { body = body.replace(0, Message.ME_COMMAND.length(), nick + " "); } + if (body.length() > Config.MAX_DISPLAY_MESSAGE_CHARS) { body = new SpannableStringBuilder(body, 0, Config.MAX_DISPLAY_MESSAGE_CHARS); body.append("\u2026"); } + Message.MergeSeparator[] mergeSeparators = body.getSpans(0, body.length(), Message.MergeSeparator.class); for (Message.MergeSeparator mergeSeparator : mergeSeparators) { int start = body.getSpanStart(mergeSeparator); @@ -656,7 +663,46 @@ public class MessageAdapter extends ArrayAdapter implements DraggableLi applyQuoteSpan(body, start, end, darkBackground, message.getReplyMessage() != null, message); } - handleTextQuotes(body, darkBackground, message.getReplyMessage() != null, message); + if (fileOrImageReply) { + viewHolder.nonTextReplyContent.setVisibility(View.VISIBLE); + WeakReference listener = new WeakReference<>(replyClickListener); + viewHolder.nonTextReplyContent.setOnClickListener(v -> { + ReplyClickListener l = listener.get(); + if (l != null) { + l.onReplyClick(message); + } + }); + + TextView text = viewHolder.nonTextReplyContent.findViewById(R.id.reply_body); + + TextView author = viewHolder.nonTextReplyContent.findViewById(R.id.context_preview_author); + ImageView contextPreviewImage = viewHolder.nonTextReplyContent.findViewById(R.id.context_preview_image); + View contextPreviewDoc = viewHolder.nonTextReplyContent.findViewById(R.id.context_preview_doc); + View contextPreviewAudio = viewHolder.nonTextReplyContent.findViewById(R.id.context_preview_audio); + + text.setText(replyMessage.getBodyForReplyPreview(activity.xmppConnectionService)); + author.setText(replyMessage.getAvatarName()); + + if (replyMessage.getFileParams().width > 0 && replyMessage.getFileParams().height > 0) { + contextPreviewImage.setVisibility(View.VISIBLE); + contextPreviewDoc.setVisibility(View.GONE); + contextPreviewAudio.setVisibility(View.GONE); + activity.loadBitmap(replyMessage, contextPreviewImage); + } else if (replyMessage.getFileParams().runtime > 0) { + contextPreviewImage.setVisibility(View.GONE); + contextPreviewDoc.setVisibility(View.GONE); + contextPreviewAudio.setVisibility(View.VISIBLE); + } else { + contextPreviewImage.setVisibility(View.GONE); + contextPreviewDoc.setVisibility(View.VISIBLE); + contextPreviewAudio.setVisibility(View.GONE); + } + } else { + viewHolder.nonTextReplyContent.setVisibility(View.GONE); + } + + handleTextQuotes(body, darkBackground, message.getReplyMessage() != null && !fileOrImageReply, message); + if (!message.isPrivateMessage()) { if (hasMeCommand) { body.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), 0, nick.length(), @@ -859,6 +905,7 @@ public class MessageAdapter extends ArrayAdapter implements DraggableLi viewHolder.edit_indicator = view.findViewById(R.id.edit_indicator); viewHolder.image = view.findViewById(R.id.message_image); viewHolder.messageBody = view.findViewById(R.id.message_body); + viewHolder.nonTextReplyContent = view.findViewById(R.id.non_text_reply_content); viewHolder.time = view.findViewById(R.id.message_time); viewHolder.indicatorReceived = view.findViewById(R.id.indicator_received); viewHolder.audioPlayer = view.findViewById(R.id.audio_player); @@ -875,6 +922,7 @@ public class MessageAdapter extends ArrayAdapter implements DraggableLi viewHolder.edit_indicator = view.findViewById(R.id.edit_indicator); viewHolder.image = view.findViewById(R.id.message_image); viewHolder.messageBody = view.findViewById(R.id.message_body); + viewHolder.nonTextReplyContent = view.findViewById(R.id.non_text_reply_content); viewHolder.time = view.findViewById(R.id.message_time); viewHolder.indicatorReceived = view.findViewById(R.id.indicator_received); viewHolder.encryption = view.findViewById(R.id.message_encryption); @@ -1302,6 +1350,8 @@ public class MessageAdapter extends ArrayAdapter implements DraggableLi protected TextView status_message; protected TextView encryption; + protected View nonTextReplyContent; + protected View clicksInterceptor; int position; diff --git a/src/main/java/eu/siacs/conversations/utils/StringUtils.java b/src/main/java/eu/siacs/conversations/utils/StringUtils.java index 1135560bb..1939099c8 100644 --- a/src/main/java/eu/siacs/conversations/utils/StringUtils.java +++ b/src/main/java/eu/siacs/conversations/utils/StringUtils.java @@ -47,4 +47,8 @@ public class StringUtils { return input == null || input.trim().isEmpty() ? null : input; } + public static String capitalize(String str) { + if(str == null || str.length()<=1) return str; + return str.substring(0, 1).toUpperCase() + str.substring(1); + } } diff --git a/src/main/res/layout/fragment_conversation.xml b/src/main/res/layout/fragment_conversation.xml index 561c82602..9f6683cec 100644 --- a/src/main/res/layout/fragment_conversation.xml +++ b/src/main/res/layout/fragment_conversation.xml @@ -70,6 +70,34 @@ android:layout_marginRight="8dp" android:contentDescription="Reply to" /> + + + + + + + + + + diff --git a/src/main/res/layout/message_content.xml b/src/main/res/layout/message_content.xml index 05af4e42c..2b7d68a5c 100644 --- a/src/main/res/layout/message_content.xml +++ b/src/main/res/layout/message_content.xml @@ -1,6 +1,74 @@ + + + + + + + + + + + + + + + + + + +