aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Gultsch <daniel@gultsch.de>2016-02-15 23:15:04 +0100
committerDaniel Gultsch <daniel@gultsch.de>2016-02-15 23:15:04 +0100
commitc0b3a3ff0c32c8025174ebb92fbcf4a7fc67f497 (patch)
tree7cd29b2a9c195277ab14114db17c848629f69b07
parent335058b78bb20f8234d98fb4f205a1954b25e7e8 (diff)
basic support for XEP-0308: Last Message Correction. fixes #864
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Conversation.java20
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Message.java38
-rw-r--r--src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java1
-rw-r--r--src/main/java/eu/siacs/conversations/generator/MessageGenerator.java3
-rw-r--r--src/main/java/eu/siacs/conversations/parser/MessageParser.java61
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java14
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java13
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationFragment.java72
-rw-r--r--src/main/java/eu/siacs/conversations/ui/EditMessage.java2
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java15
-rw-r--r--src/main/res/drawable-hdpi/ic_lock_black_18dp.pngbin0 -> 368 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_lock_white_18dp.pngbin0 -> 371 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_mode_edit_black_18dp.pngbin0 -> 264 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_mode_edit_white_18dp.pngbin0 -> 299 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_secure_indicator.pngbin294 -> 0 bytes
-rw-r--r--src/main/res/drawable-hdpi/ic_secure_indicator_white.pngbin322 -> 0 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_lock_black_18dp.pngbin0 -> 293 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_lock_white_18dp.pngbin0 -> 298 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_mode_edit_black_18dp.pngbin0 -> 229 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_mode_edit_white_18dp.pngbin0 -> 249 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_secure_indicator.pngbin295 -> 0 bytes
-rw-r--r--src/main/res/drawable-mdpi/ic_secure_indicator_white.pngbin306 -> 0 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_lock_black_18dp.pngbin0 -> 397 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_lock_white_18dp.pngbin0 -> 399 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_mode_edit_black_18dp.pngbin0 -> 291 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_mode_edit_white_18dp.pngbin0 -> 351 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_secure_indicator.pngbin410 -> 0 bytes
-rw-r--r--src/main/res/drawable-xhdpi/ic_secure_indicator_white.pngbin434 -> 0 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_lock_black_18dp.pngbin0 -> 559 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_lock_white_18dp.pngbin0 -> 558 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_mode_edit_black_18dp.pngbin0 -> 336 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_mode_edit_white_18dp.pngbin0 -> 436 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_secure_indicator.pngbin380 -> 0 bytes
-rw-r--r--src/main/res/drawable-xxhdpi/ic_secure_indicator_white.pngbin441 -> 0 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_lock_black_18dp.pngbin0 -> 636 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_lock_white_18dp.pngbin0 -> 760 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_mode_edit_black_18dp.pngbin0 -> 366 bytes
-rw-r--r--src/main/res/drawable-xxxhdpi/ic_mode_edit_white_18dp.pngbin0 -> 490 bytes
-rw-r--r--src/main/res/layout/message_received.xml12
-rw-r--r--src/main/res/layout/message_sent.xml12
-rw-r--r--src/main/res/menu/message_context.xml4
-rw-r--r--src/main/res/values/strings.xml2
42 files changed, 228 insertions, 41 deletions
diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java
index 53bd19a5..a0600d13 100644
--- a/src/main/java/eu/siacs/conversations/entities/Conversation.java
+++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java
@@ -82,6 +82,7 @@ public class Conversation extends AbstractEntity implements Blockable {
private ChatState mIncomingChatState = Config.DEFAULT_CHATSTATE;
private String mLastReceivedOtrMessageId = null;
private String mFirstMamReference = null;
+ private Message correctingMessage;
public boolean hasMessagesLeftOnServer() {
return messagesLeftOnServer;
@@ -226,6 +227,17 @@ public class Conversation extends AbstractEntity implements Blockable {
return null;
}
+ public Message findMessageWithRemoteIdAndCounterpart(String id, Jid counterpart) {
+ synchronized (this.messages) {
+ for(Message message : this.messages) {
+ if(id.equals(message.getRemoteMsgId()) && counterpart.equals(message.getCounterpart())) {
+ return message;
+ }
+ }
+ }
+ return null;
+ }
+
public Message findSentMessageWithUuid(String id) {
synchronized (this.messages) {
for (Message message : this.messages) {
@@ -294,6 +306,14 @@ public class Conversation extends AbstractEntity implements Blockable {
return getLongAttribute("last_clear_history", 0);
}
+ public void setCorrectingMessage(Message correctingMessage) {
+ this.correctingMessage = correctingMessage;
+ }
+
+ public Message getCorrectingMessage() {
+ return this.correctingMessage;
+ }
+
public interface OnMessageFound {
void onMessageFound(final Message message);
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java
index f37ae427..1f9212fd 100644
--- a/src/main/java/eu/siacs/conversations/entities/Message.java
+++ b/src/main/java/eu/siacs/conversations/entities/Message.java
@@ -52,6 +52,7 @@ public class Message extends AbstractEntity {
public static final String STATUS = "status";
public static final String TYPE = "type";
public static final String CARBON = "carbon";
+ public static final String EDITED = "edited";
public static final String REMOTE_MSG_ID = "remoteMsgId";
public static final String SERVER_MSG_ID = "serverMsgId";
public static final String RELATIVE_FILE_PATH = "relativeFilePath";
@@ -71,6 +72,7 @@ public class Message extends AbstractEntity {
protected int status;
protected int type;
protected boolean carbon = false;
+ protected String edited = null;
protected String relativeFilePath;
protected boolean read = true;
protected String remoteMsgId = null;
@@ -104,7 +106,8 @@ public class Message extends AbstractEntity {
null,
null,
null,
- true);
+ true,
+ null);
this.conversation = conversation;
}
@@ -112,7 +115,8 @@ public class Message extends AbstractEntity {
final Jid trueCounterpart, final String body, final long timeSent,
final int encryption, final int status, final int type, final boolean carbon,
final String remoteMsgId, final String relativeFilePath,
- final String serverMsgId, final String fingerprint, final boolean read) {
+ final String serverMsgId, final String fingerprint, final boolean read,
+ final String edited) {
this.uuid = uuid;
this.conversationUuid = conversationUUid;
this.counterpart = counterpart;
@@ -128,6 +132,7 @@ public class Message extends AbstractEntity {
this.serverMsgId = serverMsgId;
this.axolotlFingerprint = fingerprint;
this.read = read;
+ this.edited = edited;
}
public static Message fromCursor(Cursor cursor) {
@@ -162,12 +167,13 @@ public class Message extends AbstractEntity {
cursor.getInt(cursor.getColumnIndex(ENCRYPTION)),
cursor.getInt(cursor.getColumnIndex(STATUS)),
cursor.getInt(cursor.getColumnIndex(TYPE)),
- cursor.getInt(cursor.getColumnIndex(CARBON))>0,
+ cursor.getInt(cursor.getColumnIndex(CARBON)) > 0,
cursor.getString(cursor.getColumnIndex(REMOTE_MSG_ID)),
cursor.getString(cursor.getColumnIndex(RELATIVE_FILE_PATH)),
cursor.getString(cursor.getColumnIndex(SERVER_MSG_ID)),
cursor.getString(cursor.getColumnIndex(FINGERPRINT)),
- cursor.getInt(cursor.getColumnIndex(READ)) > 0);
+ cursor.getInt(cursor.getColumnIndex(READ)) > 0,
+ cursor.getString(cursor.getColumnIndex(EDITED)));
}
public static Message createStatusMessage(Conversation conversation, String body) {
@@ -211,7 +217,8 @@ public class Message extends AbstractEntity {
values.put(RELATIVE_FILE_PATH, relativeFilePath);
values.put(SERVER_MSG_ID, serverMsgId);
values.put(FINGERPRINT, axolotlFingerprint);
- values.put(READ,read);
+ values.put(READ,read ? 1 : 0);
+ values.put(EDITED, edited);
return values;
}
@@ -340,10 +347,22 @@ public class Message extends AbstractEntity {
this.carbon = carbon;
}
+ public void setEdited(String edited) {
+ this.edited = edited;
+ }
+
+ public boolean edited() {
+ return this.edited != null;
+ }
+
public void setTrueCounterpart(Jid trueCounterpart) {
this.trueCounterpart = trueCounterpart;
}
+ public Jid getTrueCounterpart() {
+ return this.trueCounterpart;
+ }
+
public Transferable getTransferable() {
return this.transferable;
}
@@ -421,6 +440,7 @@ public class Message extends AbstractEntity {
this.getEncryption() == message.getEncryption() &&
this.getCounterpart() != null &&
this.getCounterpart().equals(message.getCounterpart()) &&
+ this.edited() == message.edited() &&
(message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) &&
!GeoHelper.isGeoUri(message.getBody()) &&
!GeoHelper.isGeoUri(this.body) &&
@@ -510,6 +530,14 @@ public class Message extends AbstractEntity {
}
}
+ public void setUuid(String uuid) {
+ this.uuid = uuid;
+ }
+
+ public String getEditedId() {
+ return edited;
+ }
+
public enum Decision {
MUST,
SHOULD,
diff --git a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
index d5059605..d223ab84 100644
--- a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
@@ -31,6 +31,7 @@ public abstract class AbstractGenerator {
"urn:xmpp:avatar:metadata+notify",
"http://jabber.org/protocol/nick+notify",
"urn:xmpp:ping",
+ "urn:xmpp:message-correct:0",
"jabber:iq:version",
"http://jabber.org/protocol/chatstates",
AxolotlService.PEP_DEVICE_LIST+"+notify"};
diff --git a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
index b849f56f..0e7a8ce6 100644
--- a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
@@ -47,6 +47,9 @@ public class MessageGenerator extends AbstractGenerator {
}
packet.setFrom(account.getJid());
packet.setId(message.getUuid());
+ if (message.edited()) {
+ packet.addChild("replace","urn:xmpp:message-correct:0").setAttribute("id",message.getEditedId());
+ }
return packet;
}
diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
index 3bf48831..18edfdeb 100644
--- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
@@ -297,6 +297,8 @@ public class MessageParser extends AbstractParser implements
final String body = packet.getBody();
final Element mucUserElement = packet.findChild("x", "http://jabber.org/protocol/muc#user");
final String pgpEncrypted = packet.findChildContent("x", "jabber:x:encrypted");
+ final Element replaceElement = packet.findChild("replace","urn:xmpp:message-correct:0");
+ final String replacementId = replaceElement == null ? null : replaceElement.getAttribute("id");
final Element axolotlEncrypted = packet.findChild(XmppAxolotlMessage.CONTAINERTAG, AxolotlService.PEP_PREFIX);
int status;
final Jid counterpart;
@@ -390,6 +392,33 @@ public class MessageParser extends AbstractParser implements
} else {
updateLastseen(timestamp, account, packet.getFrom(), true);
}
+
+ if (replacementId != null) {
+ Message replacedMessage = conversation.findMessageWithRemoteIdAndCounterpart(replacementId, counterpart);
+ if (replacedMessage != null) {
+ final boolean fingerprintsMatch = replacedMessage.getAxolotlFingerprint() == null
+ || replacedMessage.getAxolotlFingerprint().equals(message.getAxolotlFingerprint());
+ final boolean trueCountersMatch = replacedMessage.getTrueCounterpart() != null
+ && replacedMessage.getTrueCounterpart().equals(message.getTrueCounterpart());
+ if (fingerprintsMatch && (trueCountersMatch || conversation.getMode() == Conversation.MODE_SINGLE)) {
+ Log.d(Config.LOGTAG, "replaced message '" + replacedMessage.getBody() + "' with '" + message.getBody() + "'");
+ replacedMessage.setBody(message.getBody());
+ replacedMessage.setEdited(replacedMessage.getRemoteMsgId());
+ replacedMessage.setRemoteMsgId(remoteMsgId);
+ if (replacedMessage.getStatus() == Message.STATUS_RECEIVED) {
+ replacedMessage.markUnread();
+ }
+ mXmppConnectionService.updateMessage(replacedMessage);
+ if (mXmppConnectionService.confirmMessages() && remoteMsgId != null && !isForwarded && !isTypeGroupChat) {
+ sendMessageReceipts(account, packet);
+ }
+ return;
+ } else {
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": received message correction but verification didn't check out");
+ }
+ }
+ }
+
boolean checkForDuplicates = query != null
|| (isTypeGroupChat && packet.hasChild("delay","urn:xmpp:delay"))
|| message.getType() == Message.TYPE_PRIVATE;
@@ -420,20 +449,7 @@ public class MessageParser extends AbstractParser implements
}
if (mXmppConnectionService.confirmMessages() && remoteMsgId != null && !isForwarded && !isTypeGroupChat) {
- ArrayList<String> receiptsNamespaces = new ArrayList<>();
- if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) {
- receiptsNamespaces.add("urn:xmpp:chat-markers:0");
- }
- if (packet.hasChild("request", "urn:xmpp:receipts")) {
- receiptsNamespaces.add("urn:xmpp:receipts");
- }
- if (receiptsNamespaces.size() > 0) {
- MessagePacket receipt = mXmppConnectionService.getMessageGenerator().received(account,
- packet,
- receiptsNamespaces,
- packet.getType());
- mXmppConnectionService.sendMessagePacket(account, receipt);
- }
+ sendMessageReceipts(account, packet);
}
if (message.getStatus() == Message.STATUS_RECEIVED
@@ -524,4 +540,21 @@ public class MessageParser extends AbstractParser implements
contact.setPresenceName(nick);
}
}
+
+ private void sendMessageReceipts(Account account, MessagePacket packet) {
+ ArrayList<String> receiptsNamespaces = new ArrayList<>();
+ if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) {
+ receiptsNamespaces.add("urn:xmpp:chat-markers:0");
+ }
+ if (packet.hasChild("request", "urn:xmpp:receipts")) {
+ receiptsNamespaces.add("urn:xmpp:receipts");
+ }
+ if (receiptsNamespaces.size() > 0) {
+ MessagePacket receipt = mXmppConnectionService.getMessageGenerator().received(account,
+ packet,
+ receiptsNamespaces,
+ packet.getType());
+ mXmppConnectionService.sendMessagePacket(account, receipt);
+ }
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
index 2f28a30f..dcba4f74 100644
--- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
@@ -51,7 +51,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
private static DatabaseBackend instance = null;
private static final String DATABASE_NAME = "history";
- private static final int DATABASE_VERSION = 23;
+ private static final int DATABASE_VERSION = 24;
private static String CREATE_CONTATCS_STATEMENT = "create table "
+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
@@ -161,6 +161,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ Message.SERVER_MSG_ID + " TEXT, "
+ Message.FINGERPRINT + " TEXT, "
+ Message.CARBON + " INTEGER, "
+ + Message.EDITED + " TEXT, "
+ Message.READ + " NUMBER DEFAULT 1, "
+ Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY("
+ Message.CONVERSATION + ") REFERENCES "
@@ -370,6 +371,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
if (oldVersion < 23 && newVersion >= 23) {
db.execSQL(CREATE_DISCOVERY_RESULTS_STATEMENT);
}
+
+ if (oldVersion < 24 && newVersion >= 24) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.EDITED + " TEXT");
+ }
}
public static synchronized DatabaseBackend getInstance(Context context) {
@@ -586,6 +591,13 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ "=?", args);
}
+ public void updateMessage(Message message, String uuid) {
+ SQLiteDatabase db = this.getWritableDatabase();
+ String[] args = {uuid};
+ db.update(Message.TABLENAME, message.getContentValues(), Message.UUID
+ + "=?", args);
+ }
+
public void readRoster(Roster roster) {
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor;
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index 16d7f139..10f6b5ef 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -841,8 +841,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
final Conversation conversation = message.getConversation();
account.deactivateGracePeriod();
MessagePacket packet = null;
- final boolean addToConversation = conversation.getMode() != Conversation.MODE_MULTI
- || account.getServerIdentity() != XmppConnection.Identity.SLACK;
+ final boolean addToConversation = (conversation.getMode() != Conversation.MODE_MULTI
+ || account.getServerIdentity() != XmppConnection.Identity.SLACK)
+ && !message.edited();
boolean saveInDb = addToConversation;
message.setStatus(Message.STATUS_WAITING);
@@ -966,8 +967,12 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
if (addToConversation) {
conversation.add(message);
}
- if (saveInDb && (message.getEncryption() == Message.ENCRYPTION_NONE || saveEncryptedMessages())) {
- databaseBackend.createMessage(message);
+ if (message.getEncryption() == Message.ENCRYPTION_NONE || saveEncryptedMessages()) {
+ if (saveInDb) {
+ databaseBackend.createMessage(message);
+ } else if (message.edited()) {
+ databaseBackend.updateMessage(message, message.getEditedId());
+ }
}
updateConversationUi();
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
index c555ead0..6ead9962 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
@@ -8,7 +8,6 @@ import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
import android.os.Bundle;
import android.support.annotation.Nullable;
@@ -40,6 +39,7 @@ import net.java.otr4j.session.SessionStatus;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.UUID;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
@@ -51,7 +51,6 @@ import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.Presence;
-import eu.siacs.conversations.entities.Presences;
import eu.siacs.conversations.entities.Transferable;
import eu.siacs.conversations.entities.TransferablePlaceholder;
import eu.siacs.conversations.services.XmppConnectionService;
@@ -294,8 +293,14 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_CHOOSE_IMAGE);
break;
case CANCEL:
- if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) {
- conversation.setNextCounterpart(null);
+ if (conversation != null) {
+ if (conversation.getCorrectingMessage() != null) {
+ conversation.setCorrectingMessage(null);
+ mEditMessage.getEditableText().clear();
+ }
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ conversation.setNextCounterpart(null);
+ }
updateChatMsgHint();
updateSendButton();
}
@@ -330,12 +335,21 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
if (body.length() == 0 || this.conversation == null) {
return;
}
- Message message = new Message(conversation, body, conversation.getNextEncryption());
- if (conversation.getMode() == Conversation.MODE_MULTI) {
- if (conversation.getNextCounterpart() != null) {
- message.setCounterpart(conversation.getNextCounterpart());
- message.setType(Message.TYPE_PRIVATE);
+ final Message message;
+ if (conversation.getCorrectingMessage() == null) {
+ message = new Message(conversation, body, conversation.getNextEncryption());
+ if (conversation.getMode() == Conversation.MODE_MULTI) {
+ if (conversation.getNextCounterpart() != null) {
+ message.setCounterpart(conversation.getNextCounterpart());
+ message.setType(Message.TYPE_PRIVATE);
+ }
}
+ } else {
+ message = conversation.getCorrectingMessage();
+ message.setBody(body);
+ message.setEdited(message.getUuid());
+ message.setUuid(UUID.randomUUID().toString());
+ conversation.setCorrectingMessage(null);
}
switch (conversation.getNextEncryption()) {
case Message.ENCRYPTION_OTR:
@@ -356,7 +370,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
public void updateChatMsgHint() {
final boolean multi = conversation.getMode() == Conversation.MODE_MULTI;
- if (multi && conversation.getNextCounterpart() != null) {
+ if (conversation.getCorrectingMessage() != null) {
+ this.mEditMessage.setHint(R.string.send_corrected_message);
+ } else if (multi && conversation.getNextCounterpart() != null) {
this.mEditMessage.setHint(getString(
R.string.send_private_message_to,
conversation.getNextCounterpart().getResourcepart()));
@@ -487,8 +503,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
}
@Override
- public void onCreateContextMenu(ContextMenu menu, View v,
- ContextMenuInfo menuInfo) {
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
synchronized (this.messageList) {
super.onCreateContextMenu(menu, v, menuInfo);
AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
@@ -503,6 +518,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
activity.getMenuInflater().inflate(R.menu.message_context, menu);
menu.setHeaderTitle(R.string.message_options);
MenuItem copyText = menu.findItem(R.id.copy_text);
+ MenuItem correctMessage = menu.findItem(R.id.correct_message);
MenuItem shareWith = menu.findItem(R.id.share_with);
MenuItem sendAgain = menu.findItem(R.id.send_again);
MenuItem copyUrl = menu.findItem(R.id.copy_url);
@@ -514,6 +530,11 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
&& m.treatAsDownloadable() != Message.Decision.MUST) {
copyText.setVisible(true);
}
+ if (m.getType() == Message.TYPE_TEXT
+ && m.getStatus() != Message.STATUS_RECEIVED
+ && !m.isCarbon()) {
+ correctMessage.setVisible(true);
+ }
if ((m.getType() != Message.TYPE_TEXT
&& m.getType() != Message.TYPE_PRIVATE
&& m.getTransferable() == null)
@@ -550,6 +571,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
case R.id.copy_text:
copyText(selectedMessage);
return true;
+ case R.id.correct_message:
+ correctMessage(selectedMessage);
+ return true;
case R.id.send_again:
resendMessage(selectedMessage);
return true;
@@ -652,6 +676,16 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
updateSendButton();
}
+ private void correctMessage(Message message) {
+ while(message.mergeable(message.next())) {
+ message = message.next();
+ }
+ this.conversation.setCorrectingMessage(message);
+ this.mEditMessage.getEditableText().clear();
+ this.mEditMessage.getEditableText().append(message.getBody());
+
+ }
+
protected void highlightInConference(String nick) {
String oldString = mEditMessage.getText().toString().trim();
if (oldString.isEmpty() || mEditMessage.getSelectionStart() == 0) {
@@ -958,9 +992,12 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
final Conversation c = this.conversation;
final SendButtonAction action;
final Presence.Status status;
- final boolean empty = this.mEditMessage == null || this.mEditMessage.getText().length() == 0;
+ final String text = this.mEditMessage == null ? "" : this.mEditMessage.getText().toString();
+ final boolean empty = text.length() == 0;
final boolean conference = c.getMode() == Conversation.MODE_MULTI;
- if (conference && !c.getAccount().httpUploadAvailable()) {
+ if (c.getCorrectingMessage() != null && (empty || text.equals(c.getCorrectingMessage().getBody()))) {
+ action = SendButtonAction.CANCEL;
+ } else if (conference && !c.getAccount().httpUploadAvailable()) {
if (empty && c.getNextCounterpart() != null) {
action = SendButtonAction.CANCEL;
} else {
@@ -1238,6 +1275,13 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
updateSendButton();
}
+ @Override
+ public void onTextChanged() {
+ if (conversation != null && conversation.getCorrectingMessage() != null) {
+ updateSendButton();
+ }
+ }
+
private int completionIndex = 0;
private int lastCompletionLength = 0;
private String incomplete;
diff --git a/src/main/java/eu/siacs/conversations/ui/EditMessage.java b/src/main/java/eu/siacs/conversations/ui/EditMessage.java
index fc655b0c..e3841d1d 100644
--- a/src/main/java/eu/siacs/conversations/ui/EditMessage.java
+++ b/src/main/java/eu/siacs/conversations/ui/EditMessage.java
@@ -69,6 +69,7 @@ public class EditMessage extends EditText {
this.isUserTyping = false;
this.keyboardListener.onTextDeleted();
}
+ this.keyboardListener.onTextChanged();
}
}
@@ -84,6 +85,7 @@ public class EditMessage extends EditText {
void onTypingStarted();
void onTypingStopped();
void onTextDeleted();
+ void onTextChanged();
boolean onTabPressed(boolean repeated);
}
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 fc2bd2ab..f6496b21 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
@@ -123,6 +123,16 @@ public class MessageAdapter extends ArrayAdapter<Message> {
if (viewHolder.indicatorReceived != null) {
viewHolder.indicatorReceived.setVisibility(View.GONE);
}
+
+ if (viewHolder.edit_indicator != null) {
+ if (message.edited()) {
+ viewHolder.edit_indicator.setVisibility(View.VISIBLE);
+ viewHolder.edit_indicator.setImageResource(darkBackground ? R.drawable.ic_mode_edit_white_18dp : R.drawable.ic_mode_edit_black_18dp);
+ viewHolder.edit_indicator.setAlpha(darkBackground ? 0.7f : 0.57f);
+ } else {
+ viewHolder.edit_indicator.setVisibility(View.GONE);
+ }
+ }
boolean multiReceived = message.getConversation().getMode() == Conversation.MODE_MULTI
&& message.getMergedStatus() <= Message.STATUS_RECEIVED;
if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE || message.getTransferable() != null) {
@@ -179,7 +189,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
if (message.getEncryption() == Message.ENCRYPTION_NONE) {
viewHolder.indicator.setVisibility(View.GONE);
} else {
- viewHolder.indicator.setImageResource(darkBackground ? R.drawable.ic_secure_indicator_white : R.drawable.ic_secure_indicator);
+ viewHolder.indicator.setImageResource(darkBackground ? R.drawable.ic_lock_white_18dp : R.drawable.ic_lock_black_18dp);
viewHolder.indicator.setVisibility(View.VISIBLE);
if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
XmppAxolotlSession.Trust trust = message.getConversation()
@@ -463,6 +473,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
.findViewById(R.id.download_button);
viewHolder.indicator = (ImageView) view
.findViewById(R.id.security_indicator);
+ viewHolder.edit_indicator = (ImageView) view.findViewById(R.id.edit_indicator);
viewHolder.image = (ImageView) view
.findViewById(R.id.message_image);
viewHolder.messageBody = (TextView) view
@@ -483,6 +494,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
.findViewById(R.id.download_button);
viewHolder.indicator = (ImageView) view
.findViewById(R.id.security_indicator);
+ viewHolder.edit_indicator = (ImageView) view.findViewById(R.id.edit_indicator);
viewHolder.image = (ImageView) view
.findViewById(R.id.message_image);
viewHolder.messageBody = (TextView) view
@@ -701,6 +713,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
protected TextView status_message;
protected TextView encryption;
public Button load_more_messages;
+ public ImageView edit_indicator;
}
class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
diff --git a/src/main/res/drawable-hdpi/ic_lock_black_18dp.png b/src/main/res/drawable-hdpi/ic_lock_black_18dp.png
new file mode 100644
index 00000000..4c7a7c59
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_lock_black_18dp.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_lock_white_18dp.png b/src/main/res/drawable-hdpi/ic_lock_white_18dp.png
new file mode 100644
index 00000000..29e8bfd3
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_lock_white_18dp.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_mode_edit_black_18dp.png b/src/main/res/drawable-hdpi/ic_mode_edit_black_18dp.png
new file mode 100644
index 00000000..00e7d074
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_mode_edit_black_18dp.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_mode_edit_white_18dp.png b/src/main/res/drawable-hdpi/ic_mode_edit_white_18dp.png
new file mode 100644
index 00000000..558f0ea3
--- /dev/null
+++ b/src/main/res/drawable-hdpi/ic_mode_edit_white_18dp.png
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_secure_indicator.png b/src/main/res/drawable-hdpi/ic_secure_indicator.png
deleted file mode 100644
index 220463fc..00000000
--- a/src/main/res/drawable-hdpi/ic_secure_indicator.png
+++ /dev/null
Binary files differ
diff --git a/src/main/res/drawable-hdpi/ic_secure_indicator_white.png b/src/main/res/drawable-hdpi/ic_secure_indicator_white.png
deleted file mode 100644
index 46eb1195..00000000
--- a/src/main/res/drawable-hdpi/ic_secure_indicator_white.png
+++ /dev/null
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_lock_black_18dp.png b/src/main/res/drawable-mdpi/ic_lock_black_18dp.png
new file mode 100644
index 00000000..c8b6fe71
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_lock_black_18dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_lock_white_18dp.png b/src/main/res/drawable-mdpi/ic_lock_white_18dp.png
new file mode 100644
index 00000000..1265e98e
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_lock_white_18dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_mode_edit_black_18dp.png b/src/main/res/drawable-mdpi/ic_mode_edit_black_18dp.png
new file mode 100644
index 00000000..ebd96073
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_mode_edit_black_18dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_mode_edit_white_18dp.png b/src/main/res/drawable-mdpi/ic_mode_edit_white_18dp.png
new file mode 100644
index 00000000..e23c42db
--- /dev/null
+++ b/src/main/res/drawable-mdpi/ic_mode_edit_white_18dp.png
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_secure_indicator.png b/src/main/res/drawable-mdpi/ic_secure_indicator.png
deleted file mode 100644
index 690d4d03..00000000
--- a/src/main/res/drawable-mdpi/ic_secure_indicator.png
+++ /dev/null
Binary files differ
diff --git a/src/main/res/drawable-mdpi/ic_secure_indicator_white.png b/src/main/res/drawable-mdpi/ic_secure_indicator_white.png
deleted file mode 100644
index e2f894ef..00000000
--- a/src/main/res/drawable-mdpi/ic_secure_indicator_white.png
+++ /dev/null
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_lock_black_18dp.png b/src/main/res/drawable-xhdpi/ic_lock_black_18dp.png
new file mode 100644
index 00000000..0888c617
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_lock_black_18dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_lock_white_18dp.png b/src/main/res/drawable-xhdpi/ic_lock_white_18dp.png
new file mode 100644
index 00000000..b94735ec
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_lock_white_18dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_mode_edit_black_18dp.png b/src/main/res/drawable-xhdpi/ic_mode_edit_black_18dp.png
new file mode 100644
index 00000000..b33c964d
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_mode_edit_black_18dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_mode_edit_white_18dp.png b/src/main/res/drawable-xhdpi/ic_mode_edit_white_18dp.png
new file mode 100644
index 00000000..3ee3e172
--- /dev/null
+++ b/src/main/res/drawable-xhdpi/ic_mode_edit_white_18dp.png
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_secure_indicator.png b/src/main/res/drawable-xhdpi/ic_secure_indicator.png
deleted file mode 100644
index cd0d1391..00000000
--- a/src/main/res/drawable-xhdpi/ic_secure_indicator.png
+++ /dev/null
Binary files differ
diff --git a/src/main/res/drawable-xhdpi/ic_secure_indicator_white.png b/src/main/res/drawable-xhdpi/ic_secure_indicator_white.png
deleted file mode 100644
index b624a8ce..00000000
--- a/src/main/res/drawable-xhdpi/ic_secure_indicator_white.png
+++ /dev/null
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_lock_black_18dp.png b/src/main/res/drawable-xxhdpi/ic_lock_black_18dp.png
new file mode 100644
index 00000000..dbcf3f33
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_lock_black_18dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_lock_white_18dp.png b/src/main/res/drawable-xxhdpi/ic_lock_white_18dp.png
new file mode 100644
index 00000000..895aabbf
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_lock_white_18dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_mode_edit_black_18dp.png b/src/main/res/drawable-xxhdpi/ic_mode_edit_black_18dp.png
new file mode 100644
index 00000000..66d25296
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_mode_edit_black_18dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_mode_edit_white_18dp.png b/src/main/res/drawable-xxhdpi/ic_mode_edit_white_18dp.png
new file mode 100644
index 00000000..9d7f2ff9
--- /dev/null
+++ b/src/main/res/drawable-xxhdpi/ic_mode_edit_white_18dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_secure_indicator.png b/src/main/res/drawable-xxhdpi/ic_secure_indicator.png
deleted file mode 100644
index 6a74ccbe..00000000
--- a/src/main/res/drawable-xxhdpi/ic_secure_indicator.png
+++ /dev/null
Binary files differ
diff --git a/src/main/res/drawable-xxhdpi/ic_secure_indicator_white.png b/src/main/res/drawable-xxhdpi/ic_secure_indicator_white.png
deleted file mode 100644
index 4945c959..00000000
--- a/src/main/res/drawable-xxhdpi/ic_secure_indicator_white.png
+++ /dev/null
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_lock_black_18dp.png b/src/main/res/drawable-xxxhdpi/ic_lock_black_18dp.png
new file mode 100644
index 00000000..c49d420e
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_lock_black_18dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_lock_white_18dp.png b/src/main/res/drawable-xxxhdpi/ic_lock_white_18dp.png
new file mode 100644
index 00000000..0dcada81
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_lock_white_18dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_mode_edit_black_18dp.png b/src/main/res/drawable-xxxhdpi/ic_mode_edit_black_18dp.png
new file mode 100644
index 00000000..827b6848
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_mode_edit_black_18dp.png
Binary files differ
diff --git a/src/main/res/drawable-xxxhdpi/ic_mode_edit_white_18dp.png b/src/main/res/drawable-xxxhdpi/ic_mode_edit_white_18dp.png
new file mode 100644
index 00000000..34ec7092
--- /dev/null
+++ b/src/main/res/drawable-xxxhdpi/ic_mode_edit_white_18dp.png
Binary files differ
diff --git a/src/main/res/layout/message_received.xml b/src/main/res/layout/message_received.xml
index a998bf37..466dd045 100644
--- a/src/main/res/layout/message_received.xml
+++ b/src/main/res/layout/message_received.xml
@@ -91,7 +91,17 @@
android:layout_marginRight="4sp"
android:alpha="0.70"
android:gravity="center_vertical"
- android:src="@drawable/ic_secure_indicator_white" />
+ android:src="@drawable/ic_lock_white_18dp" />
+
+ <ImageView
+ android:id="@+id/edit_indicator"
+ android:layout_width="?attr/TextSizeInfo"
+ android:layout_height="?attr/TextSizeInfo"
+ android:layout_gravity="center_vertical"
+ android:layout_marginRight="4sp"
+ android:alpha="0.70"
+ android:gravity="center_vertical"
+ android:src="@drawable/ic_mode_edit_white_18dp" />
<TextView
android:id="@+id/message_time"
diff --git a/src/main/res/layout/message_sent.xml b/src/main/res/layout/message_sent.xml
index 55f874e6..fc68c389 100644
--- a/src/main/res/layout/message_sent.xml
+++ b/src/main/res/layout/message_sent.xml
@@ -91,7 +91,17 @@
android:layout_marginLeft="4sp"
android:alpha="0.54"
android:gravity="center_vertical"
- android:src="@drawable/ic_secure_indicator" />
+ android:src="@drawable/ic_lock_black_18dp" />
+
+ <ImageView
+ android:id="@+id/edit_indicator"
+ android:layout_width="?attr/TextSizeInfo"
+ android:layout_height="?attr/TextSizeInfo"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="4sp"
+ android:alpha="0.54"
+ android:gravity="center_vertical"
+ android:src="@drawable/ic_mode_edit_black_18dp" />
<ImageView
android:id="@+id/indicator_received"
diff --git a/src/main/res/menu/message_context.xml b/src/main/res/menu/message_context.xml
index 4e1316d3..0d4e66ef 100644
--- a/src/main/res/menu/message_context.xml
+++ b/src/main/res/menu/message_context.xml
@@ -6,6 +6,10 @@
android:title="@string/copy_text"
android:visible="false"/>
<item
+ android:id="@+id/correct_message"
+ android:title="@string/correct_message"
+ android:visible="false"/>
+ <item
android:id="@+id/share_with"
android:title="@string/share_with"
android:visible="false"/>
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 0b726ae7..15322e04 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -593,4 +593,6 @@
<string name="selection_too_large">The selected area is too large</string>
<string name="no_accounts">(No activated accounts)</string>
<string name="this_field_is_required">This field is required</string>
+ <string name="correct_message">Correct message</string>
+ <string name="send_corrected_message">Send corrected message</string>
</resources>