diff --git a/src/cheogram/res/drawable/ic_reply_black.xml b/src/cheogram/res/drawable/ic_reply_black.xml new file mode 100644 index 0000000000000000000000000000000000000000..4c0db01d9d56a514fe7ec7a97a6b105e2fe956bf --- /dev/null +++ b/src/cheogram/res/drawable/ic_reply_black.xml @@ -0,0 +1,11 @@ + + + diff --git a/src/cheogram/res/values/themes.xml b/src/cheogram/res/values/themes.xml index 18019aec1a5743fefaededabcd4b26824940d858..62f863331a9ababd7f1b5e5ceb6dc9ab258feb77 100644 --- a/src/cheogram/res/values/themes.xml +++ b/src/cheogram/res/values/themes.xml @@ -108,7 +108,7 @@ @drawable/ic_save_black_24dp @drawable/ic_group_white_24dp @drawable/ic_add_white_24dp - @drawable/ic_reply_white_24dp + @drawable/ic_reply_black @drawable/ic_refresh_black_24dp @drawable/ic_attach_file_white_24dp @drawable/ic_lock_open_white_24dp diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index 2d70af3906f9d269a78619e9a4b03b35202c0d44..8382af89a349663ae58d045cbe573672f7b4262b 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -170,6 +170,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl protected Element thread = null; protected boolean lockThread = false; protected boolean userSelectedThread = false; + protected Message replyTo = null; public Conversation(final String name, final Account account, final Jid contactJid, final int mode) { @@ -671,6 +672,14 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl return this.userSelectedThread; } + public void setReplyTo(Message m) { + this.replyTo = m; + } + + public Message getReplyTo() { + return this.replyTo; + } + public boolean isRead() { synchronized (this.messages) { for(final Message message : Lists.reverse(this.messages)) { diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index 40939cb4bcb4efd028467a693cc2a34d226aee74..76c9d1cdec6eb4f997e256ad480b7c5b10d7e982 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -47,6 +47,7 @@ import eu.siacs.conversations.crypto.axolotl.FingerprintStatus; import eu.siacs.conversations.http.URL; import eu.siacs.conversations.services.AvatarService; import eu.siacs.conversations.ui.util.PresenceSelector; +import eu.siacs.conversations.ui.util.QuoteHelper; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.Emoticons; import eu.siacs.conversations.utils.GeoHelper; @@ -379,6 +380,22 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable return values; } + public Message reply() { + Message m = new Message(conversation, QuoteHelper.quote(getBody()) + "\n", ENCRYPTION_NONE); + m.setThread(getThread()); + m.addPayload( + new Element("reply", "urn:xmpp:reply:0") + .setAttribute("to", getCounterpart()) + .setAttribute("id", conversation.getMode() == Conversation.MODE_MULTI ? getServerMsgId() : getRemoteMsgId()) + ); + final Element fallback = new Element("fallback", "urn:xmpp:fallback:0").setAttribute("for", "urn:xmpp:reply:0"); + fallback.addChild("body", "urn:xmpp:fallback:0") + .setAttribute("start", "0") + .setAttribute("end", "" + m.body.length()); + m.addPayload(fallback); + return m; + } + public String getConversationUuid() { return conversationUuid; } @@ -450,6 +467,13 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable this.treatAsDownloadable = null; } + public synchronized void appendBody(String append) { + this.body += append; + this.isGeoUri = null; + this.isEmojisOnly = null; + this.treatAsDownloadable = null; + } + public String getSubject() { return subject; } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 69f9ca493e33c8c314570c75c233d62baff5284c..e3c3d4ad3f942a6a806a0fe6886c4871f158460c 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -30,6 +30,7 @@ import android.os.SystemClock; import android.preference.PreferenceManager; import android.provider.MediaStore; import android.text.Editable; +import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.util.Log; import android.view.ContextMenu; @@ -893,7 +894,13 @@ public class ConversationFragment extends XmppFragment } final Message message; if (conversation.getCorrectingMessage() == null) { - message = new Message(conversation, body, conversation.getNextEncryption()); + if (conversation.getReplyTo() != null) { + message = conversation.getReplyTo().reply(); + message.appendBody(body); + message.setEncryption(conversation.getNextEncryption()); + } else { + message = new Message(conversation, body, conversation.getNextEncryption()); + } message.setThread(conversation.getThread()); Message.configurePrivateMessage(message); } else { @@ -910,6 +917,7 @@ public class ConversationFragment extends XmppFragment default: sendMessage(message); } + setupReply(null); } private boolean trustKeysIfNeeded(final Conversation conversation, final int requestCode) { @@ -1118,6 +1126,7 @@ public class ConversationFragment extends XmppFragment } else { activity.selectPresence(conversation, callback); } + setupReply(null); } private static boolean anyNeedsExternalStoragePermission( @@ -1269,6 +1278,7 @@ public class ConversationFragment extends XmppFragment binding.textinput.setRichContentListener(new String[] {"image/*"}, mEditorContentListener); binding.textSendButton.setOnClickListener(this.mSendButtonListener); + binding.contextPreviewCancel.setOnClickListener((v) -> setupReply(null)); binding.scrollToBottomButton.setOnClickListener(this.mScrollButtonListener); binding.messagesView.setOnScrollListener(mOnScrollListener); @@ -1358,7 +1368,21 @@ public class ConversationFragment extends XmppFragment private void quoteMessage(Message message) { setThread(message.getThread()); conversation.setUserSelectedThread(true); - quoteText(MessageUtils.prepareQuote(message)); + if (message.getThread() == null) newThread(); + setupReply(message); + } + + private void setupReply(Message message) { + conversation.setReplyTo(message); + if (message == null) { + binding.contextPreview.setVisibility(View.GONE); + return; + } + + SpannableStringBuilder body = message.getSpannableBody(null, null); + messageListAdapter.handleTextQuotes(body, activity.isDarkTheme()); + binding.contextPreviewText.setText(body); + binding.contextPreview.setVisibility(View.VISIBLE); } private void setThread(Element thread) { @@ -2721,6 +2745,7 @@ public class ConversationFragment extends XmppFragment } setThread(conversation.getThread()); + setupReply(conversation.getReplyTo()); stopScrolling(); Log.d(Config.LOGTAG, "reInit(hasExtras=" + hasExtras + ")"); 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 f76bd8fb84e66bd120d5287659f1532f0c0eef1e..0e995696e0493b3dd1c9e69a189195604da8f524 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -415,7 +415,7 @@ public class MessageAdapter extends ArrayAdapter { * 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(SpannableStringBuilder body, boolean darkBackground) { + public boolean handleTextQuotes(SpannableStringBuilder body, boolean darkBackground) { boolean startsWithQuote = false; int quoteDepth = 1; while (QuoteHelper.bodyContainsQuoteStart(body) && quoteDepth <= Config.QUOTE_MAX_DEPTH) { diff --git a/src/main/java/eu/siacs/conversations/ui/util/QuoteHelper.java b/src/main/java/eu/siacs/conversations/ui/util/QuoteHelper.java index c2a69e6074f9506232f4a47ad2156ddc8e45cbfa..883c5507a9dae04016c4dd71a4822cffbaa812f1 100644 --- a/src/main/java/eu/siacs/conversations/ui/util/QuoteHelper.java +++ b/src/main/java/eu/siacs/conversations/ui/util/QuoteHelper.java @@ -103,4 +103,15 @@ public class QuoteHelper { } return text; } -} \ No newline at end of file + + public static String quote(String text) { + text = replaceAltQuoteCharsInText(text); + return text + // first replace all '>' at the beginning of the line with nice and tidy '>>' + // for nested quoting + .replaceAll("(^|\n)(" + QUOTE_CHAR + ")", "$1$2$2") + // then find all other lines and have them start with a '> ' + .replaceAll("(^|\n)(?!" + QUOTE_CHAR + ")(.*)", "$1> $2") + ; + } +} diff --git a/src/main/java/eu/siacs/conversations/ui/widget/EditMessage.java b/src/main/java/eu/siacs/conversations/ui/widget/EditMessage.java index 455c3ba440589cebdbb19f2ee7f318e4cf21a6ec..bcacb3341db388d6d4f7e8cf41a6003b65a74129 100644 --- a/src/main/java/eu/siacs/conversations/ui/widget/EditMessage.java +++ b/src/main/java/eu/siacs/conversations/ui/widget/EditMessage.java @@ -144,14 +144,7 @@ public class EditMessage extends AppCompatEditText { } public void insertAsQuote(String text) { - text = QuoteHelper.replaceAltQuoteCharsInText(text); - text = text - // first replace all '>' at the beginning of the line with nice and tidy '>>' - // for nested quoting - .replaceAll("(^|\n)(" + QuoteHelper.QUOTE_CHAR + ")", "$1$2$2") - // then find all other lines and have them start with a '> ' - .replaceAll("(^|\n)(?!" + QuoteHelper.QUOTE_CHAR + ")(.*)", "$1> $2") - ; + text = QuoteHelper.quote(text); Editable editable = getEditableText(); int position = getSelectionEnd(); if (position == -1) position = editable.length(); diff --git a/src/main/res/layout/fragment_conversation.xml b/src/main/res/layout/fragment_conversation.xml index 477ecf3f012a51b675de67ac4593e09424bc243b..e697828e9158275628fbed8559d4761d595a6ac7 100644 --- a/src/main/res/layout/fragment_conversation.xml +++ b/src/main/res/layout/fragment_conversation.xml @@ -48,6 +48,44 @@ android:transcriptMode="normal" tools:listitem="@layout/message_sent"> + + + + + + + + +