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">
+
+
+
+
+
+
+
+
+