show real reply text instead of fallback
This commit is contained in:
parent
869f92169d
commit
366e5aa389
|
@ -780,6 +780,45 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
|
|||
return unread;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Message getMessageWithAnyMatchingId(String uuid) {
|
||||
if (uuid == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
synchronized (this.messages) {
|
||||
for (int i = 0; i < messages.size(); ++i) {
|
||||
if (uuid.equals(messages.get(i).getServerMsgId())) {
|
||||
return messages.get(i);
|
||||
}
|
||||
|
||||
if (uuid.equals(messages.get(i).getRemoteMsgId())) {
|
||||
return messages.get(i);
|
||||
}
|
||||
|
||||
if (uuid.equals(messages.get(i).getUuid())) {
|
||||
return messages.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < historyPartMessages.size(); ++i) {
|
||||
if (uuid.equals(historyPartMessages.get(i).getServerMsgId())) {
|
||||
return historyPartMessages.get(i);
|
||||
}
|
||||
|
||||
if (uuid.equals(historyPartMessages.get(i).getRemoteMsgId())) {
|
||||
return historyPartMessages.get(i);
|
||||
}
|
||||
|
||||
if (uuid.equals(historyPartMessages.get(i).getUuid())) {
|
||||
return historyPartMessages.get(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Message getLatestMessage() {
|
||||
synchronized (this.messages) {
|
||||
if (this.messages.size() == 0) {
|
||||
|
@ -1190,12 +1229,14 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
|
|||
if (!message.isPrivateMessage()) {
|
||||
synchronized (this.messages) {
|
||||
this.messages.add(message);
|
||||
actualizeReplyMessages(this.messages, List.of(message));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (message.isPrivateMessage() && Objects.equals(res1, res2)) {
|
||||
synchronized (this.messages) {
|
||||
this.messages.add(message);
|
||||
actualizeReplyMessages(this.messages, List.of(message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1217,19 +1258,24 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
|
|||
if (!message.isPrivateMessage()) {
|
||||
synchronized (this.messages) {
|
||||
properListToAdd.add(Math.min(offset, properListToAdd.size()), message);
|
||||
actualizeReplyMessages(properListToAdd, List.of(message));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (message.isPrivateMessage() && Objects.equals(res1, res2)) {
|
||||
synchronized (this.messages) {
|
||||
properListToAdd.add(Math.min(offset, properListToAdd.size()), message);
|
||||
actualizeReplyMessages(properListToAdd, List.of(message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!historyPartMessages.isEmpty() && hasDuplicateMessage(historyPartMessages.get(historyPartMessages.size() - 1))) {
|
||||
messages.addAll(0, historyPartMessages);
|
||||
jumpToLatest();
|
||||
synchronized (this.messages) {
|
||||
if (!historyPartMessages.isEmpty() && hasDuplicateMessage(historyPartMessages.get(historyPartMessages.size() - 1))) {
|
||||
messages.addAll(0, historyPartMessages);
|
||||
actualizeReplyMessages(messages, List.of(message));
|
||||
jumpToLatest();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1279,10 +1325,43 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
|
|||
} else {
|
||||
properListToAdd.addAll(index, newM);
|
||||
}
|
||||
|
||||
actualizeReplyMessages(properListToAdd, messages);
|
||||
}
|
||||
account.getPgpDecryptionService().decrypt(newM);
|
||||
}
|
||||
|
||||
private void actualizeReplyMessages(List<Message> mainList, List<Message> messages) {
|
||||
for (Message m : mainList) {
|
||||
if (m.isReplyRestoredFromDb()) {
|
||||
Element reply = m.getReply();
|
||||
|
||||
if (reply == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String replyId = reply.getAttribute("id");
|
||||
|
||||
for (Message rep : messages) {
|
||||
if (replyId.equals(rep.getServerMsgId())) {
|
||||
m.setReplyMessage(rep, false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (replyId.equals(rep.getRemoteMsgId())) {
|
||||
m.setReplyMessage(rep, false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (replyId.equals(rep.getUuid())) {
|
||||
m.setReplyMessage(rep, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void expireOldMessages(long timestamp) {
|
||||
synchronized (this.messages) {
|
||||
for (ListIterator<Message> iterator = this.messages.listIterator(); iterator.hasNext(); ) {
|
||||
|
|
|
@ -4,7 +4,11 @@ import android.content.ContentValues;
|
|||
import android.database.Cursor;
|
||||
import android.graphics.Color;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.io.ByteSource;
|
||||
|
@ -122,6 +126,8 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
|||
protected Transferable transferable = null;
|
||||
private Message mNextMessage = null;
|
||||
private Message mPreviousMessage = null;
|
||||
private Message replyMessage = null;
|
||||
private boolean replyRestoredFromDb = false;
|
||||
private String axolotlFingerprint = null;
|
||||
private String errorMessage = null;
|
||||
private Set<ReadByMarker> readByMarkers = new CopyOnWriteArraySet<>();
|
||||
|
@ -411,6 +417,25 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
|||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Message getReplyMessage() {
|
||||
if (replyMessage != null && replyMessage.deleted) {
|
||||
replyMessage = null;
|
||||
replyRestoredFromDb = false;
|
||||
}
|
||||
|
||||
return replyMessage;
|
||||
}
|
||||
|
||||
public boolean isReplyRestoredFromDb() {
|
||||
return replyRestoredFromDb;
|
||||
}
|
||||
|
||||
public void setReplyMessage(Message replyMessage, boolean restoredFromDb) {
|
||||
this.replyMessage = replyMessage;
|
||||
this.replyRestoredFromDb = restoredFromDb;
|
||||
}
|
||||
|
||||
public String getConversationUuid() {
|
||||
return conversationUuid;
|
||||
}
|
||||
|
@ -815,7 +840,42 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable
|
|||
}
|
||||
|
||||
public SpannableStringBuilder getBodyForDisplaying() {
|
||||
return new SpannableStringBuilder(MessageUtils.filterLtrRtl(this.body).trim());
|
||||
if (replyMessage != null) {
|
||||
try {
|
||||
return new SpannableStringBuilder(MessageUtils.filterLtrRtl(">" + removeReplyFallback(replyMessage) + "\n" + removeReplyFallback(this)).trim());
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
return new SpannableStringBuilder(MessageUtils.filterLtrRtl(this.body).trim());
|
||||
}
|
||||
} else {
|
||||
return new SpannableStringBuilder(MessageUtils.filterLtrRtl(this.body).trim());
|
||||
}
|
||||
}
|
||||
|
||||
public SpannableStringBuilder getBodyForReplyPreview() {
|
||||
try {
|
||||
return new SpannableStringBuilder(MessageUtils.filterLtrRtl(">" + removeReplyFallback(this)).trim());
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
return new SpannableStringBuilder(MessageUtils.filterLtrRtl(this.body).trim());
|
||||
}
|
||||
}
|
||||
|
||||
private StringBuilder removeReplyFallback(Message message) {
|
||||
StringBuilder sb = new StringBuilder(message.body);
|
||||
|
||||
List<Element> replyFallback = message.getFallbacks("urn:xmpp:reply:0");
|
||||
if (replyFallback.size() == 0) {
|
||||
return sb;
|
||||
}
|
||||
|
||||
Element bodyFallback = replyFallback.get(0).findChild("body");
|
||||
int startCodePoint = Integer.parseInt(bodyFallback.getAttribute("start"));
|
||||
int endCodePoint = Integer.parseInt(bodyFallback.getAttribute("end"));
|
||||
|
||||
if (startCodePoint < 0) return sb;
|
||||
if (endCodePoint > sb.length()) return sb;
|
||||
|
||||
sb.replace(message.body.offsetByCodePoints(0, startCodePoint), message.body.offsetByCodePoints(0, endCodePoint), "");
|
||||
return sb;
|
||||
}
|
||||
|
||||
public boolean hasMeCommand() {
|
||||
|
|
|
@ -758,6 +758,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
|||
}
|
||||
}
|
||||
|
||||
mXmppConnectionService.restoreReplyForMessage(conversation, message);
|
||||
if (query != null && query.getPagingOrder() == MessageArchiveService.PagingOrder.REVERSE) {
|
||||
conversation.prepend(query.getActualInThisQuery(), message);
|
||||
} else {
|
||||
|
@ -946,6 +947,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
|||
message.setServerMsgId(serverMsgId);
|
||||
message.setTime(timestamp);
|
||||
message.setBody(new RtpSessionStatus(false, 0).toString());
|
||||
mXmppConnectionService.restoreReplyForMessage(conversation, message);
|
||||
c.add(message);
|
||||
mXmppConnectionService.databaseBackend.createMessage(message);
|
||||
}
|
||||
|
@ -988,6 +990,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
|
|||
message.setServerMsgId(serverMsgId);
|
||||
message.setTime(timestamp);
|
||||
message.setBody(new RtpSessionStatus(true, 0).toString());
|
||||
mXmppConnectionService.restoreReplyForMessage(conversation, message);
|
||||
if (query.getPagingOrder() == MessageArchiveService.PagingOrder.REVERSE) {
|
||||
c.prepend(query.getActualInThisQuery(), message);
|
||||
} else {
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.database.sqlite.SQLiteDatabase;
|
|||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.os.Environment;
|
||||
import android.os.SystemClock;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
|
@ -840,6 +841,45 @@ public class DatabaseBackend extends SQLiteOpenHelper {
|
|||
return list;
|
||||
}
|
||||
|
||||
public ArrayList<Message> getMessagesByIds(Conversation conversation, Set<String> ids) {
|
||||
SQLiteDatabase db = this.getReadableDatabase();
|
||||
List<String> parameters = new ArrayList<>();
|
||||
|
||||
for (String id : ids) {
|
||||
parameters.add("?");
|
||||
}
|
||||
|
||||
String parametersString = TextUtils.join(",", parameters);
|
||||
|
||||
String[] selectionArgs = new String[ids.size() * 3 + 1];
|
||||
selectionArgs[0] = conversation.getUuid();
|
||||
int ind = 1;
|
||||
|
||||
for (int i=0;i<3;i++) {
|
||||
for (String id : ids) {
|
||||
selectionArgs[ind] = id;
|
||||
ind++;
|
||||
}
|
||||
}
|
||||
|
||||
Cursor cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
|
||||
+ "=? and (" + Message.SERVER_MSG_ID + " in (" + parametersString + ") or " + Message.REMOTE_MSG_ID + " in (" + parametersString + ") or " + Message.UUID + " in (" + parametersString + "))", selectionArgs, null, null, Message.TIME_SENT
|
||||
+ " DESC", String.valueOf(ids.size()));
|
||||
CursorUtils.upgradeCursorWindowSize(cursor);
|
||||
ArrayList<Message> list = new ArrayList<>();
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
try {
|
||||
Message m = Message.fromCursor(cursor, conversation);
|
||||
list.add(m);
|
||||
} catch (Exception e) {
|
||||
Log.e(Config.LOGTAG, "unable to restore message");
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public ArrayList<Message> getMessages(Conversation conversation, int limit, long timestamp, boolean isForward) {
|
||||
ArrayList<Message> list = new ArrayList<>();
|
||||
SQLiteDatabase db = this.getReadableDatabase();
|
||||
|
|
|
@ -1821,6 +1821,7 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
} else {
|
||||
if (addToConversation) {
|
||||
restoreReplyForMessage(conversation, message);
|
||||
conversation.add(message);
|
||||
}
|
||||
if (saveInDb) {
|
||||
|
@ -2182,7 +2183,9 @@ public class XmppConnectionService extends Service {
|
|||
}
|
||||
|
||||
private void restoreMessages(Conversation conversation) {
|
||||
conversation.addAll(0, databaseBackend.getMessages(conversation, Config.PAGE_SIZE), false);
|
||||
List<Message> messages = databaseBackend.getMessages(conversation, Config.PAGE_SIZE);
|
||||
restoreRepliesForMessages(conversation, messages);
|
||||
conversation.addAll(0, messages, false);
|
||||
conversation.findUnsentTextMessages(message -> markMessage(message, Message.STATUS_WAITING));
|
||||
conversation.findUnreadMessagesAndCalls(mNotificationService::pushFromBacklog);
|
||||
}
|
||||
|
@ -2310,6 +2313,7 @@ public class XmppConnectionService extends Service {
|
|||
public void jumpToMessage(final Conversation conversation, final String uuid, JumpToMessageListener listener) {
|
||||
final Runnable runnable = () -> {
|
||||
List<Message> messages = databaseBackend.getMessagesNearUuid(conversation, 30, uuid);
|
||||
restoreRepliesForMessages(conversation, messages);
|
||||
if (messages != null && !messages.isEmpty()) {
|
||||
conversation.jumpToHistoryPart(messages);
|
||||
listener.onSuccess();
|
||||
|
@ -2321,6 +2325,88 @@ public class XmppConnectionService extends Service {
|
|||
mDatabaseReaderExecutor.execute(runnable);
|
||||
}
|
||||
|
||||
public void restoreReplyForMessage(final Conversation conversation, Message message) {
|
||||
restoreRepliesForMessages(conversation, List.of(message));
|
||||
}
|
||||
|
||||
public void restoreRepliesForMessages(final Conversation conversation, List<Message> messages) {
|
||||
Map<String, ArrayList<Message>> notFoundReplies = null;
|
||||
|
||||
for (Message m : messages) {
|
||||
Element reply = m.getReply();
|
||||
|
||||
if (reply == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String replyId = reply.getAttribute("id");
|
||||
|
||||
|
||||
Message replyMessage = null;
|
||||
|
||||
for (Message rep : messages) {
|
||||
if (replyId.equals(rep.getServerMsgId())) {
|
||||
replyMessage = rep;
|
||||
break;
|
||||
}
|
||||
|
||||
if (replyId.equals(rep.getRemoteMsgId())) {
|
||||
replyMessage = rep;
|
||||
break;
|
||||
}
|
||||
|
||||
if (replyId.equals(rep.getUuid())) {
|
||||
replyMessage = rep;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (replyMessage == null) {
|
||||
replyMessage = conversation.getMessageWithAnyMatchingId(replyId);
|
||||
}
|
||||
|
||||
if (replyMessage != null) {
|
||||
m.setReplyMessage(replyMessage, false);
|
||||
} else {
|
||||
if (notFoundReplies == null) {
|
||||
notFoundReplies = new HashMap<>();
|
||||
}
|
||||
|
||||
ArrayList<Message> list = notFoundReplies.computeIfAbsent(replyId, id -> new ArrayList<>());
|
||||
list.add(m);
|
||||
}
|
||||
}
|
||||
|
||||
if (notFoundReplies != null) {
|
||||
List<Message> restored = databaseBackend.getMessagesByIds(conversation, notFoundReplies.keySet());
|
||||
|
||||
for (String id : notFoundReplies.keySet()) {
|
||||
for (Message m : restored) {
|
||||
if (id.equals(m.getServerMsgId())) {
|
||||
for (Message rm : notFoundReplies.get(id)) {
|
||||
rm.setReplyMessage(m, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (id.equals(m.getRemoteMsgId())) {
|
||||
for (Message rm : notFoundReplies.get(id)) {
|
||||
rm.setReplyMessage(m, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (id.equals(m.getUuid())) {
|
||||
for (Message rm : notFoundReplies.get(id)) {
|
||||
rm.setReplyMessage(m, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void loadMoreMessages(final Conversation conversation, final long timestamp, boolean isForward, final OnMoreMessagesLoaded callback) {
|
||||
if (XmppConnectionService.this.getMessageArchiveService().queryInProgress(conversation, callback)) {
|
||||
return;
|
||||
|
@ -2339,11 +2425,14 @@ public class XmppConnectionService extends Service {
|
|||
List<Message> messages = databaseBackend.getMessages(conversation, Config.PAGE_SIZE, timestamp, isForward);
|
||||
|
||||
if (messages.size() > 0) {
|
||||
restoreRepliesForMessages(conversation, messages);
|
||||
|
||||
if (isForward) {
|
||||
conversation.addAll(-1, messages, true);
|
||||
} else {
|
||||
conversation.addAll(0, messages, true);
|
||||
}
|
||||
|
||||
callback.onMoreMessagesLoaded(messages.size(), conversation);
|
||||
} else if (!isForward &&
|
||||
conversation.hasMessagesLeftOnServer()
|
||||
|
@ -2517,7 +2606,9 @@ public class XmppConnectionService extends Service {
|
|||
final Conversation c = conversation;
|
||||
final Runnable runnable = () -> {
|
||||
if (loadMessagesFromDb) {
|
||||
c.addAll(0, databaseBackend.getMessages(c, Config.PAGE_SIZE), false);
|
||||
List<Message> messages = databaseBackend.getMessages(c, Config.PAGE_SIZE);
|
||||
restoreRepliesForMessages(c, messages);
|
||||
c.addAll(0, messages, false);
|
||||
updateConversationUi();
|
||||
c.messagesLoaded.set(true);
|
||||
}
|
||||
|
|
|
@ -1554,9 +1554,9 @@ public class ConversationFragment extends XmppFragment
|
|||
return;
|
||||
}
|
||||
|
||||
SpannableStringBuilder body = message.getBodyForDisplaying();
|
||||
SpannableStringBuilder body = message.getBodyForReplyPreview();
|
||||
if (message.isFileOrImage() || message.isOOb()) body.append(" 🖼️");
|
||||
messageListAdapter.handleTextQuotes(body, activity.isDarkTheme(), false, message);
|
||||
messageListAdapter.handleTextQuotes(body, activity.isDarkTheme(), true, message);
|
||||
binding.contextPreviewText.setText(body);
|
||||
binding.contextPreviewAuthor.setText(message.getAvatarName());
|
||||
binding.contextPreview.setVisibility(View.VISIBLE);
|
||||
|
|
|
@ -37,6 +37,7 @@ import android.widget.Toast;
|
|||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.content.res.AppCompatResources;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
@ -462,7 +463,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
|||
* 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.
|
||||
*/
|
||||
public boolean handleTextQuotes(SpannableStringBuilder body, boolean darkBackground, boolean highlightReply, Message message) {
|
||||
public void handleTextQuotes(SpannableStringBuilder body, boolean darkBackground, boolean highlightReply, Message message) {
|
||||
boolean startsWithQuote = false;
|
||||
int quoteDepth = 0;
|
||||
while (QuoteHelper.bodyContainsQuoteStart(body) && quoteDepth <= Config.QUOTE_MAX_DEPTH) {
|
||||
|
@ -546,7 +547,6 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
|||
|
||||
applyQuoteSpan(body, start, end, darkBackground, true, message);
|
||||
}
|
||||
return startsWithQuote;
|
||||
}
|
||||
|
||||
private void displayTextMessage(final ViewHolder viewHolder, final Message message, boolean darkBackground, int type) {
|
||||
|
@ -586,10 +586,10 @@ public class MessageAdapter extends ArrayAdapter<Message> {
|
|||
int start = body.getSpanStart(quote);
|
||||
int end = body.getSpanEnd(quote);
|
||||
body.removeSpan(quote);
|
||||
applyQuoteSpan(body, start, end, darkBackground, message.getReply() != null, message);
|
||||
applyQuoteSpan(body, start, end, darkBackground, message.getReplyMessage() != null, message);
|
||||
}
|
||||
|
||||
boolean startsWithQuote = handleTextQuotes(body, darkBackground, message.getReply() != null, message);
|
||||
handleTextQuotes(body, darkBackground, message.getReplyMessage() != null, message);
|
||||
if (!message.isPrivateMessage()) {
|
||||
if (hasMeCommand) {
|
||||
body.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), 0, nick.length(),
|
||||
|
|
|
@ -508,6 +508,7 @@ public class JingleFileTransferConnection extends AbstractJingleConnection imple
|
|||
}
|
||||
long size = parseLong(fileSize, 0);
|
||||
message.setBody(Long.toString(size));
|
||||
xmppConnectionService.restoreReplyForMessage(conversation, message);
|
||||
conversation.add(message);
|
||||
jingleConnectionManager.updateConversationUi(true);
|
||||
this.file = this.xmppConnectionService.getFileBackend().getFile(message, false);
|
||||
|
|
Loading…
Reference in a new issue