aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/main/java/eu/siacs/conversations/Config.java4
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/OtrEngine.java1
-rw-r--r--src/main/java/eu/siacs/conversations/entities/AbstractEntity.java1
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Bookmark.java4
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Conversation.java162
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Message.java55
-rw-r--r--src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java10
-rw-r--r--src/main/java/eu/siacs/conversations/generator/IqGenerator.java25
-rw-r--r--src/main/java/eu/siacs/conversations/parser/AbstractParser.java56
-rw-r--r--src/main/java/eu/siacs/conversations/parser/MessageParser.java81
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java7
-rw-r--r--src/main/java/eu/siacs/conversations/services/MessageArchiveService.java317
-rw-r--r--src/main/java/eu/siacs/conversations/services/XmppConnectionService.java277
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationActivity.java6
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ConversationFragment.java29
-rw-r--r--src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java60
-rw-r--r--src/main/java/eu/siacs/conversations/utils/DNSHelper.java29
-rw-r--r--src/main/java/eu/siacs/conversations/utils/PhoneHelper.java5
-rw-r--r--src/main/java/eu/siacs/conversations/xml/Element.java5
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/OnAdvancedStreamFeaturesLoaded.java7
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java16
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/forms/Data.java10
-rw-r--r--src/main/res/values-it/strings.xml2
23 files changed, 903 insertions, 266 deletions
diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java
index d777e5cc..c491d632 100644
--- a/src/main/java/eu/siacs/conversations/Config.java
+++ b/src/main/java/eu/siacs/conversations/Config.java
@@ -22,6 +22,10 @@ public final class Config {
public static final boolean NO_PROXY_LOOKUP = false; //useful to debug ibb
+ private static final long MILLISECONDS_IN_DAY = 24 * 60 * 60 * 1000;
+ public static final long MAX_HISTORY_AGE = 7 * MILLISECONDS_IN_DAY;
+ public static final long MAX_CATCHUP = MILLISECONDS_IN_DAY / 2;
+
private Config() {
}
diff --git a/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java b/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java
index 642d0ed0..3894e205 100644
--- a/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java
+++ b/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java
@@ -180,6 +180,7 @@ public class OtrEngine implements OtrEngineHost {
packet.setBody(body);
packet.addChild("private", "urn:xmpp:carbons:2");
packet.addChild("no-copy", "urn:xmpp:hints");
+ packet.addChild("no-store", "urn:xmpp:hints");
packet.setType(MessagePacket.TYPE_CHAT);
account.getXmppConnection().sendMessagePacket(packet);
}
diff --git a/src/main/java/eu/siacs/conversations/entities/AbstractEntity.java b/src/main/java/eu/siacs/conversations/entities/AbstractEntity.java
index 92b8a729..957b0a14 100644
--- a/src/main/java/eu/siacs/conversations/entities/AbstractEntity.java
+++ b/src/main/java/eu/siacs/conversations/entities/AbstractEntity.java
@@ -17,5 +17,4 @@ public abstract class AbstractEntity {
public boolean equals(AbstractEntity entity) {
return this.getUuid().equals(entity.getUuid());
}
-
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Bookmark.java b/src/main/java/eu/siacs/conversations/entities/Bookmark.java
index 559e2f2d..70d852fe 100644
--- a/src/main/java/eu/siacs/conversations/entities/Bookmark.java
+++ b/src/main/java/eu/siacs/conversations/entities/Bookmark.java
@@ -102,9 +102,7 @@ public class Bookmark extends Element implements ListItem {
}
public boolean autojoin() {
- String autojoin = this.getAttribute("autojoin");
- return (autojoin != null && (autojoin.equalsIgnoreCase("true") || autojoin
- .equalsIgnoreCase("1")));
+ return this.getAttributeAsBoolean("autojoin");
}
public String getPassword() {
diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java
index a7da0bc2..ac1343a8 100644
--- a/src/main/java/eu/siacs/conversations/entities/Conversation.java
+++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java
@@ -3,6 +3,7 @@ package eu.siacs.conversations.entities;
import android.content.ContentValues;
import android.database.Cursor;
import android.os.SystemClock;
+import android.util.Log;
import net.java.otr4j.OtrException;
import net.java.otr4j.crypto.OtrCryptoEngineImpl;
@@ -16,8 +17,11 @@ import org.json.JSONObject;
import java.security.interfaces.DSAPublicKey;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.List;
+import eu.siacs.conversations.Config;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
@@ -43,6 +47,7 @@ public class Conversation extends AbstractEntity {
public static final String ATTRIBUTE_NEXT_ENCRYPTION = "next_encryption";
public static final String ATTRIBUTE_MUC_PASSWORD = "muc_password";
public static final String ATTRIBUTE_MUTED_TILL = "muted_till";
+ public static final String ATTRIBUTE_LAST_MESSAGE_TRANSMITTED = "last_message_transmitted";
private String name;
private String contactUuid;
@@ -72,6 +77,104 @@ public class Conversation extends AbstractEntity {
private Bookmark bookmark;
+ public Message findUnsentMessageWithUuid(String uuid) {
+ synchronized(this.messages) {
+ for (final Message message : this.messages) {
+ final int s = message.getStatus();
+ if ((s == Message.STATUS_UNSEND || s == Message.STATUS_WAITING) && message.getUuid().equals(uuid)) {
+ return message;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void findWaitingMessages(OnMessageFound onMessageFound) {
+ synchronized (this.messages) {
+ for(Message message : this.messages) {
+ if (message.getStatus() == Message.STATUS_WAITING) {
+ onMessageFound.onMessageFound(message);
+ }
+ }
+ }
+ }
+
+ public void findMessagesWithFiles(OnMessageFound onMessageFound) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if ((message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE)
+ && message.getEncryption() != Message.ENCRYPTION_PGP) {
+ onMessageFound.onMessageFound(message);
+ }
+ }
+ }
+ }
+
+ public Message findMessageWithFileAndUuid(String uuid) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if (message.getType() == Message.TYPE_IMAGE
+ && message.getEncryption() != Message.ENCRYPTION_PGP
+ && message.getUuid().equals(uuid)) {
+ return message;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void clearMessages() {
+ synchronized (this.messages) {
+ this.messages.clear();
+ }
+ }
+
+ public void findUnsentMessagesWithOtrEncryption(OnMessageFound onMessageFound) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if ((message.getStatus() == Message.STATUS_UNSEND || message.getStatus() == Message.STATUS_WAITING)
+ && (message.getEncryption() == Message.ENCRYPTION_OTR)) {
+ onMessageFound.onMessageFound(message);
+ }
+ }
+ }
+ }
+
+ public void findUnsentTextMessages(OnMessageFound onMessageFound) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if (message.getType() != Message.TYPE_IMAGE
+ && message.getStatus() == Message.STATUS_UNSEND) {
+ onMessageFound.onMessageFound(message);
+ }
+ }
+ }
+ }
+
+ public Message findSentMessageWithUuid(String uuid) {
+ synchronized (this.messages) {
+ for (Message message : this.messages) {
+ if (uuid.equals(message.getUuid())
+ || (message.getStatus() >= Message.STATUS_SEND && uuid
+ .equals(message.getRemoteMsgId()))) {
+ return message;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void populateWithMessages(List<Message> messages) {
+ synchronized (this.messages) {
+ messages.clear();
+ messages.addAll(this.messages);
+ }
+ }
+
+ public interface OnMessageFound {
+ public void onMessageFound(final Message message);
+ }
+
public Conversation(final String name, final Account account, final Jid contactJid,
final int mode) {
this(java.util.UUID.randomUUID().toString(), name, null, account
@@ -98,10 +201,6 @@ public class Conversation extends AbstractEntity {
}
}
- public List<Message> getMessages() {
- return messages;
- }
-
public boolean isRead() {
return (this.messages == null) || (this.messages.size() == 0) || this.messages.get(this.messages.size() - 1).isRead();
}
@@ -450,9 +549,11 @@ public class Conversation extends AbstractEntity {
}
public boolean hasDuplicateMessage(Message message) {
- for (int i = this.getMessages().size() - 1; i >= 0; --i) {
- if (this.messages.get(i).equals(message)) {
- return true;
+ synchronized (this.messages) {
+ for (int i = this.messages.size() - 1; i >= 0; --i) {
+ if (this.messages.get(i).equals(message)) {
+ return true;
+ }
}
}
return false;
@@ -460,7 +561,7 @@ public class Conversation extends AbstractEntity {
public Message findSentMessageWithBody(String body) {
synchronized (this.messages) {
- for (int i = this.getMessages().size() - 1; i >= 0; --i) {
+ for (int i = this.messages.size() - 1; i >= 0; --i) {
Message message = this.messages.get(i);
if ((message.getStatus() == Message.STATUS_UNSEND || message.getStatus() == Message.STATUS_SEND) && message.getBody() != null && message.getBody().equals(body)) {
return message;
@@ -470,6 +571,31 @@ public class Conversation extends AbstractEntity {
}
}
+ public boolean setLastMessageTransmitted(long value) {
+ long before = getLastMessageTransmitted();
+ if (value - before > 1000) {
+ this.setAttribute(ATTRIBUTE_LAST_MESSAGE_TRANSMITTED, String.valueOf(value));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public long getLastMessageTransmitted() {
+ long timestamp = getLongAttribute(ATTRIBUTE_LAST_MESSAGE_TRANSMITTED,0);
+ if (timestamp == 0) {
+ synchronized (this.messages) {
+ for(int i = this.messages.size() - 1; i >= 0; --i) {
+ Message message = this.messages.get(i);
+ if (message.getStatus() == Message.STATUS_RECEIVED) {
+ return message.getTimeSent();
+ }
+ }
+ }
+ }
+ return timestamp;
+ }
+
public void setMutedTill(long value) {
this.setAttribute(ATTRIBUTE_MUTED_TILL, String.valueOf(value));
}
@@ -535,6 +661,26 @@ public class Conversation extends AbstractEntity {
}
}
+ public void sort() {
+ synchronized (this.messages) {
+ Collections.sort(this.messages, new Comparator<Message>() {
+ @Override
+ public int compare(Message left, Message right) {
+ if (left.getTimeSent() < right.getTimeSent()) {
+ return -1;
+ } else if (left.getTimeSent() > right.getTimeSent()) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ });
+ for(Message message : this.messages) {
+ message.untie();
+ }
+ }
+ }
+
public class Smp {
public static final int STATUS_NONE = 0;
public static final int STATUS_CONTACT_REQUESTED = 1;
diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java
index 47861d06..5b937138 100644
--- a/src/main/java/eu/siacs/conversations/entities/Message.java
+++ b/src/main/java/eu/siacs/conversations/entities/Message.java
@@ -45,6 +45,7 @@ public class Message extends AbstractEntity {
public static String STATUS = "status";
public static String TYPE = "type";
public static String REMOTE_MSG_ID = "remoteMsgId";
+ public static String SERVER_MSG_ID = "serverMsgId";
public static String RELATIVE_FILE_PATH = "relativeFilePath";
public boolean markable = false;
protected String conversationUuid;
@@ -59,6 +60,7 @@ public class Message extends AbstractEntity {
protected String relativeFilePath;
protected boolean read = true;
protected String remoteMsgId = null;
+ protected String serverMsgId = null;
protected Conversation conversation = null;
protected Downloadable downloadable = null;
private Message mNextMessage = null;
@@ -83,13 +85,15 @@ public class Message extends AbstractEntity {
status,
TYPE_TEXT,
null,
+ null,
null);
this.conversation = conversation;
}
private Message(final String uuid, final String conversationUUid, final Jid counterpart,
final Jid trueCounterpart, final String body, final long timeSent,
- final int encryption, final int status, final int type, final String remoteMsgId, final String relativeFilePath) {
+ final int encryption, final int status, final int type, final String remoteMsgId,
+ final String relativeFilePath, final String serverMsgId) {
this.uuid = uuid;
this.conversationUuid = conversationUUid;
this.counterpart = counterpart;
@@ -101,6 +105,7 @@ public class Message extends AbstractEntity {
this.type = type;
this.remoteMsgId = remoteMsgId;
this.relativeFilePath = relativeFilePath;
+ this.serverMsgId = serverMsgId;
}
public static Message fromCursor(Cursor cursor) {
@@ -136,7 +141,8 @@ public class Message extends AbstractEntity {
cursor.getInt(cursor.getColumnIndex(STATUS)),
cursor.getInt(cursor.getColumnIndex(TYPE)),
cursor.getString(cursor.getColumnIndex(REMOTE_MSG_ID)),
- cursor.getString(cursor.getColumnIndex(RELATIVE_FILE_PATH)));
+ cursor.getString(cursor.getColumnIndex(RELATIVE_FILE_PATH)),
+ cursor.getString(cursor.getColumnIndex(SERVER_MSG_ID)));
}
public static Message createStatusMessage(Conversation conversation) {
@@ -168,6 +174,7 @@ public class Message extends AbstractEntity {
values.put(TYPE, type);
values.put(REMOTE_MSG_ID, remoteMsgId);
values.put(RELATIVE_FILE_PATH, relativeFilePath);
+ values.put(SERVER_MSG_ID,serverMsgId);
return values;
}
@@ -248,6 +255,14 @@ public class Message extends AbstractEntity {
this.remoteMsgId = id;
}
+ public String getServerMsgId() {
+ return this.serverMsgId;
+ }
+
+ public void setServerMsgId(String id) {
+ this.serverMsgId = id;
+ }
+
public boolean isRead() {
return this.read;
}
@@ -293,38 +308,43 @@ public class Message extends AbstractEntity {
}
public boolean equals(Message message) {
- return (this.remoteMsgId != null) && (this.body != null) && (this.counterpart != null) && this.remoteMsgId.equals(message.getRemoteMsgId()) && this.body.equals(message.getBody()) && this.counterpart.equals(message.getCounterpart());
+ if (this.serverMsgId != null && message.getServerMsgId() != null) {
+ return this.serverMsgId.equals(message.getServerMsgId());
+ } else {
+ return this.body != null
+ && this.counterpart != null
+ && ((this.remoteMsgId != null && this.remoteMsgId.equals(message.getRemoteMsgId()))
+ || this.uuid.equals(message.getRemoteMsgId())) && this.body.equals(message.getBody())
+ && this.counterpart.equals(message.getCounterpart());
+ }
}
public Message next() {
- if (this.mNextMessage == null) {
- synchronized (this.conversation.messages) {
+ synchronized (this.conversation.messages) {
+ if (this.mNextMessage == null) {
int index = this.conversation.messages.indexOf(this);
- if (index < 0
- || index >= this.conversation.getMessages().size() - 1) {
+ if (index < 0 || index >= this.conversation.messages.size() - 1) {
this.mNextMessage = null;
} else {
- this.mNextMessage = this.conversation.messages
- .get(index + 1);
+ this.mNextMessage = this.conversation.messages.get(index + 1);
}
}
+ return this.mNextMessage;
}
- return this.mNextMessage;
}
public Message prev() {
- if (this.mPreviousMessage == null) {
- synchronized (this.conversation.messages) {
+ synchronized (this.conversation.messages) {
+ if (this.mPreviousMessage == null) {
int index = this.conversation.messages.indexOf(this);
if (index <= 0 || index > this.conversation.messages.size()) {
this.mPreviousMessage = null;
} else {
- this.mPreviousMessage = this.conversation.messages
- .get(index - 1);
+ this.mPreviousMessage = this.conversation.messages.get(index - 1);
}
}
+ return this.mPreviousMessage;
}
- return this.mPreviousMessage;
}
public boolean mergeable(final Message message) {
@@ -493,6 +513,11 @@ public class Message extends AbstractEntity {
}
}
+ public void untie() {
+ this.mNextMessage = null;
+ this.mPreviousMessage = null;
+ }
+
public class ImageParams {
public URL url;
public long size = 0;
diff --git a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
index f46e7ba4..b200b2a1 100644
--- a/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/AbstractGenerator.java
@@ -4,9 +4,12 @@ import android.util.Base64;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
import eu.siacs.conversations.services.XmppConnectionService;
@@ -23,6 +26,8 @@ public abstract class AbstractGenerator {
public final String IDENTITY_NAME = "Conversations 0.9.3";
public final String IDENTITY_TYPE = "phone";
+ private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
+
protected XmppConnectionService mXmppConnectionService;
protected AbstractGenerator(XmppConnectionService service) {
@@ -46,4 +51,9 @@ public abstract class AbstractGenerator {
byte[] sha1 = md.digest(s.toString().getBytes());
return new String(Base64.encode(sha1, Base64.DEFAULT)).trim();
}
+
+ public static String getTimestamp(long time) {
+ DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
+ return DATE_FORMAT.format(time);
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
index 5d674748..4819f96e 100644
--- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
+++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java
@@ -1,11 +1,16 @@
package eu.siacs.conversations.generator;
+import android.util.Log;
+
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.services.MessageArchiveService;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.forms.Data;
import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
@@ -94,4 +99,24 @@ public class IqGenerator extends AbstractGenerator {
}
return packet;
}
+
+ public IqPacket queryMessageArchiveManagement(MessageArchiveService.Query mam) {
+ final IqPacket packet = new IqPacket(IqPacket.TYPE_SET);
+ Element query = packet.query("urn:xmpp:mam:0");
+ query.setAttribute("queryid",mam.getQueryId());
+ Data data = new Data();
+ data.setFormType("urn:xmpp:mam:0");
+ if (mam.getWith()!=null) {
+ data.put("with", mam.getWith().toString());
+ }
+ data.put("start",getTimestamp(mam.getStart()));
+ data.put("end",getTimestamp(mam.getEnd()));
+ query.addChild(data);
+ if (mam.getPagingOrder() == MessageArchiveService.PagingOrder.REVERSE) {
+ query.addChild("set", "http://jabber.org/protocol/rsm").addChild("before").setContent(mam.getReference());
+ } else if (mam.getReference() != null) {
+ query.addChild("set", "http://jabber.org/protocol/rsm").addChild("after").setContent(mam.getReference());
+ }
+ return packet;
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
index eedfca16..c80346b7 100644
--- a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
@@ -24,50 +24,40 @@ public abstract class AbstractParser {
protected long getTimestamp(Element packet) {
long now = System.currentTimeMillis();
- ArrayList<String> stamps = new ArrayList<>();
- for (Element child : packet.getChildren()) {
- if (child.getName().equals("delay")) {
- stamps.add(child.getAttribute("stamp").replace("Z", "+0000"));
- }
+ Element delay = packet.findChild("delay");
+ if (delay == null) {
+ return now;
}
- Collections.sort(stamps);
- if (stamps.size() >= 1) {
- try {
- String stamp = stamps.get(stamps.size() - 1);
- if (stamp.contains(".")) {
- Date date = new SimpleDateFormat(
- "yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US)
- .parse(stamp);
- if (now < date.getTime()) {
- return now;
- } else {
- return date.getTime();
- }
- } else {
- Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ",
- Locale.US).parse(stamp);
- if (now < date.getTime()) {
- return now;
- } else {
- return date.getTime();
- }
- }
- } catch (ParseException e) {
- return now;
- }
- } else {
+ String stamp = delay.getAttribute("stamp");
+ if (stamp == null) {
+ return now;
+ }
+ try {
+ long time = parseTimestamp(stamp).getTime();
+ return now < time ? now : time;
+ } catch (ParseException e) {
return now;
}
}
+ public static Date parseTimestamp(String timestamp) throws ParseException {
+ timestamp = timestamp.replace("Z", "+0000");
+ SimpleDateFormat dateFormat;
+ if (timestamp.contains(".")) {
+ dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.US);
+ } else {
+ dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ",Locale.US);
+ }
+ return dateFormat.parse(timestamp);
+ }
+
protected void updateLastseen(final Element packet, final Account account,
final boolean presenceOverwrite) {
Jid from;
try {
from = Jid.fromString(packet.getAttribute("from")).toBareJid();
} catch (final InvalidJidException e) {
- // TODO: Handle this?
- from = null;
+ return;
}
String presence = from == null || from.isBareJid() ? "" : from.getResourcepart();
Contact contact = account.getRoster().getContact(from);
diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
index 3ae82e48..cd4c6401 100644
--- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
@@ -10,11 +10,11 @@ import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message;
+import eu.siacs.conversations.services.MessageArchiveService;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnMessagePacketReceived;
-import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
@@ -273,6 +273,66 @@ public class MessageParser extends AbstractParser implements
return finishedMessage;
}
+ private Message parseMamMessage(MessagePacket packet, final Account account) {
+ final Element result = packet.findChild("result","urn:xmpp:mam:0");
+ if (result == null ) {
+ return null;
+ }
+ final Element forwarded = result.findChild("forwarded","urn:xmpp:forward:0");
+ if (forwarded == null) {
+ return null;
+ }
+ final Element message = forwarded.findChild("message");
+ if (message == null) {
+ return null;
+ }
+ final Element body = message.findChild("body");
+ if (body == null || message.hasChild("private","urn:xmpp:carbons:2") || message.hasChild("no-copy","urn:xmpp:hints")) {
+ return null;
+ }
+ int encryption;
+ String content = getPgpBody(message);
+ if (content != null) {
+ encryption = Message.ENCRYPTION_PGP;
+ } else {
+ encryption = Message.ENCRYPTION_NONE;
+ content = body.getContent();
+ }
+ if (content == null) {
+ return null;
+ }
+ final long timestamp = getTimestamp(forwarded);
+ final Jid to = message.getAttributeAsJid("to");
+ final Jid from = message.getAttributeAsJid("from");
+ final MessageArchiveService.Query query = this.mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid"));
+ Jid counterpart;
+ int status;
+ Conversation conversation;
+ if (from!=null && to != null && from.toBareJid().equals(account.getJid().toBareJid())) {
+ status = Message.STATUS_SEND;
+ conversation = this.mXmppConnectionService.findOrCreateConversation(account,to.toBareJid(),false,query);
+ counterpart = to;
+ } else if (from !=null && to != null) {
+ status = Message.STATUS_RECEIVED;
+ conversation = this.mXmppConnectionService.findOrCreateConversation(account,from.toBareJid(),false,query);
+ counterpart = from;
+ } else {
+ return null;
+ }
+ Message finishedMessage = new Message(conversation,content,encryption,status);
+ finishedMessage.setTime(timestamp);
+ finishedMessage.setCounterpart(counterpart);
+ finishedMessage.setRemoteMsgId(message.getAttribute("id"));
+ finishedMessage.setServerMsgId(result.getAttribute("id"));
+ if (conversation.hasDuplicateMessage(finishedMessage)) {
+ Log.d(Config.LOGTAG, "received mam message " + content+ " (duplicate)");
+ return null;
+ } else {
+ Log.d(Config.LOGTAG, "received mam message " + content);
+ }
+ return finishedMessage;
+ }
+
private void parseError(final MessagePacket packet, final Account account) {
final Jid from = packet.getFrom();
mXmppConnectionService.markMessage(account, from.toBareJid(),
@@ -446,6 +506,17 @@ public class MessageParser extends AbstractParser implements
message.markUnread();
}
}
+ } else if (packet.hasChild("result","urn:xmpp:mam:0")) {
+ message = parseMamMessage(packet, account);
+ if (message != null) {
+ Conversation conversation = message.getConversation();
+ conversation.add(message);
+ mXmppConnectionService.databaseBackend.createMessage(message);
+ }
+ return;
+ } else if (packet.hasChild("fin","urn:xmpp:mam:0")) {
+ Element fin = packet.findChild("fin","urn:xmpp:mam:0");
+ mXmppConnectionService.getMessageArchiveService().processFin(fin);
} else {
parseNonMessage(packet, account);
}
@@ -487,12 +558,16 @@ public class MessageParser extends AbstractParser implements
}
Conversation conversation = message.getConversation();
conversation.add(message);
+ if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().advancedStreamFeaturesLoaded()) {
+ if (conversation.setLastMessageTransmitted(System.currentTimeMillis())) {
+ mXmppConnectionService.updateConversation(conversation);
+ }
+ }
if (message.getStatus() == Message.STATUS_RECEIVED
&& conversation.getOtrSession() != null
&& !conversation.getOtrSession().getSessionID().getUserID()
.equals(message.getCounterpart().getResourcepart())) {
- Log.d(Config.LOGTAG, "ending because of reasons");
conversation.endOtrIfNeeded();
}
@@ -505,7 +580,7 @@ public class MessageParser extends AbstractParser implements
if (message.trusted() && message.bodyContainsDownloadable()) {
this.mXmppConnectionService.getHttpConnectionManager()
.createNewConnection(message);
- } else {
+ } else if (!message.isRead()) {
mXmppConnectionService.getNotificationService().push(message);
}
mXmppConnectionService.updateConversationUi();
diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
index 55fcff2e..aa07d9c0 100644
--- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
@@ -22,7 +22,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
private static DatabaseBackend instance = null;
private static final String DATABASE_NAME = "history";
- private static final int DATABASE_VERSION = 11;
+ private static final int DATABASE_VERSION = 12;
private static String CREATE_CONTATCS_STATEMENT = "create table "
+ Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
@@ -65,6 +65,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ Message.BODY + " TEXT, " + Message.ENCRYPTION + " NUMBER, "
+ Message.STATUS + " NUMBER," + Message.TYPE + " NUMBER, "
+ Message.RELATIVE_FILE_PATH + " TEXT, "
+ + Message.SERVER_MSG_ID + " TEXT, "
+ Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY("
+ Message.CONVERSATION + ") REFERENCES "
+ Conversation.TABLENAME + "(" + Conversation.UUID
@@ -121,6 +122,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
db.execSQL("delete from "+Contact.TABLENAME);
db.execSQL("update "+Account.TABLENAME+" set "+Account.ROSTERVERSION+" = NULL");
}
+ if (oldVersion < 12 && newVersion >= 12) {
+ db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
+ + Message.SERVER_MSG_ID + " TEXT");
+ }
}
public static synchronized DatabaseBackend getInstance(Context context) {
diff --git a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java
new file mode 100644
index 00000000..fe1871ea
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java
@@ -0,0 +1,317 @@
+package eu.siacs.conversations.services;
+
+import android.util.Log;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+
+import eu.siacs.conversations.Config;
+import eu.siacs.conversations.entities.Account;
+import eu.siacs.conversations.entities.Conversation;
+import eu.siacs.conversations.generator.AbstractGenerator;
+import eu.siacs.conversations.parser.AbstractParser;
+import eu.siacs.conversations.xml.Element;
+import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded;
+import eu.siacs.conversations.xmpp.OnIqPacketReceived;
+import eu.siacs.conversations.xmpp.jid.Jid;
+import eu.siacs.conversations.xmpp.stanzas.IqPacket;
+
+public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
+
+ private final XmppConnectionService mXmppConnectionService;
+
+ private final HashSet<Query> queries = new HashSet<Query>();
+ private ArrayList<Query> pendingQueries = new ArrayList<Query>();
+
+ public enum PagingOrder {
+ NORMAL,
+ REVERSE
+ };
+
+ public MessageArchiveService(final XmppConnectionService service) {
+ this.mXmppConnectionService = service;
+ }
+
+ public void catchup(final Account account) {
+ long startCatchup = getLastMessageTransmitted(account);
+ long endCatchup = account.getXmppConnection().getLastSessionEstablished();
+ if (startCatchup == 0) {
+ return;
+ } else if (endCatchup - startCatchup >= Config.MAX_CATCHUP) {
+ startCatchup = endCatchup - Config.MAX_CATCHUP;
+ List<Conversation> conversations = mXmppConnectionService.getConversations();
+ for (Conversation conversation : conversations) {
+ if (conversation.getMode() == Conversation.MODE_SINGLE && conversation.getAccount() == account && startCatchup > conversation.getLastMessageTransmitted()) {
+ this.query(conversation,startCatchup);
+ }
+ }
+ }
+ final Query query = new Query(account, startCatchup, endCatchup);
+ this.queries.add(query);
+ this.execute(query);
+ }
+
+ private long getLastMessageTransmitted(final Account account) {
+ long timestamp = 0;
+ for(final Conversation conversation : mXmppConnectionService.getConversations()) {
+ if (conversation.getAccount() == account) {
+ long tmp = conversation.getLastMessageTransmitted();
+ if (tmp > timestamp) {
+ timestamp = tmp;
+ }
+ }
+ }
+ return timestamp;
+ }
+
+ public void query(final Conversation conversation) {
+ query(conversation,conversation.getAccount().getXmppConnection().getLastSessionEstablished());
+ }
+
+ public void query(final Conversation conversation, long end) {
+ synchronized (this.queries) {
+ final Account account = conversation.getAccount();
+ long start = conversation.getLastMessageTransmitted();
+ if (start > end) {
+ return;
+ } else if (end - start >= Config.MAX_HISTORY_AGE) {
+ start = end - Config.MAX_HISTORY_AGE;
+ }
+ final Query query = new Query(conversation, start, end,PagingOrder.REVERSE);
+ this.queries.add(query);
+ this.execute(query);
+ }
+ }
+
+ public void executePendingQueries(final Account account) {
+ List<Query> pending = new ArrayList<>();
+ synchronized(this.pendingQueries) {
+ for(Iterator<Query> iterator = this.pendingQueries.iterator(); iterator.hasNext();) {
+ Query query = iterator.next();
+ if (query.getAccount() == account) {
+ pending.add(query);
+ iterator.remove();
+ }
+ }
+ }
+ for(Query query : pending) {
+ this.execute(query);
+ }
+ }
+
+ private void execute(final Query query) {
+ final Account account= query.getAccount();
+ if (account.getStatus() == Account.State.ONLINE) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": running mam query " + query.toString());
+ IqPacket packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(query);
+ this.mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE_ERROR) {
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": error executing mam: " + packet.toString());
+ finalizeQuery(query);
+ }
+ }
+ });
+ } else {
+ synchronized (this.pendingQueries) {
+ this.pendingQueries.add(query);
+ }
+ }
+ }
+
+ private void finalizeQuery(Query query) {
+ synchronized (this.queries) {
+ this.queries.remove(query);
+ }
+ final Conversation conversation = query.getConversation();
+ if (conversation != null) {
+ conversation.sort();
+ if (conversation.setLastMessageTransmitted(query.getEnd())) {
+ this.mXmppConnectionService.databaseBackend.updateConversation(conversation);
+ }
+ this.mXmppConnectionService.updateConversationUi();
+ } else {
+ for(Conversation tmp : this.mXmppConnectionService.getConversations()) {
+ if (tmp.getAccount() == query.getAccount()) {
+ tmp.sort();
+ if (tmp.setLastMessageTransmitted(query.getEnd())) {
+ this.mXmppConnectionService.databaseBackend.updateConversation(tmp);
+ }
+ }
+ }
+ }
+ }
+
+ public boolean queryInProgress(Conversation conversation) {
+ synchronized (this.queries) {
+ for(Query query : queries) {
+ if (query.conversation == conversation) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ public void processFin(Element fin) {
+ if (fin == null) {
+ return;
+ }
+ Query query = findQuery(fin.getAttribute("queryid"));
+ if (query == null) {
+ return;
+ }
+ boolean complete = fin.getAttributeAsBoolean("complete");
+ Element set = fin.findChild("set","http://jabber.org/protocol/rsm");
+ Element last = set == null ? null : set.findChild("last");
+ Element first = set == null ? null : set.findChild("first");
+ Element relevant = query.getPagingOrder() == PagingOrder.NORMAL ? last : first;
+ if (complete || relevant == null) {
+ this.finalizeQuery(query);
+ } else {
+ final Query nextQuery;
+ if (query.getPagingOrder() == PagingOrder.NORMAL) {
+ nextQuery = query.next(last == null ? null : last.getContent());
+ } else {
+ nextQuery = query.prev(first == null ? null : first.getContent());
+ }
+ this.execute(nextQuery);
+ this.finalizeQuery(query);
+ synchronized (this.queries) {
+ this.queries.remove(query);
+ this.queries.add(nextQuery);
+ }
+ }
+ }
+
+ public Query findQuery(String id) {
+ if (id == null) {
+ return null;
+ }
+ synchronized (this.queries) {
+ for(Query query : this.queries) {
+ if (query.getQueryId().equals(id)) {
+ return query;
+ }
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public void onAdvancedStreamFeaturesAvailable(Account account) {
+ if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().mam()) {
+ this.catchup(account);
+ }
+ }
+
+ public class Query {
+ private long start;
+ private long end;
+ private Jid with = null;
+ private String queryId;
+ private String reference = null;
+ private Account account;
+ private Conversation conversation;
+ private PagingOrder pagingOrder = PagingOrder.NORMAL;
+
+
+ public Query(Conversation conversation, long start, long end) {
+ this(conversation.getAccount(), start, end);
+ this.conversation = conversation;
+ this.with = conversation.getContactJid().toBareJid();
+ }
+
+ public Query(Conversation conversation, long start, long end, PagingOrder order) {
+ this(conversation,start,end);
+ this.pagingOrder = order;
+ }
+
+ public Query(Account account, long start, long end) {
+ this.account = account;
+ this.start = start;
+ this.end = end;
+ this.queryId = new BigInteger(50, mXmppConnectionService.getRNG()).toString(32);
+ }
+
+ private Query page(String reference) {
+ Query query = new Query(this.account,this.start,this.end);
+ query.reference = reference;
+ query.conversation = conversation;
+ query.with = with;
+ return query;
+ }
+
+ public Query next(String reference) {
+ Query query = page(reference);
+ query.pagingOrder = PagingOrder.NORMAL;
+ return query;
+ }
+
+ public Query prev(String reference) {
+ Query query = page(reference);
+ query.pagingOrder = PagingOrder.REVERSE;
+ return query;
+ }
+
+ public String getReference() {
+ return reference;
+ }
+
+ public PagingOrder getPagingOrder() {
+ return this.pagingOrder;
+ }
+
+ public String getQueryId() {
+ return queryId;
+ }
+
+ public Jid getWith() {
+ return with;
+ }
+
+ public long getStart() {
+ return start;
+ }
+
+ public long getEnd() {
+ return end;
+ }
+
+ public Conversation getConversation() {
+ return conversation;
+ }
+
+ public Account getAccount() {
+ return this.account;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("with=");
+ if (this.with==null) {
+ builder.append("*");
+ } else {
+ builder.append(with.toString());
+ }
+ builder.append(", start=");
+ builder.append(AbstractGenerator.getTimestamp(this.start));
+ builder.append(", end=");
+ builder.append(AbstractGenerator.getTimestamp(this.end));
+ if (this.reference!=null) {
+ if (this.pagingOrder == PagingOrder.NORMAL) {
+ builder.append(", after=");
+ } else {
+ builder.append(", before=");
+ }
+ builder.append(this.reference);
+ }
+ return builder.toString();
+ }
+ }
+}
diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
index 2f44375e..b7ca699c 100644
--- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
+++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java
@@ -32,20 +32,14 @@ import net.java.otr4j.session.SessionStatus;
import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpServiceConnection;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.OutputStream;
import java.math.BigInteger;
import java.security.SecureRandom;
-import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
-import java.util.Date;
import java.util.Hashtable;
import java.util.List;
import java.util.Locale;
-import java.util.TimeZone;
import java.util.concurrent.CopyOnWriteArrayList;
import de.duenndns.ssl.MemorizingTrustManager;
@@ -97,7 +91,7 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
-public class XmppConnectionService extends Service {
+public class XmppConnectionService extends Service implements OnPhoneContactsLoadedListener {
public static String ACTION_CLEAR_NOTIFICATION = "clear_notification";
private static String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
@@ -126,7 +120,7 @@ public class XmppConnectionService extends Service {
conversation.resetOtrSession();
}
if (online && (contact.getPresences().size() == 1)) {
- sendUnsendMessages(conversation);
+ sendUnsentMessages(conversation);
}
}
}
@@ -147,6 +141,7 @@ public class XmppConnectionService extends Service {
private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager(
this);
private AvatarService mAvatarService = new AvatarService(this);
+ private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this);
private OnConversationUpdate mOnConversationUpdate = null;
private Integer convChangedListenerCount = 0;
private OnAccountUpdate mOnAccountUpdate = null;
@@ -165,12 +160,13 @@ public class XmppConnectionService extends Service {
for (Conversation conversation : account.pendingConferenceJoins) {
joinMuc(conversation);
}
+ mMessageArchiveService.executePendingQueries(account);
mJingleConnectionManager.cancelInTransmission();
List<Conversation> conversations = getConversations();
for (Conversation conversation : conversations) {
if (conversation.getAccount() == account) {
conversation.startOtrIfNeeded();
- sendUnsendMessages(conversation);
+ sendUnsentMessages(conversation);
}
}
if (connection != null && connection.getFeatures().csi()) {
@@ -209,6 +205,7 @@ public class XmppConnectionService extends Service {
getNotificationService().updateErrorNotification();
}
};
+
private int accountChangedListenerCount = 0;
private OnRosterUpdate mOnRosterUpdate = null;
private int rosterChangedListenerCount = 0;
@@ -260,15 +257,14 @@ public class XmppConnectionService extends Service {
@Override
public void onMessageAcknowledged(Account account, String uuid) {
- for (Conversation conversation : getConversations()) {
+ for (final Conversation conversation : getConversations()) {
if (conversation.getAccount() == account) {
- for (Message message : conversation.getMessages()) {
- if ((message.getStatus() == Message.STATUS_UNSEND || message
- .getStatus() == Message.STATUS_WAITING)
- && message.getUuid().equals(uuid)) {
- markMessage(message, Message.STATUS_SEND);
- return;
- }
+ Message message = conversation.findUnsentMessageWithUuid(uuid);
+ if (message != null) {
+ markMessage(message, Message.STATUS_SEND);
+ if (conversation.setLastMessageTransmitted(System.currentTimeMillis())) {
+ databaseBackend.updateConversation(conversation);
+ }
}
}
}
@@ -276,6 +272,7 @@ public class XmppConnectionService extends Service {
};
private LruCache<String, Bitmap> mBitmapCache;
private IqGenerator mIqGenerator = new IqGenerator(this);
+ private Thread mPhoneContactMergerThread;
public PgpEngine getPgpEngine() {
if (pgpServiceConnection.isBound()) {
@@ -384,7 +381,7 @@ public class XmppConnectionService extends Service {
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null && intent.getAction() != null) {
if (intent.getAction().equals(ACTION_MERGE_PHONE_CONTACTS)) {
- mergePhoneContactsWithRoster();
+ PhoneHelper.loadPhoneContacts(getApplicationContext(), new ArrayList<Bundle>(), this);
return START_STICKY;
} else if (intent.getAction().equals(Intent.ACTION_SHUTDOWN)) {
logoutAndSave();
@@ -496,7 +493,7 @@ public class XmppConnectionService extends Service {
this.databaseBackend.readRoster(account.getRoster());
}
initConversations();
- this.mergePhoneContactsWithRoster();
+ PhoneHelper.loadPhoneContacts(getApplicationContext(),new ArrayList<Bundle>(), this);
getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, contactObserver);
this.fileObserver.startWatching();
@@ -589,8 +586,8 @@ public class XmppConnectionService extends Service {
connection.setOnUnregisteredIqPacketReceivedListener(this.mIqParser);
connection.setOnJinglePacketReceivedListener(this.jingleListener);
connection.setOnBindListener(this.mOnBindListener);
- connection
- .setOnMessageAcknowledgeListener(this.mOnMessageAcknowledgedListener);
+ connection.setOnMessageAcknowledgeListener(this.mOnMessageAcknowledgedListener);
+ connection.addOnAdvancedStreamFeaturesAvailableListener(this.mMessageArchiveService);
return connection;
}
@@ -641,12 +638,22 @@ public class XmppConnectionService extends Service {
}
} else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
message.getConversation().endOtrIfNeeded();
- failWaitingOtrMessages(message.getConversation());
+ message.getConversation().findUnsentMessagesWithOtrEncryption(new Conversation.OnMessageFound() {
+ @Override
+ public void onMessageFound(Message message) {
+ markMessage(message,Message.STATUS_SEND_FAILED);
+ }
+ });
packet = mMessageGenerator.generatePgpChat(message);
send = true;
} else {
message.getConversation().endOtrIfNeeded();
- failWaitingOtrMessages(message.getConversation());
+ message.getConversation().findUnsentMessagesWithOtrEncryption(new Conversation.OnMessageFound() {
+ @Override
+ public void onMessageFound(Message message) {
+ markMessage(message,Message.STATUS_SEND_FAILED);
+ }
+ });
packet = mMessageGenerator.generateChat(message);
send = true;
}
@@ -689,13 +696,14 @@ public class XmppConnectionService extends Service {
updateConversationUi();
}
- private void sendUnsendMessages(Conversation conversation) {
- for (int i = 0; i < conversation.getMessages().size(); ++i) {
- int status = conversation.getMessages().get(i).getStatus();
- if (status == Message.STATUS_WAITING) {
- resendMessage(conversation.getMessages().get(i));
+ private void sendUnsentMessages(Conversation conversation) {
+ conversation.findWaitingMessages(new Conversation.OnMessageFound() {
+
+ @Override
+ public void onMessageFound(Message message) {
+ resendMessage(message);
}
- }
+ });
}
private void resendMessage(Message message) {
@@ -839,39 +847,43 @@ public class XmppConnectionService extends Service {
sendIqPacket(account, iqPacket, null);
}
- private void mergePhoneContactsWithRoster() {
- PhoneHelper.loadPhoneContacts(getApplicationContext(),
- new OnPhoneContactsLoadedListener() {
- @Override
- public void onPhoneContactsLoaded(List<Bundle> phoneContacts) {
- for (Account account : accounts) {
- account.getRoster().clearSystemAccounts();
+ public void onPhoneContactsLoaded(final List<Bundle> phoneContacts) {
+ if (mPhoneContactMergerThread != null) {
+ mPhoneContactMergerThread.interrupt();
+ }
+ mPhoneContactMergerThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ Log.d(Config.LOGTAG,"start merging phone contacts with roster");
+ for (Account account : accounts) {
+ account.getRoster().clearSystemAccounts();
+ for (Bundle phoneContact : phoneContacts) {
+ if (Thread.interrupted()) {
+ Log.d(Config.LOGTAG,"interrupted merging phone contacts");
+ return;
}
- for (Bundle phoneContact : phoneContacts) {
- for (Account account : accounts) {
- Jid jid;
- try {
- jid = Jid.fromString(phoneContact.getString("jid"));
- } catch (final InvalidJidException e) {
- // TODO: Warn if contact import fails here?
- break;
- }
- final Contact contact = account.getRoster()
- .getContact(jid);
- String systemAccount = phoneContact
- .getInt("phoneid")
- + "#"
- + phoneContact.getString("lookup");
- contact.setSystemAccount(systemAccount);
- contact.setPhotoUri(phoneContact
- .getString("photouri"));
- contact.setSystemName(phoneContact
- .getString("displayname"));
- getAvatarService().clear(contact);
- }
+ Jid jid;
+ try {
+ jid = Jid.fromString(phoneContact.getString("jid"));
+ } catch (final InvalidJidException e) {
+ break;
}
+ final Contact contact = account.getRoster()
+ .getContact(jid);
+ String systemAccount = phoneContact.getInt("phoneid")
+ + "#"
+ + phoneContact.getString("lookup");
+ contact.setSystemAccount(systemAccount);
+ contact.setPhotoUri(phoneContact.getString("photouri"));
+ getAvatarService().clear(contact);
+ contact.setSystemName(phoneContact.getString("displayname"));
}
- });
+ }
+ Log.d(Config.LOGTAG,"finished merging phone contacts");
+ updateAccountUi();
+ }
+ });
+ mPhoneContactMergerThread.start();
}
private void initConversations() {
@@ -895,28 +907,26 @@ public class XmppConnectionService extends Service {
}
private void checkDeletedFiles(Conversation conversation) {
- for (Message message : conversation.getMessages()) {
- if ((message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE)
- && message.getEncryption() != Message.ENCRYPTION_PGP) {
+ conversation.findMessagesWithFiles(new Conversation.OnMessageFound() {
+
+ @Override
+ public void onMessageFound(Message message) {
if (!getFileBackend().isFileAvailable(message)) {
message.setDownloadable(new DownloadablePlaceholder(Downloadable.STATUS_DELETED));
}
- }
- }
+ }
+ });
}
private void markFileDeleted(String uuid) {
for (Conversation conversation : getConversations()) {
- for (Message message : conversation.getMessages()) {
- if (message.getType() == Message.TYPE_IMAGE
- && message.getEncryption() != Message.ENCRYPTION_PGP
- && message.getUuid().equals(uuid)) {
- if (!getFileBackend().isFileAvailable(message)) {
- message.setDownloadable(new DownloadablePlaceholder(Downloadable.STATUS_DELETED));
- updateConversationUi();
- }
- return;
- }
+ Message message = conversation.findMessageWithFileAndUuid(uuid);
+ if (message != null) {
+ if (!getFileBackend().isFileAvailable(message)) {
+ message.setDownloadable(new DownloadablePlaceholder(Downloadable.STATUS_DELETED));
+ updateConversationUi();
+ }
+ return;
}
}
}
@@ -953,6 +963,9 @@ public class XmppConnectionService extends Service {
}
public int loadMoreMessages(Conversation conversation, long timestamp) {
+ if (this.getMessageArchiveService().queryInProgress(conversation)) {
+ return 0;
+ }
List<Message> messages = databaseBackend.getMessages(conversation, 50,
timestamp);
for (Message message : messages) {
@@ -990,8 +1003,11 @@ public class XmppConnectionService extends Service {
return null;
}
- public Conversation findOrCreateConversation(final Account account, final Jid jid,
- final boolean muc) {
+ public Conversation findOrCreateConversation(final Account account, final Jid jid,final boolean muc) {
+ return this.findOrCreateConversation(account,jid,muc,null);
+ }
+
+ public Conversation findOrCreateConversation(final Account account, final Jid jid,final boolean muc, final MessageArchiveService.Query query) {
synchronized (this.conversations) {
Conversation conversation = find(account, jid);
if (conversation != null) {
@@ -1025,6 +1041,13 @@ public class XmppConnectionService extends Service {
}
this.databaseBackend.createConversation(conversation);
}
+ if (query == null) {
+ this.mMessageArchiveService.query(conversation);
+ } else {
+ if (query.getConversation() == null) {
+ this.mMessageArchiveService.query(conversation,query.getStart());
+ }
+ }
this.conversations.add(conversation);
updateConversationUi();
return conversation;
@@ -1051,12 +1074,6 @@ public class XmppConnectionService extends Service {
}
}
- public void clearConversationHistory(Conversation conversation) {
- this.databaseBackend.deleteMessagesInConversation(conversation);
- conversation.getMessages().clear();
- updateConversationUi();
- }
-
public void createAccount(Account account) {
account.initOtrEngine(this);
databaseBackend.createAccount(account);
@@ -1251,27 +1268,16 @@ public class XmppConnectionService extends Service {
PresencePacket packet = new PresencePacket();
packet.setFrom(conversation.getAccount().getJid());
packet.setTo(joinJid);
- Element x = new Element("x");
- x.setAttribute("xmlns", "http://jabber.org/protocol/muc");
+ Element x = packet.addChild("x","http://jabber.org/protocol/muc");
if (conversation.getMucOptions().getPassword() != null) {
- Element password = x.addChild("password");
- password.setContent(conversation.getMucOptions().getPassword());
+ x.addChild("password").setContent(conversation.getMucOptions().getPassword());
}
+ x.addChild("history").setAttribute("since",PresenceGenerator.getTimestamp(conversation.getLastMessageTransmitted()));
String sig = account.getPgpSignature();
if (sig != null) {
packet.addChild("status").setContent("online");
packet.addChild("x", "jabber:x:signed").setContent(sig);
}
- if (conversation.getMessages().size() != 0) {
- final SimpleDateFormat mDateFormat = new SimpleDateFormat(
- "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
- mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
- Date date = new Date(conversation.getLatestMessage()
- .getTimeSent() + 1000);
- x.addChild("history").setAttribute("since",
- mDateFormat.format(date));
- }
- packet.addChild(x);
sendPresencePacket(account, packet);
if (!joinJid.equals(conversation.getContactJid())) {
conversation.setContactJid(joinJid);
@@ -1315,7 +1321,7 @@ public class XmppConnectionService extends Service {
@Override
public void onFailure() {
- callback.error(R.string.nick_in_use,conversation);
+ callback.error(R.string.nick_in_use, conversation);
}
});
@@ -1528,36 +1534,35 @@ public class XmppConnectionService extends Service {
}
public void onOtrSessionEstablished(Conversation conversation) {
- Account account = conversation.getAccount();
- List<Message> messages = conversation.getMessages();
- Session otrSession = conversation.getOtrSession();
+ final Account account = conversation.getAccount();
+ final Session otrSession = conversation.getOtrSession();
Log.d(Config.LOGTAG,
account.getJid().toBareJid() + " otr session established with "
- + conversation.getContactJid() + "/"
- + otrSession.getSessionID().getUserID());
- for (Message msg : messages) {
- if ((msg.getStatus() == Message.STATUS_UNSEND || msg.getStatus() == Message.STATUS_WAITING)
- && (msg.getEncryption() == Message.ENCRYPTION_OTR)) {
+ + conversation.getContactJid() + "/"
+ + otrSession.getSessionID().getUserID());
+ conversation.findUnsentMessagesWithOtrEncryption(new Conversation.OnMessageFound() {
+
+ @Override
+ public void onMessageFound(Message message) {
SessionID id = otrSession.getSessionID();
try {
- msg.setCounterpart(Jid.fromString(id.getAccountID() + "/" + id.getUserID()));
+ message.setCounterpart(Jid.fromString(id.getAccountID() + "/" + id.getUserID()));
} catch (InvalidJidException e) {
- break;
+ return;
}
- if (msg.getType() == Message.TYPE_TEXT) {
- MessagePacket outPacket = mMessageGenerator
- .generateOtrChat(msg, true);
+ if (message.getType() == Message.TYPE_TEXT) {
+ MessagePacket outPacket = mMessageGenerator.generateOtrChat(message, true);
if (outPacket != null) {
- msg.setStatus(Message.STATUS_SEND);
- databaseBackend.updateMessage(msg);
+ message.setStatus(Message.STATUS_SEND);
+ databaseBackend.updateMessage(message);
sendMessagePacket(account, outPacket);
}
- } else if (msg.getType() == Message.TYPE_IMAGE || msg.getType() == Message.TYPE_FILE) {
- mJingleConnectionManager.createNewConnection(msg);
+ } else if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
+ mJingleConnectionManager.createNewConnection(message);
}
- }
- }
- updateConversationUi();
+ updateConversationUi();
+ }
+ });
}
public boolean renewSymmetricKey(Conversation conversation) {
@@ -1812,12 +1817,13 @@ public class XmppConnectionService extends Service {
public void resetSendingToWaiting(Account account) {
for (Conversation conversation : getConversations()) {
if (conversation.getAccount() == account) {
- for (Message message : conversation.getMessages()) {
- if (message.getType() != Message.TYPE_IMAGE
- && message.getStatus() == Message.STATUS_UNSEND) {
+ conversation.findUnsentTextMessages(new Conversation.OnMessageFound() {
+
+ @Override
+ public void onMessageFound(Message message) {
markMessage(message, Message.STATUS_WAITING);
- }
- }
+ }
+ });
}
}
}
@@ -1842,15 +1848,13 @@ public class XmppConnectionService extends Service {
if (uuid == null) {
return false;
} else {
- for (Message message : conversation.getMessages()) {
- if (uuid.equals(message.getUuid())
- || (message.getStatus() >= Message.STATUS_SEND && uuid
- .equals(message.getRemoteMsgId()))) {
- markMessage(message, status);
- return true;
- }
+ Message message = conversation.findSentMessageWithUuid(uuid);
+ if (message!=null) {
+ markMessage(message,status);
+ return true;
+ } else {
+ return false;
}
- return false;
}
}
@@ -1944,15 +1948,6 @@ public class XmppConnectionService extends Service {
}
}
- public void failWaitingOtrMessages(Conversation conversation) {
- for (Message message : conversation.getMessages()) {
- if (message.getEncryption() == Message.ENCRYPTION_OTR
- && message.getStatus() == Message.STATUS_WAITING) {
- markMessage(message, Message.STATUS_SEND_FAILED);
- }
- }
- }
-
public SecureRandom getRNG() {
return this.mRandom;
}
@@ -2049,6 +2044,10 @@ public class XmppConnectionService extends Service {
return this.mJingleConnectionManager;
}
+ public MessageArchiveService getMessageArchiveService() {
+ return this.mMessageArchiveService;
+ }
+
public List<Contact> findContacts(Jid jid) {
ArrayList<Contact> contacts = new ArrayList<>();
for (Account account : getAccounts()) {
diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
index b2bf2fd8..a5efe12e 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java
@@ -491,10 +491,12 @@ public class ConversationActivity extends XmppActivity implements
@Override
public void onClick(DialogInterface dialog, int which) {
- ConversationActivity.this.xmppConnectionService
- .clearConversationHistory(conversation);
+ conversation.clearMessages();
if (endConversationCheckBox.isChecked()) {
endConversation(conversation);
+ } else {
+ updateConversationList();
+ ConversationActivity.this.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 0edc6b6f..7f4b5c54 100644
--- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
+++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java
@@ -120,8 +120,7 @@ public class ConversationFragment extends Fragment {
long timestamp = ConversationFragment.this.messageList.get(0).getTimeSent();
messagesLoaded = false;
int size = activity.xmppConnectionService.loadMoreMessages(conversation, timestamp);
- ConversationFragment.this.messageList.clear();
- ConversationFragment.this.messageList.addAll(conversation.getMessages());
+ conversation.populateWithMessages(ConversationFragment.this.messageList);
updateStatusMessages();
messageListAdapter.notifyDataSetChanged();
if (size != 0) {
@@ -580,25 +579,19 @@ public class ConversationFragment extends Fragment {
break;
}
}
- this.messageList.clear();
- if (this.conversation.getMessages().size() == 0) {
- messagesLoaded = false;
- } else {
- this.messageList.addAll(this.conversation.getMessages());
- messagesLoaded = true;
- for (Message message : this.messageList) {
- if (message.getEncryption() == Message.ENCRYPTION_PGP
- && (message.getStatus() == Message.STATUS_RECEIVED || message
- .getStatus() >= Message.STATUS_SEND)
- && message.getDownloadable() == null) {
- if (!mEncryptedMessages.contains(message)) {
- mEncryptedMessages.add(message);
- }
+ conversation.populateWithMessages(ConversationFragment.this.messageList);
+ for (Message message : this.messageList) {
+ if (message.getEncryption() == Message.ENCRYPTION_PGP
+ && (message.getStatus() == Message.STATUS_RECEIVED || message
+ .getStatus() >= Message.STATUS_SEND)
+ && message.getDownloadable() == null) {
+ if (!mEncryptedMessages.contains(message)) {
+ mEncryptedMessages.add(message);
}
}
- decryptNext();
- updateStatusMessages();
}
+ decryptNext();
+ updateStatusMessages();
this.messageListAdapter.notifyDataSetChanged();
updateChatMsgHint();
if (!activity.isConversationsOverviewVisable() || !activity.isConversationsOverviewHideable()) {
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 32062699..478586b9 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
@@ -268,7 +268,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
@Override
public void onClick(View v) {
- startDonwloadable(message);
+ startDownloadable(message);
}
});
viewHolder.download_button.setOnLongClickListener(openContextMenu);
@@ -284,7 +284,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
@Override
public void onClick(View v) {
- openDonwloadable(file);
+ openDownloadable(file);
}
});
viewHolder.download_button.setOnLongClickListener(openContextMenu);
@@ -438,6 +438,8 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
view.setLayoutParams(view.getLayoutParams());
return view;
+ } else if (viewHolder.messageBody == null || viewHolder.image == null) {
+ return view; //avoiding weird platform bugs
} else if (type == RECEIVED) {
Contact contact = message.getContact();
if (contact != null) {
@@ -446,38 +448,36 @@ public class MessageAdapter extends ArrayAdapter<Message> {
viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(getDisplayedMucCounterpart(message.getCounterpart()),
activity.getPixel(48)));
}
- } else if (type == SENT && viewHolder.contact_picture != null) {
+ } else if (type == SENT) {
viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(account, activity.getPixel(48)));
}
- if (viewHolder != null && viewHolder.contact_picture != null) {
- viewHolder.contact_picture
- .setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
- MessageAdapter.this.mOnContactPictureClickedListener
- .onContactPictureClicked(message);
- }
+ viewHolder.contact_picture
+ .setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
+ MessageAdapter.this.mOnContactPictureClickedListener
+ .onContactPictureClicked(message);
}
- });
- viewHolder.contact_picture
- .setOnLongClickListener(new OnLongClickListener() {
-
- @Override
- public boolean onLongClick(View v) {
- if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
- MessageAdapter.this.mOnContactPictureLongClickedListener
- .onContactPictureLongClicked(message);
- return true;
- } else {
- return false;
- }
+
+ }
+ });
+ viewHolder.contact_picture
+ .setOnLongClickListener(new OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
+ MessageAdapter.this.mOnContactPictureLongClickedListener
+ .onContactPictureLongClicked(message);
+ return true;
+ } else {
+ return false;
}
- });
- }
+ }
+ });
if (message.getDownloadable() != null && message.getDownloadable().getStatus() != Downloadable.STATUS_UPLOADING) {
Downloadable d = message.getDownloadable();
@@ -546,7 +546,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
return view;
}
- public void startDonwloadable(Message message) {
+ public void startDownloadable(Message message) {
Downloadable downloadable = message.getDownloadable();
if (downloadable != null) {
if (!downloadable.start()) {
@@ -556,7 +556,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
}
}
- public void openDonwloadable(DownloadableFile file) {
+ public void openDownloadable(DownloadableFile file) {
if (!file.exists()) {
Toast.makeText(activity,R.string.file_deleted,Toast.LENGTH_SHORT).show();
return;
diff --git a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java
index 8c1a8dea..a09b4d0f 100644
--- a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java
@@ -140,15 +140,18 @@ public class DNSHelper {
}
ArrayList<Bundle> values = new ArrayList<>();
for (SRV srv : result) {
- Bundle namePort = new Bundle();
- namePort.putString("name", srv.getName());
- namePort.putInt("port", srv.getPort());
+ boolean added = false;
+ if (ips6.containsKey(srv.getName())) {
+ values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips6));
+ added = true;
+ }
if (ips4.containsKey(srv.getName())) {
- ArrayList<String> ip = ips4.get(srv.getName());
- Collections.shuffle(ip, rnd);
- namePort.putString("ipv4", ip.get(0));
+ values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips4));
+ added = true;
+ }
+ if (!added) {
+ values.add(createNamePortBundle(srv.getName(),srv.getPort(),null));
}
- values.add(namePort);
}
bundle.putParcelableArrayList("values", values);
} catch (SocketTimeoutException e) {
@@ -159,6 +162,18 @@ public class DNSHelper {
return bundle;
}
+ private static Bundle createNamePortBundle(String name, int port, TreeMap<String, ArrayList<String>> ips) {
+ Bundle namePort = new Bundle();
+ namePort.putString("name", name);
+ namePort.putInt("port", port);
+ if (ips!=null) {
+ ArrayList<String> ip = ips.get(name);
+ Collections.shuffle(ip, new Random());
+ namePort.putString("ip", ip.get(0));
+ }
+ return namePort;
+ }
+
final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
diff --git a/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java b/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java
index 87973159..9a5cbaaf 100644
--- a/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java
+++ b/src/main/java/eu/siacs/conversations/utils/PhoneHelper.java
@@ -16,10 +16,7 @@ import android.provider.ContactsContract.Profile;
public class PhoneHelper {
- public static void loadPhoneContacts(Context context,
- final OnPhoneContactsLoadedListener listener) {
- final List<Bundle> phoneContacts = new ArrayList<Bundle>();
-
+ public static void loadPhoneContacts(Context context,final List<Bundle> phoneContacts, final OnPhoneContactsLoadedListener listener) {
final String[] PROJECTION = new String[] { ContactsContract.Data._ID,
ContactsContract.Data.DISPLAY_NAME,
ContactsContract.Data.PHOTO_URI,
diff --git a/src/main/java/eu/siacs/conversations/xml/Element.java b/src/main/java/eu/siacs/conversations/xml/Element.java
index 02c3e695..c25b9017 100644
--- a/src/main/java/eu/siacs/conversations/xml/Element.java
+++ b/src/main/java/eu/siacs/conversations/xml/Element.java
@@ -159,4 +159,9 @@ public class Element {
public void setAttribute(String name, int value) {
this.setAttribute(name, Integer.toString(value));
}
+
+ public boolean getAttributeAsBoolean(String name) {
+ String attr = getAttribute(name);
+ return (attr != null && (attr.equalsIgnoreCase("true") || attr.equalsIgnoreCase("1")));
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/OnAdvancedStreamFeaturesLoaded.java b/src/main/java/eu/siacs/conversations/xmpp/OnAdvancedStreamFeaturesLoaded.java
new file mode 100644
index 00000000..e45eba73
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/xmpp/OnAdvancedStreamFeaturesLoaded.java
@@ -0,0 +1,7 @@
+package eu.siacs.conversations.xmpp;
+
+import eu.siacs.conversations.entities.Account;
+
+public interface OnAdvancedStreamFeaturesLoaded {
+ public void onAdvancedStreamFeaturesAvailable(final Account account);
+}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index adb96fa2..b090d651 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -107,6 +107,7 @@ public class XmppConnection implements Runnable {
private OnMessagePacketReceived messageListener = null;
private OnStatusChanged statusListener = null;
private OnBindListener bindListener = null;
+ private ArrayList<OnAdvancedStreamFeaturesLoaded> advancedStreamFeaturesLoadedListeners = new ArrayList<>();
private OnMessageAcknowledged acknowledgedListener = null;
private XmppConnectionService mXmppConnectionService = null;
@@ -170,7 +171,7 @@ public class XmppConnection implements Runnable {
srvRecordServer = "";
}
int srvRecordPort = namePort.getInt("port");
- String srvIpServer = namePort.getString("ipv4");
+ String srvIpServer = namePort.getString("ip");
InetSocketAddress addr;
if (srvIpServer != null) {
addr = new InetSocketAddress(srvIpServer, srvRecordPort);
@@ -771,6 +772,9 @@ public class XmppConnection implements Runnable {
if (account.getServer().equals(server.toDomainJid())) {
enableAdvancedStreamFeatures();
+ for(OnAdvancedStreamFeaturesLoaded listener : advancedStreamFeaturesLoadedListeners) {
+ listener.onAdvancedStreamFeaturesAvailable(account);
+ }
}
}
});
@@ -943,6 +947,12 @@ public class XmppConnection implements Runnable {
this.acknowledgedListener = listener;
}
+ public void addOnAdvancedStreamFeaturesAvailableListener(OnAdvancedStreamFeaturesLoaded listener) {
+ if (!this.advancedStreamFeaturesLoadedListeners.contains(listener)) {
+ this.advancedStreamFeaturesLoadedListeners.add(listener);
+ }
+ }
+
public void disconnect(boolean force) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting");
try {
@@ -1087,6 +1097,10 @@ public class XmppConnection implements Runnable {
return hasDiscoFeature(account.getServer(), "urn:xmpp:mam:0");
}
+ public boolean advancedStreamFeaturesLoaded() {
+ return disco.containsKey(account.getServer().toString());
+ }
+
public boolean rosterVersioning() {
return connection.streamFeatures != null && connection.streamFeatures.hasChild("ver");
}
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 ff9acb3f..44794c80 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java
@@ -37,6 +37,7 @@ public class Data extends Element {
Field field = getFieldByName(name);
if (field == null) {
field = new Field(name);
+ this.addChild(field);
}
field.setValue(value);
}
@@ -45,6 +46,7 @@ public class Data extends Element {
Field field = getFieldByName(name);
if (field == null) {
field = new Field(name);
+ this.addChild(field);
}
field.setValues(values);
}
@@ -72,4 +74,12 @@ public class Data extends Element {
data.setChildren(element.getChildren());
return data;
}
+
+ public void setFormType(String formType) {
+ this.put("FORM_TYPE",formType);
+ }
+
+ public String getFormType() {
+ return this.getAttribute("FORM_TYPE");
+ }
}
diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml
index dab7471d..703dab9d 100644
--- a/src/main/res/values-it/strings.xml
+++ b/src/main/res/values-it/strings.xml
@@ -217,7 +217,7 @@
<string name="add_back">Add back</string>
<string name="contact_has_read_up_to_this_point">%s ha letto fino a questo punto</string>
<string name="publish">Pubblica</string>
- <string name="touch_to_choose_picture">Tocca l\'avatar per selezionare l\'immagine dalla gallaria</string>
+ <string name="touch_to_choose_picture">Tocca l\'avatar per selezionare l\'immagine dalla galleria</string>
<string name="publish_avatar_explanation">Nota bene: tutti i contatti sottoscritti agli aggiornamenti della tua presenza avranno il permesso di vedere questa immagine.</string>
<string name="publishing">Pubblicazione&#8230;</string>
<string name="error_publish_avatar_server_reject">Il server ha rifiutato la tua pubblicazione</string>