From e32f380dae4913d8152fc92b76193de241089cf8 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 14 May 2015 14:42:21 +0200 Subject: [PATCH 01/37] provide helper function for getting the content of a child directly --- .../conversations/entities/Bookmark.java | 14 ++--------- .../conversations/parser/AbstractParser.java | 6 +---- .../services/XmppConnectionService.java | 3 +-- .../eu/siacs/conversations/xml/Element.java | 10 ++++++++ .../siacs/conversations/xmpp/pep/Avatar.java | 3 +-- .../xmpp/stanzas/MessagePacket.java | 23 +++++++++++++++++++ 6 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Bookmark.java b/src/main/java/eu/siacs/conversations/entities/Bookmark.java index f81f1a87..cc6f146b 100644 --- a/src/main/java/eu/siacs/conversations/entities/Bookmark.java +++ b/src/main/java/eu/siacs/conversations/entities/Bookmark.java @@ -75,12 +75,7 @@ public class Bookmark extends Element implements ListItem { } public String getNick() { - Element nick = this.findChild("nick"); - if (nick != null) { - return nick.getContent(); - } else { - return null; - } + return this.findChildContent("nick"); } public void setNick(String nick) { @@ -96,12 +91,7 @@ public class Bookmark extends Element implements ListItem { } public String getPassword() { - Element password = this.findChild("password"); - if (password != null) { - return password.getContent(); - } else { - return null; - } + return this.findChildContent("password"); } public void setPassword(String password) { diff --git a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java index bfe84440..6f73f24d 100644 --- a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java +++ b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java @@ -70,10 +70,6 @@ public abstract class AbstractParser { if (item == null) { return null; } - Element data = item.findChild("data", "urn:xmpp:avatar:data"); - if (data == null) { - return null; - } - return data.getContent(); + return item.findChildContent("data", "urn:xmpp:avatar:data"); } } diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 63d9ba7a..3a74b82b 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -1994,8 +1994,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (packet.getType() == IqPacket.TYPE.RESULT) { Element vCard = packet.findChild("vCard","vcard-temp"); Element photo = vCard != null ? vCard.findChild("PHOTO") : null; - Element binval = photo != null ? photo.findChild("BINVAL") : null; - String image = binval != null ? binval.getContent() : null; + String image = photo != null ? photo.findChildContent("BINVAL") : null; if (image != null) { avatar.image = image; if (getFileBackend().save(avatar)) { diff --git a/src/main/java/eu/siacs/conversations/xml/Element.java b/src/main/java/eu/siacs/conversations/xml/Element.java index 51708759..32657c66 100644 --- a/src/main/java/eu/siacs/conversations/xml/Element.java +++ b/src/main/java/eu/siacs/conversations/xml/Element.java @@ -57,6 +57,11 @@ public class Element { return null; } + public String findChildContent(String name) { + Element element = findChild(name); + return element == null ? null : element.getContent(); + } + public Element findChild(String name, String xmlns) { for (Element child : this.children) { if (child.getName().equals(name) @@ -67,6 +72,11 @@ public class Element { return null; } + public String findChildContent(String name, String xmlns) { + Element element = findChild(name,xmlns); + return element == null ? null : element.getContent(); + } + public boolean hasChild(final String name) { return findChild(name) != null; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java b/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java index 04d55bbe..74da6a9b 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java +++ b/src/main/java/eu/siacs/conversations/xmpp/pep/Avatar.java @@ -83,8 +83,7 @@ public class Avatar { } public static Avatar parsePresence(Element x) { - Element photo = x != null ? x.findChild("photo") : null; - String hash = photo != null ? photo.getContent() : null; + String hash = x == null ? null : x.findChildContent("photo"); if (hash == null) { return null; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java b/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java index 93aaa68c..11c3452b 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java +++ b/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java @@ -66,4 +66,27 @@ public class MessagePacket extends AbstractStanza { return TYPE_NORMAL; } } + + public MessagePacket getForwardedMessagePacket(String name, String namespace) { + Element wrapper = findChild(name, namespace); + if (wrapper == null) { + return null; + } + Element forwarded = wrapper.findChild("forwarded","urn:xmpp:forward:0"); + if (forwarded == null) { + return null; + } + return MessagePacket.create(forwarded.findChild("message")); + } + + + public static MessagePacket create(Element element) { + if (element == null) { + return null; + } + MessagePacket packet = new MessagePacket(); + packet.setAttributes(element.getAttributes()); + packet.setChildren(element.getChildren()); + return packet; + } } From d261feda74ac3bd42d633d83264b14696276ade6 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 15 May 2015 05:14:15 +0200 Subject: [PATCH 02/37] rewrote parser code. mam id and possible other stuff still missing. also massivly untested --- .../conversations/parser/AbstractParser.java | 41 +- .../conversations/parser/MessageParser.java | 685 ++++++------------ .../xmpp/stanzas/AbstractStanza.java | 4 + .../xmpp/stanzas/MessagePacket.java | 24 +- 4 files changed, 240 insertions(+), 514 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java index 6f73f24d..7b3eb7e9 100644 --- a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java +++ b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java @@ -11,6 +11,7 @@ import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.jid.Jid; +import eu.siacs.conversations.xmpp.stanzas.MessagePacket; public abstract class AbstractParser { @@ -20,22 +21,23 @@ public abstract class AbstractParser { this.mXmppConnectionService = service; } + public static Long getTimestamp(Element element, Long defaultValue) { + Element delay = element.findChild("delay"); + if (delay != null) { + String stamp = delay.getAttribute("stamp"); + if (stamp != null) { + try { + return AbstractParser.parseTimestamp(delay.getAttribute("stamp")).getTime(); + } catch (ParseException e) { + return defaultValue; + } + } + } + return defaultValue; + } + protected long getTimestamp(Element packet) { - long now = System.currentTimeMillis(); - Element delay = packet.findChild("delay"); - if (delay == null) { - return now; - } - 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; - } + return getTimestamp(packet,System.currentTimeMillis()); } public static Date parseTimestamp(String timestamp) throws ParseException { @@ -46,14 +48,11 @@ public abstract class AbstractParser { return dateFormat.parse(timestamp); } - protected void updateLastseen(final Element packet, final Account account, - final boolean presenceOverwrite) { - final Jid from = packet.getAttributeAsJid("from"); - updateLastseen(packet, account, from, presenceOverwrite); + protected void updateLastseen(final Element packet, final Account account, final boolean presenceOverwrite) { + updateLastseen(packet, account, packet.getAttributeAsJid("from"), presenceOverwrite); } - protected void updateLastseen(final Element packet, final Account account, final Jid from, - final boolean presenceOverwrite) { + protected void updateLastseen(final Element packet, final Account account, final Jid from, final boolean presenceOverwrite) { final String presence = from == null || from.isBareJid() ? "" : from.getResourcepart(); final Contact contact = account.getRoster().getContact(from); final long timestamp = getTimestamp(packet); diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 7870fdbf..5a2fcbe1 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -1,8 +1,12 @@ package eu.siacs.conversations.parser; +import android.util.Log; +import android.util.Pair; + import net.java.otr4j.session.Session; import net.java.otr4j.session.SessionStatus; +import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; @@ -25,12 +29,12 @@ public class MessageParser extends AbstractParser implements super(service); } - private boolean extractChatState(Conversation conversation, final Element element) { - ChatState state = ChatState.parse(element); + private boolean extractChatState(Conversation conversation, final MessagePacket packet) { + ChatState state = ChatState.parse(packet); if (state != null && conversation != null) { final Account account = conversation.getAccount(); - Jid from = element.getAttributeAsJid("from"); - if (from != null && from.toBareJid().equals(account.getJid().toBareJid())) { + Jid from = packet.getFrom(); + if (from.toBareJid().equals(account.getJid().toBareJid())) { conversation.setOutgoingChatState(state); return false; } else { @@ -40,86 +44,27 @@ public class MessageParser extends AbstractParser implements return false; } - private Message parseChat(MessagePacket packet, Account account) { - final Jid jid = packet.getFrom(); - if (jid == null) { - return null; - } - Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, jid.toBareJid(), false); - String pgpBody = getPgpBody(packet); - Message finishedMessage; - if (pgpBody != null) { - finishedMessage = new Message(conversation, - pgpBody, Message.ENCRYPTION_PGP, Message.STATUS_RECEIVED); - } else { - finishedMessage = new Message(conversation, - packet.getBody(), Message.ENCRYPTION_NONE, - Message.STATUS_RECEIVED); - } - finishedMessage.setRemoteMsgId(packet.getId()); - finishedMessage.markable = isMarkable(packet); - if (conversation.getMode() == Conversation.MODE_MULTI - && !jid.isBareJid()) { - final Jid trueCounterpart = conversation.getMucOptions() - .getTrueCounterpart(jid.getResourcepart()); - if (trueCounterpart != null) { - updateLastseen(packet, account, trueCounterpart, false); - } - finishedMessage.setType(Message.TYPE_PRIVATE); - finishedMessage.setTrueCounterpart(trueCounterpart); - if (conversation.hasDuplicateMessage(finishedMessage)) { - return null; - } - } else { - updateLastseen(packet, account, true); - } - finishedMessage.setCounterpart(jid); - finishedMessage.setTime(getTimestamp(packet)); - extractChatState(conversation,packet); - return finishedMessage; - } - - private Message parseOtrChat(MessagePacket packet, Account account) { - final Jid to = packet.getTo(); - final Jid from = packet.getFrom(); - if (to == null || from == null) { - return null; - } - boolean properlyAddressed = !to.isBareJid() || account.countPresences() == 1; - Conversation conversation = mXmppConnectionService - .findOrCreateConversation(account, from.toBareJid(), false); + private Message parseOtrChat(String body, Jid from, String id, Conversation conversation) { String presence; if (from.isBareJid()) { presence = ""; } else { presence = from.getResourcepart(); } - extractChatState(conversation, packet); - updateLastseen(packet, account, true); - String body = packet.getBody(); if (body.matches("^\\?OTRv\\d{1,2}\\?.*")) { conversation.endOtrIfNeeded(); } if (!conversation.hasValidOtrSession()) { - if (properlyAddressed) { - conversation.startOtrSession(presence,false); - } else { - return null; - } + conversation.startOtrSession(presence,false); } else { - String foreignPresence = conversation.getOtrSession() - .getSessionID().getUserID(); + String foreignPresence = conversation.getOtrSession().getSessionID().getUserID(); if (!foreignPresence.equals(presence)) { conversation.endOtrIfNeeded(); - if (properlyAddressed) { - conversation.startOtrSession(presence, false); - } else { - return null; - } + conversation.startOtrSession(presence, false); } } try { - conversation.setLastReceivedOtrMessageId(packet.getId()); + conversation.setLastReceivedOtrMessageId(id); Session otrSession = conversation.getOtrSession(); SessionStatus before = otrSession.getSessionStatus(); body = otrSession.transformReceiving(body); @@ -140,12 +85,7 @@ public class MessageParser extends AbstractParser implements conversation.setSymmetricKey(CryptoHelper.hexToBytes(key)); return null; } - Message finishedMessage = new Message(conversation, body, Message.ENCRYPTION_OTR, - Message.STATUS_RECEIVED); - finishedMessage.setTime(getTimestamp(packet)); - finishedMessage.setRemoteMsgId(packet.getId()); - finishedMessage.markable = isMarkable(packet); - finishedMessage.setCounterpart(from); + Message finishedMessage = new Message(conversation, body, Message.ENCRYPTION_OTR, Message.STATUS_RECEIVED); conversation.setLastReceivedOtrMessageId(null); return finishedMessage; } catch (Exception e) { @@ -154,293 +94,6 @@ public class MessageParser extends AbstractParser implements } } - private Message parseGroupchat(MessagePacket packet, Account account) { - int status; - final Jid from = packet.getFrom(); - if (from == null) { - return null; - } - if (mXmppConnectionService.find(account.pendingConferenceLeaves, - account, from.toBareJid()) != null) { - return null; - } - Conversation conversation = mXmppConnectionService - .findOrCreateConversation(account, from.toBareJid(), true); - final Jid trueCounterpart = conversation.getMucOptions().getTrueCounterpart(from.getResourcepart()); - if (trueCounterpart != null) { - updateLastseen(packet, account, trueCounterpart, false); - } - if (packet.hasChild("subject")) { - conversation.setHasMessagesLeftOnServer(true); - conversation.getMucOptions().setSubject(packet.findChild("subject").getContent()); - mXmppConnectionService.updateConversationUi(); - return null; - } - - final Element x = packet.findChild("x", "http://jabber.org/protocol/muc#user"); - if (from.isBareJid() && (x == null || !x.hasChild("status"))) { - return null; - } else if (from.isBareJid() && x.hasChild("status")) { - for(Element child : x.getChildren()) { - if (child.getName().equals("status")) { - String code = child.getAttribute("code"); - if (code.contains(MucOptions.STATUS_CODE_ROOM_CONFIG_CHANGED)) { - mXmppConnectionService.fetchConferenceConfiguration(conversation); - } - } - } - return null; - } - - if (from.getResourcepart().equals(conversation.getMucOptions().getActualNick())) { - if (mXmppConnectionService.markMessage(conversation, - packet.getId(), Message.STATUS_SEND_RECEIVED)) { - return null; - } else if (packet.getId() == null) { - Message message = conversation.findSentMessageWithBody(packet.getBody()); - if (message != null) { - mXmppConnectionService.markMessage(message,Message.STATUS_SEND_RECEIVED); - return null; - } else { - status = Message.STATUS_SEND; - } - } else { - status = Message.STATUS_SEND; - } - } else { - status = Message.STATUS_RECEIVED; - } - String pgpBody = getPgpBody(packet); - Message finishedMessage; - if (pgpBody == null) { - finishedMessage = new Message(conversation, - packet.getBody(), Message.ENCRYPTION_NONE, status); - } else { - finishedMessage = new Message(conversation, pgpBody, - Message.ENCRYPTION_PGP, status); - } - finishedMessage.setRemoteMsgId(packet.getId()); - finishedMessage.markable = isMarkable(packet); - finishedMessage.setCounterpart(from); - if (status == Message.STATUS_RECEIVED) { - finishedMessage.setTrueCounterpart(conversation.getMucOptions() - .getTrueCounterpart(from.getResourcepart())); - } - if (packet.hasChild("delay") - && conversation.hasDuplicateMessage(finishedMessage)) { - return null; - } - finishedMessage.setTime(getTimestamp(packet)); - return finishedMessage; - } - - private Message parseCarbonMessage(final MessagePacket packet, final Account account) { - int status; - final Jid fullJid; - Element forwarded; - if (packet.hasChild("received", "urn:xmpp:carbons:2")) { - forwarded = packet.findChild("received", "urn:xmpp:carbons:2") - .findChild("forwarded", "urn:xmpp:forward:0"); - status = Message.STATUS_RECEIVED; - } else if (packet.hasChild("sent", "urn:xmpp:carbons:2")) { - forwarded = packet.findChild("sent", "urn:xmpp:carbons:2") - .findChild("forwarded", "urn:xmpp:forward:0"); - status = Message.STATUS_SEND; - } else { - return null; - } - if (forwarded == null) { - return null; - } - Element message = forwarded.findChild("message"); - if (message == null) { - return null; - } - if (!message.hasChild("body")) { - if (status == Message.STATUS_RECEIVED - && message.getAttribute("from") != null) { - parseNonMessage(message, account); - } else if (status == Message.STATUS_SEND - && message.hasChild("displayed", "urn:xmpp:chat-markers:0")) { - final Jid to = message.getAttributeAsJid("to"); - if (to != null) { - final Conversation conversation = mXmppConnectionService.find( - mXmppConnectionService.getConversations(), account, - to.toBareJid()); - if (conversation != null) { - mXmppConnectionService.markRead(conversation); - } - } - } - return null; - } - if (status == Message.STATUS_RECEIVED) { - fullJid = message.getAttributeAsJid("from"); - if (fullJid == null) { - return null; - } else { - updateLastseen(message, account, true); - } - } else { - fullJid = message.getAttributeAsJid("to"); - if (fullJid == null) { - return null; - } - } - if (message.hasChild("x","http://jabber.org/protocol/muc#user") - && "chat".equals(message.getAttribute("type"))) { - return null; - } - Conversation conversation = mXmppConnectionService - .findOrCreateConversation(account, fullJid.toBareJid(), false); - String pgpBody = getPgpBody(message); - Message finishedMessage; - if (pgpBody != null) { - finishedMessage = new Message(conversation, pgpBody, - Message.ENCRYPTION_PGP, status); - } else { - String body = message.findChild("body").getContent(); - finishedMessage = new Message(conversation, body, - Message.ENCRYPTION_NONE, status); - } - extractChatState(conversation,message); - finishedMessage.setTime(getTimestamp(message)); - finishedMessage.setRemoteMsgId(message.getAttribute("id")); - finishedMessage.markable = isMarkable(message); - finishedMessage.setCounterpart(fullJid); - if (conversation.getMode() == Conversation.MODE_MULTI - && !fullJid.isBareJid()) { - finishedMessage.setType(Message.TYPE_PRIVATE); - finishedMessage.setTrueCounterpart(conversation.getMucOptions() - .getTrueCounterpart(fullJid.getResourcepart())); - if (conversation.hasDuplicateMessage(finishedMessage)) { - return null; - } - } - 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 MessageArchiveService.Query query = this.mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid")); - if (query!=null) { - query.incrementTotalCount(); - } - 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"); - 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)) { - return null; - } - if (query!=null) { - query.incrementMessageCount(); - } - return finishedMessage; - } - - private void parseError(final MessagePacket packet, final Account account) { - final Jid from = packet.getFrom(); - mXmppConnectionService.markMessage(account, from.toBareJid(), - packet.getId(), Message.STATUS_SEND_FAILED); - } - - private void parseNonMessage(Element packet, Account account) { - final Jid from = packet.getAttributeAsJid("from"); - if (account.getJid().equals(from)) { - return; - } - if (extractChatState(from == null ? null : mXmppConnectionService.find(account,from), packet)) { - mXmppConnectionService.updateConversationUi(); - } - Invite invite = extractInvite(packet); - if (invite != null && invite.jid != null) { - Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, invite.jid, true); - if (!conversation.getMucOptions().online()) { - conversation.getMucOptions().setPassword(invite.password); - mXmppConnectionService.databaseBackend.updateConversation(conversation); - mXmppConnectionService.joinMuc(conversation); - mXmppConnectionService.updateConversationUi(); - } - } - if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) { - Element event = packet.findChild("event", - "http://jabber.org/protocol/pubsub#event"); - parseEvent(event, from, account); - } else if (from != null && packet.hasChild("displayed", "urn:xmpp:chat-markers:0")) { - String id = packet - .findChild("displayed", "urn:xmpp:chat-markers:0") - .getAttribute("id"); - updateLastseen(packet, account, true); - final Message displayedMessage = mXmppConnectionService.markMessage(account, from.toBareJid(), id, Message.STATUS_SEND_DISPLAYED); - Message message = displayedMessage == null ? null :displayedMessage.prev(); - while (message != null - && message.getStatus() == Message.STATUS_SEND_RECEIVED - && message.getTimeSent() < displayedMessage.getTimeSent()) { - mXmppConnectionService.markMessage(message, Message.STATUS_SEND_DISPLAYED); - message = message.prev(); - } - } else if (from != null - && packet.hasChild("received", "urn:xmpp:chat-markers:0")) { - String id = packet.findChild("received", "urn:xmpp:chat-markers:0") - .getAttribute("id"); - updateLastseen(packet, account, false); - mXmppConnectionService.markMessage(account, from.toBareJid(), - id, Message.STATUS_SEND_RECEIVED); - } else if (from != null - && packet.hasChild("received", "urn:xmpp:receipts")) { - String id = packet.findChild("received", "urn:xmpp:receipts") - .getAttribute("id"); - updateLastseen(packet, account, false); - mXmppConnectionService.markMessage(account, from.toBareJid(), - id, Message.STATUS_SEND_RECEIVED); - } - } - private class Invite { Jid jid; String password; @@ -451,7 +104,7 @@ public class MessageParser extends AbstractParser implements } private Invite extractInvite(Element message) { - Element x = message.findChild("x","http://jabber.org/protocol/muc#user"); + Element x = message.findChild("x", "http://jabber.org/protocol/muc#user"); if (x != null) { Element invite = x.findChild("invite"); if (invite != null) { @@ -523,142 +176,208 @@ public class MessageParser extends AbstractParser implements } } - private String getPgpBody(Element message) { - Element child = message.findChild("x", "jabber:x:encrypted"); - if (child == null) { - return null; - } else { - return child.getContent(); - } - } - - private boolean isMarkable(Element message) { - return message.hasChild("markable", "urn:xmpp:chat-markers:0"); - } - @Override - public void onMessagePacketReceived(Account account, MessagePacket packet) { - Message message = null; - this.parseNick(packet, account); - if ((packet.getType() == MessagePacket.TYPE_CHAT || packet.getType() == MessagePacket.TYPE_NORMAL)) { - if ((packet.getBody() != null) - && (packet.getBody().startsWith("?OTR"))) { - message = this.parseOtrChat(packet, account); - if (message != null) { - message.markUnread(); - } - } else if (packet.hasChild("body") && extractInvite(packet) == null) { - message = this.parseChat(packet, account); - if (message != null) { - message.markUnread(); - } - } else if (packet.hasChild("received", "urn:xmpp:carbons:2") - || (packet.hasChild("sent", "urn:xmpp:carbons:2"))) { - message = this.parseCarbonMessage(packet, account); - if (message != null) { - if (message.getStatus() == Message.STATUS_SEND) { - account.activateGracePeriod(); - mXmppConnectionService.markRead(message.getConversation()); - } else { - 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"); + public void onMessagePacketReceived(Account account, MessagePacket original) { + final MessagePacket packet; + Long timestamp = null; + final boolean isForwarded; + boolean carbon = false; //live carbons or mam-sub + if (original.fromServer(account)) { + Pair f; + f = original.getForwardedMessagePacket("received", "urn:xmpp:carbons:2"); + f = f == null ? original.getForwardedMessagePacket("sent", "urn:xmpp:carbons:2") : f; + f = f == null ? original.getForwardedMessagePacket("result", "urn:xmpp:mam:0") : f; + packet = f != null ? f.first : original; + timestamp = f != null ? f.second : null; + isForwarded = f != null; + carbon = original.hasChild("received", "urn:xmpp:carbons:2") || original.hasChild("received", "urn:xmpp:carbons:2"); + + Element fin = packet.findChild("fin", "urn:xmpp:mam:0"); + if (fin != null) { mXmppConnectionService.getMessageArchiveService().processFin(fin); - } else { - parseNonMessage(packet, account); + return; } - } else if (packet.getType() == MessagePacket.TYPE_GROUPCHAT) { - message = this.parseGroupchat(packet, account); - if (message != null) { - if (message.getStatus() == Message.STATUS_RECEIVED) { - message.markUnread(); + + } else { + packet = original; + isForwarded = false; + } + if (timestamp == null) { + timestamp = AbstractParser.getTimestamp(packet, System.currentTimeMillis()); + } + final String body = packet.getBody(); + final String encrypted = packet.findChildContent("x", "jabber:x:encrypted"); + int status; + final Jid to = packet.getTo(); + final Jid from = packet.getFrom(); + final Jid counterpart; + final String id = packet.getId(); + boolean isTypeGroupChat = packet.getType() == MessagePacket.TYPE_GROUPCHAT; + boolean properlyAddressed = !to.isBareJid() || account.countPresences() == 1; + if (packet.fromAccount(account)) { + status = Message.STATUS_SEND; + counterpart = to; + } else { + status = Message.STATUS_RECEIVED; + counterpart = from; + } + + if (from == null || to == null) { + Log.d(Config.LOGTAG,"no to or from in: "+packet.toString()); + return; + } + + Invite invite = extractInvite(packet); + if (invite != null && invite.jid != null) { + Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, invite.jid, true); + if (!conversation.getMucOptions().online()) { + conversation.getMucOptions().setPassword(invite.password); + mXmppConnectionService.databaseBackend.updateConversation(conversation); + mXmppConnectionService.joinMuc(conversation); + mXmppConnectionService.updateConversationUi(); + } + return; + } + + if (extractChatState(mXmppConnectionService.find(account,from), packet)) { + mXmppConnectionService.updateConversationUi(); + } + + if (body != null || encrypted != null) { + Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, counterpart.toBareJid(), isTypeGroupChat); + if (isTypeGroupChat) { + if (counterpart.getResourcepart().equals(conversation.getMucOptions().getActualNick())) { + status = Message.STATUS_SEND; + if (mXmppConnectionService.markMessage(conversation, id, Message.STATUS_SEND_RECEIVED)) { + return; + } else if (id == null) { + Message message = conversation.findSentMessageWithBody(packet.getBody()); + if (message != null) { + mXmppConnectionService.markMessage(message, Message.STATUS_SEND_RECEIVED); + return; + } + } } else { - mXmppConnectionService.markRead(message.getConversation()); - account.activateGracePeriod(); + status = Message.STATUS_RECEIVED; } } - } else if (packet.getType() == MessagePacket.TYPE_ERROR) { - this.parseError(packet, account); - return; - } else if (packet.getType() == MessagePacket.TYPE_HEADLINE) { - this.parseHeadline(packet, account); - return; - } - if ((message == null) || (message.getBody() == null)) { - return; - } - if ((mXmppConnectionService.confirmMessages()) - && ((packet.getId() != null))) { - if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) { - MessagePacket receipt = mXmppConnectionService - .getMessageGenerator().received(account, packet, - "urn:xmpp:chat-markers:0"); - mXmppConnectionService.sendMessagePacket(account, receipt); + Message message; + if (body != null && body.startsWith("?OTR")) { + if (!isForwarded && !isTypeGroupChat && properlyAddressed) { + message = parseOtrChat(body, from, id, conversation); + if (message == null) { + return; + } + } else { + message = new Message(conversation, body, Message.ENCRYPTION_NONE, status); + } + } else if (encrypted != null) { + message = new Message(conversation, encrypted, Message.ENCRYPTION_PGP, status); + } else { + message = new Message(conversation, body, Message.ENCRYPTION_NONE, status); } - if (packet.hasChild("request", "urn:xmpp:receipts")) { - MessagePacket receipt = mXmppConnectionService - .getMessageGenerator().received(account, packet, - "urn:xmpp:receipts"); - mXmppConnectionService.sendMessagePacket(account, receipt); + message.setCounterpart(counterpart); + message.setRemoteMsgId(id); + message.setTime(timestamp); + message.markable = packet.hasChild("markable", "urn:xmpp:chat-markers:0"); + if (conversation.getMode() == Conversation.MODE_MULTI) { + message.setTrueCounterpart(conversation.getMucOptions().getTrueCounterpart(counterpart.getResourcepart())); + if (!isTypeGroupChat) { + message.setType(Message.TYPE_PRIVATE); + } } - } - Conversation conversation = message.getConversation(); - conversation.add(message); - if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().advancedStreamFeaturesLoaded()) { - if (conversation.setLastMessageTransmitted(System.currentTimeMillis())) { - mXmppConnectionService.updateConversation(conversation); + updateLastseen(packet,account,true); + conversation.add(message); + if (carbon || status == Message.STATUS_RECEIVED) { + mXmppConnectionService.markRead(conversation); + account.activateGracePeriod(); + } else if (!isForwarded) { + message.markUnread(); } - } - if (message.getStatus() == Message.STATUS_RECEIVED - && conversation.getOtrSession() != null - && !conversation.getOtrSession().getSessionID().getUserID() - .equals(message.getCounterpart().getResourcepart())) { - conversation.endOtrIfNeeded(); - } - if (packet.getType() != MessagePacket.TYPE_ERROR) { + if (mXmppConnectionService.confirmMessages() && id != null && !packet.fromAccount(account)) { + if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) { + MessagePacket receipt = mXmppConnectionService + .getMessageGenerator().received(account, packet, "urn:xmpp:chat-markers:0"); + mXmppConnectionService.sendMessagePacket(account, receipt); + } + if (packet.hasChild("request", "urn:xmpp:receipts")) { + MessagePacket receipt = mXmppConnectionService + .getMessageGenerator().received(account, packet, "urn:xmpp:receipts"); + mXmppConnectionService.sendMessagePacket(account, receipt); + } + } + 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())) { + conversation.endOtrIfNeeded(); + } + if (message.getEncryption() == Message.ENCRYPTION_NONE || mXmppConnectionService.saveEncryptedMessages()) { mXmppConnectionService.databaseBackend.createMessage(message); } - } - final HttpConnectionManager manager = this.mXmppConnectionService.getHttpConnectionManager(); - if (message.trusted() && message.bodyContainsDownloadable() && manager.getAutoAcceptFileSize() > 0) { - manager.createNewConnection(message); - } else if (!message.isRead()) { - mXmppConnectionService.getNotificationService().push(message); - } - mXmppConnectionService.updateConversationUi(); - } - - private void parseHeadline(MessagePacket packet, Account account) { - if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) { - Element event = packet.findChild("event", - "http://jabber.org/protocol/pubsub#event"); - parseEvent(event, packet.getFrom(), account); - } - } - - private void parseNick(MessagePacket packet, Account account) { - Element nick = packet.findChild("nick", - "http://jabber.org/protocol/nick"); - if (nick != null) { - if (packet.getFrom() != null) { - Contact contact = account.getRoster().getContact( - packet.getFrom()); - contact.setPresenceName(nick.getContent()); + final HttpConnectionManager manager = this.mXmppConnectionService.getHttpConnectionManager(); + if (message.trusted() && message.bodyContainsDownloadable() && manager.getAutoAcceptFileSize() > 0) { + manager.createNewConnection(message); + } else if (!message.isRead()) { + mXmppConnectionService.getNotificationService().push(message); + } + mXmppConnectionService.updateConversationUi(); + } else { + if (packet.hasChild("subject") && isTypeGroupChat) { + Conversation conversation = mXmppConnectionService.find(account, from.toBareJid()); + if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) { + conversation.setHasMessagesLeftOnServer(true); + conversation.getMucOptions().setSubject(packet.findChildContent("subject")); + mXmppConnectionService.updateConversationUi(); + return; + } } } + + Element received = packet.findChild("received", "urn:xmpp:chat-markers:0"); + if (received == null) { + received = packet.findChild("received", "urn:xmpp:receipts"); + } + if (received != null && !packet.fromAccount(account)) { + mXmppConnectionService.markMessage(account, from.toBareJid(), received.getAttribute("id"), Message.STATUS_SEND_RECEIVED); + } + Element displayed = packet.findChild("displayed", "urn:xmpp:chat-markers:0"); + if (displayed != null) { + if (packet.fromAccount(account)) { + Conversation conversation = mXmppConnectionService.find(account,counterpart.toBareJid()); + mXmppConnectionService.markRead(conversation); + } else { + updateLastseen(packet, account, true); + final Message displayedMessage = mXmppConnectionService.markMessage(account, from.toBareJid(), displayed.getAttribute("id"), Message.STATUS_SEND_DISPLAYED); + Message message = displayedMessage == null ? null : displayedMessage.prev(); + while (message != null + && message.getStatus() == Message.STATUS_SEND_RECEIVED + && message.getTimeSent() < displayedMessage.getTimeSent()) { + mXmppConnectionService.markMessage(message, Message.STATUS_SEND_DISPLAYED); + message = message.prev(); + } + } + } + + Element event = packet.findChild("event", "http://jabber.org/protocol/pubsub#event"); + if (event != null) { + parseEvent(event, from, account); + } + + String nick = packet.findChildContent("nick", "http://jabber.org/protocol/nick"); + if (nick != null) { + Contact contact = account.getRoster().getContact(from); + contact.setPresenceName(nick); + } } -} +} \ No newline at end of file diff --git a/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java b/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java index 55256ece..bd706b57 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java +++ b/src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java @@ -51,4 +51,8 @@ public class AbstractStanza extends Element { || getTo().equals(account.getJid().toBareJid()) || getTo().equals(account.getJid()); } + + public boolean fromAccount(final Account account) { + return getFrom() != null && getFrom().toBareJid().equals(account.getJid().toBareJid()); + } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java b/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java index 11c3452b..e32811af 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java +++ b/src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java @@ -1,5 +1,10 @@ package eu.siacs.conversations.xmpp.stanzas; +import android.util.Pair; + +import java.text.ParseException; + +import eu.siacs.conversations.parser.AbstractParser; import eu.siacs.conversations.xml.Element; public class MessagePacket extends AbstractStanza { @@ -14,12 +19,7 @@ public class MessagePacket extends AbstractStanza { } public String getBody() { - Element body = this.findChild("body"); - if (body != null) { - return body.getContent(); - } else { - return null; - } + return findChildContent("body"); } public void setBody(String text) { @@ -67,19 +67,23 @@ public class MessagePacket extends AbstractStanza { } } - public MessagePacket getForwardedMessagePacket(String name, String namespace) { + public Pair getForwardedMessagePacket(String name, String namespace) { Element wrapper = findChild(name, namespace); if (wrapper == null) { return null; } - Element forwarded = wrapper.findChild("forwarded","urn:xmpp:forward:0"); + Element forwarded = wrapper.findChild("forwarded", "urn:xmpp:forward:0"); if (forwarded == null) { return null; } - return MessagePacket.create(forwarded.findChild("message")); + MessagePacket packet = create(forwarded.findChild("message")); + if (packet == null) { + return null; + } + Long timestamp = AbstractParser.getTimestamp(forwarded,null); + return new Pair(packet,timestamp); } - public static MessagePacket create(Element element) { if (element == null) { return null; From b731995a517932d1c86877da13dd69d66cbb23c1 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 15 May 2015 06:31:27 +0200 Subject: [PATCH 03/37] added mam stuff to new message parser --- .../siacs/conversations/entities/Message.java | 2 +- .../conversations/parser/AbstractParser.java | 2 +- .../conversations/parser/MessageParser.java | 33 +++++++++++++++---- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index 38152edb..90b74a53 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -328,7 +328,7 @@ public class Message extends AbstractEntity { return this.remoteMsgId == null && this.counterpart.equals(message.getCounterpart()) && this.body.equals(message.getBody()) - && Math.abs(this.getTimeSent() - message.getTimeSent()) < Config.PING_TIMEOUT * 500; + && Math.abs(this.getTimeSent() - message.getTimeSent()) < Config.MESSAGE_MERGE_WINDOW * 1000; } } diff --git a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java index 7b3eb7e9..24e93db1 100644 --- a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java +++ b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java @@ -22,7 +22,7 @@ public abstract class AbstractParser { } public static Long getTimestamp(Element element, Long defaultValue) { - Element delay = element.findChild("delay"); + Element delay = element.findChild("delay","urn:xmpp:delay"); if (delay != null) { String stamp = delay.getAttribute("stamp"); if (stamp != null) { diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 5a2fcbe1..69f58898 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -182,6 +182,8 @@ public class MessageParser extends AbstractParser implements Long timestamp = null; final boolean isForwarded; boolean carbon = false; //live carbons or mam-sub + MessageArchiveService.Query query = null; + String serverMsgId = null; if (original.fromServer(account)) { Pair f; f = original.getForwardedMessagePacket("received", "urn:xmpp:carbons:2"); @@ -198,6 +200,14 @@ public class MessageParser extends AbstractParser implements return; } + final Element result = packet.findChild("result","urn:xmpp:mam:0"); + if (result != null) { + query = mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid")); + if (query != null) { + query.incrementTotalCount(); + } + serverMsgId = result.getAttribute("id"); + } } else { packet = original; isForwarded = false; @@ -211,7 +221,7 @@ public class MessageParser extends AbstractParser implements final Jid to = packet.getTo(); final Jid from = packet.getFrom(); final Jid counterpart; - final String id = packet.getId(); + final String remoteMsgId = packet.getId(); boolean isTypeGroupChat = packet.getType() == MessagePacket.TYPE_GROUPCHAT; boolean properlyAddressed = !to.isBareJid() || account.countPresences() == 1; if (packet.fromAccount(account)) { @@ -229,7 +239,7 @@ public class MessageParser extends AbstractParser implements Invite invite = extractInvite(packet); if (invite != null && invite.jid != null) { - Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, invite.jid, true); + Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, invite.jid, true,query); if (!conversation.getMucOptions().online()) { conversation.getMucOptions().setPassword(invite.password); mXmppConnectionService.databaseBackend.updateConversation(conversation); @@ -248,9 +258,9 @@ public class MessageParser extends AbstractParser implements if (isTypeGroupChat) { if (counterpart.getResourcepart().equals(conversation.getMucOptions().getActualNick())) { status = Message.STATUS_SEND; - if (mXmppConnectionService.markMessage(conversation, id, Message.STATUS_SEND_RECEIVED)) { + if (mXmppConnectionService.markMessage(conversation, remoteMsgId, Message.STATUS_SEND_RECEIVED)) { return; - } else if (id == null) { + } else if (remoteMsgId == null) { Message message = conversation.findSentMessageWithBody(packet.getBody()); if (message != null) { mXmppConnectionService.markMessage(message, Message.STATUS_SEND_RECEIVED); @@ -264,7 +274,7 @@ public class MessageParser extends AbstractParser implements Message message; if (body != null && body.startsWith("?OTR")) { if (!isForwarded && !isTypeGroupChat && properlyAddressed) { - message = parseOtrChat(body, from, id, conversation); + message = parseOtrChat(body, from, remoteMsgId, conversation); if (message == null) { return; } @@ -277,7 +287,8 @@ public class MessageParser extends AbstractParser implements message = new Message(conversation, body, Message.ENCRYPTION_NONE, status); } message.setCounterpart(counterpart); - message.setRemoteMsgId(id); + message.setRemoteMsgId(remoteMsgId); + message.setServerMsgId(serverMsgId); message.setTime(timestamp); message.markable = packet.hasChild("markable", "urn:xmpp:chat-markers:0"); if (conversation.getMode() == Conversation.MODE_MULTI) { @@ -287,6 +298,14 @@ public class MessageParser extends AbstractParser implements } } updateLastseen(packet,account,true); + boolean checkForDuplicates = serverMsgId != null || (isTypeGroupChat && packet.hasChild("delay","urn:xmpp:delay")); + if (checkForDuplicates && conversation.hasDuplicateMessage(message)) { + Log.d(Config.LOGTAG,"skipping duplicate message from "+message.getCounterpart().toString()+" "+message.getBody()); + return; + } + if (query != null) { + query.incrementMessageCount(); + } conversation.add(message); if (carbon || status == Message.STATUS_RECEIVED) { mXmppConnectionService.markRead(conversation); @@ -296,7 +315,7 @@ public class MessageParser extends AbstractParser implements } - if (mXmppConnectionService.confirmMessages() && id != null && !packet.fromAccount(account)) { + if (mXmppConnectionService.confirmMessages() && remoteMsgId != null && !isForwarded) { if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) { MessagePacket receipt = mXmppConnectionService .getMessageGenerator().received(account, packet, "urn:xmpp:chat-markers:0"); From eeebebe32afef42cb3be07641d0c008a67f766ab Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 15 May 2015 11:51:20 +0200 Subject: [PATCH 04/37] fixed read/unread markers --- .../conversations/parser/MessageParser.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 69f58898..339aeb7f 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -181,7 +181,6 @@ public class MessageParser extends AbstractParser implements final MessagePacket packet; Long timestamp = null; final boolean isForwarded; - boolean carbon = false; //live carbons or mam-sub MessageArchiveService.Query query = null; String serverMsgId = null; if (original.fromServer(account)) { @@ -192,15 +191,14 @@ public class MessageParser extends AbstractParser implements packet = f != null ? f.first : original; timestamp = f != null ? f.second : null; isForwarded = f != null; - carbon = original.hasChild("received", "urn:xmpp:carbons:2") || original.hasChild("received", "urn:xmpp:carbons:2"); - Element fin = packet.findChild("fin", "urn:xmpp:mam:0"); + Element fin = original.findChild("fin", "urn:xmpp:mam:0"); if (fin != null) { mXmppConnectionService.getMessageArchiveService().processFin(fin); return; } - final Element result = packet.findChild("result","urn:xmpp:mam:0"); + final Element result = original.findChild("result","urn:xmpp:mam:0"); if (result != null) { query = mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid")); if (query != null) { @@ -307,14 +305,15 @@ public class MessageParser extends AbstractParser implements query.incrementMessageCount(); } conversation.add(message); - if (carbon || status == Message.STATUS_RECEIVED) { - mXmppConnectionService.markRead(conversation); - account.activateGracePeriod(); - } else if (!isForwarded) { - message.markUnread(); + if (serverMsgId == null) { + if (status == Message.STATUS_SEND) { + mXmppConnectionService.markRead(conversation); + account.activateGracePeriod(); + } else { + message.markUnread(); + } } - if (mXmppConnectionService.confirmMessages() && remoteMsgId != null && !isForwarded) { if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) { MessagePacket receipt = mXmppConnectionService From 1b5631c835d34fc9f7a3474b015a21ffc64ed0d2 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 15 May 2015 12:29:45 +0200 Subject: [PATCH 05/37] fixed muc mam. added a few security checks --- .../conversations/parser/MessageParser.java | 48 ++++++++++--------- .../services/MessageArchiveService.java | 12 ++++- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 339aeb7f..d254a863 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -181,31 +181,34 @@ public class MessageParser extends AbstractParser implements final MessagePacket packet; Long timestamp = null; final boolean isForwarded; - MessageArchiveService.Query query = null; String serverMsgId = null; - if (original.fromServer(account)) { + final Element fin = original.findChild("fin", "urn:xmpp:mam:0"); + if (fin != null) { + mXmppConnectionService.getMessageArchiveService().processFin(fin,original.getFrom()); + return; + } + final Element result = original.findChild("result","urn:xmpp:mam:0"); + final MessageArchiveService.Query query = result == null ? null : mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid")); + if (query != null && query.validFrom(original.getFrom())) { + Pair f = original.getForwardedMessagePacket("result", "urn:xmpp:mam:0"); + if (f == null) { + return; + } + timestamp = f.second; + packet = f.first; + isForwarded = true; + serverMsgId = result.getAttribute("id"); + query.incrementTotalCount(); + } else if (query != null) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": received mam result from invalid sender"); + return; + } else if (original.fromServer(account)) { Pair f; f = original.getForwardedMessagePacket("received", "urn:xmpp:carbons:2"); f = f == null ? original.getForwardedMessagePacket("sent", "urn:xmpp:carbons:2") : f; - f = f == null ? original.getForwardedMessagePacket("result", "urn:xmpp:mam:0") : f; packet = f != null ? f.first : original; timestamp = f != null ? f.second : null; isForwarded = f != null; - - Element fin = original.findChild("fin", "urn:xmpp:mam:0"); - if (fin != null) { - mXmppConnectionService.getMessageArchiveService().processFin(fin); - return; - } - - final Element result = original.findChild("result","urn:xmpp:mam:0"); - if (result != null) { - query = mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid")); - if (query != null) { - query.incrementTotalCount(); - } - serverMsgId = result.getAttribute("id"); - } } else { packet = original; isForwarded = false; @@ -216,9 +219,9 @@ public class MessageParser extends AbstractParser implements final String body = packet.getBody(); final String encrypted = packet.findChildContent("x", "jabber:x:encrypted"); int status; + final Jid counterpart; final Jid to = packet.getTo(); final Jid from = packet.getFrom(); - final Jid counterpart; final String remoteMsgId = packet.getId(); boolean isTypeGroupChat = packet.getType() == MessagePacket.TYPE_GROUPCHAT; boolean properlyAddressed = !to.isBareJid() || account.countPresences() == 1; @@ -312,6 +315,7 @@ public class MessageParser extends AbstractParser implements } else { message.markUnread(); } + mXmppConnectionService.updateConversationUi(); } if (mXmppConnectionService.confirmMessages() && remoteMsgId != null && !isForwarded) { @@ -339,8 +343,7 @@ public class MessageParser extends AbstractParser implements conversation.endOtrIfNeeded(); } - if (message.getEncryption() == Message.ENCRYPTION_NONE - || mXmppConnectionService.saveEncryptedMessages()) { + if (message.getEncryption() == Message.ENCRYPTION_NONE || mXmppConnectionService.saveEncryptedMessages()) { mXmppConnectionService.databaseBackend.createMessage(message); } final HttpConnectionManager manager = this.mXmppConnectionService.getHttpConnectionManager(); @@ -349,8 +352,7 @@ public class MessageParser extends AbstractParser implements } else if (!message.isRead()) { mXmppConnectionService.getNotificationService().push(message); } - mXmppConnectionService.updateConversationUi(); - } else { + } else { //no body if (packet.hasChild("subject") && isTypeGroupChat) { Conversation conversation = mXmppConnectionService.find(account, from.toBareJid()); if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) { diff --git a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java index f97077c4..0bc428c8 100644 --- a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java +++ b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java @@ -166,12 +166,12 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { } } - public void processFin(Element fin) { + public void processFin(Element fin, Jid from) { if (fin == null) { return; } Query query = findQuery(fin.getAttribute("queryid")); - if (query == null) { + if (query == null || !query.validFrom(from)) { return; } boolean complete = fin.getAttributeAsBoolean("complete"); @@ -336,6 +336,14 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { return this.messageCount; } + public boolean validFrom(Jid from) { + if (muc()) { + return getWith().equals(from); + } else { + return (from == null) || account.getJid().toBareJid().equals(from.toBareJid()); + } + } + @Override public String toString() { StringBuilder builder = new StringBuilder(); From 9658146575841c402f1492bcc53b02bdddc4d458 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 16 May 2015 12:43:38 +0200 Subject: [PATCH 06/37] fixed npe in new message parser --- .../java/eu/siacs/conversations/parser/MessageParser.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index d254a863..d2fa2c24 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -375,7 +375,9 @@ public class MessageParser extends AbstractParser implements if (displayed != null) { if (packet.fromAccount(account)) { Conversation conversation = mXmppConnectionService.find(account,counterpart.toBareJid()); - mXmppConnectionService.markRead(conversation); + if (conversation != null) { + mXmppConnectionService.markRead(conversation); + } } else { updateLastseen(packet, account, true); final Message displayedMessage = mXmppConnectionService.markMessage(account, from.toBareJid(), displayed.getAttribute("id"), Message.STATUS_SEND_DISPLAYED); From 201bc158bd9fa77c1ffcea53758280bd830e5710 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 19 May 2015 08:23:12 +0200 Subject: [PATCH 07/37] proper error parsing. some clean up --- .../conversations/parser/MessageParser.java | 90 ++++++++++--------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index d2fa2c24..1e683892 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -101,6 +101,20 @@ public class MessageParser extends AbstractParser implements this.jid = jid; this.password = password; } + + public boolean execute(Account account) { + if (jid != null) { + Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, jid, true); + if (!conversation.getMucOptions().online()) { + conversation.getMucOptions().setPassword(password); + mXmppConnectionService.databaseBackend.updateConversation(conversation); + mXmppConnectionService.joinMuc(conversation); + mXmppConnectionService.updateConversationUi(); + } + return true; + } + return false; + } } private Invite extractInvite(Element message) { @@ -122,34 +136,23 @@ public class MessageParser extends AbstractParser implements private void parseEvent(final Element event, final Jid from, final Account account) { Element items = event.findChild("items"); - if (items == null) { - return; - } - String node = items.getAttribute("node"); - if (node == null) { - return; - } - if (node.equals("urn:xmpp:avatar:metadata")) { + String node = items == null ? null : items.getAttribute("node"); + if ("urn:xmpp:avatar:metadata".equals(node)) { Avatar avatar = Avatar.parseMetadata(items); if (avatar != null) { avatar.owner = from; - if (mXmppConnectionService.getFileBackend().isAvatarCached( - avatar)) { + if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) { if (account.getJid().toBareJid().equals(from)) { if (account.setAvatar(avatar.getFilename())) { - mXmppConnectionService.databaseBackend - .updateAccount(account); + mXmppConnectionService.databaseBackend.updateAccount(account); } - mXmppConnectionService.getAvatarService().clear( - account); + mXmppConnectionService.getAvatarService().clear(account); mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateAccountUi(); } else { - Contact contact = account.getRoster().getContact( - from); + Contact contact = account.getRoster().getContact(from); contact.setAvatar(avatar); - mXmppConnectionService.getAvatarService().clear( - contact); + mXmppConnectionService.getAvatarService().clear(contact); mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateRosterUi(); } @@ -157,27 +160,35 @@ public class MessageParser extends AbstractParser implements mXmppConnectionService.fetchAvatar(account, avatar); } } - } else if (node.equals("http://jabber.org/protocol/nick")) { - Element item = items.findChild("item"); - if (item != null) { - Element nick = item.findChild("nick", - "http://jabber.org/protocol/nick"); - if (nick != null) { - if (from != null) { - Contact contact = account.getRoster().getContact( - from); - contact.setPresenceName(nick.getContent()); - mXmppConnectionService.getAvatarService().clear(account); - mXmppConnectionService.updateConversationUi(); - mXmppConnectionService.updateAccountUi(); - } - } + } else if ("http://jabber.org/protocol/nick".equals(node)) { + Element i = items.findChild("item"); + Element nick = i == null ? null : i.findChild("nick", "http://jabber.org/protocol/nick"); + if (nick != null) { + Contact contact = account.getRoster().getContact(from); + contact.setPresenceName(nick.getContent()); + mXmppConnectionService.getAvatarService().clear(account); + mXmppConnectionService.updateConversationUi(); + mXmppConnectionService.updateAccountUi(); } } } + private boolean handleErrorMessage(Account account, MessagePacket packet) { + if (packet.getType() == MessagePacket.TYPE_ERROR) { + Jid from = packet.getFrom(); + if (from != null) { + mXmppConnectionService.markMessage(account, from.toBareJid(), packet.getId(), Message.STATUS_SEND_FAILED); + } + return true; + } + return false; + } + @Override public void onMessagePacketReceived(Account account, MessagePacket original) { + if (handleErrorMessage(account, original)) { + return; + } final MessagePacket packet; Long timestamp = null; final boolean isForwarded; @@ -207,12 +218,16 @@ public class MessageParser extends AbstractParser implements f = original.getForwardedMessagePacket("received", "urn:xmpp:carbons:2"); f = f == null ? original.getForwardedMessagePacket("sent", "urn:xmpp:carbons:2") : f; packet = f != null ? f.first : original; + if (handleErrorMessage(account, packet)) { + return; + } timestamp = f != null ? f.second : null; isForwarded = f != null; } else { packet = original; isForwarded = false; } + if (timestamp == null) { timestamp = AbstractParser.getTimestamp(packet, System.currentTimeMillis()); } @@ -239,14 +254,7 @@ public class MessageParser extends AbstractParser implements } Invite invite = extractInvite(packet); - if (invite != null && invite.jid != null) { - Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, invite.jid, true,query); - if (!conversation.getMucOptions().online()) { - conversation.getMucOptions().setPassword(invite.password); - mXmppConnectionService.databaseBackend.updateConversation(conversation); - mXmppConnectionService.joinMuc(conversation); - mXmppConnectionService.updateConversationUi(); - } + if (invite != null && invite.execute(account)) { return; } From 78847d0749765b7c1e424a8a264d8f23a43170b0 Mon Sep 17 00:00:00 2001 From: "M. Dietrich" Date: Wed, 20 May 2015 11:34:11 +0200 Subject: [PATCH 08/37] add extra jid to intend "show location" --- src/main/java/eu/siacs/conversations/utils/GeoHelper.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/eu/siacs/conversations/utils/GeoHelper.java b/src/main/java/eu/siacs/conversations/utils/GeoHelper.java index b31b9018..dd8ebd3d 100644 --- a/src/main/java/eu/siacs/conversations/utils/GeoHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/GeoHelper.java @@ -54,6 +54,7 @@ public class GeoHelper { Intent locationPluginIntent = new Intent("eu.siacs.conversations.location.show"); locationPluginIntent.putExtra("latitude",latitude); locationPluginIntent.putExtra("longitude",longitude); + locationPluginIntent.putExtra("jid",conversation.getJid().toString()); if (conversation.getMode() == Conversation.MODE_SINGLE && message.getStatus() == Message.STATUS_RECEIVED) { locationPluginIntent.putExtra("name",conversation.getName()); } From a4ec7a7df1b840e52d56bc7b31c629a0e96b6fc5 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 20 May 2015 12:26:38 +0200 Subject: [PATCH 09/37] pulled translations from transifex --- src/main/res/values-bg/strings.xml | 5 +++++ src/main/res/values-de/strings.xml | 5 +++++ src/main/res/values-id/strings.xml | 5 +++++ src/main/res/values-nl/strings.xml | 5 +++++ src/main/res/values-pl/strings.xml | 5 +++++ src/main/res/values-sr/strings.xml | 5 +++++ src/main/res/values-sv/strings.xml | 5 +++++ 7 files changed, 35 insertions(+) diff --git a/src/main/res/values-bg/strings.xml b/src/main/res/values-bg/strings.xml index b27481dd..52bb0054 100644 --- a/src/main/res/values-bg/strings.xml +++ b/src/main/res/values-bg/strings.xml @@ -446,4 +446,9 @@ Изберете %d контакт Изберете %d контакта + Замяна на бутона за изпращане с бързо действие + Бързо действие + Нищо + Използвани наскоро + Изберете бързо действие diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index 08b0d4a5..000c253b 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -446,4 +446,9 @@ %d Kontakt ausgewählt %d Kontakte ausgewählt + Ersetze Absende-Knopf durch Schnell-Tasten + Schnell-Tasten + keine + zuletzt verwendet + wähle Schnell-Taste diff --git a/src/main/res/values-id/strings.xml b/src/main/res/values-id/strings.xml index 69bd0911..3ce9cc82 100644 --- a/src/main/res/values-id/strings.xml +++ b/src/main/res/values-id/strings.xml @@ -444,4 +444,9 @@ Pilih %d kontak + Timpa tombol kirim dengan aksi cepat + Aksi Cepat + Tak satupun + Maling sering digunakan + Pilih aksi cepat diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index 2532130d..e3f0e69b 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -446,4 +446,9 @@ Selecteer %d contact Selecteer %d contacten + Vervang verzendknop door snelle actie + Snelle actie + Geen + Recent gebruikt + Kies snelle actie diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index 70c3a49d..6a983f50 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -448,4 +448,9 @@ %d kontakty wybrane %d kontaktów wybranych + Zastąp przycisk wysyłania szybką akcją + Szybka akcja + Brak + Ostatnio używana + Wybierz szybką akcję diff --git a/src/main/res/values-sr/strings.xml b/src/main/res/values-sr/strings.xml index ea5cbdb8..60f2e586 100644 --- a/src/main/res/values-sr/strings.xml +++ b/src/main/res/values-sr/strings.xml @@ -448,4 +448,9 @@ Изабери %d контакта Изабери %d контаката + Замени дугме за слање брзом радњом + Брза радња + Ниједна + Недавно коришћена + Изаберите брзу радњу diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml index 4f84110b..9c60db53 100644 --- a/src/main/res/values-sv/strings.xml +++ b/src/main/res/values-sv/strings.xml @@ -446,4 +446,9 @@ Välj %d kontakt Välj %d kontakter + Byt sänd-knappen mot snabbfunktion + Snabbfunktion + Ingen + Senast använd + Välj snabbfunktion From 2364710afb345dfd797a1832c25b134fd34b0e29 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 20 May 2015 12:47:04 +0200 Subject: [PATCH 10/37] added ShortcutBadger as a dependency to create unread counts on launcher icon --- build.gradle | 5 ++-- .../services/NotificationService.java | 1 + .../services/XmppConnectionService.java | 24 ++++++++++++++----- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index d572892d..635da8f8 100644 --- a/build.gradle +++ b/build.gradle @@ -35,6 +35,7 @@ dependencies { compile 'com.google.zxing:android-integration:3.1.0' compile 'de.measite.minidns:minidns:0.1.3' compile 'de.timroes.android:EnhancedListView:0.3.4' + compile 'me.leolin:ShortcutBadger:1.1.1@aar' } android { @@ -44,8 +45,8 @@ android { defaultConfig { minSdkVersion 14 targetSdkVersion 21 - versionCode 67 - versionName "1.4.1" + versionCode 68 + versionName "1.5.0-alpha" } compileOptions { diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java index e111da95..49543eeb 100644 --- a/src/main/java/eu/siacs/conversations/services/NotificationService.java +++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java @@ -134,6 +134,7 @@ public class NotificationService { } public void push(final Message message) { + mXmppConnectionService.updateUnreadCountBadge(); if (!notify(message)) { return; } diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 3a74b82b..f682bccd 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -100,6 +100,7 @@ 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; +import me.leolin.shortcutbadger.ShortcutBadger; public class XmppConnectionService extends Service implements OnPhoneContactsLoadedListener { @@ -1033,7 +1034,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void loadMoreMessages(final Conversation conversation, final long timestamp, final OnMoreMessagesLoaded callback) { - Log.d(Config.LOGTAG,"load more messages for "+conversation.getName() + " prior to "+MessageGenerator.getTimestamp(timestamp)); + Log.d(Config.LOGTAG, "load more messages for " + conversation.getName() + " prior to " + MessageGenerator.getTimestamp(timestamp)); if (XmppConnectionService.this.getMessageArchiveService().queryInProgress(conversation,callback)) { return; } @@ -1985,14 +1986,14 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa private void fetchAvatarVcard(final Account account, final Avatar avatar, final UiCallback callback) { IqPacket packet = this.mIqGenerator.retrieveVcardAvatar(avatar); - this.sendIqPacket(account,packet,new OnIqPacketReceived() { + this.sendIqPacket(account, packet, new OnIqPacketReceived() { @Override public void onIqPacketReceived(Account account, IqPacket packet) { - synchronized(mInProgressAvatarFetches) { - mInProgressAvatarFetches.remove(generateFetchKey(account,avatar)); + synchronized (mInProgressAvatarFetches) { + mInProgressAvatarFetches.remove(generateFetchKey(account, avatar)); } if (packet.getType() == IqPacket.TYPE.RESULT) { - Element vCard = packet.findChild("vCard","vcard-temp"); + Element vCard = packet.findChild("vCard", "vcard-temp"); Element photo = vCard != null ? vCard.findChild("PHOTO") : null; String image = photo != null ? photo.findChildContent("BINVAL") : null; if (image != null) { @@ -2110,7 +2111,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void directInvite(Conversation conversation, Jid jid) { - MessagePacket packet = mMessageGenerator.directInvite(conversation,jid); + MessagePacket packet = mMessageGenerator.directInvite(conversation, jid); sendMessagePacket(conversation.getAccount(),packet); } @@ -2254,6 +2255,17 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void markRead(final Conversation conversation) { mNotificationService.clear(conversation); conversation.markRead(); + updateUnreadCountBadge(); + } + + public void updateUnreadCountBadge() { + int count = unreadCount(); + Log.d(Config.LOGTAG,"update unread count to "+count); + if (count > 0) { + ShortcutBadger.with(getApplicationContext()).count(count); + } else { + ShortcutBadger.with(getApplicationContext()).remove(); + } } public void sendReadMarker(final Conversation conversation) { From 4759607a772642735d57b476c9c474b128695027 Mon Sep 17 00:00:00 2001 From: "M. Dietrich" Date: Wed, 20 May 2015 15:43:45 +0200 Subject: [PATCH 11/37] fix to detect the sender jid correctly --- .../java/eu/siacs/conversations/utils/GeoHelper.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/utils/GeoHelper.java b/src/main/java/eu/siacs/conversations/utils/GeoHelper.java index dd8ebd3d..74f91a98 100644 --- a/src/main/java/eu/siacs/conversations/utils/GeoHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/GeoHelper.java @@ -54,9 +54,14 @@ public class GeoHelper { Intent locationPluginIntent = new Intent("eu.siacs.conversations.location.show"); locationPluginIntent.putExtra("latitude",latitude); locationPluginIntent.putExtra("longitude",longitude); - locationPluginIntent.putExtra("jid",conversation.getJid().toString()); - if (conversation.getMode() == Conversation.MODE_SINGLE && message.getStatus() == Message.STATUS_RECEIVED) { - locationPluginIntent.putExtra("name",conversation.getName()); + if (conversation.getMode() == Conversation.MODE_SINGLE) { + if (message.getStatus() == Message.STATUS_RECEIVED) { + locationPluginIntent.putExtra("name",conversation.getName()); + locationPluginIntent.putExtra("jid",message.getCounterpart().toString()); + } + else { + locationPluginIntent.putExtra("jid",conversation.getAccount().getJid().toString()); + } } intents.add(locationPluginIntent); From dc91ff8f291d4b142d28e77137a74dc8e7205149 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 26 May 2015 04:36:32 +0200 Subject: [PATCH 12/37] renamed OtrEngine to OtrService --- .../crypto/{OtrEngine.java => OtrService.java} | 6 +++--- .../eu/siacs/conversations/entities/Account.java | 16 ++++++++-------- .../conversations/entities/Conversation.java | 4 ++-- .../services/XmppConnectionService.java | 6 +++--- 4 files changed, 16 insertions(+), 16 deletions(-) rename src/main/java/eu/siacs/conversations/crypto/{OtrEngine.java => OtrService.java} (97%) diff --git a/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java b/src/main/java/eu/siacs/conversations/crypto/OtrService.java similarity index 97% rename from src/main/java/eu/siacs/conversations/crypto/OtrEngine.java rename to src/main/java/eu/siacs/conversations/crypto/OtrService.java index 0dc7c37e..1e905bac 100644 --- a/src/main/java/eu/siacs/conversations/crypto/OtrEngine.java +++ b/src/main/java/eu/siacs/conversations/crypto/OtrService.java @@ -36,14 +36,14 @@ import net.java.otr4j.session.InstanceTag; import net.java.otr4j.session.SessionID; import net.java.otr4j.session.FragmenterInstructions; -public class OtrEngine extends OtrCryptoEngineImpl implements OtrEngineHost { +public class OtrService extends OtrCryptoEngineImpl implements OtrEngineHost { private Account account; private OtrPolicy otrPolicy; private KeyPair keyPair; private XmppConnectionService mXmppConnectionService; - public OtrEngine(XmppConnectionService service, Account account) { + public OtrService(XmppConnectionService service, Account account) { this.account = account; this.otrPolicy = new OtrPolicyImpl(); this.otrPolicy.setAllowV1(false); @@ -285,7 +285,7 @@ public class OtrEngine extends OtrCryptoEngineImpl implements OtrEngineHost { @Override public void verify(SessionID id, String fingerprint, boolean approved) { - Log.d(Config.LOGTAG,"OtrEngine.verify("+id.toString()+","+fingerprint+","+String.valueOf(approved)+")"); + Log.d(Config.LOGTAG,"OtrService.verify("+id.toString()+","+fingerprint+","+String.valueOf(approved)+")"); try { final Jid jid = Jid.fromSessionID(id); Conversation conversation = this.mXmppConnectionService.find(this.account,jid); diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java index fe103094..6e6dcb6a 100644 --- a/src/main/java/eu/siacs/conversations/entities/Account.java +++ b/src/main/java/eu/siacs/conversations/entities/Account.java @@ -19,7 +19,7 @@ import java.util.concurrent.CopyOnWriteArraySet; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; -import eu.siacs.conversations.crypto.OtrEngine; +import eu.siacs.conversations.crypto.OtrService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.jid.InvalidJidException; @@ -117,7 +117,7 @@ public class Account extends AbstractEntity { protected JSONObject keys = new JSONObject(); protected String avatar; protected boolean online = false; - private OtrEngine otrEngine = null; + private OtrService mOtrService = null; private XmppConnection xmppConnection = null; private long mEndGracePeriod = 0L; private String otrFingerprint; @@ -273,12 +273,12 @@ public class Account extends AbstractEntity { return values; } - public void initOtrEngine(final XmppConnectionService context) { - this.otrEngine = new OtrEngine(context, this); + public void initAccountServices(final XmppConnectionService context) { + this.mOtrService = new OtrService(context, this); } - public OtrEngine getOtrEngine() { - return this.otrEngine; + public OtrService getOtrService() { + return this.mOtrService; } public XmppConnection getXmppConnection() { @@ -292,10 +292,10 @@ public class Account extends AbstractEntity { public String getOtrFingerprint() { if (this.otrFingerprint == null) { try { - if (this.otrEngine == null) { + if (this.mOtrService == null) { return null; } - final PublicKey publicKey = this.otrEngine.getPublicKey(); + final PublicKey publicKey = this.mOtrService.getPublicKey(); if (publicKey == null || !(publicKey instanceof DSAPublicKey)) { return null; } diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index 95a8c957..b5f604b0 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -419,7 +419,7 @@ public class Conversation extends AbstractEntity implements Blockable { final SessionID sessionId = new SessionID(this.getJid().toBareJid().toString(), presence, "xmpp"); - this.otrSession = new SessionImpl(sessionId, getAccount().getOtrEngine()); + this.otrSession = new SessionImpl(sessionId, getAccount().getOtrService()); try { if (sendStart) { this.otrSession.startSession(); @@ -491,7 +491,7 @@ public class Conversation extends AbstractEntity implements Blockable { return null; } DSAPublicKey remotePubKey = (DSAPublicKey) getOtrSession().getRemotePublicKey(); - this.otrFingerprint = getAccount().getOtrEngine().getFingerprint(remotePubKey); + this.otrFingerprint = getAccount().getOtrService().getFingerprint(remotePubKey); } catch (final OtrCryptoException | UnsupportedOperationException ignored) { return null; } diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 0d48e1a5..48b0624d 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -574,7 +574,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa this.accounts = databaseBackend.getAccounts(); for (final Account account : this.accounts) { - account.initOtrEngine(this); + account.initAccountServices(this); } restoreFromDatabase(); @@ -1176,7 +1176,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void createAccount(final Account account) { - account.initOtrEngine(this); + account.initAccountServices(this); databaseBackend.createAccount(account); this.accounts.add(account); this.reconnectAccountInBackground(account); @@ -2267,7 +2267,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void updateUnreadCountBadge() { int count = unreadCount(); - Log.d(Config.LOGTAG,"update unread count to "+count); + Log.d(Config.LOGTAG, "update unread count to " + count); if (count > 0) { ShortcutBadger.with(getApplicationContext()).count(count); } else { From 997b11dbecc6023ad0679843d8a267d843a2951c Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 26 May 2015 05:34:58 +0200 Subject: [PATCH 13/37] added choose picture as another quick action. fixes #1221 --- art/ic_send_picture_away.svg | 55 ++++++++++++++++++ art/ic_send_picture_dnd.svg | 55 ++++++++++++++++++ art/ic_send_picture_offline.svg | 55 ++++++++++++++++++ art/ic_send_picture_online.svg | 55 ++++++++++++++++++ art/render.rb | 4 ++ .../ui/ConversationActivity.java | 3 + .../ui/ConversationFragment.java | 21 ++++++- .../drawable-hdpi/ic_send_picture_away.png | Bin 0 -> 790 bytes .../res/drawable-hdpi/ic_send_picture_dnd.png | Bin 0 -> 884 bytes .../drawable-hdpi/ic_send_picture_offline.png | Bin 0 -> 657 bytes .../drawable-hdpi/ic_send_picture_online.png | Bin 0 -> 867 bytes .../drawable-mdpi/ic_send_picture_away.png | Bin 0 -> 512 bytes .../res/drawable-mdpi/ic_send_picture_dnd.png | Bin 0 -> 585 bytes .../drawable-mdpi/ic_send_picture_offline.png | Bin 0 -> 456 bytes .../drawable-mdpi/ic_send_picture_online.png | Bin 0 -> 580 bytes .../drawable-xhdpi/ic_send_picture_away.png | Bin 0 -> 899 bytes .../drawable-xhdpi/ic_send_picture_dnd.png | Bin 0 -> 1016 bytes .../ic_send_picture_offline.png | Bin 0 -> 799 bytes .../drawable-xhdpi/ic_send_picture_online.png | Bin 0 -> 997 bytes .../drawable-xxhdpi/ic_send_picture_away.png | Bin 0 -> 1327 bytes .../drawable-xxhdpi/ic_send_picture_dnd.png | Bin 0 -> 1516 bytes .../ic_send_picture_offline.png | Bin 0 -> 1159 bytes .../ic_send_picture_online.png | Bin 0 -> 1493 bytes .../drawable-xxxhdpi/ic_send_picture_away.png | Bin 0 -> 1632 bytes .../drawable-xxxhdpi/ic_send_picture_dnd.png | Bin 0 -> 1784 bytes .../ic_send_picture_offline.png | Bin 0 -> 1428 bytes .../ic_send_picture_online.png | Bin 0 -> 1800 bytes src/main/res/values/arrays.xml | 2 + 28 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 art/ic_send_picture_away.svg create mode 100644 art/ic_send_picture_dnd.svg create mode 100644 art/ic_send_picture_offline.svg create mode 100644 art/ic_send_picture_online.svg create mode 100644 src/main/res/drawable-hdpi/ic_send_picture_away.png create mode 100644 src/main/res/drawable-hdpi/ic_send_picture_dnd.png create mode 100644 src/main/res/drawable-hdpi/ic_send_picture_offline.png create mode 100644 src/main/res/drawable-hdpi/ic_send_picture_online.png create mode 100644 src/main/res/drawable-mdpi/ic_send_picture_away.png create mode 100644 src/main/res/drawable-mdpi/ic_send_picture_dnd.png create mode 100644 src/main/res/drawable-mdpi/ic_send_picture_offline.png create mode 100644 src/main/res/drawable-mdpi/ic_send_picture_online.png create mode 100644 src/main/res/drawable-xhdpi/ic_send_picture_away.png create mode 100644 src/main/res/drawable-xhdpi/ic_send_picture_dnd.png create mode 100644 src/main/res/drawable-xhdpi/ic_send_picture_offline.png create mode 100644 src/main/res/drawable-xhdpi/ic_send_picture_online.png create mode 100644 src/main/res/drawable-xxhdpi/ic_send_picture_away.png create mode 100644 src/main/res/drawable-xxhdpi/ic_send_picture_dnd.png create mode 100644 src/main/res/drawable-xxhdpi/ic_send_picture_offline.png create mode 100644 src/main/res/drawable-xxhdpi/ic_send_picture_online.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_send_picture_away.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_send_picture_dnd.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_send_picture_offline.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_send_picture_online.png diff --git a/art/ic_send_picture_away.svg b/art/ic_send_picture_away.svg new file mode 100644 index 00000000..a85a1eec --- /dev/null +++ b/art/ic_send_picture_away.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/art/ic_send_picture_dnd.svg b/art/ic_send_picture_dnd.svg new file mode 100644 index 00000000..0c7d0635 --- /dev/null +++ b/art/ic_send_picture_dnd.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/art/ic_send_picture_offline.svg b/art/ic_send_picture_offline.svg new file mode 100644 index 00000000..048508a3 --- /dev/null +++ b/art/ic_send_picture_offline.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/art/ic_send_picture_online.svg b/art/ic_send_picture_online.svg new file mode 100644 index 00000000..06181bbd --- /dev/null +++ b/art/ic_send_picture_online.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/art/render.rb b/art/render.rb index ad53b1f4..698abea5 100755 --- a/art/render.rb +++ b/art/render.rb @@ -30,6 +30,10 @@ images = { 'ic_send_cancel_offline.svg' => ['ic_send_cancel_offline', 36], 'ic_send_cancel_away.svg' => ['ic_send_cancel_away', 36], 'ic_send_cancel_dnd.svg' => ['ic_send_cancel_dnd', 36], + 'ic_send_picture_online.svg' => ['ic_send_picture_online', 36], + 'ic_send_picture_offline.svg' => ['ic_send_picture_offline', 36], + 'ic_send_picture_away.svg' => ['ic_send_picture_away', 36], + 'ic_send_picture_dnd.svg' => ['ic_send_picture_dnd', 36] } images.each do |source, result| resolutions.each do |name, factor| diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java index d9f56c5d..c48b5865 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationActivity.java @@ -479,6 +479,9 @@ public class ConversationActivity extends XmppActivity case ATTACHMENT_CHOICE_TAKE_PHOTO: getPreferences().edit().putString("recently_used_quick_action","photo").apply(); break; + case ATTACHMENT_CHOICE_CHOOSE_IMAGE: + getPreferences().edit().putString("recently_used_quick_action","picture").apply(); + break; } final Conversation conversation = getSelectedConversation(); final int encryption = conversation.getNextEncryption(forceEncryption()); diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index 02f664db..a817b27b 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -256,6 +256,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa case RECORD_VOICE: activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_RECORD_VOICE); break; + case CHOOSE_PICTURE: + activity.attachFile(ConversationActivity.ATTACHMENT_CHOICE_CHOOSE_IMAGE); + break; case CANCEL: if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) { conversation.setNextCounterpart(null); @@ -818,7 +821,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa updateChatMsgHint(); } - enum SendButtonAction {TEXT, TAKE_PHOTO, SEND_LOCATION, RECORD_VOICE, CANCEL} + enum SendButtonAction {TEXT, TAKE_PHOTO, SEND_LOCATION, RECORD_VOICE, CANCEL, CHOOSE_PICTURE} private int getSendButtonImageResource(SendButtonAction action, int status) { switch (action) { @@ -887,6 +890,19 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa default: return R.drawable.ic_send_cancel_offline; } + case CHOOSE_PICTURE: + switch (status) { + case Presences.CHAT: + case Presences.ONLINE: + return R.drawable.ic_send_picture_online; + case Presences.AWAY: + return R.drawable.ic_send_picture_away; + case Presences.XA: + case Presences.DND: + return R.drawable.ic_send_picture_dnd; + default: + return R.drawable.ic_send_picture_offline; + } } return R.drawable.ic_send_text_offline; } @@ -920,6 +936,9 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa case "voice": action = SendButtonAction.RECORD_VOICE; break; + case "picture": + action = SendButtonAction.CHOOSE_PICTURE; + break; default: action = SendButtonAction.TEXT; break; diff --git a/src/main/res/drawable-hdpi/ic_send_picture_away.png b/src/main/res/drawable-hdpi/ic_send_picture_away.png new file mode 100644 index 0000000000000000000000000000000000000000..09d1c7b7a404c258bb563b3e83bcf636d0b7f38a GIT binary patch literal 790 zcmV+x1L^#UP)8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10+UHZK~!jg?V3$%6Hyd~pELbX5VWAu_6NANb?MSS(M^jx86bSPSQ)7U7ur|bc>+gsav-o(_2TM3MSkkb;l7VS^!m55L|=})2+K3%wtKe@)9AB;F)aQyE*K1zz>?l1qpImKg1^TMy4sd+ z<}?N1Jr1r+^CL|Axbqe*SPxHRis$WQGDd@^+?2^^+bYhyg%9Si#yRc=mCF*1(AU^Y z#CZ!BOsKHQtXv8B|}f6nzv--vAn+>1omLSVa5d z5ObWThjBaxjhc%}^R{SFQmp#Pf*)XU8Ny+Joi5#VAH&0AgA}D82Usi?i^Z~+-=qAn UNa=mo`~Uy|07*qoM6N<$f*>|#uK)l5 literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-hdpi/ic_send_picture_dnd.png b/src/main/res/drawable-hdpi/ic_send_picture_dnd.png new file mode 100644 index 0000000000000000000000000000000000000000..77964b5b57859c49933652f25d5a67f44f40be1a GIT binary patch literal 884 zcmV-)1B?8LP)8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10`W;iK~!jg?V8U{6Hye0f9FntIE8`|11yX_fnZ25h80o58@Pdo z@I_2WjAG*sNHo@sE|5YCEYwP&V!(fiLF2+eLo`saK+Cz|^d(WKy zzRa97Gjjoh!C)}_hrARqK>5M`E~O6Jpp>U$v?BxyQP{R;S2hUXa^+=1?hf>I!=e!| zU(}N@Ak14JG0~h{Ty-mR726%?>xQ5^0N3U$IYFer7#WfI*Q5Sexy2i^dS$>>a^x4hQ`LKgwcviMrW})$B5M% zMy%d2V)cd*t2c~Tyj#I@@NtxhEP#Ev_A?U>sHC2 z1EH*Ymr|*{V;ulhY@#t;^QJ{i508-7jwGi zEw~ud`$!@^b@VYD&uoy8W2cAXL}dQ$Yqxf~<}IjLHoPaIC!eJ^Co*f2)zi}N7_=Ui z;U`|2+R5~D5IzP8RAZ#f6f@`>q`a|ntIuD$pcEMZ zeFt#O>mk0xK-^<7dVI#xMA^5q$8f$B3x%Lbs*g)XM=`5G<*E`!BUTf3oYox0?AetK zFmF{Sk`}UdJhSPv=qN@2gT#dH8$c}(DUdbkwk@Lkc8HtTuL3*_phIi8%E^MP$>v0A z&8^7$29}}T&X88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10uD(;K~!jg?b^#~Q$ZNO@n5UEinp_NLDzIs3*n#Kam+7Hb+=tZ8Jirjf;( zMiy%tS*&Sfv8FL?u`VtOlQc=0w%83k#05bz8PgPdg!TURE3BpLJf+xYye=5Kh0Fe& zA6&!BRLP_g>j|}TT4YMB&v%@`x1rCwI4@DXr4qY|XZRw>eT~Gf;N6(EjzsmALhLba z54>*)-~6cL?%=_M{*u&NU9q+>1c&F+pW^z^XB)Uyu-{vFUMI0SVn1-s-?g&CI_~wi zJ2;D<1^eEq*~;}+M{EUeC&aAcg^=WjlKt1Jx2j@yalb&^HcqKNtSZ-A6|oImEfK$6 z;TVHds<*OYWosn`mFjJG$%DMXv6B0wViQM-2A35(+<(nH)4sd^XT-!h|1FE^d>Ypf z6Ke^7WX~#X;VEGxF|nR-flc}Vn20W}h%Aekv}&L&oQ)1)PrAHn(Z(g=esnBl7O*S| r3s|Og!s!avvMmJ`uw*isOlFW@1d(hWah_ET00000NkvXXu0mjfgJdgC literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-hdpi/ic_send_picture_online.png b/src/main/res/drawable-hdpi/ic_send_picture_online.png new file mode 100644 index 0000000000000000000000000000000000000000..feb926c6fafdcee8eb3aa8b3159346cf2fcead90 GIT binary patch literal 867 zcmV-p1DyPcP)8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10^mtRK~!jg?V7=A6Hyd~|8o;lL}C%_zrs5{C zqZ<*Qpf4a4L~AJ7v;{@o*oAbV(1qeoq_x&eG7}0ZZ74`>(Kc!39v4Qfr8Ak#+?WpB zZJ>=*mgdh-vGeP%A1C~m>asx zj<^oOStS9(!q^fdXY)5pOI~H3VhyRgpbniSS4avtlU{Pd>*&UrI$=@(z(TSZgU5++ z6-(t(11Dzt9S5OrI6K(yw(Kg#^=fx}5~;%h^|-BhY{f)7vM9zqqt}c@8Z8zzv{=;8 zVo^hjMGY+$HMCgN&|>X@Bn!%dv8}puN5v%b^J2RAo;daxAgQuu3?wM4YJw_BaYq5Feo*_vnORaJS^h%uN2vZuSwOoCBqQeDM?cu>Aq z-uvg>2}taVDOK~PNQ_@t8SB&1jiv8#u1^B=!>v>jX3X2>`f+Q;JH;JU^A=i+!5<5T z{p$DYRby?#k#g#|a145FWvq8z{Z!3cI59?Lzo(YPk$dU3VJ}OEpB<^TlIzK*e%hL8 z>m`7dFwp{wF`-r$@=LJ9al9C4TZNgoAYxYXeDaC! z>QOiWKI0ET=dD$-U$b(}=d7q8;Jn3KDQ2);a}rlCWY4u0E;JGVN&sE)dqhiO1R8}9Vq-w&`1X9xT1BGJpt t9l7czA{m#p?Z)(^b8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10ewkCK~z|U?U%nw!%!55zf*uYi zA`_97bi>Y!6^g?O#bJfwutIU9BQ&Ij^75alX$UoCn!PQ$Kwo#i%HX`=_eDRYaA}e237E+ zOhZ897BTIo$wJC2UmTeP8ez~+leeqFA`7_cCZc0R_zii1&pA!TWQB$1c+5m5A}b^$ z8{7iz40PIl)@Xjz-TonN6(ygj#?Sw#Gfu{0NrG=TJZnj{6Id$%0000(^b8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10mey0K~z|U?U%7i!%!54|8pA<)28C)A}Ee^tb(HmzKZW7ii(4R zgCaPJMH`!;QrfCG=pb4gL~#%)MuqClJr1I1k~XO(PWi_F+>`SqoZ$vYNcbDnu3%5+ zM}@?o6hcHfP*X}N4cEr*;b~|gdun`!fQcwY+%Qlv%-WJW_q@G5oga-`1^@_{uqVfI zfkMI&LvhP|%@Q>1$$ZXf|KQSgR-_9X1&L4)iBJ%UP!New5b+2pUIX~{H?7eNF`1cC z-4v|NGpH525D_0R!S=0z8fn$L4gmUoYM!i<=4X~I zR?7@3eAIcX?Ui)rvenH#g%V=IX0~*A?#?r2b<+WF=w@xZQ-3%66ez^t<8-mM>sKQ{ z^_leo0UY$4u=~_mh;Y|@(U!vu3`3ZG>U>>g4D{>H!qe1v6h3%<4L;-dg``gw7oJ%j zA*J~0e_Z@$d4!Bo{fvQ1+%gPQj8gr~owxs$o*c^wEj2=p>WNe&C0a^44L_NLgeZ6i X1je%URck6b00000NkvXXu0mjfGnW2? literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-mdpi/ic_send_picture_offline.png b/src/main/res/drawable-mdpi/ic_send_picture_offline.png new file mode 100644 index 0000000000000000000000000000000000000000..95d5621e6db22aa6829c07d7f0dba8301061e108 GIT binary patch literal 456 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbBSc;uILpXq-h9ji|$mcBZh%9Dc z;7SBx#y@AgRDpt$C9V-A!TD(=<%vb94CUqJdYO6I#mR{Use1WE>9gP2NHH)lx_Y`e zhD5Z!oxa!0*-)fmf7gZZ0zr}E-&kuF?eh=ZVxduzAt=KgRIs|F_>yDJONFlH<}I;Y zf2D;od?!D4Qw5{m+)V2I)jc&U@Mguy9xVx+M;AD_E+{^~85rsep00i_>zopr0I$!tsQ>@~ literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-mdpi/ic_send_picture_online.png b/src/main/res/drawable-mdpi/ic_send_picture_online.png new file mode 100644 index 0000000000000000000000000000000000000000..be4194d342dd5c140ff70182687a978abe910fbf GIT binary patch literal 580 zcmV-K0=xZ*P)(^b8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10l`T`K~z|U?U%7i!%!54|8uK|gS&`=l6Goez{inFTNj_9&mb1H zEhsoBf}>Cbp+lvclO3Agqy@1!IEWP_*xch#6>X9x)x;^^c+S22-%U>728fCI8|2l{ zs#*pLD}q^s8K{U91n!JY<2JBR>OjjwU_DF`rx}`^ym2sIcAQ?TYMIDoM#<~Rax(2J zBowbkF7p9FtvT`?iv-R5kB)m*r~?}Yv5+6JkRP#-AF+@hkqD8^$IRUS&EC+3nCVI{ zm{-D{DFLt|7b3{tQ6m~5#`D{(y<0EVhi1X-2*%NO8z$P<^ZKJ>pM|OBDVPwGEfT8K+vq)gmh`;Y zAV%$D!MWF!RBlUant2K&#K2iQ+ij26^}cbYY@Bv-=EbD_)67#4p&OZ#m#&>6lud*++<>6!r&dhlEo(R Si-7?E0000JgU literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xhdpi/ic_send_picture_away.png b/src/main/res/drawable-xhdpi/ic_send_picture_away.png new file mode 100644 index 0000000000000000000000000000000000000000..f9aa21dc81b9ef39258440b55604c49c6fc46c22 GIT binary patch literal 899 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!EX7WqAsj$Z!;#Vf4nJ zh#UrCM$rOaC!nBYiEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$QVa~t44y8I zAr*7p&i3^e4issdZ`^ugM~Pve>H`g!xnko+>!f-QD`L_=fu_{y|W}r7w2`g zX?Q(4)N+Ky{-=fC1S;=J=S)n`t(o~)_SwFrc~c$Z8} zGImq9lMI@}u**QEGhlHbF#6kRm`cmEDtAVa$B-<+&y#odC)7h4Ey3+-9~%$QuCTx9yhjm zD#^v>rRs%vuuG|RTFjd~dx3Kg*QNzcIx7^owN5k<&0MQ|VzEotYL49>x8;gz`J7`=E+om>jEcNV4$^l|v_# zmtX(*zjNXJo=fsCtQV<0Xr2G(x#NFH(UgO}6Wlc)Sw3>V^X~f{mZM)PjKgHkpZqFR zEtfDQCF#z&lfD7`t~}k1Y+hpAYmA=;y-@Km`Y}Z!PuP=NqAm9QU#Z`L`4jn`yb9hW}s1!>JRs7FPl@41=eupUXO@geCwE CtbrQ< literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xhdpi/ic_send_picture_dnd.png b/src/main/res/drawable-xhdpi/ic_send_picture_dnd.png new file mode 100644 index 0000000000000000000000000000000000000000..95e4acce7b9c44d7858fc326118126ff5b48fadd GIT binary patch literal 1016 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!EX7WqAsj$Z!;#Vf4nJ zh#UrCM$rOaC!nBYiEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$QVa~trJgR1 zAr*7p&OYlW94K=9fBKBB+@Bq4c`CI`(|mc}WX1fD+rzm20k^{&C3V|P7&8rfh<;pv*$x|yny2w|8+c-a(W0OO_@XzU=83g9fnH|>i z@UVPW>WArWbAIW5kXvcW zF16?S0?FHUCKgPM-d}YnGQ$2vjZ*v%5jT6TkO@q6Ir+!e2`-qnZ)(2Ki@h&zSpCTB z|NQE0qG95f#RpgnHwYK6eY0}r%=ru#;BxYg<{_M_B)okij#^%r#_cZ84F zh?S>J|0ZYkP-ji~fqf@@wzfUyh_BDNxtL{x=d1<40}Yxw=9N9~KF?<*dVYUI*b+C! zU)~IiqTZz`!L5hNj9)grao@+CwCw6r{n^|3yI-Ca?Ota$;o^SO2yEN!=_zUyc;G@5NY__L=nC!Ce$06^33cn`yH+4wvxM%zC&+b5z1KRQ1 z@(dm`H{Vlz-Dy2fn`43ePqmH%{JV;Z^Jht32Kppqjq>fMVVlpciM8X||9hf+6US1Q zynl(>&eiH%*;f*bZfMngQ$FAH&5!M`#tHpDuiWRcyq(YE#<~C3#O>-Y8crrm`LI}C zQula1iy&|AJZs}8Po*3mZ@SpBM{(kR`7i4Q9i`5E%VLq@AP4srP3M z0&~jzmsOi`fs%{MsTcElSXA%+5HVg`rLAdYkf9psZ>6>|&Gtm$E3LF%8CFl8sKV77 z1uy6Ce`fYI&8Y0=x|A7}7nr_pJ#D7Rq@{50?v-eNtGZeV?j{8eAo_2(pCKqU4nJ zh#UrCM$rOaC!nBYiEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$QVa}Cb39!f zLn`LHoqak>#8ITp-qf+2aeCXH2LB0f&dlENf%VSLZ>&w`Ax9MW@t(gIq}(drwR_d&xqsw&&O8md{<43jbE&_qfD?yei@+j&>!M@) zOpgy}81TI5n-?fGt%3hQastAsQm zufmm?|NpjB#W2rXQ(4Pi5mEH-#aE4G3q&92d{A}&Q~5ymgVYWg#r79pGZd~hHoQL2 zJtuh@r|EL%zP+c}_Ewc>96Oj)+nm-Y&3asK!d?j-uPs#~&KGVSO_cWL*R7{^O5WUdy+i`Xmi)01s*2!rbEqAQ@zNblR=iW%e zb;X~!szQ_|?s{aUzJ7t`q0nDpiF`A@u~b>}Ssy5NT7EBJMr literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xhdpi/ic_send_picture_online.png b/src/main/res/drawable-xhdpi/ic_send_picture_online.png new file mode 100644 index 0000000000000000000000000000000000000000..0f68d5f52aeb5baebb6072497a4c9ac77c8e6e34 GIT binary patch literal 997 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!EX7WqAsj$Z!;#Vf4nJ zh#UrCM$rOaC!nBYiEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$QVa~tNuDl_ zAr*7p&i3^e4iss-Z@evJo8HO;PBA-En0J2)Zs>Y&h^5}~vAjZ)8|NvfM{d47y8_Ka zoJCX%6O9Cy$zQn?Camn#(YURokGqL6Xvg-s{A?EIKb^DOn?A3^@4e4+rX~dr5DH+= zQrS_x$$C%USGx&!om~PM1p8kV%{FRaN;vPn+bVr>?aAD`F1e23axr@XHYzVx*kT@< z6p*lHi^NkyyVQ3dUhc%lAaF~&EXBl3n)_u-Hs`IKYy2AL#B#h{8e+Fh zc$F$g%6AJ5^$7-n>;2|TQ()R^sjc2%u*fQL)_sLJDQ-lOw929(R24t>?=%SnV9Gw?R=BfsN&f9O8*uGr9Imoou1ElKJ zw%&OZXju0}`84_=xRJHXM0XPto%}3yx4}dq4e3uUGkMG9ru?$TKWBJUro1uz4?LQ1y)QF z(JQ2cTW7bPbxZraNq(n($Hq%RIkhz*9i~%a518$@vdClZUn98s#+fZ~UzNjI4KK#? zUEDF#fPZgGnI~`heB&RN|1-|MpVnq1FC+TE(Z0xEp!s}Zu95A8e@kZzYHqVj`C!!G z^TqwR@N(6Pi~TXeE^Ki+v9bSddGyUR&=y?f6XLwJv$^|VTH$W_ovJ6caLt_Vt9p!k zb-OOx?4xJvo&`aszb7Ux04h{?7-nCjvf^Hnfya)E{afOW+ly>t*Yl5+ zD)=HPcbHHA&jWjYt2AJY+dB5>_P@5^+w$)F78b)lm3v+qM?GJ%eP-*A#has=*f-v@ z%;fAUD?Qh=@zI(k4iodWOI1pl&flBnqY%F??D(hapX*DSq;Fl3p55E(wX&wCv+7{S wycxMc*7ioMOy{>xwVIT7SArXo@Y841yREHoO`0j<1zopr09JddX#fBK literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xxhdpi/ic_send_picture_away.png b/src/main/res/drawable-xxhdpi/ic_send_picture_away.png new file mode 100644 index 0000000000000000000000000000000000000000..7898ed4fb2ffd667e606d690dbe9cc08821cae76 GIT binary patch literal 1327 zcmeAS@N?(olHy`uVBq!ia0vp^IUvlz1|<8_!p{OJmSQK*5Dp-y;YjHK@;M7UB8wRq zGb8#+R`4k#wp1}G_&Jv)5GuY>TE@}G`8NYOe;&$@$5` zFT;FRrRls<;U}g`cNynQd|YNK;bmTKu}t$yor+xGwVAT6Ri>%C4z#$3mOiTwmAgFU zCDZBlT@i*0?App6*d{kt9%K64!}?5;`;4*JjC37?^AQR2HXg8{hQtov-j7Sl7llUh z=ggH!t?bN{xjy;c%&-)5gM$|j&Y1FpyFu%*#WUf1tCww(mZ;II{j=qBb4&vB!k!Hi zpZJ+hK6{{Le&mD8w)^EC_^aIgtW({{DSAy}YWEfCeKC)!W<8Ib#T?zgXK}#NW2%)? zPc^>x{%GT!onWrB{{ecGJoY9BmVYV$XrYRJa(TKVH+s?1~?_ocg<9^$V zUFX!MKW+N-SU2^vj&0-9pFwOt7Cy|jt#Rp#Zmj0wdsr)ZMgGHv|UXsnK$pW|5~}knM}yPgef}p2#kFs(n1#=WVP^_R`dO zs?rgCQUTM9eXqIRexmdFN%XFwn54Pi7ybGE*g@-AmlX%{+sqVWba*AO-<8`ThGoE+Nd3@c#`dZ5E&U2enm~KbPGzWh_Vc7R( zXZFXvbCR|0oHL#N#HQPRLu_}luI9$0$~?+{c<=mIuF&}LC_;B(rC5Ceuh{XZPd2AH zoDT?@wmtQc&Xah*{cX#$$UTjm@o#rqT-&Dek=_53+l-?dt8R0i+5Ce^_*uk*tmvru zeSg-~y}sR7Iq#@&M0a;`{!xqbx8x%Z9NpOeXij?JoWw@)<91!y^R`+We0_Aoz3|ND zZ>(ZRfvntl>9TLLdJ0j?Be3|$YnNdx8Czdhh3e? zuAA9a()ctY0LWa~w@`0`X4>}sjCWqFF}VC^;zzfT#$5Y5MvGUPHW;j}b?~U4s^Oz_ zC-{2$_1LhVPSI=le7x3dI%=?C<=cslvrCLbo_*xt+|B+jwl64Qf|YGzwQN!O%M_2e z2j2~Ro!HqPsJ&Qp$TQ1^`CIFq-D?=`Z#b56gS}s2(SfoZ&rf_dIj(W3`!VMN<5U0S vZaVEa&Q`wTSk4oF1!+WSk=$b$^N;o1f49dz2eRG)3pWN&S3j3^P6<54-x>NqV}CaLSAi1fv6B};~svI;2%!LhT#BY zGFclR5=6R&CE~ORVF3lprVs$&Al}aUoGYd9^SzgZBRDXp>m}mR%#dbw2~Z;WDe$Y1 z42ob4(mi%&>{}eqrNP@Y-_TVb>@ausVQ;*T=Xi*xcyloJHBq|!DJ)YymMJJwqF&V8L=N@;+an`lF#U` z-6bC?UNaP#Eb+hL2i7kLYeSH*+*X)zM#3ctmGKX|>)O(Ul*|!r_=s;hceB+t-M>e} zqGg(YfNGC>DYDb<*N-K=-={=t93A%i{bfov^y%YQuP~w{#6v+ zzJ1L`HrV~Td#Fh7E4*I`)E6U+;)grJFR_sz{%@&2TP65 z98c8!z*=;3dc~{i8V#M~^eU$_|2j~^AIjL?XPPws1Cut4AotWKStyw>bT z&N9(~oTl$4UaMlTI!!;&#|vl_yH=r7q}0A@Z)Wh$as{H>z|WB_d(ycm&XLJ{_0m_i zcc%6|QCFv$FDAQ?%-g+mRgE;C$L?v!kZTnV-DFY{%#_naeeem04-9p##`|U;u5To7 z*m?b6y+h&tgO6;C1czIQuKrZvAbBf_9$(qSa@!QFRv$A;#nys2+y}AT=%l!iTl2{^ zX8I-*sb^9dVat>qkDx@|A&VPE*_jHb_GbqR$R!{Sn#&8&lC|x*>&@FRfFigXkVMQT zGh3uK!l>nU%m%aKa4p%WCF9@jE{J0|ToW_uiglq~Q^ea?1Qq=u{9Rj$$qvN8!3f0E z%v`tCq5Q}cHMDndNX(K-1C0OQkt5xu@@b~j(II3*F%oRtrc-?}TNOykGuf=4#{tA+6G?%;b`gS#|pZgn4Xzc)5dPN}S;)KppZy-ZH7*-am( zeom-Vi_PG~D|m&#(d)L@Q--QH!2vV%^{8iu>A+MsWLNsGDA-jPOKAz-DK)16^^xN; zSLefqg2;qrhswb;c$uSbnPMK{FscgDt@k!?Yjb8JCq#co)X&D{$dACquSn*(X(i8g SVjc=UGr-Qq(YoH!C+;8aKFPcQ literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xxhdpi/ic_send_picture_offline.png b/src/main/res/drawable-xxhdpi/ic_send_picture_offline.png new file mode 100644 index 0000000000000000000000000000000000000000..7b5687e42e3d5814d0aa65885f25ac297a345c4b GIT binary patch literal 1159 zcmeAS@N?(olHy`uVBq!ia0vp^IUvlz1|<8_!p{OJmSQK*5Dp-y;YjHK@;M7UB8wRq zoZsi>7=M0dV}C+vXL%go^OJYVpB3z2 zcY%YHtC5p;++)}vD#2#w7jt{IWCD`}>$6}{U(p)^8>DY^HdS5cDUg(4oOfKzTkyv6 z191t#O;M|FsuVC(2n44nd{VGu-p6#!{aR*of5Y1YvQ8Ch@-8w51U9fs^sKmO)_h?4 z0juoB8Is>vH|QqSM6YGGIuLt0;Wdx6f{wwFH1T66lX!Eqqj--=FxhSA*}}H%riI_G zHr?hmjkyMWlh^f5bG&vSDQvpXjB^Y9_84=Wkz{?=!&L0nXu0^noWO*08V2biGbj`` z;G1)zRv>ik*;;?(vBEu3`}OuofsH#5`MJ%{@8B_7{IIUS0qCz*4(=@hSG&x*H-NMDH?w zZT2C!Y1iWehKF}N`W2bkD4Ux3UV?F*%au8-`;Pzn7#3o$t+`=-<80eFYrZ?iSKct# z3zZ-0ewDtI#q8K``(MvE)!)9id)>#dKZ^eq@6j)tg_vfm>1>T(|6yPHfx1=eW!`W6 z^X$f|j=E{q8;;Hl{%T#YdY#PjhQlg{f4qjR(XuA5;wev+R-`+jD zk(Iab+~n5#>Kk(V)^7T;S*B5UyXCf7YBsrvT>^2k;yu+TFP)#o_eQ)bzi;h7mTwc& zzh0}b&oK__E~|HnUtA z_1Iugs#5EDyySEV-`d7rp?M5?X2v1AQ)@W06F#>b-`agvwa1*}=o%sShI-{=+ph*c z*i%|?rg{5w4)fhP{cA3(t#8P0s9(dH?p1O=&C0J|d`;c5RXA#rDtJ7!L^_Xm9jy;1|CBJ*n@){r^g9Rs`Q~)KP+i c)hGXZ?mU}jE4|zjSZFYKy85}Sb4q9e0HXX0y#N3J literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xxhdpi/ic_send_picture_online.png b/src/main/res/drawable-xxhdpi/ic_send_picture_online.png new file mode 100644 index 0000000000000000000000000000000000000000..82eab70c0ee41fb9439f2ebee2c355d747ef0fa1 GIT binary patch literal 1493 zcmb_c`8S&h6#a~yrqQCdOtiWjYEA4#OpPFvDu${~Mbn4~Dhah!+t`oADDN}z3;qte|zWMo9gOf50X)p0RRAmar(cL_~xhK7>HV_=RHiu9I-I&z7nHAnt~;J?jx!u=t*R9)eYt{IVoq=_MJbgchBx z4DRFJF6Q{eOn)r(zBN z)gnMZN6PNF-Wbb!pO+JhFddGi7xqR((4&&2Wu=ZK$l3f$W+Y912qtu;XDdV^;vS#f zH8KjX9IW65atMMlPP{>ND?^NL&duvFe@+Hia^xQ+>y_lI;)odMn!7m?j)V(4$b$=m zZ_20Az8pDJZ@(B`0{g7}_P~qS5SSy)fl>&yiiLk%d1)Jv#(i?Z0Mk>nvJ)~^Kn!^M zdo8)d@X7PtvV@<^HH;DqTKbLoyr7YGv)Y0z3(41lvbDQuDWf#6d{#c6CT=1j$y6Co z3XZ(gvK^rA*{4~%OKSCDNfeJ?-r%M-qe?HJFU@04g-=R5=N-&CQ_M_x+F9bVzzuY} zrjY9vbn}@86#hQ>*_7^ainrZ&byP8b73p61;auzqgB}NT_I;+#{~@61Hm%vKb{-WB zQB2Uau&Ch}t6a2mW9EkfN23t57(aniPI+!E0??YedP@@;=UhQ*%7(F41Ez*so4EbQ zugYKd8rffl1z({ZyP zD&x>?Cp|)9Pu({T>U}U2o+rOz)W19PPbrxTUo0Xw5Cd^pM1Np4jM7Dk^2t)c3X_ySzH z4E4d@ux`^!|KuShs_1SO(Y-6lEf4%Pz4gxtU93RIm~v>sr>1dp^j3y*qr|W9nUFQ- zwxV|R#aMZdGC7FU1+#~YsAh3QeYFApL zX8pJD=gG34(@1c2;9;RTe6xE0-M&(H85hpZ1W~m-et{yweqK%S33ZZDEHu~4G29L; z*uMq&;q)i(#}r09`<)G^s;p~K`-8}QytVl`zc-hcX^&L#xj2->jZyv0F`9zQhNLe{ zz}~U4>mQ2WW2QIYC7q(_Cl3!#b;1CXMb6vcA?9#6FRtxX^w&1~j(q?;CiWM#I+_@yQxxU7!Cd!&&{li}>N9fjK=$|DD*0B{YCGanqP`YF zd78QTGX6++SsZ0NV~yVzF&QnqN@|qnE2i?EtZcV+4RU^q9*{hwIcLS5lfRgljv?@f zbRMGiv{JW8#kA#bQRq|Y7&RV&&O2i;)ycR!2V(~wxd=(eX!9cJ*g#%xN``TxCHbWF zHmT~0MyWFKcKwXYANx^A59B`|-@XJ+pXJv{_`d-6A^Z{~?ZU4LUb_X(aC{r zyUyMXzE?vltjhFYrIG6;_hIPOu55gUT|2d^a=}Le9$fAZw}_)t^2+EUSY|Dr#ivfBZnn^OD{qejTe?ergI8A^Mg=;s;v7 zG-|X4iWD00t|t7wY}fJNDm;p~rC$?#(g$g008MH5<*EBrECd7IIP*cYt)pWc}iG6np+Q~&k94<>?2tY>G*FyUr$Plh7%x0*e#Z% zqJXp`q`e=04DrooVxDjZGy|?q?gb1{aywcx*3R=Y^B{Z+bZ(WHh@^v~rYy_TP0gBv z{6b^wlm2EQ+1=Gdy#w9y2GjOerSoin7X(QipBLzyC7; zS=3Zh9>rIK<-MWr2&uZCTS@RR|mp4$LF-pg!ye%;)y z*x@;Y?&k0fFZWtxJ#1$geM}w_Z0ctck0fm4<$TnqG=`n5LT{Btj4vp8y$^G5b`Nvc zY4o$m$Hs@}%eKst;1FN2jmJ74&?OsK7Qvs7KCgrJhg+nJ0pkCgvO$EMZ4f06?q=Rl z*9l0l;9=8IP8jFoipEiOT}j}q!C2eXq6@MZ2hArtHqsl(R@dDNgj*aFxD-G7>Gt#Q zf_Bx1W>jfz1qATj71DyYU>v|G)!u>Q-&|7A^1!b zN%c-Fj7%GZ2eq8e5TQ3@^5bG7RedFPIKv!Q4_fN;{zU6$q;>JcKt-dlr)h^Q$*Mr8 zG%IZIx&NS6UU3wA+&p1ADcyEpZgbElo+J+h>l?bb!b-v4^NT;!Z(%`6qwbe*l$IJLghv;H04h^=cPE= zx_0kzSe7HL#<_pC>5-$iF$*MPb9P28r-(+``|U^F0`mb3R-Y|Z@AJyuy2DOe9<6PVp7nkgIb6^ZJbI3+ za#P|A5!*csu=*NlOe?UoFxeb^t!h>2ThX2ulSN`(Ni~B;?rAz`gskS`@6s}QTOyMu z0)}{!s~~1P75S2BICx;v*+dv)yz7xTqT;-pA;Q69$CXrn%+f;d;-MX)Dysiv(Q$}P zJT)l6Cr@~Oy+tX5Hjao!*^&6cS5glis^m$KAkxFlW#t>&yw!#jF@yFJ!9X!t{B;ic z_74CJ#TDFri9yRj&```{{3G(sWzs~Iw^}t4@>9sRZc_uA4;J|;DtR7(W z#oe`vG^sdR@>vHiA3wCWGd|@|KB$w`*`k=C@^q)p{(vF2%=g^Ae2m`{ySmH4N8h2i zF&YG>yNa-i0^{HBkl(!9myFQtY}6z3$ziFC*7)n$`BoudI=BywD?3cJgy46pw+?nm zmp0A}6x>2V&T?a#F&$6ke=WATUudlv663rKJM=guZ!HaQ;kO-`HOtR5+=X)rPt@`J z$>vkJE*t*6aMTIs>E+i)>>~L_sXuZmenEw<7i7}QO7F9*51Vi$?Cb`30v4y@sk(C0 dzJ;&L8PG>RIjBFq)K>cq0HP0>aNjHR>^~cC+dTjP literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xxxhdpi/ic_send_picture_dnd.png b/src/main/res/drawable-xxxhdpi/ic_send_picture_dnd.png new file mode 100644 index 0000000000000000000000000000000000000000..d8257aad00c003aab9c2a30a380f8fc9ca3fa249 GIT binary patch literal 1784 zcmcgtdsxy37yhBBC?HO;%x!6wmtmSKZDlAVXzILWV%`#UcncFnmxd0hlo_* z008%J$NQ@?dyf!JRg}3&I#mgr;^GkiQ6UpT%2UDcY7ojjfWSjR8ROTMzu%fOO94)HEf4fYkhx!mSu{ ziCxK#$SlJAi02=&0^I_cQH;aAHtuNsm2!mIg1{G3wKqWL564%$X&%+B4dZ2NdH<%T z;W{YN<(8g~RaQIH2E|BMx@BR1({KNpGEq67SrMG!4Kn*5xGh+VN6&hR(h&SnlyrPt97MC|uzw)8?*GT*gx-S)~Sq zw}S1G;t{y-M>(HW|E%^(Q?HJ;!fI5)(;_^^P8L@S+me(#*0ztDy>PXy<50TEXilc9 zzrzEdP^OtLOSstnv2AV367k&N7z%Krc2z-{U}X6o1Vg9 zUZDEjhQEZT*89o;o7#kR+bV|!jfFXdH<>_UHPGgVK1Ezjl(!OwLKx_QdNj|H!7I~7 zO~f2xA<*fQ;Wkth8{Y023}d4yP5VxpH!Jvj4R^-L(6#}a^Fe2r!B9P4;)}@j;N%7K z!!W%Ak&FR|I^NC5^!PL2&L^6A1$J1{O_16DS7KY69ljoAztk~jtUk}+&1^3!R-R_A zdm*ZO@jLXeD8^9(ckSVNXIeascJ?J5^DbUd?IL|hCpOw)1DU8%F6P7L@1lznu)-aV z;RasWxVNIeP%(o|zN}oXhl<3lFL5xo&5cC|^)hGT;z&F<*U0qD$MP!jb`T??7CPpL z_!28cmB4iQ!lb#8%ljWCABo7=6!xJ7OD00ey?F5&C0a+6AaM z`m4_yCr%U`8No53Y_;1}nUpLW)kNHCP!>#T+2U!zs?5l_^V>D6WYCSvBTIAdO~~D0 zw2YkzFQqAii0eSUTUc@|&5krrKIj?9z`Lz|Zy;OlX~^P%iI@Ps3E4%eJ6?FQNmuaT*PEZf$3OR*U#V!4E^Yr-)I{K^KDt?(%KT z{3`36OB%#>T|>(&3jRfZX-&FT-7f;W)X6vDnEN&+KW1@;)I`H!HR)4VN9>C15TcXV zHe2ZPqK{9CNIT#8a)yv6l*&iAMq17%Ti2{gLAOHShJX`nnu1jG%*P8w+@7`wqIvUvvWQXmm95A%q{6j0B;1eUxUq&PA zfeKsbueOEjehF$Ma&Y<8g7!TM=16N6)aT>&M9`(wc3-3*XZm6~i?RV0kJ&A?@s*o28z z<7$OioS}BQuSsx9pCFbO&XEIs2PgJ3yRJ)+NK1ezw~PvLLz?|em09qIop%Gr+ug0!+PQRgoEhQcD!g=4E6KtyEFk~@ literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xxxhdpi/ic_send_picture_offline.png b/src/main/res/drawable-xxxhdpi/ic_send_picture_offline.png new file mode 100644 index 0000000000000000000000000000000000000000..d487709be99746da81f95d3d5e7aedfa1fc136bf GIT binary patch literal 1428 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q4M;wBd$a>cu@pObhHwBu4M$1`kk47*5n0T@ zpxFk(j7o3r_W%VYOI#yLg7ec#$`gxH8OqDc^)mCai<1)zQuXqS(r3T3kz!zA74&p* z45^s&_U_sIkW`W5AM+WNr58ICJiKJ9loKHlyQ5&`M|%Uqi}vo1rFSVz5;5IyaC`4D zbB=?(Vyl_Bg;fMZUAsFMx-PwTG*{T9_1vA}dDZWWf7S_IS~Ax%ebVpKUu@Es@pw#9 z@kBzdXG^Z6vu|L(!TW|~8)JFdmEUC_)eBg6@SI^TKH&6n*;3{$ycJ9pEH^@SO#he7 z`h&@W=TFos&yx&qS?4is6MM6;H9@GzMU?C4-6k}k)e3tn=9=fb@wRa(iyXZ`fnMoeQD%3itVZe*(wb4h-Z?$oJeYk{nC zFiYq(&xGactCqxSx5u?gC=3uUj5)3{RsL{_J>r+PvmHd&0K)?ZNXHPkrV4@YU+R{XviCip*1< zn|yeuTW81p=f=h4mpzQ9K1zSk`fy%1&-hg335KJw>~T#0W<1!ocy>Eaz#W|re?_*- zU$rq=#vsdV_io4YlUMg$mO5)=GL2!a?z?*|=SpsTPB{I$FHKI$xgjs+j_&1{8Q!wr zacG=zw4XVzIwj!7IVqwqjyywh|Wr1lJlOixby3(dF@$z z7u>pcUp^3h()K(C#uvIh(S*$baCf7JbLD z*s6QpnRWg*H}`5iUns?F!aZmHqi@;jo98pz@*CdWv|;V5XA5sJmGD^HdsG+R_`Kn$ z*!RCOKh_-mduv6W+$v3n?9+#~-I{Yok7+%7)waN^HX+^(Mq=d*_nF$&_q+mY-q2%GaF5U6m`^81CjoN)&QfRCmwc&tEh1PH|M|8E0qy z3vNJDr)Azdy!qbAH#!Ad9L}4u+4J*SI!xB%z8zJ4_RX=$H<`{Zh+UN~-8`?oO=>~r z^w!+JE}LWH;{I;m-#jmur%X;Nt6^EJm|G3A%)*)7`(CejzJ<-2pV!bKcY3R!{DzMw z*W@?PX>Su-@M2TJ8#Z%(UQLI{(}y-nU$HTn!jP)ZeLN@N^MwGWkfcb9wC0)ZZ2}8| zK+;ESu9P=;0j1Y{uUv7Sp>(T`%;tN~7uqpjdAh0KOx4`>G@)!+pEXNA%{!%W={wJo zQ<6xDJ%#N>6fiq%of4{H?66<`T2N%>buD9ueUsw)w`5P1T=4Tql-BFjYn)~>&N?ja zn!VwS1M`*x4EN#|@X9XV8K$|Z|D*dy6SEX=ll*($n=Lm>_*UpK{60~;FsxA9B>%6) z{74gPhYNppeo)%L?Q9~VZBqZX$Y^&#Cj;Z0#g%GR0`{UC^Sw>Z+q`_(`02brIb%O_ ze~{jqsQX-J|F0`+Y4X0euI^|K`!|N&jdKr}C1ktr%@@Dkpl#xPsPchG=W<=-g2{8z clA?dCx_R6pVOtvefF&q{r>mdKI;Vst07X`9ga7~l literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xxxhdpi/ic_send_picture_online.png b/src/main/res/drawable-xxxhdpi/ic_send_picture_online.png new file mode 100644 index 0000000000000000000000000000000000000000..c095d7959e73c2c7ac6b53a3d97d62991e67fb09 GIT binary patch literal 1800 zcmcgsdpHvc8=w0aLzt+eXt|tnInm{4?!#8Yn(-N74%>=GSgo1MT4tvDD4JnsE|Gh_ zb2O21Svln(6sIsLcgc?0>CC0(%ya%a&-wp*pXYtw=l#9EKYs5Y?~jA__tnud)dBzj zI!FW@qv(0t2~=0q1)u3wMN>`oL}G!8NCn1ZD`w5}2wXA%V3W0-N){ict|>qhigySl zFo8&+MkNsdR4Uc(Yk+$@fRwrutnvU#eRubp0y=#W|LfUX#nBbB%1? z{Y=qvQB{8SZ}%!>TZo{{1sHb%n&dh&`W&)BB$AH`P0Ox7-gW#{rDKzUv=2T#_CZZo ze@B$O8}ul;r+fPMmhlh}Ivv`13#XoKL#o$sa$gi&f=$cT7tR&j-%<7jArB(buCyL* zLWHpL^w_>9z7}?j*fq*SX{!TQR$DVU$cIOzx7W>9Ueqa#!yc3`cgKO=CF&R?TVE*w zlT$Fw4Y7>6n-f_@Poq$6sL6>tIE=0PwtC*8cVi0vs_e!5uugdF=shj9xVo@JY_7^j zNnkJ;f}y`fy#a*^yY4#t(Q;-K1Ih!sR0V+=0_nlUO?+XFu*<^o)N#&|8nC))4V@ai zGo>4-tm>n;*MSMRtgi~Zw$~l6s-(ORsJGLD(e?jb#au@ro>fnz8MjnTq^WSAl*9B# z(GSOi2)(_I{a+~U3B8b16&r(j8dAE+a$yy3{WtP6^N)0kx|Yq3a~=#QP2>rtenxc$ z{he>mWKZ0O-jL%7eU_>SeL5wnZtKPDV>f$A9pC>wEEBL~V@Ue7^x$Swkg36qW!s0P zTz7^cuy2o`P|o-h-6|&Cbk;R@&w^J9ZsO0xz8ZM6(c?<#rUnOZv>U_~}N zfo8;b@;c)QzLpzr_N-bJcQ|-R(T9|`g*QfvGemBlDC5vk$L1rlQw1mUopgI#-LD!` zM^1V?6;EvztrJhC{0WIY6$d4l3? z8JZg*HoAzKs?5tm9z!RD76yFv*lGxU#2fTV1f$DSqt|=Y;EDbLdoO(ht%L!*>E~YyJWru%m-DmObUEACe zLBHbUY1Ctv+u6%7!*;-T^yp5BKoGyijL>raIagv;9yVN(!mi$^HjRMzKpxYqiApy) zn8+n;^MCx}OZkIDrYw8S|D~;$^NehB>Tp`PY9gOKZBVAsC#oqlRve&>D?VPN89s1v z9_+nh;w_#p4^dro>+@8Z>-c1EL239YK>DLYS(>GSKkcO3wx?`Amu30IV+#acnlN&E z0yXdL{v`R7VCu4u(+itTWpKkF#nuuj|7!{`nOYFC5#+q@7MeNhMLpgn-XDK$9 zpt9FP7%NBpwvY!A-*)_mHA*vJK;|pn zqR|h?zfG6u9C%XWsW39^Do$sJIdFT|14&?umG&dsC-(&2&L0OVQHuhH!5c$ld+kBe j3t@6`we7#WUjfkNN|CwBt?&^i+V&dh?GL}}iD&)=R$&;b literal 0 HcmV?d00001 diff --git a/src/main/res/values/arrays.xml b/src/main/res/values/arrays.xml index fec077cc..5be352d1 100644 --- a/src/main/res/values/arrays.xml +++ b/src/main/res/values/arrays.xml @@ -44,6 +44,7 @@ @string/none @string/recently_used @string/attach_take_picture + @string/attach_choose_picture @string/attach_record_voice @string/send_location @@ -52,6 +53,7 @@ none recent photo + picture voice location From a423fc04facb5590998ebde98a7fc3b3edb163ba Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 26 May 2015 11:09:17 +0200 Subject: [PATCH 14/37] pulled translations from transifex --- src/main/res/values-cs/strings.xml | 5 +++++ src/main/res/values-es/strings.xml | 5 +++++ src/main/res/values-eu/strings.xml | 5 +++++ src/main/res/values-ja/strings.xml | 5 +++++ 4 files changed, 20 insertions(+) diff --git a/src/main/res/values-cs/strings.xml b/src/main/res/values-cs/strings.xml index f49cb1b9..c199ec76 100644 --- a/src/main/res/values-cs/strings.xml +++ b/src/main/res/values-cs/strings.xml @@ -448,4 +448,9 @@ Vybrat %d kontakty Vybrat %d kontaktů + Nahradit tlačítko odeslání rychlou akcí + Rychlá akce + Žádná + Naposledy použitá + Vybrat rychlou akci diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index 109715e3..880ee66b 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -446,4 +446,9 @@ Seleccionado %d contacto Seleccionados %d contactos + Cambiar el botón de enviar por botón de acción rápida + Acción Rápida + Ninguna + Usada más recientemente + Elegir acción rápida diff --git a/src/main/res/values-eu/strings.xml b/src/main/res/values-eu/strings.xml index ac66bfc1..b594c069 100644 --- a/src/main/res/values-eu/strings.xml +++ b/src/main/res/values-eu/strings.xml @@ -446,4 +446,9 @@ Hautatu kontaktu %d Hautatu %d kontaktu + Bidaltze botoia ekintza azkar batekin aldatu + Ekintza azkarra + Bat ere ez + Azkenengo aldiz erabilitakoa + Ekintza azkarra aukeratu diff --git a/src/main/res/values-ja/strings.xml b/src/main/res/values-ja/strings.xml index 35fb5472..38735f39 100644 --- a/src/main/res/values-ja/strings.xml +++ b/src/main/res/values-ja/strings.xml @@ -444,4 +444,9 @@ %d 連絡先を選択 + 送信ボタンをクイックアクションで置き換えます + クイックアクション + なし + 最近使用した + クイックアクションの選択 From 9debf8037b5def6c634d72042566fe63ab9e9ce1 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 26 May 2015 11:31:33 +0200 Subject: [PATCH 15/37] added default iq handler to print some iq error messages --- .../services/XmppConnectionService.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 48b0624d..ebb14669 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -204,6 +204,18 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa private OnMessagePacketReceived mMessageParser = new MessageParser(this); private OnPresencePacketReceived mPresenceParser = new PresenceParser(this); private IqParser mIqParser = new IqParser(this); + private OnIqPacketReceived mDefaultIqHandler = new OnIqPacketReceived() { + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.getType() == IqPacket.TYPE.ERROR) { + Element error = packet.findChild("error"); + String text = error != null ? error.findChildContent("text") : null; + if (text != null) { + Log.d(Config.LOGTAG,account.getJid().toBareJid()+": received iq error - "+text); + } + } + } + }; private MessageGenerator mMessageGenerator = new MessageGenerator(this); private PresenceGenerator mPresenceGenerator = new PresenceGenerator(this); private List accounts; @@ -903,7 +915,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa for (Bookmark bookmark : account.getBookmarks()) { storage.addChild(bookmark); } - sendIqPacket(account, iqPacket, null); + sendIqPacket(account, iqPacket, mDefaultIqHandler); } public void onPhoneContactsLoaded(final List phoneContacts) { @@ -1691,7 +1703,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } } IqPacket request = this.mIqGenerator.changeAffiliation(conference, jids, after.toString()); - sendIqPacket(conference.getAccount(), request, null); + sendIqPacket(conference.getAccount(), request, mDefaultIqHandler); } public void changeRoleInConference(final Conversation conference, final String nick, MucOptions.Role role, final OnRoleChanged callback) { @@ -1835,7 +1847,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa && contact.getOption(Contact.Options.PREEMPTIVE_GRANT); final IqPacket iq = new IqPacket(IqPacket.TYPE.SET); iq.query(Xmlns.ROSTER).addChild(contact.asElement()); - account.getXmppConnection().sendIqPacket(iq, null); + account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler); if (sendUpdates) { sendPresencePacket(account, mPresenceGenerator.sendPresenceUpdatesTo(contact)); @@ -2065,7 +2077,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa Element item = iq.query(Xmlns.ROSTER).addChild("item"); item.setAttribute("jid", contact.getJid().toString()); item.setAttribute("subscription", "remove"); - account.getXmppConnection().sendIqPacket(iq, null); + account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler); } } From 6059ed47388c174db4d4eaa7e5d0b70e16dab6ac Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 26 May 2015 12:00:38 +0200 Subject: [PATCH 16/37] update unread count badge only when necessary --- .../services/XmppConnectionService.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index ebb14669..cfb2d50d 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -227,7 +227,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa private final List mInProgressAvatarFetches = new ArrayList<>(); private MessageArchiveService mMessageArchiveService = new MessageArchiveService(this); private OnConversationUpdate mOnConversationUpdate = null; - private Integer convChangedListenerCount = 0; + private int convChangedListenerCount = 0; + private int unreadCount = 0; private OnAccountUpdate mOnAccountUpdate = null; private OnStatusChanged statusListener = new OnStatusChanged() { @@ -2277,13 +2278,16 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa updateUnreadCountBadge(); } - public void updateUnreadCountBadge() { + public synchronized void updateUnreadCountBadge() { int count = unreadCount(); - Log.d(Config.LOGTAG, "update unread count to " + count); - if (count > 0) { - ShortcutBadger.with(getApplicationContext()).count(count); - } else { - ShortcutBadger.with(getApplicationContext()).remove(); + if (unreadCount != count) { + Log.d(Config.LOGTAG, "update unread count to " + count); + if (count > 0) { + ShortcutBadger.with(getApplicationContext()).count(count); + } else { + ShortcutBadger.with(getApplicationContext()).remove(); + } + unreadCount = count; } } From 402e5363d1db774e5e244948a2f0e961b48bb0a7 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 26 May 2015 12:00:55 +0200 Subject: [PATCH 17/37] deduplicate private muc messages --- .../java/eu/siacs/conversations/parser/MessageParser.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 1e683892..898613e3 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -307,7 +307,9 @@ public class MessageParser extends AbstractParser implements } } updateLastseen(packet,account,true); - boolean checkForDuplicates = serverMsgId != null || (isTypeGroupChat && packet.hasChild("delay","urn:xmpp:delay")); + boolean checkForDuplicates = serverMsgId != null + || (isTypeGroupChat && packet.hasChild("delay","urn:xmpp:delay")) + || message.getType() == Message.TYPE_PRIVATE; if (checkForDuplicates && conversation.hasDuplicateMessage(message)) { Log.d(Config.LOGTAG,"skipping duplicate message from "+message.getCounterpart().toString()+" "+message.getBody()); return; From 0f6f6adca0aa198eb78f88e4fc1b36a30ce8410c Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 26 May 2015 13:04:22 +0200 Subject: [PATCH 18/37] removed unnecessary / inacurate debug logging --- .../eu/siacs/conversations/services/XmppConnectionService.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index cfb2d50d..fa8dd7d6 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -499,8 +499,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa long pingTimeoutIn = (lastSent + Config.PING_TIMEOUT * 1000) - SystemClock.elapsedRealtime(); if (lastSent > lastReceived) { if (pingTimeoutIn < 0) { - long age = (SystemClock.elapsedRealtime() - account.getXmppConnection().getLastConnect()) / 1000; - Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": ping timeout. connection age was: "+age+"s"); + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": ping timeout"); this.reconnectAccount(account, true); } else { int secs = (int) (pingTimeoutIn / 1000); From 165965bb83bb3ee9750b8cfecea87ddb154fc649 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 27 May 2015 11:44:44 +0200 Subject: [PATCH 19/37] parse nick and avatar only from available presences to avoid potential error reflection --- .../conversations/parser/PresenceParser.java | 73 +++++++------------ 1 file changed, 26 insertions(+), 47 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java index f7872210..d83347d8 100644 --- a/src/main/java/eu/siacs/conversations/parser/PresenceParser.java +++ b/src/main/java/eu/siacs/conversations/parser/PresenceParser.java @@ -45,38 +45,37 @@ public class PresenceParser extends AbstractParser implements } public void parseContactPresence(PresencePacket packet, Account account) { - PresenceGenerator mPresenceGenerator = mXmppConnectionService - .getPresenceGenerator(); - if (packet.getFrom() == null) { + PresenceGenerator mPresenceGenerator = mXmppConnectionService.getPresenceGenerator(); + final Jid from = packet.getFrom(); + if (from == null) { return; } - final Jid from = packet.getFrom(); - String type = packet.getAttribute("type"); - Contact contact = account.getRoster().getContact(packet.getFrom()); + final String type = packet.getAttribute("type"); + final Contact contact = account.getRoster().getContact(from); if (type == null) { - String presence; - if (!from.isBareJid()) { - presence = from.getResourcepart(); - } else { - presence = ""; + String presence = from.isBareJid() ? "" : from.getResourcepart(); + contact.setPresenceName(packet.findChildContent("nick", "http://jabber.org/protocol/nick")); + Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update")); + if (avatar != null && !contact.isSelf()) { + avatar.owner = from.toBareJid(); + if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) { + if (contact.setAvatar(avatar)) { + mXmppConnectionService.getAvatarService().clear(contact); + mXmppConnectionService.updateConversationUi(); + mXmppConnectionService.updateRosterUi(); + } + } else { + mXmppConnectionService.fetchAvatar(account, avatar); + } } int sizeBefore = contact.getPresences().size(); - contact.updatePresence(presence, - Presences.parseShow(packet.findChild("show"))); + contact.updatePresence(presence, Presences.parseShow(packet.findChild("show"))); PgpEngine pgp = mXmppConnectionService.getPgpEngine(); - if (pgp != null) { - Element x = packet.findChild("x", "jabber:x:signed"); - if (x != null) { - Element status = packet.findChild("status"); - String msg; - if (status != null) { - msg = status.getContent(); - } else { - msg = ""; - } - contact.setPgpKeyId(pgp.fetchKeyId(account, msg, - x.getContent())); - } + Element x = packet.findChild("x", "jabber:x:signed"); + if (pgp != null && x != null) { + Element status = packet.findChild("status"); + String msg = status != null ? status.getContent() : ""; + contact.setPgpKeyId(pgp.fetchKeyId(account, msg, x.getContent())); } boolean online = sizeBefore < contact.getPresences().size(); updateLastseen(packet, account, false); @@ -87,8 +86,7 @@ public class PresenceParser extends AbstractParser implements } else { contact.removePresence(from.getResourcepart()); } - mXmppConnectionService.onContactStatusChanged - .onContactStatusChanged(contact, false); + mXmppConnectionService.onContactStatusChanged.onContactStatusChanged(contact, false); } else if (type.equals("subscribe")) { if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) { mXmppConnectionService.sendPresencePacket(account, @@ -97,25 +95,6 @@ public class PresenceParser extends AbstractParser implements contact.setOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST); } } - Element nick = packet.findChild("nick", - "http://jabber.org/protocol/nick"); - if (nick != null) { - contact.setPresenceName(nick.getContent()); - } - Element x = packet.findChild("x","vcard-temp:x:update"); - Avatar avatar = Avatar.parsePresence(x); - if (avatar != null && !contact.isSelf()) { - avatar.owner = from.toBareJid(); - if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) { - if (contact.setAvatar(avatar)) { - mXmppConnectionService.getAvatarService().clear(contact); - mXmppConnectionService.updateConversationUi(); - mXmppConnectionService.updateRosterUi(); - } - } else { - mXmppConnectionService.fetchAvatar(account,avatar); - } - } mXmppConnectionService.updateRosterUi(); } From 5373956e19f318551841d59885f4c2567ffd46a3 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 28 May 2015 16:55:48 +0200 Subject: [PATCH 20/37] use dns library to resolve missing ipv6 or ipv4 addresses --- .../siacs/conversations/utils/DNSHelper.java | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java index 42dd1c95..5135acf7 100644 --- a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java @@ -142,11 +142,21 @@ public class DNSHelper { for (SRV srv : result) { if (ips6.containsKey(srv.getName())) { values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips6)); + } else { + DNSMessage response = client.query(srv.getName(), TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress()); + if (response.getAnswers().length >= 1) { + values.add(createNamePortBundle(srv.getName(),srv.getPort(),response.getAnswers()[0].getPayload())); + } } if (ips4.containsKey(srv.getName())) { values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips4)); + } else { + DNSMessage response = client.query(srv.getName(), TYPE.A, CLASS.IN, dnsServer.getHostAddress()); + if (response.getAnswers().length >= 1) { + values.add(createNamePortBundle(srv.getName(),srv.getPort(),response.getAnswers()[0].getPayload())); + } } - values.add(createNamePortBundle(srv.getName(),srv.getPort(),null)); + values.add(createNamePortBundle(srv.getName(), srv.getPort())); } bundle.putParcelableArrayList("values", values); } catch (SocketTimeoutException e) { @@ -157,6 +167,13 @@ public class DNSHelper { return bundle; } + private static Bundle createNamePortBundle(String name, int port) { + Bundle namePort = new Bundle(); + namePort.putString("name", name); + namePort.putInt("port", port); + return namePort; + } + private static Bundle createNamePortBundle(String name, int port, TreeMap> ips) { Bundle namePort = new Bundle(); namePort.putString("name", name); @@ -169,6 +186,18 @@ public class DNSHelper { return namePort; } + private static Bundle createNamePortBundle(String name, int port, Data data) { + Bundle namePort = new Bundle(); + namePort.putString("name", name); + namePort.putInt("port", port); + if (data instanceof A) { + namePort.putString("ip", data.toString()); + } else if (data instanceof AAAA) { + namePort.putString("ip","["+data.toString()+"]"); + } + return namePort; + } + final protected static char[] hexArray = "0123456789ABCDEF".toCharArray(); public static String bytesToHex(byte[] bytes) { From a577ec7c318d734de3ed8c2dc5ab1dd4b773b78a Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 28 May 2015 17:31:46 +0200 Subject: [PATCH 21/37] let dns library take care of no-srv style hosts as well --- .../siacs/conversations/utils/DNSHelper.java | 25 ++++++++++++------- .../conversations/xmpp/XmppConnection.java | 3 --- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java index 5135acf7..e91005a1 100644 --- a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java @@ -37,9 +37,6 @@ public class DNSHelper { Bundle b = queryDNS(host, ip); if (b.containsKey("values")) { return b; - } else if (b.containsKey("error") - && "nosrv".equals(b.getString("error", null))) { - return b; } } } @@ -134,26 +131,35 @@ public class DNSHelper { } + ArrayList values = new ArrayList<>(); if (result.size() == 0) { - bundle.putString("error", "nosrv"); + DNSMessage response; + response = client.query(host, TYPE.A, CLASS.IN, dnsServer.getHostAddress()); + for(int i = 0; i < response.getAnswers().length; ++i) { + values.add(createNamePortBundle(host,5222,response.getAnswers()[i].getPayload())); + } + response = client.query(host, TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress()); + for(int i = 0; i < response.getAnswers().length; ++i) { + values.add(createNamePortBundle(host,5222,response.getAnswers()[i].getPayload())); + } + bundle.putParcelableArrayList("values", values); return bundle; } - ArrayList values = new ArrayList<>(); for (SRV srv : result) { if (ips6.containsKey(srv.getName())) { values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips6)); } else { DNSMessage response = client.query(srv.getName(), TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress()); - if (response.getAnswers().length >= 1) { - values.add(createNamePortBundle(srv.getName(),srv.getPort(),response.getAnswers()[0].getPayload())); + for(int i = 0; i < response.getAnswers().length; ++i) { + values.add(createNamePortBundle(host,5222,response.getAnswers()[i].getPayload())); } } if (ips4.containsKey(srv.getName())) { values.add(createNamePortBundle(srv.getName(),srv.getPort(),ips4)); } else { DNSMessage response = client.query(srv.getName(), TYPE.A, CLASS.IN, dnsServer.getHostAddress()); - if (response.getAnswers().length >= 1) { - values.add(createNamePortBundle(srv.getName(),srv.getPort(),response.getAnswers()[0].getPayload())); + for(int i = 0; i < response.getAnswers().length; ++i) { + values.add(createNamePortBundle(host,5222,response.getAnswers()[i].getPayload())); } } values.add(createNamePortBundle(srv.getName(), srv.getPort())); @@ -162,6 +168,7 @@ public class DNSHelper { } catch (SocketTimeoutException e) { bundle.putString("error", "timeout"); } catch (Exception e) { + Log.d(Config.LOGTAG,e.getMessage()); bundle.putString("error", "unhandled"); } return bundle; diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index d52ebee5..f6e0bfdc 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -200,9 +200,6 @@ public class XmppConnection implements Runnable { if (socketError) { throw new UnknownHostException(); } - } else if (result.containsKey("error") - && "nosrv".equals(result.getString("error", null))) { - socket = new Socket(account.getServer().getDomainpart(), 5222); } else { throw new IOException("unhandled exception in DNS resolver"); } From df86b0fc47cb7af8e97826f97d0e202405cff414 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 2 Jun 2015 06:23:32 +0200 Subject: [PATCH 22/37] improved compatibility with muc components that change the message id --- .../java/eu/siacs/conversations/parser/MessageParser.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 898613e3..fe42a2ae 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -269,9 +269,10 @@ public class MessageParser extends AbstractParser implements status = Message.STATUS_SEND; if (mXmppConnectionService.markMessage(conversation, remoteMsgId, Message.STATUS_SEND_RECEIVED)) { return; - } else if (remoteMsgId == null) { - Message message = conversation.findSentMessageWithBody(packet.getBody()); + } else { + Message message = conversation.findSentMessageWithBody(body); if (message != null) { + message.setRemoteMsgId(remoteMsgId); mXmppConnectionService.markMessage(message, Message.STATUS_SEND_RECEIVED); return; } From 8ac933be9f8fddfda42159636d41c7652f7b79d6 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 2 Jun 2015 12:21:35 +0200 Subject: [PATCH 23/37] fixed more edge cases in muc message parser --- .../java/eu/siacs/conversations/parser/MessageParser.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index fe42a2ae..bbb550b2 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -266,14 +266,14 @@ public class MessageParser extends AbstractParser implements Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, counterpart.toBareJid(), isTypeGroupChat); if (isTypeGroupChat) { if (counterpart.getResourcepart().equals(conversation.getMucOptions().getActualNick())) { - status = Message.STATUS_SEND; - if (mXmppConnectionService.markMessage(conversation, remoteMsgId, Message.STATUS_SEND_RECEIVED)) { + status = Message.STATUS_SEND_RECEIVED; + if (mXmppConnectionService.markMessage(conversation, remoteMsgId, status)) { return; } else { Message message = conversation.findSentMessageWithBody(body); if (message != null) { message.setRemoteMsgId(remoteMsgId); - mXmppConnectionService.markMessage(message, Message.STATUS_SEND_RECEIVED); + mXmppConnectionService.markMessage(message, status); return; } } From ba5d0892a6114bd85ea667428518f6f75a096af3 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 2 Jun 2015 12:23:54 +0200 Subject: [PATCH 24/37] stick with 1.4.x --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 7580be36..41f39941 100644 --- a/build.gradle +++ b/build.gradle @@ -46,7 +46,7 @@ android { minSdkVersion 14 targetSdkVersion 21 versionCode 70 - versionName "1.5.0-alpha" + versionName "1.4.4-beta" } compileOptions { From fe1cff016f3e1bccaf8287fb490380281cb13983 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 2 Jun 2015 13:33:20 +0200 Subject: [PATCH 25/37] avoid unnessary muc mam queries when message count is 0 after subject --- .../java/eu/siacs/conversations/entities/Conversation.java | 6 ++++++ .../java/eu/siacs/conversations/parser/MessageParser.java | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index b5f604b0..289ed4ea 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -249,6 +249,12 @@ public class Conversation extends AbstractEntity implements Blockable { this.mLastReceivedOtrMessageId = id; } + public int countMessages() { + synchronized (this.messages) { + return this.messages.size(); + } + } + public interface OnMessageFound { public void onMessageFound(final Message message); diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index bbb550b2..d0d517ee 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -367,7 +367,7 @@ public class MessageParser extends AbstractParser implements if (packet.hasChild("subject") && isTypeGroupChat) { Conversation conversation = mXmppConnectionService.find(account, from.toBareJid()); if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) { - conversation.setHasMessagesLeftOnServer(true); + conversation.setHasMessagesLeftOnServer(conversation.countMessages() > 0); conversation.getMucOptions().setSubject(packet.findChildContent("subject")); mXmppConnectionService.updateConversationUi(); return; From b40922d7475249e07af77aacd68f39cfd4dd3859 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 2 Jun 2015 14:10:49 +0200 Subject: [PATCH 26/37] upgrade build tools --- src/main/res/values-ca@valencia/strings.xml | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 src/main/res/values-ca@valencia/strings.xml diff --git a/src/main/res/values-ca@valencia/strings.xml b/src/main/res/values-ca@valencia/strings.xml deleted file mode 100644 index c757504a..00000000 --- a/src/main/res/values-ca@valencia/strings.xml +++ /dev/null @@ -1,2 +0,0 @@ - - From a4fb6bb3a3fd06435cbb3244f120506e47792e1b Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 2 Jun 2015 15:24:58 +0200 Subject: [PATCH 27/37] really update build tools --- build.gradle | 2 +- libs/MemorizingTrustManager/build.gradle | 2 +- libs/openpgp-api-lib/build.gradle | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 41f39941..c75c3187 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:1.0.1' + classpath 'com.android.tools.build:gradle:1.2.3' } } diff --git a/libs/MemorizingTrustManager/build.gradle b/libs/MemorizingTrustManager/build.gradle index aa022a93..47491fec 100644 --- a/libs/MemorizingTrustManager/build.gradle +++ b/libs/MemorizingTrustManager/build.gradle @@ -3,7 +3,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:0.7.+' + classpath 'com.android.tools.build:gradle:1.2.3' } } diff --git a/libs/openpgp-api-lib/build.gradle b/libs/openpgp-api-lib/build.gradle index 6e8fdfb5..a991cf26 100644 --- a/libs/openpgp-api-lib/build.gradle +++ b/libs/openpgp-api-lib/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:0.12.2' + classpath 'com.android.tools.build:gradle:1.2.3' } } From 3eab3291dee7a3d20d2cf6ccb14da3e59562470f Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 3 Jun 2015 03:05:20 +0200 Subject: [PATCH 28/37] properly calculate remaining size. should fix #1243 --- .../conversations/xmpp/jingle/JingleInbandTransport.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java index 29efcf8f..8da53c1b 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleInbandTransport.java @@ -160,13 +160,18 @@ public class JingleInbandTransport extends JingleTransport { byte[] buffer = new byte[this.bufferSize]; try { int count = fileInputStream.read(buffer); - this.remainingSize -= count; - if (count != buffer.length && count != -1) { + if (count == -1) { + file.setSha1Sum(CryptoHelper.bytesToHex(digest.digest())); + this.onFileTransmissionStatusChanged.onFileTransmitted(file); + fileInputStream.close(); + return; + } else if (count != buffer.length) { int rem = fileInputStream.read(buffer,count,buffer.length-count); if (rem > 0) { count += rem; } } + this.remainingSize -= count; this.digest.update(buffer,0,count); String base64 = Base64.encodeToString(buffer,0,count, Base64.NO_WRAP); IqPacket iq = new IqPacket(IqPacket.TYPE.SET); From 53e8964dc17e5c994eda369e78c3bb81133abd9b Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 3 Jun 2015 14:05:54 +0200 Subject: [PATCH 29/37] reworked handeling of system contacts --- .../siacs/conversations/entities/Contact.java | 12 +++++- .../siacs/conversations/entities/Roster.java | 14 ++++--- .../services/XmppConnectionService.java | 15 +++++-- .../ui/ContactDetailsActivity.java | 42 +++++++++---------- 4 files changed, 51 insertions(+), 32 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Contact.java b/src/main/java/eu/siacs/conversations/entities/Contact.java index 9dbca59a..e546f214 100644 --- a/src/main/java/eu/siacs/conversations/entities/Contact.java +++ b/src/main/java/eu/siacs/conversations/entities/Contact.java @@ -237,8 +237,16 @@ public class Contact implements ListItem, Blockable { return this.presences.getMostAvailableStatus(); } - public void setPhotoUri(String uri) { - this.photoUri = uri; + public boolean setPhotoUri(String uri) { + if (uri != null && !uri.equals(this.photoUri)) { + this.photoUri = uri; + return true; + } else if (this.photoUri != null && uri == null) { + this.photoUri = null; + return true; + } else { + return false; + } } public void setServerName(String serverName) { diff --git a/src/main/java/eu/siacs/conversations/entities/Roster.java b/src/main/java/eu/siacs/conversations/entities/Roster.java index ce058004..d6777ef6 100644 --- a/src/main/java/eu/siacs/conversations/entities/Roster.java +++ b/src/main/java/eu/siacs/conversations/entities/Roster.java @@ -2,6 +2,7 @@ package eu.siacs.conversations.entities; import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import eu.siacs.conversations.xmpp.jid.Jid; @@ -55,12 +56,15 @@ public class Roster { } } - public void clearSystemAccounts() { - for (Contact contact : getContacts()) { - contact.setPhotoUri(null); - contact.setSystemName(null); - contact.setSystemAccount(null); + public List getWithSystemAccounts() { + List with = getContacts(); + for(Iterator iterator = with.iterator(); iterator.hasNext();) { + Contact contact = iterator.next(); + if (contact.getSystemAccount() == null) { + iterator.remove(); + } } + return with; } public List getContacts() { diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index fa8dd7d6..7b5532a4 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -927,7 +927,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa public void run() { Log.d(Config.LOGTAG,"start merging phone contacts with roster"); for (Account account : accounts) { - account.getRoster().clearSystemAccounts(); + List withSystemAccounts = account.getRoster().getWithSystemAccounts(); for (Bundle phoneContact : phoneContacts) { if (Thread.interrupted()) { Log.d(Config.LOGTAG,"interrupted merging phone contacts"); @@ -944,9 +944,18 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa + "#" + phoneContact.getString("lookup"); contact.setSystemAccount(systemAccount); - contact.setPhotoUri(phoneContact.getString("photouri")); - getAvatarService().clear(contact); + if (contact.setPhotoUri(phoneContact.getString("photouri"))) { + getAvatarService().clear(contact); + } contact.setSystemName(phoneContact.getString("displayname")); + withSystemAccounts.remove(contact); + } + for(Contact contact : withSystemAccounts) { + contact.setSystemAccount(null); + contact.setSystemName(null); + if (contact.setPhotoUri(null)) { + getAvatarService().clear(contact); + } } } Log.d(Config.LOGTAG,"finished merging phone contacts"); diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java index f7156d7a..c190caed 100644 --- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java @@ -10,6 +10,7 @@ import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceManager; +import android.provider.ContactsContract; import android.provider.ContactsContract.CommonDataKinds; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Intents; @@ -126,14 +127,23 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd @Override public void onClick(View v) { - AlertDialog.Builder builder = new AlertDialog.Builder( - ContactDetailsActivity.this); - builder.setTitle(getString(R.string.action_add_phone_book)); - builder.setMessage(getString(R.string.add_phone_book_text, + if (contact.getSystemAccount() == null) { + AlertDialog.Builder builder = new AlertDialog.Builder( + ContactDetailsActivity.this); + builder.setTitle(getString(R.string.action_add_phone_book)); + builder.setMessage(getString(R.string.add_phone_book_text, contact.getJid())); - builder.setNegativeButton(getString(R.string.cancel), null); - builder.setPositiveButton(getString(R.string.add), addToPhonebook); - builder.create().show(); + builder.setNegativeButton(getString(R.string.cancel), null); + builder.setPositiveButton(getString(R.string.add), addToPhonebook); + builder.create().show(); + } else { + String[] systemAccount = contact.getSystemAccount().split("#"); + long id = Long.parseLong(systemAccount[0]); + Uri uri = ContactsContract.Contacts.getLookupUri(id, systemAccount[1]); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(uri); + startActivity(intent); + } } }; @@ -340,12 +350,9 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd } else { contactJidTv.setText(contact.getJid().toString()); } - accountJidTv.setText(getString(R.string.using_account, contact - .getAccount().getJid().toBareJid())); - prepareContactBadge(badge, contact); - if (contact.getSystemAccount() == null) { - badge.setOnClickListener(onBadgeClick); - } + accountJidTv.setText(getString(R.string.using_account, contact.getAccount().getJid().toBareJid())); + badge.setImageBitmap(avatarService().get(contact, getPixel(72))); + badge.setOnClickListener(this.onBadgeClick); keys.removeAllViews(); boolean hasKeys = false; @@ -419,15 +426,6 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd } } - private void prepareContactBadge(QuickContactBadge badge, Contact contact) { - if (contact.getSystemAccount() != null) { - String[] systemAccount = contact.getSystemAccount().split("#"); - long id = Long.parseLong(systemAccount[0]); - badge.assignContactUri(Contacts.getLookupUri(id, systemAccount[1])); - } - badge.setImageBitmap(avatarService().get(contact, getPixel(72))); - } - protected void confirmToDeleteFingerprint(final String fingerprint) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.delete_fingerprint); From 6b794eca2cfc8d1edf4f73c740a3e1b4e966da27 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 4 Jun 2015 02:47:24 +0200 Subject: [PATCH 30/37] send_received muc messages will mark a conversation a read --- src/main/java/eu/siacs/conversations/parser/MessageParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index d0d517ee..96568fbd 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -320,7 +320,7 @@ public class MessageParser extends AbstractParser implements } conversation.add(message); if (serverMsgId == null) { - if (status == Message.STATUS_SEND) { + if (status == Message.STATUS_SEND || status == Message.STATUS_SEND_RECEIVED) { mXmppConnectionService.markRead(conversation); account.activateGracePeriod(); } else { From 8f07e4c441778e476119f25cc8cac833e0937508 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 4 Jun 2015 02:48:07 +0200 Subject: [PATCH 31/37] streamlined dns helper by ignoring weight --- .../siacs/conversations/utils/DNSHelper.java | 65 +++---------------- 1 file changed, 10 insertions(+), 55 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java index e91005a1..7a51c1ce 100644 --- a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java @@ -47,88 +47,43 @@ public class DNSHelper { Bundle bundle = new Bundle(); try { String qname = "_xmpp-client._tcp." + host; - Log.d(Config.LOGTAG, - "using dns server: " + dnsServer.getHostAddress() - + " to look up " + host); - DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN, - dnsServer.getHostAddress()); - - // How should we handle priorities and weight? - // Wikipedia has a nice article about priorities vs. weights: - // https://en.wikipedia.org/wiki/SRV_record#Provisioning_for_high_service_availability - - // we bucket the SRV records based on priority, pick per priority - // a random order respecting the weight, and dump that priority by - // priority + Log.d(Config.LOGTAG, "using dns server: " + dnsServer.getHostAddress() + " to look up " + host); + DNSMessage message = client.query(qname, TYPE.SRV, CLASS.IN, dnsServer.getHostAddress()); TreeMap> priorities = new TreeMap<>(); TreeMap> ips4 = new TreeMap<>(); TreeMap> ips6 = new TreeMap<>(); - for (Record[] rrset : new Record[][] { message.getAnswers(), - message.getAdditionalResourceRecords() }) { + for (Record[] rrset : new Record[][] { message.getAnswers(), message.getAdditionalResourceRecords() }) { for (Record rr : rrset) { Data d = rr.getPayload(); - if (d instanceof SRV - && NameUtil.idnEquals(qname, rr.getName())) { + if (d instanceof SRV && NameUtil.idnEquals(qname, rr.getName())) { SRV srv = (SRV) d; if (!priorities.containsKey(srv.getPriority())) { - priorities.put(srv.getPriority(), - new ArrayList(2)); + priorities.put(srv.getPriority(),new ArrayList()); } priorities.get(srv.getPriority()).add(srv); } if (d instanceof A) { - A arecord = (A) d; + A a = (A) d; if (!ips4.containsKey(rr.getName())) { - ips4.put(rr.getName(), new ArrayList(3)); + ips4.put(rr.getName(), new ArrayList()); } - ips4.get(rr.getName()).add(arecord.toString()); + ips4.get(rr.getName()).add(a.toString()); } if (d instanceof AAAA) { AAAA aaaa = (AAAA) d; if (!ips6.containsKey(rr.getName())) { - ips6.put(rr.getName(), new ArrayList(3)); + ips6.put(rr.getName(), new ArrayList()); } ips6.get(rr.getName()).add("[" + aaaa.toString() + "]"); } } } - Random rnd = new Random(); - ArrayList result = new ArrayList<>( - priorities.size() * 2 + 1); + ArrayList result = new ArrayList<>(); for (ArrayList s : priorities.values()) { - - // trivial case - if (s.size() <= 1) { - result.addAll(s); - continue; - } - - long totalweight = 0l; - for (SRV srv : s) { - totalweight += srv.getWeight(); - } - - while (totalweight > 0l && s.size() > 0) { - long p = (rnd.nextLong() & 0x7fffffffffffffffl) - % totalweight; - int i = 0; - while (p > 0) { - p -= s.get(i++).getPriority(); - } - if (i>0) i--; - // remove is expensive, but we have only a few entries - // anyway - SRV srv = s.remove(i); - totalweight -= srv.getWeight(); - result.add(srv); - } - - Collections.shuffle(s, rnd); result.addAll(s); - } ArrayList values = new ArrayList<>(); From a4e9f0c9c04c5fcc6e9686552a5fcc0198526a49 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 4 Jun 2015 16:26:51 +0200 Subject: [PATCH 32/37] fixed obvious bug in dns helper --- src/main/java/eu/siacs/conversations/utils/DNSHelper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java index 7a51c1ce..6f414ccc 100644 --- a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java @@ -106,7 +106,7 @@ public class DNSHelper { } else { DNSMessage response = client.query(srv.getName(), TYPE.AAAA, CLASS.IN, dnsServer.getHostAddress()); for(int i = 0; i < response.getAnswers().length; ++i) { - values.add(createNamePortBundle(host,5222,response.getAnswers()[i].getPayload())); + values.add(createNamePortBundle(srv.getName(),srv.getPort(),response.getAnswers()[i].getPayload())); } } if (ips4.containsKey(srv.getName())) { @@ -114,7 +114,7 @@ public class DNSHelper { } else { DNSMessage response = client.query(srv.getName(), TYPE.A, CLASS.IN, dnsServer.getHostAddress()); for(int i = 0; i < response.getAnswers().length; ++i) { - values.add(createNamePortBundle(host,5222,response.getAnswers()[i].getPayload())); + values.add(createNamePortBundle(srv.getName(),srv.getPort(),response.getAnswers()[i].getPayload())); } } values.add(createNamePortBundle(srv.getName(), srv.getPort())); From 31deb44780f1d6c955897d6adefea1d1ea63eb2c Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 4 Jun 2015 16:27:27 +0200 Subject: [PATCH 33/37] version bump --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index c75c3187..35cf1133 100644 --- a/build.gradle +++ b/build.gradle @@ -45,8 +45,8 @@ android { defaultConfig { minSdkVersion 14 targetSdkVersion 21 - versionCode 70 - versionName "1.4.4-beta" + versionCode 71 + versionName "1.4.4-beta2" } compileOptions { From 9d1e8205a2d362a33e142a49db1787fe5145ce3f Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 5 Jun 2015 08:46:06 +0200 Subject: [PATCH 34/37] made i/o and memory intensive operations execute in serial order --- .../services/XmppConnectionService.java | 29 ++++++++++------ .../utils/SerialSingleThreadExecutor.java | 34 +++++++++++++++++++ 2 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 src/main/java/eu/siacs/conversations/utils/SerialSingleThreadExecutor.java diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 7b5532a4..0a264dd1 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -77,6 +77,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; @@ -119,6 +120,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa startService(intent); } }; + + private final SerialSingleThreadExecutor mFileAddingExecutor = new SerialSingleThreadExecutor(); + private final SerialSingleThreadExecutor mDatabaseExecutor = new SerialSingleThreadExecutor(); + private final IBinder mBinder = new XmppConnectionBinder(); private final List conversations = new CopyOnWriteArrayList<>(); private final FileObserver fileObserver = new FileObserver( @@ -373,7 +378,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa callback.success(message); } } else { - new Thread(new Runnable() { + mFileAddingExecutor.execute(new Runnable() { @Override public void run() { try { @@ -388,8 +393,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa callback.error(e.getResId(),message); } } - }).start(); - + }); } } @@ -405,7 +409,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } message.setCounterpart(conversation.getNextCounterpart()); message.setType(Message.TYPE_IMAGE); - new Thread(new Runnable() { + mFileAddingExecutor.execute(new Runnable() { @Override public void run() { @@ -420,7 +424,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa callback.error(e.getResId(), message); } } - }).start(); + }); } public Conversation find(Bookmark bookmark) { @@ -976,7 +980,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa Account account = accountLookupTable.get(conversation.getAccountUuid()); conversation.setAccount(account); } - new Thread(new Runnable() { + Runnable runnable =new Runnable() { @Override public void run() { Log.d(Config.LOGTAG,"restoring roster"); @@ -997,7 +1001,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa Log.d(Config.LOGTAG,"restored all messages"); updateConversationUi(); } - }).start(); + }; + mDatabaseExecutor.execute(runnable); } } @@ -1066,7 +1071,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa if (XmppConnectionService.this.getMessageArchiveService().queryInProgress(conversation,callback)) { return; } - new Thread(new Runnable() { + Runnable runnable = new Runnable() { @Override public void run() { final Account account = conversation.getAccount(); @@ -1085,7 +1090,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa callback.informUser(R.string.fetching_history_from_server); } } - }).start(); + }; + mDatabaseExecutor.execute(runnable); } public List getAccounts() { @@ -2344,13 +2350,14 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa } public void syncRosterToDisk(final Account account) { - new Thread(new Runnable() { + Runnable runnable = new Runnable() { @Override public void run() { databaseBackend.writeRoster(account.getRoster()); } - }).start(); + }; + mDatabaseExecutor.execute(runnable); } diff --git a/src/main/java/eu/siacs/conversations/utils/SerialSingleThreadExecutor.java b/src/main/java/eu/siacs/conversations/utils/SerialSingleThreadExecutor.java new file mode 100644 index 00000000..bfb4668d --- /dev/null +++ b/src/main/java/eu/siacs/conversations/utils/SerialSingleThreadExecutor.java @@ -0,0 +1,34 @@ +package eu.siacs.conversations.utils; + +import java.util.ArrayDeque; +import java.util.Queue; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +public class SerialSingleThreadExecutor implements Executor { + + final Executor executor = Executors.newSingleThreadExecutor(); + final Queue tasks = new ArrayDeque(); + Runnable active; + + public synchronized void execute(final Runnable r) { + tasks.offer(new Runnable() { + public void run() { + try { + r.run(); + } finally { + scheduleNext(); + } + } + }); + if (active == null) { + scheduleNext(); + } + } + + protected synchronized void scheduleNext() { + if ((active = tasks.poll()) != null) { + executor.execute(active); + } + } +} \ No newline at end of file From 0ea83832cde4982d94cbee7b5779d0f32721fea0 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 10 Jun 2015 03:29:52 +0200 Subject: [PATCH 35/37] pulled new translations from transifex --- src/main/res/values-ko/strings.xml | 5 +++++ src/main/res/values-zh-rCN/strings.xml | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/main/res/values-ko/strings.xml b/src/main/res/values-ko/strings.xml index d00ab0fd..9354c432 100644 --- a/src/main/res/values-ko/strings.xml +++ b/src/main/res/values-ko/strings.xml @@ -444,4 +444,9 @@ %d 연락처 선택 + 전송 버튼을 빠른 동작 버튼으로 교체 + 빠른 동작 + 없음 + 최근 사용된 항목 + 빠른 동작 선택 diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index 2f80e064..e194bffd 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -444,4 +444,9 @@ 选择 %d 个联系人 + 以快速动作替代发送按钮 + 快速动作 + + 最近使用过的 + 选择快速动作 From 58201b440856a60d45d3a72cf6aaacd3a36dc828 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 10 Jun 2015 03:30:17 +0200 Subject: [PATCH 36/37] changed paragraph divider --- src/main/java/eu/siacs/conversations/entities/Message.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index 90b74a53..a63d033d 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -17,7 +17,7 @@ public class Message extends AbstractEntity { public static final String TABLENAME = "messages"; - public static final String MERGE_SEPARATOR = "\u200B\n\n"; + public static final String MERGE_SEPARATOR = " \u200B\n\n"; public static final int STATUS_RECEIVED = 0; public static final int STATUS_UNSEND = 1; From 5a48afdd4d44b5865831df470a2c5d00e9cc9599 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 19 Jun 2015 16:25:08 +0200 Subject: [PATCH 37/37] don't perform dns lookups on domain parts that obviously look like ip addresses --- .../siacs/conversations/utils/DNSHelper.java | 24 +++-- .../conversations/xmpp/XmppConnection.java | 98 ++++++++++--------- 2 files changed, 68 insertions(+), 54 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java index 6f414ccc..bb354ae0 100644 --- a/src/main/java/eu/siacs/conversations/utils/DNSHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/DNSHelper.java @@ -20,11 +20,19 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Random; import java.util.TreeMap; +import java.util.regex.Pattern; import android.os.Bundle; import android.util.Log; public class DNSHelper { + + public static final Pattern PATTERN_IPV4 = Pattern.compile("\\A(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z"); + public static final Pattern PATTERN_IPV6_HEX4DECCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?) ::((?:[0-9A-Fa-f]{1,4}:)*)(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z"); + public static final Pattern PATTERN_IPV6_6HEX4DEC = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}:){6,6})(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\z"); + public static final Pattern PATTERN_IPV6_HEXCOMPRESSED = Pattern.compile("\\A((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)\\z"); + public static final Pattern PATTERN_IPV6 = Pattern.compile("\\A(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\\z"); + protected static Client client = new Client(); public static Bundle getSRVRecord(final Jid jid) throws IOException { @@ -160,15 +168,11 @@ public class DNSHelper { return namePort; } - final protected static char[] hexArray = "0123456789ABCDEF".toCharArray(); - - public static String bytesToHex(byte[] bytes) { - char[] hexChars = new char[bytes.length * 2]; - for (int j = 0; j < bytes.length; j++) { - int v = bytes[j] & 0xFF; - hexChars[j * 2] = hexArray[v >>> 4]; - hexChars[j * 2 + 1] = hexArray[v & 0x0F]; - } - return new String(hexChars); + public static boolean isIp(final String server) { + return PATTERN_IPV4.matcher(server).matches() + || PATTERN_IPV6.matcher(server).matches() + || PATTERN_IPV6_6HEX4DEC.matcher(server).matches() + || PATTERN_IPV6_HEX4DECCOMPRESSED.matcher(server).matches() + || PATTERN_IPV6_HEXCOMPRESSED.matcher(server).matches(); } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index f6e0bfdc..8a438906 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -26,6 +26,7 @@ import java.net.IDN; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; +import java.net.SocketAddress; import java.net.UnknownHostException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; @@ -155,53 +156,62 @@ public class XmppConnection implements Runnable { tagWriter = new TagWriter(); packetCallbacks.clear(); this.changeStatus(Account.State.CONNECTING); - final Bundle result = DNSHelper.getSRVRecord(account.getServer()); - final ArrayList values = result.getParcelableArrayList("values"); - if ("timeout".equals(result.getString("error"))) { - throw new IOException("timeout in dns"); - } else if (values != null) { - int i = 0; - boolean socketError = true; - while (socketError && values.size() > i) { - final Bundle namePort = (Bundle) values.get(i); - try { - String srvRecordServer; - try { - srvRecordServer = IDN.toASCII(namePort.getString("name")); - } catch (final IllegalArgumentException e) { - // TODO: Handle me?` - srvRecordServer = ""; - } - final int srvRecordPort = namePort.getInt("port"); - final String srvIpServer = namePort.getString("ip"); - final InetSocketAddress addr; - if (srvIpServer != null) { - addr = new InetSocketAddress(srvIpServer, srvRecordPort); - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() - + ": using values from dns " + srvRecordServer - + "[" + srvIpServer + "]:" + srvRecordPort); - } else { - addr = new InetSocketAddress(srvRecordServer, srvRecordPort); - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() - + ": using values from dns " - + srvRecordServer + ":" + srvRecordPort); - } - socket = new Socket(); - socket.connect(addr, Config.SOCKET_TIMEOUT * 1000); - socketError = false; - } catch (final UnknownHostException e) { - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); - i++; - } catch (final IOException e) { - Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); - i++; - } - } - if (socketError) { + if (DNSHelper.isIp(account.getServer().toString())) { + socket = new Socket(); + try { + socket.connect(new InetSocketAddress(account.getServer().toString(), 5222), Config.SOCKET_TIMEOUT * 1000); + } catch (IOException e) { throw new UnknownHostException(); } } else { - throw new IOException("unhandled exception in DNS resolver"); + final Bundle result = DNSHelper.getSRVRecord(account.getServer()); + final ArrayList values = result.getParcelableArrayList("values"); + if ("timeout".equals(result.getString("error"))) { + throw new IOException("timeout in dns"); + } else if (values != null) { + int i = 0; + boolean socketError = true; + while (socketError && values.size() > i) { + final Bundle namePort = (Bundle) values.get(i); + try { + String srvRecordServer; + try { + srvRecordServer = IDN.toASCII(namePort.getString("name")); + } catch (final IllegalArgumentException e) { + // TODO: Handle me?` + srvRecordServer = ""; + } + final int srvRecordPort = namePort.getInt("port"); + final String srvIpServer = namePort.getString("ip"); + final InetSocketAddress addr; + if (srvIpServer != null) { + addr = new InetSocketAddress(srvIpServer, srvRecordPort); + Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + + ": using values from dns " + srvRecordServer + + "[" + srvIpServer + "]:" + srvRecordPort); + } else { + addr = new InetSocketAddress(srvRecordServer, srvRecordPort); + Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + + ": using values from dns " + + srvRecordServer + ":" + srvRecordPort); + } + socket = new Socket(); + socket.connect(addr, Config.SOCKET_TIMEOUT * 1000); + socketError = false; + } catch (final UnknownHostException e) { + Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); + i++; + } catch (final IOException e) { + Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); + i++; + } + } + if (socketError) { + throw new UnknownHostException(); + } + } else { + throw new IOException("unhandled exception in DNS resolver"); + } } final OutputStream out = socket.getOutputStream(); tagWriter.setOutputStream(out);