From c0b3a3ff0c32c8025174ebb92fbcf4a7fc67f497 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Mon, 15 Feb 2016 23:15:04 +0100 Subject: [PATCH] basic support for XEP-0308: Last Message Correction. fixes #864 --- .../conversations/entities/Conversation.java | 20 +++++ .../siacs/conversations/entities/Message.java | 38 +++++++-- .../generator/AbstractGenerator.java | 1 + .../generator/MessageGenerator.java | 3 + .../conversations/parser/MessageParser.java | 61 +++++++++++---- .../persistance/DatabaseBackend.java | 14 +++- .../services/XmppConnectionService.java | 13 +++- .../ui/ConversationFragment.java | 72 ++++++++++++++---- .../siacs/conversations/ui/EditMessage.java | 2 + .../ui/adapter/MessageAdapter.java | 15 +++- .../res/drawable-hdpi/ic_lock_black_18dp.png | Bin 0 -> 368 bytes .../res/drawable-hdpi/ic_lock_white_18dp.png | Bin 0 -> 371 bytes .../drawable-hdpi/ic_mode_edit_black_18dp.png | Bin 0 -> 264 bytes .../drawable-hdpi/ic_mode_edit_white_18dp.png | Bin 0 -> 299 bytes .../res/drawable-hdpi/ic_secure_indicator.png | Bin 294 -> 0 bytes .../ic_secure_indicator_white.png | Bin 322 -> 0 bytes .../res/drawable-mdpi/ic_lock_black_18dp.png | Bin 0 -> 293 bytes .../res/drawable-mdpi/ic_lock_white_18dp.png | Bin 0 -> 298 bytes .../drawable-mdpi/ic_mode_edit_black_18dp.png | Bin 0 -> 229 bytes .../drawable-mdpi/ic_mode_edit_white_18dp.png | Bin 0 -> 249 bytes .../res/drawable-mdpi/ic_secure_indicator.png | Bin 295 -> 0 bytes .../ic_secure_indicator_white.png | Bin 306 -> 0 bytes .../res/drawable-xhdpi/ic_lock_black_18dp.png | Bin 0 -> 397 bytes .../res/drawable-xhdpi/ic_lock_white_18dp.png | Bin 0 -> 399 bytes .../ic_mode_edit_black_18dp.png | Bin 0 -> 291 bytes .../ic_mode_edit_white_18dp.png | Bin 0 -> 351 bytes .../drawable-xhdpi/ic_secure_indicator.png | Bin 410 -> 0 bytes .../ic_secure_indicator_white.png | Bin 434 -> 0 bytes .../drawable-xxhdpi/ic_lock_black_18dp.png | Bin 0 -> 559 bytes .../drawable-xxhdpi/ic_lock_white_18dp.png | Bin 0 -> 558 bytes .../ic_mode_edit_black_18dp.png | Bin 0 -> 336 bytes .../ic_mode_edit_white_18dp.png | Bin 0 -> 436 bytes .../drawable-xxhdpi/ic_secure_indicator.png | Bin 380 -> 0 bytes .../ic_secure_indicator_white.png | Bin 441 -> 0 bytes .../drawable-xxxhdpi/ic_lock_black_18dp.png | Bin 0 -> 636 bytes .../drawable-xxxhdpi/ic_lock_white_18dp.png | Bin 0 -> 760 bytes .../ic_mode_edit_black_18dp.png | Bin 0 -> 366 bytes .../ic_mode_edit_white_18dp.png | Bin 0 -> 490 bytes src/main/res/layout/message_received.xml | 12 ++- src/main/res/layout/message_sent.xml | 12 ++- src/main/res/menu/message_context.xml | 4 + src/main/res/values/strings.xml | 2 + 42 files changed, 228 insertions(+), 41 deletions(-) create mode 100644 src/main/res/drawable-hdpi/ic_lock_black_18dp.png create mode 100644 src/main/res/drawable-hdpi/ic_lock_white_18dp.png create mode 100644 src/main/res/drawable-hdpi/ic_mode_edit_black_18dp.png create mode 100644 src/main/res/drawable-hdpi/ic_mode_edit_white_18dp.png delete mode 100644 src/main/res/drawable-hdpi/ic_secure_indicator.png delete mode 100644 src/main/res/drawable-hdpi/ic_secure_indicator_white.png create mode 100644 src/main/res/drawable-mdpi/ic_lock_black_18dp.png create mode 100644 src/main/res/drawable-mdpi/ic_lock_white_18dp.png create mode 100644 src/main/res/drawable-mdpi/ic_mode_edit_black_18dp.png create mode 100644 src/main/res/drawable-mdpi/ic_mode_edit_white_18dp.png delete mode 100644 src/main/res/drawable-mdpi/ic_secure_indicator.png delete mode 100644 src/main/res/drawable-mdpi/ic_secure_indicator_white.png create mode 100644 src/main/res/drawable-xhdpi/ic_lock_black_18dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_lock_white_18dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_mode_edit_black_18dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_mode_edit_white_18dp.png delete mode 100644 src/main/res/drawable-xhdpi/ic_secure_indicator.png delete mode 100644 src/main/res/drawable-xhdpi/ic_secure_indicator_white.png create mode 100644 src/main/res/drawable-xxhdpi/ic_lock_black_18dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_lock_white_18dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_mode_edit_black_18dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_mode_edit_white_18dp.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_secure_indicator.png delete mode 100644 src/main/res/drawable-xxhdpi/ic_secure_indicator_white.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_lock_black_18dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_lock_white_18dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_mode_edit_black_18dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_mode_edit_white_18dp.png diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index 53bd19a5f..a0600d130 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 f37ae4271..1f9212fdb 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 d50596056..d223ab842 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 b849f56f4..0e7a8ce6e 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 3bf488316..18edfdeb5 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 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 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 2f28a30ff..dcba4f748 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 16d7f139c..10f6b5efd 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 c555ead03..6ead99625 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 fc655b0ce..e3841d1d5 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 fc2bd2abc..f6496b21c 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 { 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 { 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 { .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 { .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 { protected TextView status_message; protected TextView encryption; public Button load_more_messages; + public ImageView edit_indicator; } class BitmapWorkerTask extends AsyncTask { 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 0000000000000000000000000000000000000000..4c7a7c59f27adcb7e14766d40485f62ee0b27240 GIT binary patch literal 368 zcmeAS@N?(olHy`uVBq!ia0vp^(jd&i0wmS%+S~(D3dtTpz6=aiY77hwEes65fIjwr$5YkXfLxbX@i>EWTV6C3m!@HyyQ8@ zGm~d~;~Rb+u9mI3%vv^Q5AZ(yXf%iYleqAuBZoeSt~ev+c|`KW|3e3Q;%D>RuwUb$ zG$%nY^Up1j16=`c**i4?BBhNFm$b=yF!uB9H}*+QICMp<)t#HCEjeqox>L06vTbZi zTTCzb+|F%NFPS4vLVMLjz_$#b(xwf*iu(-Nm5qoxY1P}P^Paa=kSq3ljWdR zZ|hot=DDqAN?l<;bHsRVsu)XO{J!ev!{ix~&r~OQR#oiLxPI;Gw8)OsmE!habClcG zRvg~z?CWk-ag}4U_nleVVV~a>$R%C2oA6E5chBnH^wSIOO+VAL^y3;v=HJO5O7^Fj zrtj-n=f8Z}sT}_uWv6wz6C!6m71a4}e68@|Lap6F(`OXM_U4Mc3^Uypzl8m2#PwB2 zW*uJ@e(Cd)@A>=!C;gOe6wX}gcxAKQoBeMyc1qsVy}i)V@?iz3;`$ Oo59o7&t;ucLK6U-AChqZ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..00e7d0743d5e49b2c185e56fb0be03dc2e0d455f GIT binary patch literal 264 zcmeAS@N?(olHy`uVBq!ia0vp^(jd&i0wmS%+S~(D3dtTpz6=aiY77hwEes65fIGgDR46!(U_ky*MgM$Fe1-}BL>jDo1f@VGv61sUU$#S;OPuAZPXWSIhxchPSE6E4u zodJrE7fas0*mV3z>kWfrixRzNFP~tsVadHMQ2|N|><%4xC)p|`$suQ@T{GdyIpxJK z+kWLMer1!ICwSq9!h)}kdeSRTr17vkVYi7czHodqbHKNb=WUT^Qh{z^@O1TaS?83{ F1OT8^T>k(7 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..558f0ea32eea703a9c1e6390179db33820e3180e GIT binary patch literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^(jd&i0wmS%+S~(D3dtTpz6=aiY77hwEes65fIq(5v@lP|gzF+Mx zvA-mF#@RAQjg)1BT0(^OIf0BC`cUXpbH^;nFZ_F>JGgX_Ziis(1;MF+) zrh`R{UqHEG-I<^(-qn`N95grEq`#Q9$TE1*jFlIsM6&4$Ozopr02ugT*Z=?k literal 0 HcmV?d00001 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 220463fc7ac0647354b2bacfa1097db0bd3954d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 294 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|wj^(N7l!{JxM1({$v_d#0*}aI z1_o|n5N2eUHAey{$X?><>&kwgT|iJ!VaGLBFQCw7PZ!4!i_>qX_;MXG5NO?R(zt8O z_ZMC^?O){NR*QcUdUP!IBma~gmEDOQN6OTHJP>?#cJ};~i5`-ICl0XPV2nPhK6$kg z6Q{+h2U=;Fp7YjEXk31-J7cSsRKf&HW?O+H_g*ybk)PPWY{0m9x6>4}lav3j$??xM zPxrgDX88>bEk4$@g|}JP-|U<#6nIQ5V)o0m5>H+>h!-65J+I@u;M=F-5BXoZdna|j lH+~(y)UCQ!xLd4}Ik!OVQn+~JA)xaaJYD@<);T3K0RRutZ|ML4 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 46eb1195b286a0b51d01382ba7eff4c51a5ce3f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 322 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+1|-AI^@Rf|wj^(N7l!{JxM1({$v_d#0*}aI z1_o|n5N2eUHAey{$X?><>&kwgots}?_4xcl>w!Y&JzX3_EKa|j=$pk9DA2O{esw~D z==+CS4{Z0i_dP9SdZfDPwBH4eQ`23uJgoghj`k)v8vE>h^=khGh6X7?_6NR~&U|rq z(b;Vt%M{SaR`aR5m${gC#os+Yj`Mi3dxl;*_GQOoB_+Mm;0DY38B7mnsMYaJDR$$H zV9ZXK=>FjZ_x`mF(~8?;%Kzo5rWP7cH(lRWsq9_$ zO4&Wa_~WPjee0fi$M(K!axFh}`GsI_)cgOxPyLy$xVYT&ceM0R#(5Lwy!;~d*dFLL N22WQ%mvv4FO#ne{fAatU 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 0000000000000000000000000000000000000000..c8b6fe7125dfb51b1334b4c04a4c4a792b50a1f3 GIT binary patch literal 293 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+0wn(&ce?|m6p}rHd>I(3)EF2VS{N990fib~ zFff!FFfhDIU|_JC!N4G1FlSew4N!t9$=lt9;eUJonf*W>XMsm#F#`j)FbFd;%$g$s z6rAhn;uxZFzW1CV*C7XymWT5N1m6p;*&_J*#&e-ZmX{*DH(aysVDUce^iELFLHrtt&H4(-KC$sb_WC)2z1{J3}`#7FG)aSQnCX<14@iyh7Tko1r zk)0ZIYi{s2N4MsZTLlJz^EnQkcX_meIn<^tz|o=M(_)^6#qzZ`Ha)ifl6j?&zbf+N jWtZZhZ*!Nq^qiM3VrYvt{`TWF(4h>Tu6{1-oD!MI(3)EF2VS{N990fib~ zFff!FFfhDIU|_JC!N4G1FlSew4N!t9$=lt9;eUJonf*W>XMsm#F#`j)FbFd;%$g$s z6kO!#;uxZFzW0(L*C7Xi)`yB3k8D+@nkB8R)l;-|m&)e6R>dy3(M0EoT$jS3Yf)^> z`(qO(-R=E6>-Y!J;2s4n=4Uz*uhyz@iZOUUayHglwpS66s5?34O=tU2$_?*K=a7S6>ojyGB_&NkJYzi;+GE1QOS oexELAs3l+aI(Fo%|GYm8zuHc!`L65|0lJpK)78&qol`;+0E@_IPyhe` literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..ebd96073bf52c4ae819c87f3660b55bbd15f2886 GIT binary patch literal 229 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+0wn(&ce?|m6p}rHd>I(3)EF2VS{N990fib~ zFff!FFfhDIU|_JC!N4G1FlSew4N!t9$=lt9;eUJonf*W>XMsm#F#`j)FbFd;%$g$s z6pZzBaSYKozxC`v&O;6it_Lq>tI(3)EF2VS{N990fib~ zFff!FFfhDIU|_JC!N4G1FlSew4N!t9$=lt9;eUJonf*W>XMsm#F#`j)FbFd;%$g$s z6fE>~aSYKoKlP$7*C7K5mJ24!U+%m$&$H`Cv6o&wRS)tbymRxph=IB0Qs px+<>JP`SUt^V<>&kwgT|iJ!S@FKe2cXavPZ!4!jq_747<>&kwgots}?D_r5e9Z+b$r;B5V#`&rJwpmVzA};4^y##`m zU)5sa_KvO2y7Cm32)J}~%oDlCFQ>*fCoL`E**})_fa(Lm zw+dZ+u6A&|UZ_?1f-}2x@~>9=W%pE8`&~)|k0wldT1B8K;Lb6AYF9SoB8UsT^3j@P1pisjL z28L1t28LG&3=CE?7#PG0=Ijcz0ZK3>dAqwX{BQ3+vmeOgEbxddW?mdKI;Vst0PLoQegFUf literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b94735ecbb6043e2842514aaed9124dbcf18d34f GIT binary patch literal 399 zcmV;A0dW3_P)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00002VoOIv0RM-N%)bBt010qN zS#tmY3ljhU3ljkVnw%H_009R{L_t(Y$L-d!O2beT2H=2L!6J09?!{3-)K{tK;35=X zpo?yOg3zp`=&K0DSJ1gy=g`3lSw06fh;1}!svz{g+c}r-!c92bK(Ltg(}tR+woAu0 z=ZETg5+6xi*_sF1G4wT0c8fywcV?omMY2yA;n zGW*(RL(Pn{ufb7%s0~_E^>c8MAL@XUsro7C{6}a_B$g**kxDicDUv7~iVHz;HdHiA z=q*E`&_h}1#=1bDYETZ^OM3_9pp~?@S`OMzdxzzqfvP}Y!)@8G_Nc3CwEVLd4UHEy tkBUvmFdM4mp4qYRyP5rq53Rq4UI7l%BzuJ0j)(vN002ovPDHLkV1i&mp)3FZ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b33c964d29a1786e9699db1d7ea6c14703266d94 GIT binary patch literal 291 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8K;Lb6AYF9SoB8UsT^3j@P1pisjL z28L1t28LG&3=CE?7#PG0=Ijcz0ZK3>dAqwX{BQ3+vmeOgEbxddW?wr71e&pm1=*sm;5~Au{HDav6(tr=Oo;gC!X7v%N0ChzTpaq5FRZy k(dI74qYIL}_C4oaxKwS{@2F-EpfedfUHx3vIVCg!0KWxkEdT%j literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..3ee3e1720bd76cac1d0ba20d1315af562512663d GIT binary patch literal 351 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8K;Lb6AYF9SoB8UsT^3j@P1pisjL z28L1t28LG&3=CE?7#PG0=Ijcz0ZK3>dAqwX{BQ3+vmeOgEbxddW?xq+X@R~r-W;^-qY;7chg60 z?&cYx!Ygtoy_Mwg^>;29~AGdj*J sn^^*Tcdx@v7EBk$R0YO2vz^xDJ85kJVJzX3_JdVGeYUsyoDBwE(x`M#O zj7t}mcps14!M>9@L`Kowb3RvL*O#15rACh%Ycl^W>U>%;*SB?wes5a&zccbTUw^dv zRNydSKXaaFJoDCvOw0wU{~2@|^*LE>^^e}Xv)GV5s&kuz^5%^{13u1lG5citL2b2i zURloucCCcOZBImJ%$U=eB)dPLS3k3WwN^v&z6X3Yxy|_gfY!ma zN8T1Og&yo$75qwH=((M_u-L-%vPry5A8H*mR5Oz2?-Z?g`?S05gmfK`%`DAhKA&%7 z*myo>t1z2YJz1h>>&5DGkM7u?z2csAM)vYTTLr22)tA3d+?^6$5EbJXd(qIA^Eumd zw*M;{yZ>c4KYZ-?My*o0p8wC6nA?^t4=!c>W4gK_p`iBDnO{Sr_sDzS87>DG<2^d9sMFmZ$)r8^Vi6zt82kcdc0^J8(qiqeoY z6H+aG`Xu}L`TOH%S%(3V1RyiJF|%tAy_;E9$0T7x4Vd`80-k`JU(4OMir7<%Y=Fv1 zE1>iheVbFs*G^LSo&hhuvPNt@gg6BfU>ly~8CV3&!hN-EJtyr`U<%AUaSyx!%NE?y zV|&NWfN2w#{88^G_%~y(h#2mcR|G#)I1Ly_x>MOk{)VK)k>695RUDqQ{V26Sf4KwNk!Wy zl`Z6z`{JtN>$nV@TQmC_FoW7= c(q-iQ28(7b+iT|Ao&W#<07*qoM6N<$g7$H^p8x;= 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 0000000000000000000000000000000000000000..dbcf3f33d3d5be7d45ff06ad9b8e7c4ccc1bc51c GIT binary patch literal 559 zcmV+~0?_@5P)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00002VoOIv0RM-N%)bBt010qN zS#tmY3ljhU3ljkVnw%H_00E~-L_t(o!|m8hO9D|C#_>PhsHCz8lAukrPa+5+#APlD zH`S^>MYmN+84>P+zJaSC3Rhy=AV`uf&?bu9S5$;n#7v8(!`L)6I_D6`oOd_x!~0Vl zkDdWc&zGcOs|O~^IW)C1!xFIyYKdw5-m}tbSsw%CxiX34SC{|*002ovPDHLkV1lCz@23C& literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..895aabbfab5b03d0783e7573f44521ec6608439e GIT binary patch literal 558 zcmV+}0@3}6P)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00002VoOIv0RM-N%)bBt010qN zS#tmY3ljhU3ljkVnw%H_00E{+L_t(o!|m9;OF~f?$8qV6N=l0$30k5(i6Dp&#~ccm zYO22?tH~mj2)CgBz$u8rDQXRZBBeWv!w?$5x-nx0t>5coG?LI%AkK*;{ zxegrPw;aD2^D;|<9d<|%qE8gGI#193h&;btm&Yir#_>tV;~MWuUA(NVd?;lcH}Kap;c@ycO7F{s`&(? zh;pMFJEBwMJe=snp6Dq__VTO`+bqi_^9VuhK){Rbaz+E~T)3jW&?f+Lb>9smFU07*qoM6N<$g6z8MSO5S3 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..66d25296d145fe4bb8105e4995c35eaca089044e GIT binary patch literal 336 zcmeAS@N?(olHy`uVBq!ia0vp^W+2SL0wmRZ7KH&Rg=CK)Uj~LMH3o);76yi2K%s^g z3=E|P3=FRl7#OT(FffQ0%-I!a1C(G&@^*J&_}|`tWO>_%)r1c48n{Iv*t(u z1y6XoIEF+VetXH9uPH#l?P2nrhV#7*s-4-}j9X+M?7r;kknAbLt=qw5u2z05&CmMi zbf3Bpb(uM=x;H;Pa9ifDyF-EF(A=$NrFFrt?#v194tkmMzS!$-q{Y)I!G4xcLkyg`ywTyQ&;M=<7xhnj;k;t~DWIPi NJYD@<);T3K0RZ*1b&vo6 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..9d7f2ff90d63cf8cd97965391b8321e4b261a72f GIT binary patch literal 436 zcmV;l0ZaagP)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00002VoOIv0RM-N%)bBt010qN zS#tmY3ljhU3ljkVnw%H_00AsXL_t(o!|m8TPQySDhG9osfS7QRgfbFmrbAI~5FmjL z4iPt$h?l}bvG>Qj_I?wIh;Pa*9<63HD{Vz}zN-E{c6Bc7mPfw%U~P{rc%#iH_qN!A zS5EObC3VArejH9m)$H-p<8;`Ta5|h0TXKwYYkkk++`Gjzt|HtQGX++&Wtrq26gR7R z0gegonRz+ZQUtiBLp}XcCD@4?;yToHtWUMvs&jrv1MIji-yl8};x>J18WyH}PVmIU zsU)YR0Gc?brT{hyERWi`V=)CQrt&cXm5WKJJWNF8U@|I=38@q&rG_yvHH68jIA%b_ zFcT__8Brn3jPfx<%Ee467c-??%#<376;b1`LTU_FOyy$&Di@Pbd6|k1|%Oc%$NbB*pj^6T^Rm@;DWu&Cj&(|3p^r= z85p>QL70(Y)*K0-AbW|YuPgg~b^$>_xytPk4;dI3IXzt*Ln02po#DuLNI}3=e(SD_ zOIFJrl&%nc|76qm4?J;;#W!|xuii0JtwmvJ$FZY6P98HPD-P(JIP6>7=)0g+d7sW| zqoS;p{0HPV#NF&Z-|K5O*;0_ZC!ekKHgEhE*#e;%=?STSjE>Gx*}h_Jqn^4h4{U2*ZW@+k4ci{=Z PaANRu^>bP0l+XkKtoN6U 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 4945c9595fbde5ad2bb34e0164fd658319350264..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 441 zcmV;q0Y?6bP)2QE&&-_~ogQLl#p=0L5#LcZV970V_#Ov<1DS9Wc+? z?~cwT?YuTbqXAaHY;=Zv@sp&FG!MTx9%4O9I+$5M*}?c|6`%riuZEn|m85o}x2!g^ zZm>f@1?-d0i8fi&Gf>ev6x1dq`}7kfw`GS6#Mo`VR@-{766yj}fC^9nDnJFO02QDc jKrfQ&Kj>Y1um7zt0#H@V=1JnH00000NkvXXu0mjfsA{xX 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 0000000000000000000000000000000000000000..c49d420e0b12568c3b8a16db4377cfde1fbae142 GIT binary patch literal 636 zcmV-?0)zdDP)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00002VoOIv0RM-N%)bBt010qN zS#tmY3ljhU3ljkVnw%H_00Hz#L_t(&-tE}WD+FN}!0~VSG4_C5$zLFnAAf~7D!FV9 zZX$nxwB;zjR{0y0a}5D<;++yPR7jcurp(yeZ})U$9bOKPjBz@&NQI$ zucDC=(qt)6q)34*YYfw12Gd502b_SsXP(wtbH*qHVm|Vg(b{3^*!ULnk!k8`!`TYO zad6nI1*v>@UdWLlLyniSXY2B3$G_kiNt)fKElhAxQZXt!S>B`k${h7&H4UVA^Br%& ziA(u7<6qV#eWW?LYw#iTG7G+YN$9E^@tvLe3iwM;^^goG?O7sOv3k-|y&?n3x(gIN zllAogyt?&!GN8{1%Y~{xckAzDKt*?fOsM)@xBgiM^m!(>L)GtiIuxWJ1qDYFvm9ZE z{vfNAHiyh$#pQHzS~ZF{Pw0rZJ2D&xhdhnZL9=2U2Z!nCpd(SxL3EHU3VQs>kjtck z6r>;p{aMfxi}Yb((a%zZ`&|fhMVGIun`@JxrxFf856|X6i-DcX=0JUcodf1T!C%J- z1-aQD*g0qpv=rF6Y7S)6UDnrYa_k+h=_%pdn0|HGEHglY1cR)G+#P+}D@Z{KQcyKO zzwUR;p(toSI%q}|G!-3WaU#Y!j`PzKI>-xg+T;GIwwUIC$0{D>eWv2=j)sOtwc;Jw W{bhWT-u(Ok0000004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00002VoOIv0RM-N%)bBt010qN zS#tmY3ljhU3ljkVnw%H_00MAHL_t(&-tC&tYZE~f$GA*^?>jW+CAVzE=gNm%VWvwb%QAoa+xpboF1$tkAgIdWWAqwZG$o+dd;xehj;;B&O!#^xv8i+Ul6<5l9j>v&JoB99eZ|!gi`1sbH$Vo+ z039o!mgv0u`apG2pt?TLhA7awehF)#f}#-4HqPl^dbV%@H6fhYFdX!_IJbC{6*d#Q qp>FamZ!i}%XUcqGWq=Hju>1iAuTa5a>{Dg{0000I(3)EF2VS{N990fib~ zFff!FFfhDIU|_JC!N4G1FlSew4N!t9$=lt9;eUJonf*W>XMsm#F#`j)FbFd;%$g$s z6ny6C;uw-~@9hmkv4ajGtq*6r9dpyjJ-$(_JHUeLHOHY@A|hKvLX0N+Ox#mG_fEap z2hsO?lNgpxk@9BclJhwr=WnyY`Q(9_kD9K3zslhw!*ogi_)L+<7L$Tx%r9x3EL2`= z?V&22nQdzQ+2_*TtSRdwQYPk=&7SIhD?+czeC44d-`;JRylP>&P8r)}6>+~=PmXm? zW_#Wu<~MoEmuDuP?_`{V^qxDYG*OyQA}Pp&-ra>TMrnNOQN zM`lCefn$u)>ii{}{#;Uh+|bKx#&^Ra0nF$N^6K4VZfACO{YOKFO+X(rc)I$ztaD0e F0szKpiLw9y literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..34ec7092f142ac50ca97d3f18f1486582a12d2f6 GIT binary patch literal 490 zcmV004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00002VoOIv0RM-N%)bBt010qN zS#tmY3ljhU3ljkVnw%H_00Cl2L_t(&-tF44Q3Npz1<<{O+ie6w+F_`LA_y1)>M(F@ zO#~1?`U#kuIaph^bo=EEBsK76Y)e1zfMHub1SmiO3J|Mz*lhZa0*QNMcNNm!|5D4O2OrRct47kN2WY~KcU(7- zKKP*H%W5Otvq7g1y-rHF;DY|o9hsE!i;V+3LDM_hlo%l72 z)~p-k2OM zh|bZ0bev}kM+0)Dtspl}8_1E<0&?ZlK_d>HoEpfTBZG9Dmo+#Ns0*hI>clC5 zx^eQLj+{KGBPS2)$T<&0;G6@ZaO|L04#x_jacm$W#{!~q^dK@v3!-y$AP0^H + android:src="@drawable/ic_lock_white_18dp" /> + + + android:src="@drawable/ic_lock_black_18dp" /> + + + The selected area is too large (No activated accounts) This field is required + Correct message + Send corrected message