aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/eu/siacs/conversations
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/eu/siacs/conversations')
-rw-r--r--src/main/java/eu/siacs/conversations/Config.java3
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/PgpEngine.java13
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Conversation.java38
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Message.java53
-rw-r--r--src/main/java/eu/siacs/conversations/entities/MucOptions.java4
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Presence.java26
-rw-r--r--src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java29
-rw-r--r--src/main/java/eu/siacs/conversations/generator/IqGenerator.java30
-rw-r--r--src/main/java/eu/siacs/conversations/generator/MessageGenerator.java3
-rw-r--r--src/main/java/eu/siacs/conversations/parser/MessageParser.java89
-rw-r--r--src/main/java/eu/siacs/conversations/parser/PresenceParser.java2
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java14
-rw-r--r--src/main/java/eu/siacs/conversations/services/MessageArchiveService.java4
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java116
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationActivity.java7
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationFragment.java92
-rw-r--r--src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java25
-rw-r--r--src/main/java/eu/siacs/conversations/ui/EditMessage.java2
-rw-r--r--src/main/java/eu/siacs/conversations/ui/SettingsActivity.java3
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java15
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java76
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/forms/Data.java9
22 files changed, 509 insertions, 144 deletions
diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java
index 75715045..a1da283f 100644
--- a/src/main/java/eu/siacs/conversations/Config.java
+++ b/src/main/java/eu/siacs/conversations/Config.java
@@ -31,6 +31,8 @@ public final class Config {
public static final int CARBON_GRACE_PERIOD = 90;
public static final int MINI_GRACE_PERIOD = 750;
+ public static final boolean CLOSE_TCP_WHEN_SWITCHING_TO_BACKGROUND = false;
+
public static final int AVATAR_SIZE = 192;
public static final Bitmap.CompressFormat AVATAR_FORMAT = Bitmap.CompressFormat.PNG;
@@ -112,6 +114,5 @@ public final class Config {
};
private Config() {
-
}
}
diff --git a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java
index 40baa479..d9464f8c 100644
--- a/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java
+++ b/src/main/java/eu/siacs/conversations/crypto/PgpEngine.java
@@ -40,25 +40,24 @@ public class PgpEngine {
this.mXmppConnectionService = service;
}
- public void decrypt(final Message message,
- final UiCallback<Message> callback) {
+ public void decrypt(final Message message, final UiCallback<Message> callback) {
Intent params = new Intent();
params.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY);
+ final String uuid = message.getUuid();
if (message.getType() == Message.TYPE_TEXT) {
- InputStream is = new ByteArrayInputStream(message.getBody()
- .getBytes());
+ InputStream is = new ByteArrayInputStream(message.getBody().getBytes());
final OutputStream os = new ByteArrayOutputStream();
api.executeApiAsync(params, is, os, new IOpenPgpCallback() {
@Override
public void onReturn(Intent result) {
notifyPgpDecryptionService(message.getConversation().getAccount(), OpenPgpApi.ACTION_DECRYPT_VERIFY, result);
- switch (result.getIntExtra(OpenPgpApi.RESULT_CODE,
- OpenPgpApi.RESULT_CODE_ERROR)) {
+ switch (result.getIntExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR)) {
case OpenPgpApi.RESULT_CODE_SUCCESS:
try {
os.flush();
- if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ if (message.getEncryption() == Message.ENCRYPTION_PGP
+ && message.getUuid().equals(uuid)) {
message.setBody(os.toString());
message.setEncryption(Message.ENCRYPTION_DECRYPTED);
final HttpConnectionManager manager = mXmppConnectionService.getHttpConnectionManager();
diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java
index ba1244d1..4eea56b8 100644
--- a/src/main/java/eu/siacs/conversations/entities/Conversation.java
+++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java
@@ -84,6 +84,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;
@@ -228,6 +229,24 @@ public class Conversation extends AbstractEntity implements Blockable {
return null;
}
+ public Message findMessageWithRemoteIdAndCounterpart(String id, Jid counterpart, boolean received, boolean carbon) {
+ synchronized (this.messages) {
+ for(int i = this.messages.size() - 1; i >= 0; --i) {
+ Message message = messages.get(i);
+ if (counterpart.equals(message.getCounterpart())
+ && ((message.getStatus() == Message.STATUS_RECEIVED) == received)
+ && (carbon == message.isCarbon() || received) ) {
+ if (id.equals(message.getRemoteMsgId())) {
+ return message;
+ } else {
+ return null;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
public Message findSentMessageWithUuid(String id) {
synchronized (this.messages) {
for (Message message : this.messages) {
@@ -296,6 +315,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);
}
@@ -499,15 +526,18 @@ public class Conversation extends AbstractEntity implements Blockable {
return mSmp;
}
- public void startOtrIfNeeded() {
- if (this.otrSession != null
- && this.otrSession.getSessionStatus() != SessionStatus.ENCRYPTED) {
+ public boolean startOtrIfNeeded() {
+ if (this.otrSession != null && this.otrSession.getSessionStatus() != SessionStatus.ENCRYPTED) {
try {
this.otrSession.startSession();
+ return true;
} catch (OtrException e) {
this.resetOtrSession();
+ return false;
}
- }
+ } else {
+ return true;
+ }
}
public boolean endOtrIfNeeded() {
diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java
index f800def1..3f923c41 100644
--- a/src/main/java/eu/siacs/conversations/entities/Message.java
+++ b/src/main/java/eu/siacs/conversations/entities/Message.java
@@ -51,6 +51,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";
@@ -70,6 +71,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;
@@ -103,7 +105,8 @@ public class Message extends AbstractEntity {
null,
null,
null,
- true);
+ true,
+ null);
this.conversation = conversation;
}
@@ -111,7 +114,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;
@@ -127,6 +131,7 @@ public class Message extends AbstractEntity {
this.serverMsgId = serverMsgId;
this.axolotlFingerprint = fingerprint;
this.read = read;
+ this.edited = edited;
}
public static Message fromCursor(Cursor cursor) {
@@ -161,12 +166,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) {
@@ -210,7 +216,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;
}
@@ -339,10 +346,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;
}
@@ -408,6 +427,21 @@ public class Message extends AbstractEntity {
}
}
+ public boolean isLastCorrectableMessage() {
+ Message next = next();
+ while(next != null) {
+ if (next.isCorrectable()) {
+ return false;
+ }
+ next = next.next();
+ }
+ return isCorrectable();
+ }
+
+ private boolean isCorrectable() {
+ return getStatus() != STATUS_RECEIVED && !isCarbon();
+ }
+
public boolean mergeable(final Message message) {
return message != null &&
(message.getType() == Message.TYPE_TEXT &&
@@ -420,6 +454,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.getBody()) &&
@@ -507,6 +542,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/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
index 7f4ded11..be9d790d 100644
--- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java
+++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java
@@ -282,7 +282,9 @@ public class MucOptions {
}
public boolean participating() {
- return !online() || self.getRole().ranks(Role.PARTICIPANT);
+ return !online()
+ || self.getRole().ranks(Role.PARTICIPANT)
+ || hasFeature("muc_unmoderated");
}
public boolean membersOnly() {
diff --git a/src/main/java/eu/siacs/conversations/entities/Presence.java b/src/main/java/eu/siacs/conversations/entities/Presence.java
index 69cde832..442f1bca 100644
--- a/src/main/java/eu/siacs/conversations/entities/Presence.java
+++ b/src/main/java/eu/siacs/conversations/entities/Presence.java
@@ -1,6 +1,7 @@
package eu.siacs.conversations.entities;
import java.lang.Comparable;
+import java.util.Locale;
import eu.siacs.conversations.xml.Element;
@@ -32,21 +33,24 @@ public class Presence implements Comparable {
this.hash = hash;
}
- public static Presence parse(Element show, Element caps) {
+ public static Presence parse(String show, Element caps) {
final String hash = caps == null ? null : caps.getAttribute("hash");
final String ver = caps == null ? null : caps.getAttribute("ver");
- if ((show == null) || (show.getContent() == null)) {
+ if (show == null) {
return new Presence(Status.ONLINE, ver, hash);
- } else if (show.getContent().equals("away")) {
- return new Presence(Status.AWAY, ver, hash);
- } else if (show.getContent().equals("xa")) {
- return new Presence(Status.XA, ver, hash);
- } else if (show.getContent().equals("chat")) {
- return new Presence(Status.CHAT, ver, hash);
- } else if (show.getContent().equals("dnd")) {
- return new Presence(Status.DND, ver, hash);
} else {
- return new Presence(Status.OFFLINE, ver, hash);
+ switch (show.toLowerCase(Locale.US)) {
+ case "away":
+ return new Presence(Status.AWAY, ver, hash);
+ case "xa":
+ return new Presence(Status.XA, ver, hash);
+ case "dnd":
+ return new Presence(Status.DND, ver, hash);
+ case "chat":
+ return new Presence(Status.CHAT, ver, hash);
+ default:
+ return new Presence(Status.ONLINE, ver, hash);
+ }
}
}
diff --git a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
index 649f767d..7e2ff213 100644
--- a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
@@ -15,6 +15,10 @@ import java.util.TimeZone;
import de.thedevstack.conversationsplus.ConversationsPlusApplication;
import de.tzur.conversations.Settings;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
+import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.utils.PhoneHelper;
+import eu.siacs.conversations.xmpp.jid.Jid;
+import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public abstract class AbstractGenerator {
private final String[] FEATURES = {
@@ -36,10 +40,32 @@ public abstract class AbstractGenerator {
"urn:xmpp:chat-markers:0",
"urn:xmpp:receipts"
};
+ private final String[] MESSAGE_CORRECTION_FEATURES = {
+ "urn:xmpp:message-correct:0"
+ };
+ private String mVersion = null;
+ protected final String IDENTITY_NAME = "Conversations";
protected final String IDENTITY_TYPE = "phone";
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
+ protected XmppConnectionService mXmppConnectionService;
+
+ protected AbstractGenerator(XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
+
+ protected String getIdentityVersion() {
+ if (mVersion == null) {
+ this.mVersion = PhoneHelper.getVersionName(mXmppConnectionService);
+ }
+ return this.mVersion;
+ }
+
+ public String getIdentityName() {
+ return IDENTITY_NAME + " " + getIdentityVersion();
+ }
+
public String getCapHash() {
StringBuilder s = new StringBuilder();
s.append("client/" + IDENTITY_TYPE + "//" + ConversationsPlusApplication.getNameAndVersion() + "<");
@@ -68,6 +94,9 @@ public abstract class AbstractGenerator {
if (Settings.CONFIRM_MESSAGE_RECEIVED) {
features.addAll(Arrays.asList(MESSAGE_CONFIRMATION_FEATURES));
}
+ if (mXmppConnectionService.allowMessageCorrection()) {
+ features.addAll(Arrays.asList(MESSAGE_CORRECTION_FEATURES));
+ }
Collections.sort(features);
return features;
}
diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
index a275c01d..0c1f6ee4 100644
--- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
@@ -25,6 +25,7 @@ import eu.siacs.conversations.services.MessageArchiveService;
import eu.siacs.conversations.utils.Xmlns;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.forms.Data;
+import eu.siacs.conversations.xmpp.forms.Field;
import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
@@ -247,7 +248,7 @@ public class IqGenerator extends AbstractGenerator {
public IqPacket requestHttpUploadSlot(Jid host, DownloadableFile file, String mime) {
IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
packet.setTo(host);
- Element request = packet.addChild("request",Xmlns.HTTP_UPLOAD);
+ Element request = packet.addChild("request", Xmlns.HTTP_UPLOAD);
request.addChild("filename").setContent(file.getName());
request.addChild("size").setContent(String.valueOf(file.getExpectedSize()));
if (mime != null) {
@@ -265,4 +266,31 @@ public class IqGenerator extends AbstractGenerator {
return register;
}
+
+ public IqPacket pushTokenToAppServer(Jid appServer, String token, String deviceId) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
+ packet.setTo(appServer);
+ Element command = packet.addChild("command", "http://jabber.org/protocol/commands");
+ command.setAttribute("node","register-push-gcm");
+ command.setAttribute("action","execute");
+ Data data = new Data();
+ data.put("token", token);
+ data.put("device-id", deviceId);
+ data.submit();
+ command.addChild(data);
+ return packet;
+ }
+
+ public IqPacket enablePush(Jid jid, String node, String secret) {
+ IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
+ Element enable = packet.addChild("enable","urn:xmpp:push:0");
+ enable.setAttribute("jid",jid.toString());
+ enable.setAttribute("node", node);
+ Data data = new Data();
+ data.setFormType("http://jabber.org/protocol/pubsub#publish-options");
+ data.put("secret",secret);
+ data.submit();
+ enable.addChild(data);
+ return packet;
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
index 66ad2a7b..1824a0f8 100644
--- a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java
@@ -44,6 +44,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 35fed7b6..794badae 100644
--- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
@@ -3,11 +3,13 @@ package eu.siacs.conversations.parser;
import android.util.Log;
import android.util.Pair;
+import eu.siacs.conversations.crypto.PgpDecryptionService;
import net.java.otr4j.session.Session;
import net.java.otr4j.session.SessionStatus;
import java.util.ArrayList;
import java.util.Set;
+import java.util.UUID;
import de.thedevstack.android.logcat.Logging;
import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
@@ -121,13 +123,6 @@ public class MessageParser extends AbstractParser implements
return finishedMessage;
}
- private Message parsePGPChat(final Conversation conversation, String pgpEncrypted, int status) {
- final Message message = new Message(conversation, pgpEncrypted, Message.ENCRYPTION_PGP, status);
- PgpDecryptionService pgpDecryptionService = conversation.getAccount().getPgpDecryptionService();
- pgpDecryptionService.add(message);
- return message;
- }
-
private class Invite {
Jid jid;
String password;
@@ -301,6 +296,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;
@@ -314,7 +311,7 @@ public class MessageParser extends AbstractParser implements
}
boolean isTypeGroupChat = packet.getType() == MessagePacket.TYPE_GROUPCHAT;
- boolean isProperlyAddressed = (to != null ) && (!to.isBareJid() || account.countPresences() == 1);
+ boolean isProperlyAddressed = (to != null ) && (!to.isBareJid() || account.countPresences() <= 1);
boolean isMucStatusMessage = from.isBareJid() && mucUserElement != null && mucUserElement.hasChild("status");
if (packet.fromAccount(account)) {
status = Message.STATUS_SEND;
@@ -338,6 +335,7 @@ public class MessageParser extends AbstractParser implements
if (isTypeGroupChat) {
if (counterpart.getResourcepart().equals(conversation.getMucOptions().getActualNick())) {
status = Message.STATUS_SEND_RECEIVED;
+ isCarbon = true; //not really carbon but received from another resource
if (mXmppConnectionService.markMessage(conversation, remoteMsgId, status)) {
return;
} else if (remoteMsgId == null || Config.IGNORE_ID_REWRITE_IN_MUC) {
@@ -359,10 +357,11 @@ public class MessageParser extends AbstractParser implements
return;
}
} else {
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": ignoring OTR message from "+from+" isForwarded="+Boolean.toString(isForwarded)+", isProperlyAddressed="+Boolean.valueOf(isProperlyAddressed));
message = new Message(conversation, body, Message.ENCRYPTION_NONE, status);
}
} else if (pgpEncrypted != null) {
- message = parsePGPChat(conversation, pgpEncrypted, status);
+ message = new Message(conversation, pgpEncrypted, Message.ENCRYPTION_PGP, status);
} else if (axolotlEncrypted != null) {
message = parseAxolotlChat(axolotlEncrypted, from, remoteMsgId, conversation, status);
if (message == null) {
@@ -394,6 +393,42 @@ public class MessageParser extends AbstractParser implements
} else {
updateLastseen(timestamp, account, packet.getFrom(), true);
}
+
+ if (replacementId != null && mXmppConnectionService.allowMessageCorrection()) {
+ Message replacedMessage = conversation.findMessageWithRemoteIdAndCounterpart(replacementId,
+ counterpart,
+ message.getStatus() == Message.STATUS_RECEIVED,
+ message.isCarbon());
+ 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() + "'");
+ final String uuid = replacedMessage.getUuid();
+ replacedMessage.setUuid(UUID.randomUUID().toString());
+ replacedMessage.setBody(message.getBody());
+ replacedMessage.setEdited(replacedMessage.getRemoteMsgId());
+ replacedMessage.setRemoteMsgId(remoteMsgId);
+ replacedMessage.setEncryption(message.getEncryption());
+ if (replacedMessage.getStatus() == Message.STATUS_RECEIVED) {
+ replacedMessage.markUnread();
+ }
+ mXmppConnectionService.updateMessage(replacedMessage, uuid);
+ if (mXmppConnectionService.confirmMessages() && remoteMsgId != null && !isForwarded && !isTypeGroupChat) {
+ sendMessageReceipts(account, packet);
+ }
+ if (replacedMessage.getEncryption() == Message.ENCRYPTION_PGP) {
+ conversation.getAccount().getPgpDecryptionService().add(replacedMessage);
+ }
+ 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;
@@ -408,6 +443,10 @@ public class MessageParser extends AbstractParser implements
conversation.add(message);
}
+ if (message.getEncryption() == Message.ENCRYPTION_PGP) {
+ conversation.getAccount().getPgpDecryptionService().add(message);
+ }
+
if (query == null || query.getWith() == null) { //either no mam or catchup
if (status == Message.STATUS_SEND || status == Message.STATUS_SEND_RECEIVED) {
mXmppConnectionService.markRead(conversation);
@@ -424,20 +463,7 @@ public class MessageParser extends AbstractParser implements
}
if (ConversationsPlusPreferences.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
@@ -532,4 +558,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/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
index fcff5c4c..c765dbd0 100644
--- a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java
@@ -192,7 +192,7 @@ public class PresenceParser extends AbstractParser implements
}
int sizeBefore = contact.getPresences().size();
- final Element show = packet.findChild("show");
+ final String show = packet.findChildContent("show");
final Element caps = packet.findChild("c", "http://jabber.org/protocol/caps");
final Presence presence = Presence.parse(show, caps);
contact.updatePresence(resource, presence);
diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
index 040d2f77..96a04fa4 100644
--- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
@@ -52,7 +52,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, "
@@ -162,6 +162,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 "
@@ -371,6 +372,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) {
@@ -587,6 +592,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/MessageArchiveService.java b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java
index 3ad0c514..0ca62b97 100644
--- a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java
+++ b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java
@@ -192,6 +192,10 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
}
}
+ public boolean queryInProgress(Conversation conversation) {
+ return queryInProgress(conversation, null);
+ }
+
public void processFin(Element fin, Jid from) {
if (fin == null) {
return;
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index 53b60d5f..9da9ffb2 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -102,6 +102,7 @@ import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.utils.OnPhoneContactsLoadedListener;
import eu.siacs.conversations.utils.PRNGFixes;
import eu.siacs.conversations.utils.PhoneHelper;
+import eu.siacs.conversations.utils.SerialSingleThreadExecutor;
import eu.siacs.conversations.utils.Xmlns;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnBindListener;
@@ -122,6 +123,7 @@ import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
import eu.siacs.conversations.xmpp.jingle.OnJinglePacketReceived;
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
+import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
@@ -134,6 +136,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public static final String ACTION_TRY_AGAIN = "try_again";
public static final String ACTION_DISABLE_ACCOUNT = "disable_account";
private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
+ public static final String ACTION_GCM_TOKEN_REFRESH = "gcm_token_refresh";
+ public static final String ACTION_GCM_MESSAGE_RECEIVED = "gcm_message_received";
private final IBinder mBinder = new XmppConnectionBinder();
private final List<Conversation> conversations = new CopyOnWriteArrayList<>();
private final IqGenerator mIqGenerator = new IqGenerator();
@@ -201,6 +205,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager(
this);
private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this);
+ private PushManagementService mPushManagementService = new PushManagementService(this);
private OnConversationUpdate mOnConversationUpdate = null;
private final FileObserver fileObserver = new FileObserver(
FileBackend.getConversationsImageDirectory()) {
@@ -260,6 +265,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
fetchRosterFromServer(account);
fetchBookmarks(account);
sendPresence(account);
+ if (mPushManagementService.available(account)) {
+ mPushManagementService.registerPushTokenOnServer(account);
+ }
mMessageArchiveService.executePendingQueries(account);
connectMultiModeConversations(account);
syncDirtyContacts(account);
@@ -268,7 +276,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
private OnStatusChanged statusListener = new OnStatusChanged() {
@Override
- public void onStatusChanged(Account account) {
+ public void onStatusChanged(final Account account) {
XmppConnection connection = account.getXmppConnection();
if (mOnAccountUpdate != null) {
mOnAccountUpdate.onAccountUpdate();
@@ -285,8 +293,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
List<Conversation> conversations = getConversations();
for (Conversation conversation : conversations) {
- if (conversation.getAccount() == account && conversation.getMode() == Conversation.MODE_SINGLE) {
- conversation.startOtrIfNeeded();
+ if (conversation.getAccount() == account
+ && !account.pendingConferenceJoins.contains(conversation)) {
+ if (!conversation.startOtrIfNeeded()) {
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": couldn't start OTR with "+conversation.getContact().getJid()+" when needed");
+ }
sendUnsentMessages(conversation);
}
}
@@ -301,7 +312,12 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
scheduleWakeUpCall(Config.PING_MAX_INTERVAL, account.getUuid().hashCode());
} else if (account.getStatus() == Account.State.OFFLINE) {
resetSendingToWaiting(account);
- if (!account.isOptionSet(Account.OPTION_DISABLED)) {
+ final boolean disabled = account.isOptionSet(Account.OPTION_DISABLED);
+ final boolean pushMode = Config.CLOSE_TCP_WHEN_SWITCHING_TO_BACKGROUND
+ && mPushManagementService.available(account)
+ && checkListeners();
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": push mode "+Boolean.toString(pushMode));
+ if (!disabled && !pushMode) {
int timeToReconnect = mRandom.nextInt(20) + 10;
scheduleWakeUpCall(timeToReconnect, account.getUuid().hashCode());
}
@@ -468,6 +484,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
refreshAllPresences();
}
break;
+ case ACTION_GCM_TOKEN_REFRESH:
+ refreshAllGcmTokens();
+ break;
+ case ACTION_GCM_MESSAGE_RECEIVED:
+ Log.d(Config.LOGTAG,"gcm push message arrived in service. extras="+intent.getExtras());
}
}
this.wakeLock.acquire();
@@ -725,18 +746,20 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
disconnect(account, false);
}
}).start();
-
+ cancelWakeUpCall(account.getUuid().hashCode());
}
}
- Context context = getApplicationContext();
- AlarmManager alarmManager = (AlarmManager) context
- .getSystemService(Context.ALARM_SERVICE);
- Intent intent = new Intent(context, EventReceiver.class);
- alarmManager.cancel(PendingIntent.getBroadcast(context, 0, intent, 0));
Logging.d(Config.LOGTAG, "good bye");
stopSelf();
}
+ private void cancelWakeUpCall(int requestCode) {
+ final AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+ final Intent intent = new Intent(this, EventReceiver.class);
+ intent.setAction("ping");
+ alarmManager.cancel(PendingIntent.getBroadcast(this, requestCode, intent, 0));
+ }
+
public void scheduleWakeUpCall(int seconds, int requestCode) {
final long timeToWake = SystemClock.elapsedRealtime() + (seconds < 0 ? 1 : seconds + 1) * 1000;
@@ -794,8 +817,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);
@@ -852,8 +876,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
if (message.fixCounterpart()) {
conversation.startOtrSession(message.getCounterpart().getResourcepart(), true);
} else {
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": could not fix counterpart for OTR message to contact "+message.getContact().getJid());
break;
}
+ } else {
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+" OTR session with "+message.getContact()+" is in wrong state: "+otrSession.getSessionStatus().toString());
}
break;
case Message.ENCRYPTION_AXOLOTL:
@@ -898,6 +925,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
break;
case Message.ENCRYPTION_OTR:
if (!conversation.hasValidOtrSession() && message.getCounterpart() != null) {
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": create otr session without starting for "+message.getContact().getJid());
conversation.startOtrSession(message.getCounterpart().getResourcepart(), false);
}
break;
@@ -919,8 +947,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();
}
@@ -1695,8 +1727,14 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
for (Account account : getAccounts()) {
if (account.getStatus() == Account.State.ONLINE) {
XmppConnection connection = account.getXmppConnection();
- if (connection != null && connection.getFeatures().csi()) {
- connection.sendInactive();
+ if (connection != null) {
+ if (connection.getFeatures().csi()) {
+ connection.sendInactive();
+ }
+ if (Config.CLOSE_TCP_WHEN_SWITCHING_TO_BACKGROUND && mPushManagementService.available(account)) {
+ connection.waitForPush();
+ cancelWakeUpCall(account.getUuid().hashCode());
+ }
}
}
}
@@ -1708,20 +1746,20 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
List<Conversation> conversations = getConversations();
for (Conversation conversation : conversations) {
if (conversation.getMode() == Conversation.MODE_MULTI && conversation.getAccount() == account) {
- joinMuc(conversation, true, null);
+ joinMuc(conversation);
}
}
}
public void joinMuc(Conversation conversation) {
- joinMuc(conversation, false, null);
+ joinMuc(conversation, null);
}
- private void joinMuc(Conversation conversation, boolean now, final OnConferenceJoined onConferenceJoined) {
+ private void joinMuc(Conversation conversation, final OnConferenceJoined onConferenceJoined) {
Account account = conversation.getAccount();
account.pendingConferenceJoins.remove(conversation);
account.pendingConferenceLeaves.remove(conversation);
- if (account.getStatus() == Account.State.ONLINE || now) {
+ if (account.getStatus() == Account.State.ONLINE) {
conversation.resetMucOptions();
fetchConferenceConfiguration(conversation, new OnConferenceConfigurationFetched() {
@@ -1900,7 +1938,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
String name = new BigInteger(75, getRNG()).toString(32);
Jid jid = Jid.fromParts(name, server, null);
final Conversation conversation = findOrCreateConversation(account, jid, true);
- joinMuc(conversation, true, new OnConferenceJoined() {
+ joinMuc(conversation, new OnConferenceJoined() {
@Override
public void onConferenceJoined(final Conversation conversation) {
Bundle options = new Bundle();
@@ -2110,6 +2148,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
updateConversationUi();
}
+ public void updateMessage(Message message, String uuid) {
+ databaseBackend.updateMessage(message, uuid);
+ updateConversationUi();
+ }
+
protected void syncDirtyContacts(Account account) {
for (Contact contact : account.getRoster().getContacts()) {
if (contact.getOption(Contact.Options.DIRTY_PUSH)) {
@@ -2350,6 +2393,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return getPreferences().getBoolean("confirm_messages", true);
}
+ public boolean allowMessageCorrection() {
+ return getPreferences().getBoolean("allow_message_correction", true);
+ }
+
public boolean sendChatStates() {
return getPreferences().getBoolean("chat_states", false);
}
@@ -2457,7 +2504,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return null;
}
- public void markRead(final Conversation conversation) {
+ public boolean markRead(final Conversation conversation) {
mNotificationService.clear(conversation);
final List<Message> readMessages = conversation.markRead();
if (readMessages.size() > 0) {
@@ -2470,8 +2517,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
};
ConversationsPlusApplication.executeDatabaseOperation(runnable);
+ updateUnreadCountBadge();
+ return true;
+ } else {
+ return false;
}
- updateUnreadCountBadge();
}
public synchronized void updateUnreadCountBadge() {
@@ -2489,7 +2539,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public void sendReadMarker(final Conversation conversation) {
final Message markable = conversation.getLatestMarkableMessage();
- this.markRead(conversation);
+ if (this.markRead(conversation)) {
+ updateConversationUi();
+ }
if (Settings.CONFIRM_MESSAGE_READ && markable != null && markable.getRemoteMsgId() != null) {
Logging.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": sending read marker to " + markable.getCounterpart().toString());
Account account = conversation.getAccount();
@@ -2497,7 +2549,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
MessagePacket packet = mMessageGenerator.confirm(account, to, markable.getRemoteMsgId());
this.sendMessagePacket(conversation.getAccount(), packet);
}
- updateConversationUi();
}
public SecureRandom getRNG() {
@@ -2612,6 +2663,14 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
}
+ private void refreshAllGcmTokens() {
+ for(Account account : getAccounts()) {
+ if (account.isOnlineAndConnected() && mPushManagementService.available(account)) {
+ mPushManagementService.registerPushTokenOnServer(account);
+ }
+ }
+ }
+
public void sendOfflinePresence(final Account account) {
sendPresencePacket(account, mPresenceGenerator.sendOfflinePresence(account));
}
@@ -2687,6 +2746,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
* Therefore set this flag to true and try to get messages from server
*/
conversation.setHasMessagesLeftOnServer(true);
+ conversation.setLastClearHistory(System.currentTimeMillis());
Runnable runnable = new Runnable() {
@Override
public void run() {
@@ -2776,7 +2836,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
databaseBackend.insertDiscoveryResult(disco);
injectServiceDiscorveryResult(account.getRoster(), presence.getHash(), presence.getVer(), disco);
} else {
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": mismatch in caps for contact " + jid+" "+presence.getVer()+" vs "+disco.getVer());
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": mismatch in caps for contact " + jid + " " + presence.getVer() + " vs " + disco.getVer());
}
}
account.inProgressDiscoFetches.remove(key);
@@ -2812,6 +2872,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
});
}
+ public PushManagementService getPushManagementService() {
+ return mPushManagementService;
+ }
+
public interface OnMamPreferencesFetched {
void onPreferencesFetched(Element prefs);
void onPreferencesFetchFailed();
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
index d71958d2..8e25c8ef 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
@@ -372,11 +372,7 @@ public class ConversationActivity extends XmppActivity
public void sendReadMarkerIfNecessary(final Conversation conversation) {
if (!mActivityPaused && conversation != null) {
- if (!conversation.isRead()) {
- xmppConnectionService.sendReadMarker(conversation);
- } else {
- xmppConnectionService.markRead(conversation);
- }
+ xmppConnectionService.sendReadMarker(conversation);
}
}
@@ -1581,6 +1577,7 @@ public class ConversationActivity extends XmppActivity
public void setMessagesLoaded() {
if (mConversationFragment != null) {
mConversationFragment.setMessagesLoaded();
+ mConversationFragment.updateMessages();
}
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
index ea3d7e71..4257bce3 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
@@ -46,6 +46,7 @@ import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.UUID;
import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
import eu.siacs.conversations.Config;
@@ -62,6 +63,7 @@ import eu.siacs.conversations.entities.Presences;
import eu.siacs.conversations.entities.Transferable;
import eu.siacs.conversations.entities.TransferablePlaceholder;
import eu.siacs.conversations.persistance.FileBackend;
+import eu.siacs.conversations.services.MessageArchiveService;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.XmppActivity.OnPresenceSelected;
import eu.siacs.conversations.ui.XmppActivity.OnValueEdited;
@@ -71,6 +73,7 @@ import eu.siacs.conversations.ui.adapter.MessageAdapter.OnContactPictureLongClic
import eu.siacs.conversations.ui.listeners.ConversationSwipeRefreshListener;
import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.UIHelper;
+import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.chatstate.ChatState;
import eu.siacs.conversations.xmpp.jid.Jid;
import github.ankushsachdeva.emojicon.EmojiconGridView;
@@ -216,8 +219,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();
}
@@ -252,12 +261,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:
@@ -278,7 +296,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()));
@@ -515,8 +535,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;
@@ -527,10 +546,15 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
private void populateContextMenu(ContextMenu menu) {
final Message m = this.selectedMessage;
+ Message relevantForCorrection = m;
+ while(relevantForCorrection.mergeable(relevantForCorrection.next())) {
+ relevantForCorrection = relevantForCorrection.next();
+ }
if (m.getType() != Message.TYPE_STATUS) {
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);
@@ -542,6 +566,10 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
&& m.treatAsDownloadable() != Message.Decision.MUST) {
copyText.setVisible(true);
}
+ if (relevantForCorrection.getType() == Message.TYPE_TEXT
+ && relevantForCorrection.isLastCorrectableMessage()) {
+ correctMessage.setVisible(true);
+ }
if ((m.getType() != Message.TYPE_TEXT
&& m.getType() != Message.TYPE_PRIVATE
&& m.getTransferable() == null)
@@ -578,6 +606,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;
@@ -679,6 +710,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();
if (oldString.isEmpty() || mEditMessage.getSelectionStart() == 0) {
@@ -728,9 +769,6 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
this.keychainUnlock = KEYCHAIN_UNLOCK_NOT_REQUIRED;
this.conversation = conversation;
- if (this.conversation.getMode() == Conversation.MODE_MULTI) {
- this.conversation.setNextCounterpart(null);
- }
boolean canWrite = this.conversation.getMode() == Conversation.MODE_SINGLE || this.conversation.getMucOptions().participating();
this.mEditMessage.setEnabled(canWrite);
this.mSendButton.setEnabled(canWrite);
@@ -985,9 +1023,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 {
@@ -1040,9 +1081,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
this.mSendButton.setImageResource(getSendButtonImageResource(action, status));
}
- public void updateStatusMessages() {
+ protected void updateStatusMessages() {
synchronized (this.messageList) {
- if (conversation.getLastClearHistory() != 0) {
+ if (showLoadMoreMessages(conversation)) {
this.messageList.add(0, Message.createLoadMoreMessage(conversation));
}
if (conversation.getMode() == Conversation.MODE_SINGLE) {
@@ -1068,8 +1109,14 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
}
}
- protected void showSnackbar(final int message, final int action,
- final OnClickListener clickListener) {
+ private boolean showLoadMoreMessages(final Conversation c) {
+ final XmppConnection connection = c.getAccount().getXmppConnection();
+ final boolean mam = connection != null && connection.getFeatures().mam();
+ final MessageArchiveService service = activity.xmppConnectionService.getMessageArchiveService();
+ return mam && (c.getLastClearHistory() != 0 || (c.countMessages() == 0 && c.hasMessagesLeftOnServer() && !service.queryInProgress(c)));
+ }
+
+ protected void showSnackbar(final int message, final int action, final OnClickListener clickListener) {
snackbar.setVisibility(View.VISIBLE);
snackbar.setOnClickListener(null);
snackbarMessage.setText(message);
@@ -1262,6 +1309,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/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
index db3a451b..2b89ea05 100644
--- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
@@ -29,6 +29,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TableLayout;
+import android.widget.TableRow;
import android.widget.TextView;
import android.widget.Toast;
@@ -51,6 +52,7 @@ import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
+import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.XmppConnection.Features;
import eu.siacs.conversations.xmpp.forms.Data;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
@@ -79,6 +81,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
private TextView mServerInfoBlocking;
private TextView mServerInfoPep;
private TextView mServerInfoHttpUpload;
+ private TextView mServerInfoPush;
private TextView mSessionEst;
private TextView mOtrFingerprint;
private TextView mAxolotlFingerprint;
@@ -225,6 +228,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
}
};
private Toast mFetchingMamPrefsToast;
+ private TableRow mPushRow;
public void refreshUiReal() {
invalidateOptionsMenu();
@@ -303,15 +307,14 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
@Override
public void run() {
final Intent intent;
- if (avatar != null) {
- intent = new Intent(getApplicationContext(),
- StartConversationActivity.class);
+ final XmppConnection connection = mAccount.getXmppConnection();
+ if (avatar != null || (connection != null && !connection.getFeatures().pep())) {
+ intent = new Intent(getApplicationContext(), StartConversationActivity.class);
if (xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1) {
intent.putExtra("init", true);
}
} else {
- intent = new Intent(getApplicationContext(),
- PublishProfilePictureActivity.class);
+ intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class);
intent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toBareJid().toString());
intent.putExtra("setup", true);
}
@@ -424,6 +427,8 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
this.mServerInfoSm = (TextView) findViewById(R.id.server_info_sm);
this.mServerInfoPep = (TextView) findViewById(R.id.server_info_pep);
this.mServerInfoHttpUpload = (TextView) findViewById(R.id.server_info_http_upload);
+ this.mPushRow = (TableRow) findViewById(R.id.push_row);
+ this.mServerInfoPush = (TextView) findViewById(R.id.server_info_push);
this.mOtrFingerprint = (TextView) findViewById(R.id.otr_fingerprint);
this.mOtrFingerprintBox = (RelativeLayout) findViewById(R.id.otr_fingerprint_box);
this.mOtrFingerprintToClipboardButton = (ImageButton) findViewById(R.id.action_copy_to_clipboard);
@@ -691,6 +696,14 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
} else {
this.mServerInfoHttpUpload.setText(R.string.server_info_unavailable);
}
+
+ this.mPushRow.setVisibility(xmppConnectionService.getPushManagementService().isStub() ? View.GONE : View.VISIBLE);
+
+ if (xmppConnectionService.getPushManagementService().available(mAccount)) {
+ this.mServerInfoPush.setText(R.string.server_info_available);
+ } else {
+ this.mServerInfoPush.setText(R.string.server_info_unavailable);
+ }
final String otrFingerprint = this.mAccount.getOtrFingerprint();
if (otrFingerprint != null) {
this.mOtrFingerprintBox.setVisibility(View.VISIBLE);
@@ -916,7 +929,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
mFetchingMamPrefsToast.cancel();
}
AlertDialog.Builder builder = new Builder(EditAccountActivity.this);
- builder.setTitle(R.string.mam_prefs);
+ builder.setTitle(R.string.server_side_mam_prefs);
String defaultAttr = prefs.getAttribute("default");
final List<String> defaults = Arrays.asList("never", "roster", "always");
final AtomicInteger choice = new AtomicInteger(Math.max(0,defaults.indexOf(defaultAttr)));
diff --git a/src/main/java/eu/siacs/conversations/ui/EditMessage.java b/src/main/java/eu/siacs/conversations/ui/EditMessage.java
index 06868a98..8cc59cde 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 EmojiconEditText {
this.isUserTyping = false;
this.keyboardListener.onTextDeleted();
}
+ this.keyboardListener.onTextChanged();
}
}
@@ -84,6 +85,7 @@ public class EditMessage extends EmojiconEditText {
void onTypingStarted();
void onTypingStopped();
void onTextDeleted();
+ void onTextChanged();
boolean onTabPressed(boolean repeated);
}
diff --git a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
index 83ab83e7..49deab38 100644
--- a/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/SettingsActivity.java
@@ -165,7 +165,8 @@ public class SettingsActivity extends XmppActivity implements
xmppConnectionService.toggleForegroundService();
} else if (name.equals("confirm_messages")
|| name.equals("xa_on_silent_mode")
- || name.equals("away_when_screen_off")) {
+ || name.equals("away_when_screen_off")
+ || name.equals("allow_message_correction")) {
if (xmppConnectionServiceBound) {
if (name.equals("away_when_screen_off")) {
xmppConnectionService.toggleScreenEventReceiver();
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 78c8f67c..99b7d49d 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
@@ -124,6 +124,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) {
@@ -180,7 +190,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()
@@ -466,6 +476,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
@@ -486,6 +497,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
@@ -691,6 +703,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/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index 6cef8d8c..2653e5f3 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -2,6 +2,8 @@ package eu.siacs.conversations.xmpp;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.os.Bundle;
+import android.os.Parcelable;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.SystemClock;
@@ -20,11 +22,12 @@ import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.net.ConnectException;
+import java.net.IDN;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
-import java.net.URL;
import java.net.UnknownHostException;
+import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
@@ -336,13 +339,7 @@ public class XmppConnection implements Runnable {
this.changeStatus(Account.State.OFFLINE);
this.attempt--; //don't count attempt when reconnecting instantly anyway
} finally {
- if (socket != null) {
- try {
- socket.close();
- } catch (IOException e) {
-
- }
- }
+ forceCloseSocket();
if (wakeLock.isHeld()) {
try {
wakeLock.release();
@@ -414,13 +411,7 @@ public class XmppConnection implements Runnable {
@Override
public void run() {
- try {
- if (socket != null) {
- socket.close();
- }
- } catch (final IOException ignored) {
-
- }
+ forceCloseSocket();
connect();
}
@@ -1074,10 +1065,10 @@ public class XmppConnection implements Runnable {
if (mPendingServiceDiscoveries == 0) {
Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": done with service discovery");
Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": online with resource " + account.getResource());
- changeStatus(Account.State.ONLINE);
if (bindListener != null) {
bindListener.onBind(account);
}
+ changeStatus(Account.State.ONLINE);
}
}
}
@@ -1267,14 +1258,45 @@ public class XmppConnection implements Runnable {
}
}
- public void disconnect(final boolean force) {
- Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting force="+Boolean.valueOf(force));
- if (force) {
+ public void waitForPush() {
+ if (tagWriter.isActive()) {
+ tagWriter.finish();
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ while(!tagWriter.finished()) {
+ Thread.sleep(10);
+ }
+ socket.close();
+ Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": closed tcp without closing stream");
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }).start();
+ } else {
+ forceCloseSocket();
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": closed tcp without closing stream (no waiting)");
+ }
+ }
+
+ private void forceCloseSocket() {
+ if (socket != null) {
try {
socket.close();
- } catch(Exception e) {
+ } catch (IOException e) {
Logging.d(Config.LOGTAG,account.getJid().toBareJid().toString()+": exception during force close ("+e.getMessage()+")");
}
+ }
+ }
+
+ public void disconnect(final boolean force) {
+ Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting force="+Boolean.valueOf(force));
+ if (force) {
+ forceCloseSocket();
return;
} else {
if (tagWriter.isActive()) {
@@ -1484,17 +1506,13 @@ public class XmppConnection implements Runnable {
}
public boolean mam() {
- if (hasDiscoFeature(account.getJid().toBareJid(), "urn:xmpp:mam:0")) {
- return true;
- } else {
- return hasDiscoFeature(account.getServer(), "urn:xmpp:mam:0");
- }
+ return hasDiscoFeature(account.getJid().toBareJid(), "urn:xmpp:mam:0")
+ || hasDiscoFeature(account.getServer(), "urn:xmpp:mam:0");
}
- public boolean advancedStreamFeaturesLoaded() {
- synchronized (XmppConnection.this.disco) {
- return disco.containsKey(account.getServer());
- }
+ public boolean push() {
+ return hasDiscoFeature(account.getJid().toBareJid(), "urn:xmpp:push:0")
+ || hasDiscoFeature(account.getServer(), "urn:xmpp:push:0");
}
public boolean rosterVersioning() {
diff --git a/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java b/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java
index 50a41892..380f0280 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java
@@ -80,8 +80,13 @@ public class Data extends Element {
}
public String getFormType() {
- Field typeFiled = this.getFieldByName("FORM_TYPE");
- return typeFiled == null ? "" : typeFiled.getValue();
+ String type = getValue("FORM_TYPE");
+ return type == null ? "" : type;
+ }
+
+ public String getValue(String name) {
+ Field field = this.getFieldByName(name);
+ return field == null ? null : field.getValue();
}
public String getTitle() {