diff options
Diffstat (limited to 'src/eu/siacs')
31 files changed, 1859 insertions, 1445 deletions
diff --git a/src/eu/siacs/conversations/crypto/PgpEngine.java b/src/eu/siacs/conversations/crypto/PgpEngine.java index c0d8ca07..2d0c56e1 100644 --- a/src/eu/siacs/conversations/crypto/PgpEngine.java +++ b/src/eu/siacs/conversations/crypto/PgpEngine.java @@ -98,8 +98,7 @@ public class PgpEngine { message.setEncryption(Message.ENCRYPTION_DECRYPTED); PgpEngine.this.mXmppConnectionService .updateMessage(message); - PgpEngine.this.mXmppConnectionService.updateUi( - message.getConversation(), false); + PgpEngine.this.mXmppConnectionService.updateConversationUi(); callback.success(message); return; case OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED: diff --git a/src/eu/siacs/conversations/entities/Account.java b/src/eu/siacs/conversations/entities/Account.java index a73d49f9..ac62cf7b 100644 --- a/src/eu/siacs/conversations/entities/Account.java +++ b/src/eu/siacs/conversations/entities/Account.java @@ -1,6 +1,8 @@ package eu.siacs.conversations.entities; import java.security.interfaces.DSAPublicKey; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; import net.java.otr4j.crypto.OtrCryptoEngineImpl; @@ -67,6 +69,8 @@ public class Account extends AbstractEntity{ private String otrFingerprint; private Roster roster = null; + + private List<Bookmark> bookmarks = new ArrayList<Bookmark>(); public Account() { this.uuid = "0"; @@ -297,4 +301,21 @@ public class Account extends AbstractEntity{ } return this.roster; } + + public void setBookmarks(List<Bookmark> bookmarks) { + this.bookmarks = bookmarks; + } + + public List<Bookmark> getBookmarks() { + return this.bookmarks; + } + + public boolean hasBookmarkFor(String conferenceJid) { + for(Bookmark bmark : this.bookmarks) { + if (bmark.getJid().equals(conferenceJid)) { + return true; + } + } + return false; + } } diff --git a/src/eu/siacs/conversations/entities/Bookmark.java b/src/eu/siacs/conversations/entities/Bookmark.java new file mode 100644 index 00000000..c4e151cb --- /dev/null +++ b/src/eu/siacs/conversations/entities/Bookmark.java @@ -0,0 +1,125 @@ +package eu.siacs.conversations.entities; + +import java.util.Locale; + +import android.content.Context; +import android.graphics.Bitmap; +import eu.siacs.conversations.utils.UIHelper; +import eu.siacs.conversations.xml.Element; + +public class Bookmark implements ListItem { + + private Account account; + private String jid; + private String nick; + private String name; + private boolean autojoin; + private Conversation mJoinedConversation; + + public Bookmark(Account account, String jid) { + this.account = account; + this.jid = jid; + } + + public static Bookmark parse(Element element, Account account) { + Bookmark bookmark = new Bookmark(account,element.getAttribute("jid")); + bookmark.setName(element.getAttribute("name")); + String autojoin = element.getAttribute("autojoin"); + if (autojoin!=null && (autojoin.equals("true")||autojoin.equals("1"))) { + bookmark.setAutojoin(true); + } else { + bookmark.setAutojoin(false); + } + Element nick = element.findChild("nick"); + if (nick!=null) { + bookmark.setNick(nick.getContent()); + } + return bookmark; + } + + public void setAutojoin(boolean autojoin) { + this.autojoin = autojoin; + } + + public void setName(String name) { + this.name = name; + } + + public void setNick(String nick) { + this.nick = nick; + } + + @Override + public int compareTo(ListItem another) { + return this.getDisplayName().compareToIgnoreCase(another.getDisplayName()); + } + + @Override + public String getDisplayName() { + if (this.mJoinedConversation!=null && (this.mJoinedConversation.getMucOptions().getSubject() != null)) { + return this.mJoinedConversation.getMucOptions().getSubject(); + } else if (name!=null) { + return name; + } else { + return this.jid.split("@")[0]; + } + } + + @Override + public String getJid() { + return this.jid.toLowerCase(Locale.US); + } + + public String getNick() { + return this.nick; + } + + public boolean autojoin() { + return autojoin; + } + + public boolean match(String needle) { + return needle == null + || getJid().contains(needle.toLowerCase(Locale.US)) + || getDisplayName().toLowerCase(Locale.US) + .contains(needle.toLowerCase(Locale.US)); + } + + public Account getAccount() { + return this.account; + } + + @Override + public Bitmap getImage(int dpSize, Context context) { + if (this.mJoinedConversation==null) { + return UIHelper.getContactPicture(getDisplayName(), dpSize, context, false); + } else { + return UIHelper.getContactPicture(this.mJoinedConversation, dpSize, context, false); + } + } + + public void setConversation(Conversation conversation) { + this.mJoinedConversation = conversation; + } + + public String getName() { + return name; + } + + public Element toElement() { + Element element = new Element("conference"); + element.setAttribute("jid", this.getJid()); + if (this.getName() != null) { + element.setAttribute("name", this.getName()); + } + if (this.autojoin) { + element.setAttribute("autojoin", "true"); + } else { + element.setAttribute("autojoin", "false"); + } + if (this.nick != null) { + element.addChild("nick").setContent(this.nick); + } + return element; + } +} diff --git a/src/eu/siacs/conversations/entities/Contact.java b/src/eu/siacs/conversations/entities/Contact.java index 50d7af8b..8f8e38a5 100644 --- a/src/eu/siacs/conversations/entities/Contact.java +++ b/src/eu/siacs/conversations/entities/Contact.java @@ -8,11 +8,14 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xml.Element; import android.content.ContentValues; +import android.content.Context; import android.database.Cursor; +import android.graphics.Bitmap; -public class Contact { +public class Contact implements ListItem { public static final String TABLENAME = "contacts"; public static final String SYSTEMNAME = "systemname"; @@ -37,7 +40,7 @@ public class Contact { protected Account account; protected boolean inRoster = true; - + public Lastseen lastseen = new Lastseen(); public Contact(String account, String systemName, String serverName, @@ -83,8 +86,10 @@ public class Contact { } public boolean match(String needle) { - return (jid.toLowerCase().contains(needle.toLowerCase()) || (getDisplayName() - .toLowerCase().contains(needle.toLowerCase()))); + return needle == null + || jid.contains(needle.toLowerCase()) + || getDisplayName().toLowerCase() + .contains(needle.toLowerCase()); } public ContentValues getContentValues() { @@ -127,27 +132,7 @@ public class Contact { public Account getAccount() { return this.account; } - - public boolean couldBeMuc() { - String[] split = this.getJid().split("@"); - if (split.length != 2) { - return false; - } else { - String[] domainParts = split[1].split("\\."); - if (domainParts.length < 3) { - return false; - } else { - return (domainParts[0].equals("conf") - || domainParts[0].equals("conference") - || domainParts[0].equals("room") - || domainParts[0].equals("muc") - || domainParts[0].equals("chat") - || domainParts[0].equals("sala") - || domainParts[0].equals("salas")); - } - } - } - + public Presences getPresences() { return this.presences; } @@ -270,9 +255,11 @@ public class Contact { } else if (subscription.equals("from")) { this.resetOption(Contact.Options.TO); this.setOption(Contact.Options.FROM); + this.resetOption(Contact.Options.PREEMPTIVE_GRANT); } else if (subscription.equals("both")) { this.setOption(Contact.Options.TO); this.setOption(Contact.Options.FROM); + this.resetOption(Contact.Options.PREEMPTIVE_GRANT); } else if (subscription.equals("none")) { this.resetOption(Contact.Options.FROM); this.resetOption(Contact.Options.TO); @@ -308,9 +295,28 @@ public class Contact { public static final int DIRTY_PUSH = 6; public static final int DIRTY_DELETE = 7; } - + public class Lastseen { public long time = 0; public String presence = null; } + + @Override + public int compareTo(ListItem another) { + return this.getDisplayName().compareToIgnoreCase(another.getDisplayName()); + } + + public String getServer() { + String[] split = getJid().split("@"); + if (split.length >= 2) { + return split[1]; + } else { + return null; + } + } + + @Override + public Bitmap getImage(int dpSize, Context context) { + return UIHelper.getContactPicture(this, dpSize, context, false); + } } diff --git a/src/eu/siacs/conversations/entities/Conversation.java b/src/eu/siacs/conversations/entities/Conversation.java index c207a5c9..70752adc 100644 --- a/src/eu/siacs/conversations/entities/Conversation.java +++ b/src/eu/siacs/conversations/entities/Conversation.java @@ -4,8 +4,6 @@ import java.security.interfaces.DSAPublicKey; import java.util.ArrayList; import java.util.List; -import eu.siacs.conversations.services.XmppConnectionService; - import net.java.otr4j.OtrException; import net.java.otr4j.crypto.OtrCryptoEngineImpl; import net.java.otr4j.crypto.OtrCryptoException; @@ -66,6 +64,8 @@ public class Conversation extends AbstractEntity { private boolean otrSessionNeedsStarting = false; + private Bookmark bookmark; + public Conversation(String name, Account account, String contactJid, int mode) { this(java.util.UUID.randomUUID().toString(), name, null, account @@ -117,14 +117,11 @@ public class Conversation extends AbstractEntity { this.messages.get(i).markRead(); } } - - public void markRead(XmppConnectionService service) { - markRead(); - if (service.confirmMessages() && this.latestMarkableMessageId != null) { - service.sendConfirmMessage(getAccount(), getContactJid(), - this.latestMarkableMessageId); - this.latestMarkableMessageId = null; - } + + public String popLatestMarkableMessageId() { + String id = this.latestMarkableMessageId; + this.latestMarkableMessageId = null; + return id; } public Message getLatestMessage() { @@ -147,6 +144,8 @@ public class Conversation extends AbstractEntity { if ((getMode() == MODE_MULTI) && (getMucOptions().getSubject() != null) && useSubject) { return getMucOptions().getSubject(); + } else if (getMode() == MODE_MULTI && bookmark!=null && bookmark.getName() != null) { + return bookmark.getName(); } else { return this.getContact().getDisplayName(); } @@ -380,4 +379,19 @@ public class Conversation extends AbstractEntity { public byte[] getSymmetricKey() { return this.symmetricKey; } + + public void setBookmark(Bookmark bookmark) { + this.bookmark = bookmark; + this.bookmark.setConversation(this); + } + + public void deregisterWithBookmark() { + if (this.bookmark != null) { + this.bookmark.setConversation(null); + } + } + + public Bookmark getBookmark() { + return this.bookmark; + } } diff --git a/src/eu/siacs/conversations/entities/ListItem.java b/src/eu/siacs/conversations/entities/ListItem.java new file mode 100644 index 00000000..c89c85d9 --- /dev/null +++ b/src/eu/siacs/conversations/entities/ListItem.java @@ -0,0 +1,10 @@ +package eu.siacs.conversations.entities; + +import android.content.Context; +import android.graphics.Bitmap; + +public interface ListItem extends Comparable<ListItem> { + public String getDisplayName(); + public String getJid(); + public Bitmap getImage(int dpSize, Context context); +} diff --git a/src/eu/siacs/conversations/generator/MessageGenerator.java b/src/eu/siacs/conversations/generator/MessageGenerator.java index 28504b21..5a216a7e 100644 --- a/src/eu/siacs/conversations/generator/MessageGenerator.java +++ b/src/eu/siacs/conversations/generator/MessageGenerator.java @@ -42,7 +42,7 @@ public class MessageGenerator { delay.setAttribute("stamp", mDateFormat.format(date)); } - public MessagePacket generateOtrChat(Message message) throws OtrException { + public MessagePacket generateOtrChat(Message message) { return generateOtrChat(message, false); } @@ -106,4 +106,26 @@ public class MessageGenerator { packet.setType(MessagePacket.TYPE_ERROR); return packet; } + + public MessagePacket confirm(Account account, String to, String id) { + MessagePacket packet = new MessagePacket(); + packet.setType(MessagePacket.TYPE_NORMAL); + packet.setTo(to); + packet.setFrom(account.getFullJid()); + Element received = packet.addChild("displayed", + "urn:xmpp:chat-markers:0"); + received.setAttribute("id", id); + return packet; + } + + public MessagePacket conferenceSubject(Conversation conversation,String subject) { + MessagePacket packet = new MessagePacket(); + packet.setType(MessagePacket.TYPE_GROUPCHAT); + packet.setTo(conversation.getContactJid().split("/")[0]); + Element subjectChild = new Element("subject"); + subjectChild.setContent(subject); + packet.addChild(subjectChild); + packet.setFrom(conversation.getAccount().getJid()); + return packet; + } } diff --git a/src/eu/siacs/conversations/generator/PresenceGenerator.java b/src/eu/siacs/conversations/generator/PresenceGenerator.java new file mode 100644 index 00000000..a301392e --- /dev/null +++ b/src/eu/siacs/conversations/generator/PresenceGenerator.java @@ -0,0 +1,43 @@ +package eu.siacs.conversations.generator; + +import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.entities.Contact; +import eu.siacs.conversations.xmpp.stanzas.PresencePacket; + +public class PresenceGenerator { + + private PresencePacket subscription(String type, Contact contact) { + PresencePacket packet = new PresencePacket(); + packet.setAttribute("type", type); + packet.setAttribute("to", contact.getJid()); + packet.setAttribute("from", contact.getAccount().getJid()); + return packet; + } + + public PresencePacket requestPresenceUpdatesFrom(Contact contact) { + return subscription("subscribe", contact); + } + + public PresencePacket stopPresenceUpdatesFrom(Contact contact) { + return subscription("unsubscribe", contact); + } + + public PresencePacket stopPresenceUpdatesTo(Contact contact) { + return subscription("unsubscribed", contact); + } + + public PresencePacket sendPresenceUpdatesTo(Contact contact) { + return subscription("subscribed", contact); + } + + public PresencePacket sendPresence(Account account) { + PresencePacket packet = new PresencePacket(); + packet.setAttribute("from", account.getFullJid()); + String sig = account.getPgpSignature(); + if (sig != null) { + packet.addChild("status").setContent("online"); + packet.addChild("x", "jabber:x:signed").setContent(sig); + } + return packet; + } +}
\ No newline at end of file diff --git a/src/eu/siacs/conversations/parser/IqParser.java b/src/eu/siacs/conversations/parser/IqParser.java new file mode 100644 index 00000000..acbeee4d --- /dev/null +++ b/src/eu/siacs/conversations/parser/IqParser.java @@ -0,0 +1,89 @@ +package eu.siacs.conversations.parser; + +import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.entities.Contact; +import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.OnIqPacketReceived; +import eu.siacs.conversations.xmpp.stanzas.IqPacket; + +public class IqParser extends AbstractParser implements OnIqPacketReceived { + + public IqParser(XmppConnectionService service) { + super(service); + } + + public void rosterItems(Account account, Element query) { + String version = query.getAttribute("ver"); + if (version != null) { + account.getRoster().setVersion(version); + } + for (Element item : query.getChildren()) { + if (item.getName().equals("item")) { + String jid = item.getAttribute("jid"); + String name = item.getAttribute("name"); + String subscription = item.getAttribute("subscription"); + Contact contact = account.getRoster().getContact(jid); + if (!contact.getOption(Contact.Options.DIRTY_PUSH)) { + contact.setServerName(name); + } + if (subscription.equals("remove")) { + contact.resetOption(Contact.Options.IN_ROSTER); + contact.resetOption(Contact.Options.DIRTY_DELETE); + contact.resetOption(Contact.Options.PREEMPTIVE_GRANT); + } else { + contact.setOption(Contact.Options.IN_ROSTER); + contact.resetOption(Contact.Options.DIRTY_PUSH); + contact.parseSubscriptionFromElement(item); + } + } + } + } + + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + if (packet.hasChild("query", "jabber:iq:roster")) { + String from = packet.getFrom(); + if ((from == null) || (from.equals(account.getJid()))) { + Element query = packet.findChild("query"); + this.rosterItems(account, query); + } + } else if (packet + .hasChild("open", "http://jabber.org/protocol/ibb") + || packet + .hasChild("data", "http://jabber.org/protocol/ibb")) { + mXmppConnectionService.getJingleConnectionManager().deliverIbbPacket(account, packet); + } else if (packet.hasChild("query", + "http://jabber.org/protocol/disco#info")) { + IqPacket iqResponse = packet + .generateRespone(IqPacket.TYPE_RESULT); + Element query = iqResponse.addChild("query", + "http://jabber.org/protocol/disco#info"); + query.addChild("feature").setAttribute("var", + "urn:xmpp:jingle:1"); + query.addChild("feature").setAttribute("var", + "urn:xmpp:jingle:apps:file-transfer:3"); + query.addChild("feature").setAttribute("var", + "urn:xmpp:jingle:transports:s5b:1"); + query.addChild("feature").setAttribute("var", + "urn:xmpp:jingle:transports:ibb:1"); + if (mXmppConnectionService.confirmMessages()) { + query.addChild("feature").setAttribute("var", + "urn:xmpp:receipts"); + } + account.getXmppConnection().sendIqPacket(iqResponse, null); + } else { + if ((packet.getType() == IqPacket.TYPE_GET) + || (packet.getType() == IqPacket.TYPE_SET)) { + IqPacket response = packet + .generateRespone(IqPacket.TYPE_ERROR); + Element error = response.addChild("error"); + error.setAttribute("type", "cancel"); + error.addChild("feature-not-implemented", + "urn:ietf:params:xml:ns:xmpp-stanzas"); + account.getXmppConnection().sendIqPacket(response, null); + } + } + } + +} diff --git a/src/eu/siacs/conversations/parser/MessageParser.java b/src/eu/siacs/conversations/parser/MessageParser.java index 598cf830..1673fbf0 100644 --- a/src/eu/siacs/conversations/parser/MessageParser.java +++ b/src/eu/siacs/conversations/parser/MessageParser.java @@ -1,5 +1,6 @@ package eu.siacs.conversations.parser; +import android.os.SystemClock; import net.java.otr4j.session.Session; import net.java.otr4j.session.SessionStatus; import eu.siacs.conversations.entities.Account; @@ -8,25 +9,29 @@ import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.OnMessagePacketReceived; import eu.siacs.conversations.xmpp.stanzas.MessagePacket; -public class MessageParser extends AbstractParser { +public class MessageParser extends AbstractParser implements + OnMessagePacketReceived { + + private long lastCarbonMessageReceived = -XmppConnectionService.CARBON_GRACE_PERIOD; public MessageParser(XmppConnectionService service) { super(service); } - public Message parseChat(MessagePacket packet, Account account) { + private Message parseChat(MessagePacket packet, Account account) { String[] fromParts = packet.getFrom().split("/"); Conversation conversation = mXmppConnectionService .findOrCreateConversation(account, fromParts[0], false); conversation.setLatestMarkableMessageId(getMarkableMessageId(packet)); - updateLastseen(packet, account,true); + updateLastseen(packet, account, true); String pgpBody = getPgpBody(packet); Message finishedMessage; if (pgpBody != null) { - finishedMessage = new Message(conversation, packet.getFrom(), pgpBody, - Message.ENCRYPTION_PGP, Message.STATUS_RECIEVED); + finishedMessage = new Message(conversation, packet.getFrom(), + pgpBody, Message.ENCRYPTION_PGP, Message.STATUS_RECIEVED); } else { finishedMessage = new Message(conversation, packet.getFrom(), packet.getBody(), Message.ENCRYPTION_NONE, @@ -36,13 +41,13 @@ public class MessageParser extends AbstractParser { return finishedMessage; } - public Message parseOtrChat(MessagePacket packet, Account account) { + private Message parseOtrChat(MessagePacket packet, Account account) { boolean properlyAddressed = (packet.getTo().split("/").length == 2) || (account.countPresences() == 1); String[] fromParts = packet.getFrom().split("/"); Conversation conversation = mXmppConnectionService .findOrCreateConversation(account, fromParts[0], false); - updateLastseen(packet, account,true); + updateLastseen(packet, account, true); String body = packet.getBody(); if (!conversation.hasValidOtrSession()) { if (properlyAddressed) { @@ -84,22 +89,24 @@ public class MessageParser extends AbstractParser { conversation.setSymmetricKey(CryptoHelper.hexToBytes(key)); return null; } - conversation.setLatestMarkableMessageId(getMarkableMessageId(packet)); - Message finishedMessage = new Message(conversation, packet.getFrom(), body, - Message.ENCRYPTION_OTR, Message.STATUS_RECIEVED); + conversation + .setLatestMarkableMessageId(getMarkableMessageId(packet)); + Message finishedMessage = new Message(conversation, + packet.getFrom(), body, Message.ENCRYPTION_OTR, + Message.STATUS_RECIEVED); finishedMessage.setTime(getTimestamp(packet)); return finishedMessage; } catch (Exception e) { String receivedId = packet.getId(); - if (receivedId!=null) { - mXmppConnectionService.replyWithNotAcceptable(account,packet); + if (receivedId != null) { + mXmppConnectionService.replyWithNotAcceptable(account, packet); } conversation.endOtrIfNeeded(); return null; } } - public Message parseGroupchat(MessagePacket packet, Account account) { + private Message parseGroupchat(MessagePacket packet, Account account) { int status; String[] fromParts = packet.getFrom().split("/"); Conversation conversation = mXmppConnectionService @@ -107,7 +114,7 @@ public class MessageParser extends AbstractParser { if (packet.hasChild("subject")) { conversation.getMucOptions().setSubject( packet.findChild("subject").getContent()); - mXmppConnectionService.updateUi(conversation, false); + mXmppConnectionService.updateConversationUi(); return null; } if ((fromParts.length == 1)) { @@ -128,17 +135,17 @@ public class MessageParser extends AbstractParser { conversation.setLatestMarkableMessageId(getMarkableMessageId(packet)); Message finishedMessage; if (pgpBody == null) { - finishedMessage = new Message(conversation, counterPart, packet.getBody(), - Message.ENCRYPTION_NONE, status); + finishedMessage = new Message(conversation, counterPart, + packet.getBody(), Message.ENCRYPTION_NONE, status); } else { - finishedMessage= new Message(conversation, counterPart, pgpBody, + finishedMessage = new Message(conversation, counterPart, pgpBody, Message.ENCRYPTION_PGP, status); } finishedMessage.setTime(getTimestamp(packet)); return finishedMessage; } - public Message parseCarbonMessage(MessagePacket packet, Account account) { + private Message parseCarbonMessage(MessagePacket packet, Account account) { int status; String fullJid; Element forwarded; @@ -155,11 +162,15 @@ public class MessageParser extends AbstractParser { return null; } Element message = forwarded.findChild("message"); - if ((message == null) || (!message.hasChild("body"))) - return null; // either malformed or boring + if ((message == null) || (!message.hasChild("body"))) { + if (status == Message.STATUS_RECIEVED) { + parseNormal(message, account); + } + return null; + } if (status == Message.STATUS_RECIEVED) { fullJid = message.getAttribute("from"); - updateLastseen(message, account,true); + updateLastseen(message, account, true); } else { fullJid = message.getAttribute("to"); } @@ -170,38 +181,46 @@ public class MessageParser extends AbstractParser { String pgpBody = getPgpBody(message); Message finishedMessage; if (pgpBody != null) { - finishedMessage = new Message(conversation, fullJid, pgpBody,Message.ENCRYPTION_PGP, status); + finishedMessage = new Message(conversation, fullJid, pgpBody, + Message.ENCRYPTION_PGP, status); } else { String body = message.findChild("body").getContent(); - finishedMessage= new Message(conversation, fullJid, body,Message.ENCRYPTION_NONE, status); + finishedMessage = new Message(conversation, fullJid, body, + Message.ENCRYPTION_NONE, status); } finishedMessage.setTime(getTimestamp(message)); return finishedMessage; } - public void parseError(MessagePacket packet, Account account) { + private void parseError(MessagePacket packet, Account account) { String[] fromParts = packet.getFrom().split("/"); mXmppConnectionService.markMessage(account, fromParts[0], packet.getId(), Message.STATUS_SEND_FAILED); } - - public void parseNormal(MessagePacket packet, Account account) { - if (packet.hasChild("displayed","urn:xmpp:chat-markers:0")) { - String id = packet.findChild("displayed","urn:xmpp:chat-markers:0").getAttribute("id"); - String[] fromParts = packet.getFrom().split("/"); - updateLastseen(packet, account,true); - mXmppConnectionService.markMessage(account,fromParts[0], id, Message.STATUS_SEND_DISPLAYED); - } else if (packet.hasChild("received","urn:xmpp:chat-markers:0")) { - String id = packet.findChild("received","urn:xmpp:chat-markers:0").getAttribute("id"); - String[] fromParts = packet.getFrom().split("/"); - updateLastseen(packet, account,false); - mXmppConnectionService.markMessage(account,fromParts[0], id, Message.STATUS_SEND_RECEIVED); + + private void parseNormal(Element packet, Account account) { + if (packet.hasChild("displayed", "urn:xmpp:chat-markers:0")) { + String id = packet + .findChild("displayed", "urn:xmpp:chat-markers:0") + .getAttribute("id"); + String[] fromParts = packet.getAttribute("from").split("/"); + updateLastseen(packet, account, true); + mXmppConnectionService.markMessage(account, fromParts[0], id, + Message.STATUS_SEND_DISPLAYED); + } else if (packet.hasChild("received", "urn:xmpp:chat-markers:0")) { + String id = packet.findChild("received", "urn:xmpp:chat-markers:0") + .getAttribute("id"); + String[] fromParts = packet.getAttribute("from").split("/"); + updateLastseen(packet, account, false); + mXmppConnectionService.markMessage(account, fromParts[0], id, + Message.STATUS_SEND_RECEIVED); } else if (packet.hasChild("x")) { Element x = packet.findChild("x"); if (x.hasChild("invite")) { - Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, packet.getFrom(), - true); - mXmppConnectionService.updateUi(conversation, false); + mXmppConnectionService + .findOrCreateConversation(account, + packet.getAttribute("from"), true); + mXmppConnectionService.updateConversationUi(); } } @@ -215,7 +234,7 @@ public class MessageParser extends AbstractParser { return child.getContent(); } } - + private String getMarkableMessageId(Element message) { if (message.hasChild("markable", "urn:xmpp:chat-markers:0")) { return message.getAttribute("id"); @@ -224,5 +243,81 @@ public class MessageParser extends AbstractParser { } } - + @Override + public void onMessagePacketReceived(Account account, MessagePacket packet) { + Message message = null; + boolean notify = true; + if (mXmppConnectionService.getPreferences().getBoolean( + "notification_grace_period_after_carbon_received", true)) { + notify = (SystemClock.elapsedRealtime() - lastCarbonMessageReceived) > XmppConnectionService.CARBON_GRACE_PERIOD; + } + + if ((packet.getType() == MessagePacket.TYPE_CHAT)) { + if ((packet.getBody() != null) + && (packet.getBody().startsWith("?OTR"))) { + message = this.parseOtrChat(packet, account); + if (message != null) { + message.markUnread(); + } + } else if (packet.hasChild("body")) { + message = this.parseChat(packet, account); + message.markUnread(); + } else if (packet.hasChild("received") || (packet.hasChild("sent"))) { + message = this.parseCarbonMessage(packet, account); + if (message != null) { + if (message.getStatus() == Message.STATUS_SEND) { + lastCarbonMessageReceived = SystemClock + .elapsedRealtime(); + notify = false; + message.getConversation().markRead(); + } else { + message.markUnread(); + } + } + } + + } else if (packet.getType() == MessagePacket.TYPE_GROUPCHAT) { + message = this.parseGroupchat(packet, account); + if (message != null) { + if (message.getStatus() == Message.STATUS_RECIEVED) { + message.markUnread(); + } else { + message.getConversation().markRead(); + notify = false; + } + } + } else if (packet.getType() == MessagePacket.TYPE_ERROR) { + this.parseError(packet, account); + return; + } else if (packet.getType() == MessagePacket.TYPE_NORMAL) { + this.parseNormal(packet, account); + } + if ((message == null) || (message.getBody() == null)) { + return; + } + if ((mXmppConnectionService.confirmMessages()) + && ((packet.getId() != null))) { + MessagePacket receivedPacket = new MessagePacket(); + receivedPacket.setType(MessagePacket.TYPE_NORMAL); + receivedPacket.setTo(message.getCounterpart()); + receivedPacket.setFrom(account.getFullJid()); + if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) { + Element received = receivedPacket.addChild("received", + "urn:xmpp:chat-markers:0"); + received.setAttribute("id", packet.getId()); + account.getXmppConnection().sendMessagePacket(receivedPacket); + } else if (packet.hasChild("request", "urn:xmpp:receipts")) { + Element received = receivedPacket.addChild("received", + "urn:xmpp:receipts"); + received.setAttribute("id", packet.getId()); + account.getXmppConnection().sendMessagePacket(receivedPacket); + } + } + Conversation conversation = message.getConversation(); + conversation.getMessages().add(message); + if (packet.getType() != MessagePacket.TYPE_ERROR) { + mXmppConnectionService.databaseBackend.createMessage(message); + } + mXmppConnectionService.notifyUi(conversation, notify); + } } diff --git a/src/eu/siacs/conversations/parser/PresenceParser.java b/src/eu/siacs/conversations/parser/PresenceParser.java index 8cc57bad..bd2aa636 100644 --- a/src/eu/siacs/conversations/parser/PresenceParser.java +++ b/src/eu/siacs/conversations/parser/PresenceParser.java @@ -5,12 +5,15 @@ import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Presences; +import eu.siacs.conversations.generator.PresenceGenerator; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.OnPresencePacketReceived; import eu.siacs.conversations.xmpp.stanzas.PresencePacket; -public class PresenceParser extends AbstractParser { - +public class PresenceParser extends AbstractParser implements + OnPresencePacketReceived { + public PresenceParser(XmppConnectionService service) { super(service); } @@ -30,14 +33,15 @@ public class PresenceParser extends AbstractParser { int error = muc.getMucOptions().getError(); muc.getMucOptions().processPacket(packet, mPgpEngine); if (muc.getMucOptions().getError() != error) { - mXmppConnectionService.updateUi(muc, false); + mXmppConnectionService.updateConversationUi(); } } } } public void parseContactPresence(PresencePacket packet, Account account) { - if (packet.getFrom()==null) { + PresenceGenerator mPresenceGenerator = mXmppConnectionService.getPresenceGenerator(); + if (packet.getFrom() == null) { return; } String[] fromParts = packet.getFrom().split("/"); @@ -75,9 +79,9 @@ public class PresenceParser extends AbstractParser { } } boolean online = sizeBefore < contact.getPresences().size(); - updateLastseen(packet, account,true); + updateLastseen(packet, account, true); mXmppConnectionService.onContactStatusChanged - .onContactStatusChanged(contact,online); + .onContactStatusChanged(contact, online); } } else if (type.equals("unavailable")) { if (fromParts.length != 2) { @@ -86,17 +90,10 @@ public class PresenceParser extends AbstractParser { contact.removePresence(fromParts[1]); } mXmppConnectionService.onContactStatusChanged - .onContactStatusChanged(contact,false); + .onContactStatusChanged(contact, false); } else if (type.equals("subscribe")) { if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) { - mXmppConnectionService.sendPresenceUpdatesTo(contact); - contact.setOption(Contact.Options.FROM); - contact.resetOption(Contact.Options.PREEMPTIVE_GRANT); - if ((contact.getOption(Contact.Options.ASKING)) - && (!contact.getOption(Contact.Options.TO))) { - mXmppConnectionService - .requestPresenceUpdatesFrom(contact); - } + mXmppConnectionService.sendPresencePacket(account, mPresenceGenerator.sendPresenceUpdatesTo(contact)); } else { contact.setOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST); } @@ -104,4 +101,15 @@ public class PresenceParser extends AbstractParser { } } + @Override + public void onPresencePacketReceived(Account account, PresencePacket packet) { + if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) { + this.parseConferencePresence(packet, account); + } else if (packet.hasChild("x", "http://jabber.org/protocol/muc")) { + this.parseConferencePresence(packet, account); + } else { + this.parseContactPresence(packet, account); + } + } + } diff --git a/src/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/eu/siacs/conversations/persistance/DatabaseBackend.java index fbf45d25..7643076a 100644 --- a/src/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -116,8 +116,8 @@ public class DatabaseBackend extends SQLiteOpenHelper { return cursor.getInt(0); } - public List<Conversation> getConversations(int status) { - List<Conversation> list = new ArrayList<Conversation>(); + public CopyOnWriteArrayList<Conversation> getConversations(int status) { + CopyOnWriteArrayList<Conversation> list = new CopyOnWriteArrayList<Conversation>(); SQLiteDatabase db = this.getReadableDatabase(); String[] selectionArgs = { "" + status }; Cursor cursor = db.rawQuery("select * from " + Conversation.TABLENAME diff --git a/src/eu/siacs/conversations/services/XmppConnectionService.java b/src/eu/siacs/conversations/services/XmppConnectionService.java index 3db52753..c43a34b7 100644 --- a/src/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/eu/siacs/conversations/services/XmppConnectionService.java @@ -2,6 +2,7 @@ package eu.siacs.conversations.services; import java.security.SecureRandom; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; @@ -9,6 +10,7 @@ import java.util.Hashtable; import java.util.List; import java.util.Locale; import java.util.TimeZone; +import java.util.concurrent.CopyOnWriteArrayList; import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpServiceConnection; @@ -18,6 +20,7 @@ import net.java.otr4j.session.Session; import net.java.otr4j.session.SessionStatus; import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.entities.Bookmark; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; @@ -25,12 +28,12 @@ import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.MucOptions.OnRenameListener; import eu.siacs.conversations.entities.Presences; import eu.siacs.conversations.generator.MessageGenerator; +import eu.siacs.conversations.generator.PresenceGenerator; +import eu.siacs.conversations.parser.IqParser; import eu.siacs.conversations.parser.MessageParser; import eu.siacs.conversations.parser.PresenceParser; import eu.siacs.conversations.persistance.DatabaseBackend; import eu.siacs.conversations.persistance.FileBackend; -import eu.siacs.conversations.ui.OnAccountListChangedListener; -import eu.siacs.conversations.ui.OnConversationListChangedListener; import eu.siacs.conversations.ui.UiCallback; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.utils.ExceptionHelper; @@ -42,8 +45,6 @@ import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.OnBindListener; import eu.siacs.conversations.xmpp.OnContactStatusChanged; import eu.siacs.conversations.xmpp.OnIqPacketReceived; -import eu.siacs.conversations.xmpp.OnMessagePacketReceived; -import eu.siacs.conversations.xmpp.OnPresencePacketReceived; import eu.siacs.conversations.xmpp.OnStatusChanged; import eu.siacs.conversations.xmpp.OnTLSExceptionReceived; import eu.siacs.conversations.xmpp.XmppConnection; @@ -86,22 +87,24 @@ public class XmppConnectionService extends Service { private static final int PING_MIN_INTERVAL = 30; private static final int PING_TIMEOUT = 10; private static final int CONNECT_TIMEOUT = 90; - private static final long CARBON_GRACE_PERIOD = 60000L; + public static final long CARBON_GRACE_PERIOD = 60000L; private static String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts"; private MessageParser mMessageParser = new MessageParser(this); private PresenceParser mPresenceParser = new PresenceParser(this); + private IqParser mIqParser = new IqParser(this); private MessageGenerator mMessageGenerator = new MessageGenerator(); - + private PresenceGenerator mPresenceGenerator = new PresenceGenerator(); + private List<Account> accounts; - private List<Conversation> conversations = null; + private CopyOnWriteArrayList<Conversation> conversations = null; private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager( this); - private OnConversationListChangedListener convChangedListener = null; + private OnConversationUpdate mOnConversationUpdate = null; private int convChangedListenerCount = 0; - private OnAccountListChangedListener accountChangedListener = null; + private OnAccountUpdate mOnAccountUpdate = null; private OnTLSExceptionReceived tlsException = null; public OnContactStatusChanged onContactStatusChanged = new OnContactStatusChanged() { @@ -124,8 +127,6 @@ public class XmppConnectionService extends Service { private SecureRandom mRandom; - private long lastCarbonMessageReceived = -CARBON_GRACE_PERIOD; - private ContentObserver contactObserver = new ContentObserver(null) { @Override public void onChange(boolean selfChange) { @@ -138,101 +139,12 @@ public class XmppConnectionService extends Service { }; private final IBinder mBinder = new XmppConnectionBinder(); - private OnMessagePacketReceived messageListener = new OnMessagePacketReceived() { - - @Override - public void onMessagePacketReceived(Account account, - MessagePacket packet) { - Message message = null; - boolean notify = true; - if (getPreferences().getBoolean( - "notification_grace_period_after_carbon_received", true)) { - notify = (SystemClock.elapsedRealtime() - lastCarbonMessageReceived) > CARBON_GRACE_PERIOD; - } - - if ((packet.getType() == MessagePacket.TYPE_CHAT)) { - if ((packet.getBody() != null) - && (packet.getBody().startsWith("?OTR"))) { - message = mMessageParser.parseOtrChat(packet, account); - if (message != null) { - message.markUnread(); - } - } else if (packet.hasChild("body")) { - message = mMessageParser.parseChat(packet, account); - message.markUnread(); - } else if (packet.hasChild("received") - || (packet.hasChild("sent"))) { - message = mMessageParser - .parseCarbonMessage(packet, account); - if (message != null) { - if (message.getStatus() == Message.STATUS_SEND) { - lastCarbonMessageReceived = SystemClock - .elapsedRealtime(); - notify = false; - message.getConversation().markRead(); - } else { - message.markUnread(); - } - } - } - - } else if (packet.getType() == MessagePacket.TYPE_GROUPCHAT) { - message = mMessageParser.parseGroupchat(packet, account); - if (message != null) { - if (message.getStatus() == Message.STATUS_RECIEVED) { - message.markUnread(); - } else { - message.getConversation().markRead(); - notify = false; - } - } - } else if (packet.getType() == MessagePacket.TYPE_ERROR) { - mMessageParser.parseError(packet, account); - return; - } else if (packet.getType() == MessagePacket.TYPE_NORMAL) { - mMessageParser.parseNormal(packet, account); - } - if ((message == null) || (message.getBody() == null)) { - return; - } - if ((confirmMessages()) && ((packet.getId() != null))) { - MessagePacket receivedPacket = new MessagePacket(); - receivedPacket.setType(MessagePacket.TYPE_NORMAL); - receivedPacket.setTo(message.getCounterpart()); - receivedPacket.setFrom(account.getFullJid()); - if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) { - Element received = receivedPacket.addChild("received", - "urn:xmpp:chat-markers:0"); - received.setAttribute("id", packet.getId()); - account.getXmppConnection().sendMessagePacket( - receivedPacket); - } else if (packet.hasChild("request", "urn:xmpp:receipts")) { - Element received = receivedPacket.addChild("received", - "urn:xmpp:receipts"); - received.setAttribute("id", packet.getId()); - account.getXmppConnection().sendMessagePacket( - receivedPacket); - } - } - Conversation conversation = message.getConversation(); - conversation.getMessages().add(message); - if (packet.getType() != MessagePacket.TYPE_ERROR) { - databaseBackend.createMessage(message); - } - if (convChangedListener != null) { - convChangedListener.onConversationListChanged(); - } else { - UIHelper.updateNotification(getApplicationContext(), - getConversations(), message.getConversation(), notify); - } - } - }; private OnStatusChanged statusListener = new OnStatusChanged() { @Override public void onStatusChanged(Account account) { - if (accountChangedListener != null) { - accountChangedListener.onAccountListChangedListener(); + if (mOnAccountUpdate != null) { + mOnAccountUpdate.onAccountUpdate();; } if (account.getStatus() == Account.STATUS_ONLINE) { mJingleConnectionManager.cancelInTransmission(); @@ -269,73 +181,6 @@ public class XmppConnectionService extends Service { } }; - private OnPresencePacketReceived presenceListener = new OnPresencePacketReceived() { - - @Override - public void onPresencePacketReceived(final Account account, - PresencePacket packet) { - if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) { - mPresenceParser.parseConferencePresence(packet, account); - } else if (packet.hasChild("x", "http://jabber.org/protocol/muc")) { - mPresenceParser.parseConferencePresence(packet, account); - } else { - mPresenceParser.parseContactPresence(packet, account); - } - } - }; - - private OnIqPacketReceived unknownIqListener = new OnIqPacketReceived() { - - @Override - public void onIqPacketReceived(Account account, IqPacket packet) { - if (packet.hasChild("query", "jabber:iq:roster")) { - String from = packet.getFrom(); - if ((from == null) || (from.equals(account.getJid()))) { - Element query = packet.findChild("query"); - processRosterItems(account, query); - } else { - Log.d(LOGTAG, "unauthorized roster push from: " + from); - } - } else if (packet - .hasChild("open", "http://jabber.org/protocol/ibb") - || packet - .hasChild("data", "http://jabber.org/protocol/ibb")) { - XmppConnectionService.this.mJingleConnectionManager - .deliverIbbPacket(account, packet); - } else if (packet.hasChild("query", - "http://jabber.org/protocol/disco#info")) { - IqPacket iqResponse = packet - .generateRespone(IqPacket.TYPE_RESULT); - Element query = iqResponse.addChild("query", - "http://jabber.org/protocol/disco#info"); - query.addChild("feature").setAttribute("var", - "urn:xmpp:jingle:1"); - query.addChild("feature").setAttribute("var", - "urn:xmpp:jingle:apps:file-transfer:3"); - query.addChild("feature").setAttribute("var", - "urn:xmpp:jingle:transports:s5b:1"); - query.addChild("feature").setAttribute("var", - "urn:xmpp:jingle:transports:ibb:1"); - if (confirmMessages()) { - query.addChild("feature").setAttribute("var", - "urn:xmpp:receipts"); - } - account.getXmppConnection().sendIqPacket(iqResponse, null); - } else { - if ((packet.getType() == IqPacket.TYPE_GET) - || (packet.getType() == IqPacket.TYPE_SET)) { - IqPacket response = packet - .generateRespone(IqPacket.TYPE_ERROR); - Element error = response.addChild("error"); - error.setAttribute("type", "cancel"); - error.addChild("feature-not-implemented", - "urn:ietf:params:xml:ns:xmpp-stanzas"); - account.getXmppConnection().sendIqPacket(response, null); - } - } - } - }; - private OnJinglePacketReceived jingleListener = new OnJinglePacketReceived() { @Override @@ -401,9 +246,13 @@ public class XmppConnectionService extends Service { return message; } - public Conversation findMuc(String name, Account account) { + public Conversation findMuc(Bookmark bookmark) { + return findMuc(bookmark.getJid(), bookmark.getAccount()); + } + + public Conversation findMuc(String jid, Account account) { for (Conversation conversation : this.conversations) { - if (conversation.getContactJid().split("/")[0].equals(name) + if (conversation.getContactJid().split("/")[0].equals(jid) && (conversation.getAccount() == account)) { return conversation; } @@ -411,31 +260,6 @@ public class XmppConnectionService extends Service { return null; } - private void processRosterItems(Account account, Element elements) { - String version = elements.getAttribute("ver"); - if (version != null) { - account.getRoster().setVersion(version); - } - for (Element item : elements.getChildren()) { - if (item.getName().equals("item")) { - String jid = item.getAttribute("jid"); - String name = item.getAttribute("name"); - String subscription = item.getAttribute("subscription"); - Contact contact = account.getRoster().getContact(jid); - if (!contact.getOption(Contact.Options.DIRTY_PUSH)) { - contact.setServerName(name); - } - if (subscription.equals("remove")) { - contact.resetOption(Contact.Options.IN_ROSTER); - contact.resetOption(Contact.Options.DIRTY_DELETE); - } else { - contact.setOption(Contact.Options.IN_ROSTER); - contact.parseSubscriptionFromElement(item); - } - } - } - } - public class XmppConnectionBinder extends Binder { public XmppConnectionService getService() { return XmppConnectionService.this; @@ -508,13 +332,16 @@ public class XmppConnectionService extends Service { // in any case. reschedule wakup call this.scheduleWakeupCall(PING_MAX_INTERVAL, true); } - if (accountChangedListener != null) { - accountChangedListener.onAccountListChangedListener(); + if (mOnAccountUpdate != null) { + mOnAccountUpdate.onAccountUpdate(); } } } if (wakeLock.isHeld()) { - try { wakeLock.release();} catch (RuntimeException re) {} + try { + wakeLock.release(); + } catch (RuntimeException re) { + } } return START_STICKY; } @@ -618,11 +445,11 @@ public class XmppConnectionService extends Service { account.setResource(sharedPref.getString("resource", "mobile") .toLowerCase(Locale.getDefault())); XmppConnection connection = new XmppConnection(account, this); - connection.setOnMessagePacketReceivedListener(this.messageListener); + connection.setOnMessagePacketReceivedListener(this.mMessageParser); connection.setOnStatusChangedListener(this.statusListener); - connection.setOnPresencePacketReceivedListener(this.presenceListener); + connection.setOnPresencePacketReceivedListener(this.mPresenceParser); connection - .setOnUnregisteredIqPacketReceivedListener(this.unknownIqListener); + .setOnUnregisteredIqPacketReceivedListener(this.mIqParser); connection.setOnJinglePacketReceivedListener(this.jingleListener); connection .setOnTLSExceptionReceivedListener(new OnTLSExceptionReceived() { @@ -644,11 +471,10 @@ public class XmppConnectionService extends Service { account.getRoster().clearPresences(); account.clearPresences(); // self presences fetchRosterFromServer(account); - sendPresence(account); + fetchBookmarks(account); + sendPresencePacket(account, mPresenceGenerator.sendPresence(account)); connectMultiModeConversations(account); - if (convChangedListener != null) { - convChangedListener.onConversationListChanged(); - } + updateConversationUi(); } }); return connection; @@ -691,15 +517,12 @@ public class XmppConnectionService extends Service { message.setStatus(Message.STATUS_WAITING); } else if (conv.hasValidOtrSession() && conv.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) { - message.setPresence(conv.getOtrSession().getSessionID().getUserID()); - try { - packet = mMessageGenerator.generateOtrChat(message); - send = true; - message.setStatus(Message.STATUS_SEND); - } catch (OtrException e) { - Log.e(LOGTAG,"error generating otr packet"); - packet = null; - } + message.setPresence(conv.getOtrSession().getSessionID() + .getUserID()); + packet = mMessageGenerator.generateOtrChat(message); + send = true; + message.setStatus(Message.STATUS_SEND); + } else if (message.getPresence() == null) { message.setStatus(Message.STATUS_WAITING); } @@ -744,11 +567,9 @@ public class XmppConnectionService extends Service { databaseBackend.createMessage(message); } conv.getMessages().add(message); - if (convChangedListener != null) { - convChangedListener.onConversationListChanged(); - } + updateConversationUi(); if ((send) && (packet != null)) { - account.getXmppConnection().sendMessagePacket(packet); + sendMessagePacket(account, packet); } } @@ -782,9 +603,11 @@ public class XmppConnectionService extends Service { } } } else { - if (message.getConversation().getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) { + if (message.getConversation().getOtrSession() + .getSessionStatus() == SessionStatus.ENCRYPTED) { if (message.getType() == Message.TYPE_TEXT) { - packet = mMessageGenerator.generateOtrChat(message,true); + packet = mMessageGenerator.generateOtrChat(message, + true); } else if (message.getType() == Message.TYPE_IMAGE) { mJingleConnectionManager.createNewConnection(message); } @@ -792,9 +615,10 @@ public class XmppConnectionService extends Service { } } else if (message.getType() == Message.TYPE_TEXT) { if (message.getEncryption() == Message.ENCRYPTION_NONE) { - packet = mMessageGenerator.generateChat(message,true); - } else if ((message.getEncryption() == Message.ENCRYPTION_DECRYPTED)||(message.getEncryption() == Message.ENCRYPTION_PGP)) { - packet = mMessageGenerator.generatePgpChat(message,true); + packet = mMessageGenerator.generateChat(message, true); + } else if ((message.getEncryption() == Message.ENCRYPTION_DECRYPTED) + || (message.getEncryption() == Message.ENCRYPTION_PGP)) { + packet = mMessageGenerator.generatePgpChat(message, true); } } else if (message.getType() == Message.TYPE_IMAGE) { Presences presences = message.getConversation().getContact() @@ -813,7 +637,7 @@ public class XmppConnectionService extends Service { } } if (packet != null) { - account.getXmppConnection().sendMessagePacket(packet); + sendMessagePacket(account,packet); markMessage(message, Message.STATUS_SEND); } } @@ -834,14 +658,60 @@ public class XmppConnectionService extends Service { @Override public void onIqPacketReceived(final Account account, IqPacket packet) { - Element roster = packet.findChild("query"); - if (roster != null) { + Element query = packet.findChild("query"); + if (query != null) { account.getRoster().markAllAsNotInRoster(); - processRosterItems(account, roster); + mIqParser.rosterItems(account, query); } } }); } + + public void fetchBookmarks(Account account) { + IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET); + Element query = iqPacket.query("jabber:iq:private"); + query.addChild("storage", "storage:bookmarks"); + OnIqPacketReceived callback = new OnIqPacketReceived() { + + @Override + public void onIqPacketReceived(Account account, IqPacket packet) { + Element query = packet.query(); + List<Bookmark> bookmarks = new ArrayList<Bookmark>(); + Element storage = query.findChild("storage", "storage:bookmarks"); + if (storage!=null) { + for(Element item : storage.getChildren()) { + if (item.getName().equals("conference")) { + Log.d(LOGTAG,item.toString()); + Bookmark bookmark = Bookmark.parse(item,account); + bookmarks.add(bookmark); + Conversation conversation = findMuc(bookmark); + if (conversation!=null) { + conversation.setBookmark(bookmark); + } else { + if (bookmark.autojoin()) { + conversation = findOrCreateConversation(account, bookmark.getJid(), true); + conversation.setBookmark(bookmark); + } + } + } + } + } + account.setBookmarks(bookmarks); + } + }; + sendIqPacket(account, iqPacket, callback); + + } + + public void pushBookmarks(Account account) { + IqPacket iqPacket = new IqPacket(IqPacket.TYPE_SET); + Element query = iqPacket.query("jabber:iq:private"); + Element storage = query.addChild("storage", "storage:bookmarks"); + for(Bookmark bookmark : account.getBookmarks()) { + storage.addChild(bookmark.toElement()); + } + sendIqPacket(account, iqPacket,null); + } private void mergePhoneContactsWithRoster() { PhoneHelper.loadPhoneContacts(getApplicationContext(), @@ -885,7 +755,14 @@ public class XmppConnectionService extends Service { conv.setMessages(databaseBackend.getMessages(conv, 50)); } } - Collections.sort(this.conversations, new Comparator<Conversation>() { + + return this.conversations; + } + + public void populateWithOrderedConversations(List<Conversation> list) { + list.clear(); + list.addAll(getConversations()); + Collections.sort(list, new Comparator<Conversation>() { @Override public int compare(Conversation lhs, Conversation rhs) { Message left = lhs.getLatestMessage(); @@ -899,7 +776,6 @@ public class XmppConnectionService extends Service { } } }); - return this.conversations; } public List<Message> getMoreMessages(Conversation conversation, @@ -968,32 +844,31 @@ public class XmppConnectionService extends Service { && (conversation.getMode() == Conversation.MODE_MULTI)) { joinMuc(conversation); } - if (this.convChangedListener != null) { - this.convChangedListener.onConversationListChanged(); - } + updateConversationUi(); return conversation; } public void archiveConversation(Conversation conversation) { if (conversation.getMode() == Conversation.MODE_MULTI) { + Bookmark bookmark = conversation.getBookmark(); + if (bookmark!=null && bookmark.autojoin()) { + bookmark.setAutojoin(false); + pushBookmarks(bookmark.getAccount()); + } leaveMuc(conversation); } else { conversation.endOtrIfNeeded(); } this.databaseBackend.updateConversation(conversation); this.conversations.remove(conversation); - if (this.convChangedListener != null) { - this.convChangedListener.onConversationListChanged(); - } + updateConversationUi(); } public void clearConversationHistory(Conversation conversation) { this.databaseBackend.deleteMessagesInConversation(conversation); this.fileBackend.removeFiles(conversation); conversation.getMessages().clear(); - if (this.convChangedListener != null) { - this.convChangedListener.onConversationListChanged(); - } + updateConversationUi(); } public int getConversationCount() { @@ -1004,17 +879,14 @@ public class XmppConnectionService extends Service { databaseBackend.createAccount(account); this.accounts.add(account); this.reconnectAccount(account, false); - if (accountChangedListener != null) - accountChangedListener.onAccountListChangedListener(); + updateAccountUi(); } public void updateAccount(Account account) { this.statusListener.onStatusChanged(account); databaseBackend.updateAccount(account); reconnectAccount(account, false); - if (accountChangedListener != null) { - accountChangedListener.onAccountListChangedListener(); - } + updateAccountUi(); UIHelper.showErrorNotification(getApplicationContext(), getAccounts()); } @@ -1024,32 +896,29 @@ public class XmppConnectionService extends Service { } databaseBackend.deleteAccount(account); this.accounts.remove(account); - if (accountChangedListener != null) { - accountChangedListener.onAccountListChangedListener(); - } + updateAccountUi(); UIHelper.showErrorNotification(getApplicationContext(), getAccounts()); } public void setOnConversationListChangedListener( - OnConversationListChangedListener listener) { - this.convChangedListener = listener; + OnConversationUpdate listener) { + this.mOnConversationUpdate = listener; this.convChangedListenerCount++; } public void removeOnConversationListChangedListener() { this.convChangedListenerCount--; if (this.convChangedListenerCount == 0) { - this.convChangedListener = null; + this.mOnConversationUpdate = null; } } - public void setOnAccountListChangedListener( - OnAccountListChangedListener listener) { - this.accountChangedListener = listener; + public void setOnAccountListChangedListener(OnAccountUpdate listener) { + this.mOnAccountUpdate = listener; } public void removeOnAccountListChangedListener() { - this.accountChangedListener = null; + this.mOnAccountUpdate = null; } public void connectMultiModeConversations(Account account) { @@ -1085,13 +954,16 @@ public class XmppConnectionService extends Service { packet.addChild("x", "jabber:x:signed").setContent(sig); } if (conversation.getMessages().size() != 0) { - final SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",Locale.US); + final SimpleDateFormat mDateFormat = new SimpleDateFormat( + "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US); mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - Date date = new Date(conversation.getLatestMessage().getTimeSent() + 1000); - x.addChild("history").setAttribute("since",mDateFormat.format(date)); + Date date = new Date( + conversation.getLatestMessage().getTimeSent() + 1000); + x.addChild("history").setAttribute("since", + mDateFormat.format(date)); } packet.addChild(x); - account.getXmppConnection().sendPresencePacket(packet); + sendPresencePacket(account, packet); } private OnRenameListener renameListener = null; @@ -1130,8 +1002,7 @@ public class XmppConnectionService extends Service { packet.addChild("status").setContent("online"); packet.addChild("x", "jabber:x:signed").setContent(sig); } - - account.getXmppConnection().sendPresencePacket(packet, null); + sendPresencePacket(account,packet); } else { String jid = conversation.getContactJid().split("/")[0] + "/" + nick; @@ -1149,10 +1020,10 @@ public class XmppConnectionService extends Service { + "/" + conversation.getMucOptions().getNick()); packet.setAttribute("from", conversation.getAccount().getFullJid()); packet.setAttribute("type", "unavailable"); - Log.d(LOGTAG, "send leaving muc " + packet); - conversation.getAccount().getXmppConnection() - .sendPresencePacket(packet); + sendPresencePacket(conversation.getAccount(),packet); conversation.getMucOptions().setOffline(); + conversation.deregisterWithBookmark(); + Log.d(LOGTAG,conversation.getAccount().getJid()+" leaving muc "+conversation.getContactJid()); } public void disconnect(Account account, boolean force) { @@ -1219,18 +1090,19 @@ public class XmppConnectionService extends Service { && (msg.getEncryption() == Message.ENCRYPTION_OTR)) { msg.setPresence(otrSession.getSessionID().getUserID()); if (msg.getType() == Message.TYPE_TEXT) { - MessagePacket outPacket = mMessageGenerator.generateOtrChat(msg,true); - if (outPacket!=null) { + MessagePacket outPacket = mMessageGenerator + .generateOtrChat(msg, true); + if (outPacket != null) { msg.setStatus(Message.STATUS_SEND); databaseBackend.updateMessage(msg); - account.getXmppConnection().sendMessagePacket(outPacket); + sendMessagePacket(account,outPacket); } } else if (msg.getType() == Message.TYPE_IMAGE) { mJingleConnectionManager.createNewConnection(msg); } } } - updateUi(conversation, false); + notifyUi(conversation, false); } public boolean renewSymmetricKey(Conversation conversation) { @@ -1250,7 +1122,7 @@ public class XmppConnectionService extends Service { packet.setBody(otrSession .transformSending(CryptoHelper.FILETRANSFER + CryptoHelper.bytesToHex(symmetricKey))); - account.getXmppConnection().sendMessagePacket(packet); + sendMessagePacket(account,packet); conversation.setSymmetricKey(symmetricKey); return true; } catch (OtrException e) { @@ -1262,26 +1134,28 @@ public class XmppConnectionService extends Service { public void pushContactToServer(Contact contact) { contact.resetOption(Contact.Options.DIRTY_DELETE); + contact.setOption(Contact.Options.DIRTY_PUSH); Account account = contact.getAccount(); if (account.getStatus() == Account.STATUS_ONLINE) { + boolean ask = contact.getOption(Contact.Options.ASKING); + boolean sendUpdates = contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST) + && contact.getOption(Contact.Options.PREEMPTIVE_GRANT); IqPacket iq = new IqPacket(IqPacket.TYPE_SET); iq.query("jabber:iq:roster").addChild(contact.asElement()); account.getXmppConnection().sendIqPacket(iq, null); - if (contact.getOption(Contact.Options.ASKING)) { - requestPresenceUpdatesFrom(contact); + if (sendUpdates) { + sendPresencePacket(account, mPresenceGenerator.sendPresenceUpdatesTo(contact)); } - if (contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) { - Log.d("xmppService", "contact had pending subscription"); - sendPresenceUpdatesTo(contact); + if (ask) { + sendPresencePacket(account, mPresenceGenerator.requestPresenceUpdatesFrom(contact)); } - contact.resetOption(Contact.Options.DIRTY_PUSH); - } else { - contact.setOption(Contact.Options.DIRTY_PUSH); } } public void deleteContactOnServer(Contact contact) { + contact.resetOption(Contact.Options.PREEMPTIVE_GRANT); contact.resetOption(Contact.Options.DIRTY_PUSH); + contact.setOption(Contact.Options.DIRTY_DELETE); Account account = contact.getAccount(); if (account.getStatus() == Account.STATUS_ONLINE) { IqPacket iq = new IqPacket(IqPacket.TYPE_SET); @@ -1289,53 +1163,7 @@ public class XmppConnectionService extends Service { item.setAttribute("jid", contact.getJid()); item.setAttribute("subscription", "remove"); account.getXmppConnection().sendIqPacket(iq, null); - contact.resetOption(Contact.Options.DIRTY_DELETE); - } else { - contact.setOption(Contact.Options.DIRTY_DELETE); - } - } - - public void requestPresenceUpdatesFrom(Contact contact) { - PresencePacket packet = new PresencePacket(); - packet.setAttribute("type", "subscribe"); - packet.setAttribute("to", contact.getJid()); - packet.setAttribute("from", contact.getAccount().getJid()); - contact.getAccount().getXmppConnection().sendPresencePacket(packet); - } - - public void stopPresenceUpdatesFrom(Contact contact) { - PresencePacket packet = new PresencePacket(); - packet.setAttribute("type", "unsubscribe"); - packet.setAttribute("to", contact.getJid()); - packet.setAttribute("from", contact.getAccount().getJid()); - contact.getAccount().getXmppConnection().sendPresencePacket(packet); - } - - public void stopPresenceUpdatesTo(Contact contact) { - PresencePacket packet = new PresencePacket(); - packet.setAttribute("type", "unsubscribed"); - packet.setAttribute("to", contact.getJid()); - packet.setAttribute("from", contact.getAccount().getJid()); - contact.getAccount().getXmppConnection().sendPresencePacket(packet); - } - - public void sendPresenceUpdatesTo(Contact contact) { - PresencePacket packet = new PresencePacket(); - packet.setAttribute("type", "subscribed"); - packet.setAttribute("to", contact.getJid()); - packet.setAttribute("from", contact.getAccount().getJid()); - contact.getAccount().getXmppConnection().sendPresencePacket(packet); - } - - public void sendPresence(Account account) { - PresencePacket packet = new PresencePacket(); - packet.setAttribute("from", account.getFullJid()); - String sig = account.getPgpSignature(); - if (sig != null) { - packet.addChild("status").setContent("online"); - packet.addChild("x", "jabber:x:signed").setContent(sig); } - account.getXmppConnection().sendPresencePacket(packet); } public void updateConversation(Conversation conversation) { @@ -1366,21 +1194,6 @@ public class XmppConnectionService extends Service { }).start(); } - public void sendConversationSubject(Conversation conversation, - String subject) { - MessagePacket packet = new MessagePacket(); - packet.setType(MessagePacket.TYPE_GROUPCHAT); - packet.setTo(conversation.getContactJid().split("/")[0]); - Element subjectChild = new Element("subject"); - subjectChild.setContent(subject); - packet.addChild(subjectChild); - packet.setFrom(conversation.getAccount().getJid()); - Account account = conversation.getAccount(); - if (account.getStatus() == Account.STATUS_ONLINE) { - account.getXmppConnection().sendMessagePacket(packet); - } - } - public void inviteToConference(Conversation conversation, List<Contact> contacts) { for (Contact contact : contacts) { @@ -1394,8 +1207,7 @@ public class XmppConnectionService extends Service { x.addChild(invite); packet.addChild(x); Log.d(LOGTAG, packet.toString()); - conversation.getAccount().getXmppConnection() - .sendMessagePacket(packet); + sendMessagePacket(conversation.getAccount(),packet); } } @@ -1425,9 +1237,7 @@ public class XmppConnectionService extends Service { public void markMessage(Message message, int status) { message.setStatus(status); databaseBackend.updateMessage(message); - if (convChangedListener != null) { - convChangedListener.onConversationListChanged(); - } + updateConversationUi(); } public SharedPreferences getPreferences() { @@ -1439,14 +1249,26 @@ public class XmppConnectionService extends Service { return getPreferences().getBoolean("confirm_messages", true); } - public void updateUi(Conversation conversation, boolean notify) { - if (convChangedListener != null) { - convChangedListener.onConversationListChanged(); + public void notifyUi(Conversation conversation, boolean notify) { + if (mOnConversationUpdate != null) { + mOnConversationUpdate.onConversationUpdate(); } else { UIHelper.updateNotification(getApplicationContext(), getConversations(), conversation, notify); } } + + public void updateConversationUi() { + if (mOnConversationUpdate != null) { + mOnConversationUpdate.onConversationUpdate(); + } + } + + public void updateAccountUi() { + if (mOnAccountUpdate != null) { + mOnAccountUpdate.onAccountUpdate(); + } + } public Account findAccountByJid(String accountJid) { for (Account account : this.accounts) { @@ -1458,18 +1280,13 @@ public class XmppConnectionService extends Service { } public void markRead(Conversation conversation) { - conversation.markRead(this); - } - - public void sendConfirmMessage(Account account, String to, String id) { - MessagePacket receivedPacket = new MessagePacket(); - receivedPacket.setType(MessagePacket.TYPE_NORMAL); - receivedPacket.setTo(to); - receivedPacket.setFrom(account.getFullJid()); - Element received = receivedPacket.addChild("displayed", - "urn:xmpp:chat-markers:0"); - received.setAttribute("id", id); - account.getXmppConnection().sendMessagePacket(receivedPacket); + conversation.markRead(); + String id = conversation.popLatestMarkableMessageId(); + if (confirmMessages() && id != null) { + Account account = conversation.getAccount(); + String to = conversation.getContactJid(); + this.sendMessagePacket(conversation.getAccount(), mMessageGenerator.confirm(account, to, id)); + } } public SecureRandom getRNG() { @@ -1482,19 +1299,87 @@ public class XmppConnectionService extends Service { public void replyWithNotAcceptable(Account account, MessagePacket packet) { if (account.getStatus() == Account.STATUS_ONLINE) { - MessagePacket error = this.mMessageGenerator.generateNotAcceptable(packet); - account.getXmppConnection().sendMessagePacket(error); + MessagePacket error = this.mMessageGenerator + .generateNotAcceptable(packet); + sendMessagePacket(account,error); } } - + public void syncRosterToDisk(final Account account) { new Thread(new Runnable() { - + @Override public void run() { databaseBackend.writeRoster(account.getRoster()); } }).start(); - + + } + + public List<String> getKnownHosts() { + List<String> hosts = new ArrayList<String>(); + for (Account account : getAccounts()) { + if (!hosts.contains(account.getServer())) { + hosts.add(account.getServer()); + } + for (Contact contact : account.getRoster().getContacts()) { + if (contact.showInRoster()) { + String server = contact.getServer(); + if (server != null && !hosts.contains(server)) { + hosts.add(server); + } + } + } + } + return hosts; + } + + public List<String> getKnownConferenceHosts() { + ArrayList<String> mucServers = new ArrayList<String>(); + for (Account account : accounts) { + if (account.getXmppConnection() != null) { + String server = account.getXmppConnection().getMucServer(); + if (server != null) { + mucServers.add(server); + } + } + } + return mucServers; + } + + public void sendMessagePacket(Account account, MessagePacket packet) { + account.getXmppConnection().sendMessagePacket(packet); + } + + public void sendPresencePacket(Account account, PresencePacket packet) { + account.getXmppConnection().sendPresencePacket(packet); + } + + public void sendIqPacket(Account account, IqPacket packet, OnIqPacketReceived callback) { + account.getXmppConnection().sendIqPacket(packet, callback); + } + + public MessageGenerator getMessageGenerator() { + return this.mMessageGenerator; + } + + public PresenceGenerator getPresenceGenerator() { + return this.mPresenceGenerator; + } + + public JingleConnectionManager getJingleConnectionManager() { + return this.mJingleConnectionManager; + } + + public interface OnConversationUpdate { + public void onConversationUpdate(); + } + + public interface OnAccountUpdate { + public void onAccountUpdate(); + } + + public interface OnRosterUpdate { + public void onRosterUpdate(); } } diff --git a/src/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/eu/siacs/conversations/ui/ContactDetailsActivity.java index bee93713..9321f229 100644 --- a/src/eu/siacs/conversations/ui/ContactDetailsActivity.java +++ b/src/eu/siacs/conversations/ui/ContactDetailsActivity.java @@ -31,7 +31,9 @@ import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Presences; +import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.UIHelper; +import eu.siacs.conversations.xmpp.stanzas.PresencePacket; public class ContactDetailsActivity extends XmppActivity { public static final String ACTION_VIEW_CONTACT = "view_contact"; @@ -47,7 +49,6 @@ public class ContactDetailsActivity extends XmppActivity { private TextView contactJidTv; private TextView accountJidTv; private TextView status; - private TextView askAgain; private TextView lastseen; private CheckBox send; private CheckBox receive; @@ -115,7 +116,6 @@ public class ContactDetailsActivity extends XmppActivity { lastseen = (TextView) findViewById(R.id.details_lastseen); send = (CheckBox) findViewById(R.id.details_send_presence); receive = (CheckBox) findViewById(R.id.details_receive_presence); - askAgain = (TextView) findViewById(R.id.ask_again); badge = (QuickContactBadge) findViewById(R.id.details_contact_badge); keys = (LinearLayout) findViewById(R.id.details_contact_keys); getActionBar().setHomeButtonEnabled(true); @@ -174,6 +174,8 @@ public class ContactDetailsActivity extends XmppActivity { setTitle(contact.getDisplayName()); if (contact.getOption(Contact.Options.FROM)) { send.setChecked(true); + } else if (contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)){ + send.setChecked(false); } else { send.setText(R.string.preemptively_grant); if (contact @@ -187,17 +189,6 @@ public class ContactDetailsActivity extends XmppActivity { receive.setChecked(true); } else { receive.setText(R.string.ask_for_presence_updates); - askAgain.setVisibility(View.VISIBLE); - askAgain.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - Toast.makeText(getApplicationContext(), getString(R.string.asked_for_presence_updates), - Toast.LENGTH_SHORT).show(); - xmppConnectionService.requestPresenceUpdatesFrom(contact); - - } - }); if (contact.getOption(Contact.Options.ASKING)) { receive.setChecked(true); } else { @@ -304,6 +295,8 @@ public class ContactDetailsActivity extends XmppActivity { @Override protected void onStop() { super.onStop(); + XmppConnectionService xcs = activity.xmppConnectionService; + PresencePacket packet = null; boolean updated = false; if (contact!=null) { boolean online = contact.getAccount().getStatus() == Account.STATUS_ONLINE; @@ -312,13 +305,12 @@ public class ContactDetailsActivity extends XmppActivity { if (online) { contact.resetOption(Contact.Options.FROM); contact.resetOption(Contact.Options.PREEMPTIVE_GRANT); - activity.xmppConnectionService.stopPresenceUpdatesTo(contact); + packet = xcs.getPresenceGenerator().stopPresenceUpdatesTo(contact); } updated = true; } } else { - if (contact - .getOption(Contact.Options.PREEMPTIVE_GRANT)) { + if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) { if (!send.isChecked()) { if (online) { contact.resetOption(Contact.Options.PREEMPTIVE_GRANT); @@ -328,7 +320,11 @@ public class ContactDetailsActivity extends XmppActivity { } else { if (send.isChecked()) { if (online) { - contact.setOption(Contact.Options.PREEMPTIVE_GRANT); + if (contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) { + packet = xcs.getPresenceGenerator().sendPresenceUpdatesTo(contact); + } else { + contact.setOption(Contact.Options.PREEMPTIVE_GRANT); + } } updated = true; } @@ -338,7 +334,7 @@ public class ContactDetailsActivity extends XmppActivity { if (!receive.isChecked()) { if (online) { contact.resetOption(Contact.Options.TO); - activity.xmppConnectionService.stopPresenceUpdatesFrom(contact); + packet = xcs.getPresenceGenerator().stopPresenceUpdatesFrom(contact); } updated = true; } @@ -347,8 +343,7 @@ public class ContactDetailsActivity extends XmppActivity { if (!receive.isChecked()) { if (online) { contact.resetOption(Contact.Options.ASKING); - activity.xmppConnectionService - .stopPresenceUpdatesFrom(contact); + packet = xcs.getPresenceGenerator().stopPresenceUpdatesFrom(contact); } updated = true; } @@ -356,8 +351,7 @@ public class ContactDetailsActivity extends XmppActivity { if (receive.isChecked()) { if (online) { contact.setOption(Contact.Options.ASKING); - activity.xmppConnectionService - .requestPresenceUpdatesFrom(contact); + packet = xcs.getPresenceGenerator().requestPresenceUpdatesFrom(contact); } updated = true; } @@ -365,6 +359,9 @@ public class ContactDetailsActivity extends XmppActivity { } if (updated) { if (online) { + if (packet!=null) { + xcs.sendPresencePacket(contact.getAccount(), packet); + } Toast.makeText(getApplicationContext(), getString(R.string.subscription_updated), Toast.LENGTH_SHORT).show(); } else { Toast.makeText(getApplicationContext(), getString(R.string.subscription_not_updated_offline), Toast.LENGTH_SHORT).show(); diff --git a/src/eu/siacs/conversations/ui/ContactsActivity.java b/src/eu/siacs/conversations/ui/ContactsActivity.java deleted file mode 100644 index fee3de7a..00000000 --- a/src/eu/siacs/conversations/ui/ContactsActivity.java +++ /dev/null @@ -1,598 +0,0 @@ -package eu.siacs.conversations.ui; - -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -import eu.siacs.conversations.R; -import eu.siacs.conversations.entities.Account; -import eu.siacs.conversations.entities.Contact; -import eu.siacs.conversations.entities.Conversation; -import eu.siacs.conversations.utils.CryptoHelper; -import eu.siacs.conversations.utils.UIHelper; -import eu.siacs.conversations.utils.Validator; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.text.Editable; -import android.text.TextWatcher; -import android.util.SparseBooleanArray; -import android.view.ActionMode; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AbsListView; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.AdapterView.OnItemLongClickListener; -import android.widget.ArrayAdapter; -import android.widget.EditText; -import android.widget.ListView; -import android.widget.TextView; -import android.widget.ImageView; -import android.widget.Toast; -import android.annotation.SuppressLint; -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.content.DialogInterface.OnClickListener; -import android.content.Intent; - -public class ContactsActivity extends XmppActivity { - - protected List<Contact> rosterContacts = new ArrayList<Contact>(); - protected List<Contact> aggregatedContacts = new ArrayList<Contact>(); - protected ListView contactsView; - protected ArrayAdapter<Contact> contactsAdapter; - - protected EditText search; - protected String searchString = ""; - private TextView contactsHeader; - private List<Account> accounts; - private List<Contact> selectedContacts = new ArrayList<Contact>(); - - private ContactsActivity activity = this; - - private boolean useSubject = true; - private boolean isActionMode = false; - private boolean inviteIntent = false; - private ActionMode actionMode = null; - private AbsListView.MultiChoiceModeListener actionModeCallback = new AbsListView.MultiChoiceModeListener() { - - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - menu.clear(); - MenuInflater inflater = mode.getMenuInflater(); - inflater.inflate(R.menu.newconversation_context, menu); - SparseBooleanArray checkedItems = contactsView - .getCheckedItemPositions(); - selectedContacts.clear(); - for (int i = 0; i < aggregatedContacts.size(); ++i) { - if (checkedItems.get(i, false)) { - selectedContacts.add(aggregatedContacts.get(i)); - } - } - if (selectedContacts.size() == 0) { - menu.findItem(R.id.action_start_conversation).setVisible(false); - menu.findItem(R.id.action_contact_details).setVisible(false); - menu.findItem(R.id.action_invite).setVisible(false); - menu.findItem(R.id.action_invite_to_existing).setVisible(false); - } else if ((selectedContacts.size() == 1) && (!inviteIntent)) { - menu.findItem(R.id.action_start_conversation).setVisible(true); - menu.findItem(R.id.action_contact_details).setVisible(true); - menu.findItem(R.id.action_invite).setVisible(false); - menu.findItem(R.id.action_invite_to_existing).setVisible(true); - } else if (!inviteIntent) { - menu.findItem(R.id.action_start_conversation).setVisible(true); - menu.findItem(R.id.action_contact_details).setVisible(false); - menu.findItem(R.id.action_invite).setVisible(false); - menu.findItem(R.id.action_invite_to_existing).setVisible(true); - } else { - menu.findItem(R.id.action_invite).setVisible(true); - menu.findItem(R.id.action_start_conversation).setVisible(false); - menu.findItem(R.id.action_contact_details).setVisible(false); - menu.findItem(R.id.action_invite_to_existing).setVisible(false); - } - return true; - } - - @Override - public void onDestroyActionMode(ActionMode mode) { - // TODO Auto-generated method stub - - } - - @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - return true; - } - - @Override - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - switch (item.getItemId()) { - case R.id.action_start_conversation: - if (selectedContacts.size() == 1) { - startConversation(selectedContacts.get(0)); - } else { - startConference(); - } - break; - case R.id.action_contact_details: - Intent intent = new Intent(getApplicationContext(), - ContactDetailsActivity.class); - intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT); - intent.putExtra("account", selectedContacts.get(0).getAccount().getJid()); - intent.putExtra("contact",selectedContacts.get(0).getJid()); - startActivity(intent); - finish(); - break; - case R.id.action_invite: - invite(); - break; - case R.id.action_invite_to_existing: - final List<Conversation> mucs = new ArrayList<Conversation>(); - for(Conversation conv : xmppConnectionService.getConversations()) { - if (conv.getMode() == Conversation.MODE_MULTI) { - mucs.add(conv); - } - } - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setTitle(getString(R.string.invite_contacts_to_existing)); - if (mucs.size() >= 1) { - String[] options = new String[mucs.size()]; - for(int i = 0; i < options.length; ++i) { - options[i] = mucs.get(i).getName(useSubject); - } - builder.setItems(options, new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - Conversation conversation = mucs.get(which); - if (isOnline(conversation.getAccount())) { - xmppConnectionService.inviteToConference(conversation, selectedContacts); - Toast.makeText(activity, getString(R.string.invitation_sent), Toast.LENGTH_SHORT).show(); - actionMode.finish(); - } - } - }); - } else { - builder.setMessage(getString(R.string.no_open_mucs)); - } - builder.setNegativeButton(getString(R.string.cancel),null); - builder.create().show(); - break; - default: - break; - } - return false; - } - - @Override - public void onItemCheckedStateChanged(ActionMode mode, int position, - long id, boolean checked) { - } - }; - - private boolean isOnline(Account account) { - if (account.getStatus() == Account.STATUS_ONLINE) { - return true; - } else { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(getString(R.string.account_offline)); - builder.setMessage(getString(R.string.cant_invite_while_offline)); - builder.setNegativeButton(getString(R.string.ok), null); - builder.setIconAttribute(android.R.attr.alertDialogIcon); - builder.create().show(); - return false; - } - } - - private void invite() { - List<Conversation> conversations = xmppConnectionService - .getConversations(); - Conversation conversation = null; - for (Conversation tmpConversation : conversations) { - if (tmpConversation.getUuid().equals( - getIntent().getStringExtra("uuid"))) { - conversation = tmpConversation; - break; - } - } - if (conversation != null) { - xmppConnectionService.inviteToConference(conversation, - selectedContacts); - } - finish(); - } - - private void startConference() { - if (accounts.size() > 1) { - getAccountChooser(new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - startConference(accounts.get(which)); - } - }).show(); - } else { - startConference(accounts.get(0)); - } - - } - - private void startConference(final Account account) { - if (isOnline(account)) { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(getString(R.string.new_conference)); - builder.setMessage(getString(R.string.new_conference_explained)); - builder.setNegativeButton(getString(R.string.cancel), null); - builder.setPositiveButton(getString(R.string.create_invite), - new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - String mucName = CryptoHelper.randomMucName(xmppConnectionService.getRNG()); - String serverName = account.getXmppConnection() - .getMucServer(); - if (serverName==null) { - List<String> servers = getMucServers(); - if (servers.size() >= 1) { - serverName = servers.get(0); - } else { - displayErrorDialog(R.string.no_muc_server_found); - return; - } - } - String jid = mucName + "@" + serverName; - Conversation conversation = xmppConnectionService - .findOrCreateConversation(account, jid, true); - StringBuilder subject = new StringBuilder(); - subject.append(account.getUsername() + ", "); - for (int i = 0; i < selectedContacts.size(); ++i) { - if (i + 1 != selectedContacts.size()) { - subject.append(selectedContacts.get(i) - .getDisplayName() + ", "); - } else { - subject.append(selectedContacts.get(i) - .getDisplayName()); - } - } - xmppConnectionService.sendConversationSubject( - conversation, subject.toString()); - xmppConnectionService.inviteToConference(conversation, - selectedContacts); - switchToConversation(conversation, null,false); - } - }); - builder.create().show(); - } - } - - protected void updateAggregatedContacts() { - - aggregatedContacts.clear(); - for (Contact contact : rosterContacts) { - if (contact.match(searchString)&&(contact.showInRoster())) - aggregatedContacts.add(contact); - } - - Collections.sort(aggregatedContacts, new Comparator<Contact>() { - - @SuppressLint("DefaultLocale") - @Override - public int compare(Contact lhs, Contact rhs) { - return lhs.getDisplayName().toLowerCase() - .compareTo(rhs.getDisplayName().toLowerCase()); - } - }); - - if (aggregatedContacts.size() == 0) { - - if (Validator.isValidJid(searchString)) { - Contact newContact = new Contact(searchString); - newContact.resetOption(Contact.Options.IN_ROSTER); - aggregatedContacts.add(newContact); - contactsHeader.setText(getString(R.string.new_contact)); - } else { - contactsHeader.setText(getString(R.string.contacts)); - } - } else { - contactsHeader.setText(getString(R.string.contacts)); - } - - contactsAdapter.notifyDataSetChanged(); - contactsView.setScrollX(0); - } - - private OnItemLongClickListener onLongClickListener = new OnItemLongClickListener() { - - @Override - public boolean onItemLongClick(AdapterView<?> arg0, View view, - int position, long arg3) { - if (!isActionMode) { - contactsView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); - contactsView.setItemChecked(position, true); - actionMode = contactsView.startActionMode(actionModeCallback); - } - return true; - } - }; - - @Override - protected void onStart() { - super.onStart(); - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity); - this.useSubject = preferences.getBoolean("use_subject_in_muc", true); - inviteIntent = "invite".equals(getIntent().getAction()); - if (inviteIntent) { - contactsHeader.setVisibility(View.GONE); - actionMode = contactsView.startActionMode(actionModeCallback); - search.setVisibility(View.GONE); - } - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - - super.onCreate(savedInstanceState); - - setContentView(R.layout.activity_new_conversation); - - contactsHeader = (TextView) findViewById(R.id.contacts_header); - - search = (EditText) findViewById(R.id.new_conversation_search); - search.addTextChangedListener(new TextWatcher() { - - @Override - public void onTextChanged(CharSequence s, int start, int before, - int count) { - searchString = search.getText().toString(); - updateAggregatedContacts(); - } - - @Override - public void afterTextChanged(Editable s) { - // TODO Auto-generated method stub - - } - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, - int after) { - // TODO Auto-generated method stub - - } - }); - - contactsView = (ListView) findViewById(R.id.contactList); - contactsAdapter = new ArrayAdapter<Contact>(getApplicationContext(), - R.layout.contact, aggregatedContacts) { - @Override - public View getView(int position, View view, ViewGroup parent) { - LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); - Contact contact = getItem(position); - if (view == null) { - view = (View) inflater.inflate(R.layout.contact, null); - } - - ((TextView) view.findViewById(R.id.contact_display_name)) - .setText(getItem(position).getDisplayName()); - TextView contactJid = (TextView) view - .findViewById(R.id.contact_jid); - contactJid.setText(contact.getJid()); - ImageView imageView = (ImageView) view - .findViewById(R.id.contact_photo); - imageView.setImageBitmap(UIHelper.getContactPicture(contact, 48, this.getContext(), false)); - return view; - } - }; - contactsView.setAdapter(contactsAdapter); - contactsView.setMultiChoiceModeListener(actionModeCallback); - contactsView.setOnItemClickListener(new OnItemClickListener() { - - @Override - public void onItemClick(AdapterView<?> arg0, final View view, - int pos, long arg3) { - if (!isActionMode) { - Contact clickedContact = aggregatedContacts.get(pos); - startConversation(clickedContact); - - } else { - actionMode.invalidate(); - } - } - }); - contactsView.setOnItemLongClickListener(this.onLongClickListener); - } - - public void startConversation(final Contact contact) { - if ((contact.getAccount() == null) && (accounts.size() > 1)) { - getAccountChooser(new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - contact.setAccount(accounts.get(which)); - showIsMucDialogIfNeeded(contact); - } - }).show(); - } else { - if (contact.getAccount() == null) { - contact.setAccount(accounts.get(0)); - } - showIsMucDialogIfNeeded(contact); - } - } - - protected AlertDialog getAccountChooser(OnClickListener listener) { - String[] accountList = new String[accounts.size()]; - for (int i = 0; i < accounts.size(); ++i) { - accountList[i] = accounts.get(i).getJid(); - } - - AlertDialog.Builder accountChooser = new AlertDialog.Builder(this); - accountChooser.setTitle(getString(R.string.choose_account)); - accountChooser.setItems(accountList, listener); - return accountChooser.create(); - } - - public void showIsMucDialogIfNeeded(final Contact clickedContact) { - if (isMuc(clickedContact)) { - startConversation(clickedContact,clickedContact.getAccount(), true); - } else if (clickedContact.couldBeMuc()) { - AlertDialog.Builder dialog = new AlertDialog.Builder(this); - dialog.setTitle(getString(R.string.multi_user_conference)); - dialog.setMessage(getString(R.string.trying_join_conference)); - dialog.setPositiveButton(getString(R.string.yes), new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - startConversation(clickedContact, - clickedContact.getAccount(), true); - } - }); - dialog.setNegativeButton(getString(R.string.no), new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - startConversation(clickedContact, - clickedContact.getAccount(), false); - } - }); - dialog.create().show(); - } else { - startConversation(clickedContact, clickedContact.getAccount(), - false); - } - } - - private List<String> getMucServers() { - ArrayList<String> mucServers = new ArrayList<String>(); - for(Account account : accounts) { - if (account.getXmppConnection()!=null) { - String server = account.getXmppConnection().getMucServer(); - if (server!=null) { - mucServers.add(server); - } - } - } - return mucServers; - } - - private boolean isMuc(Contact contact) { - String[] parts = contact.getJid().split("@"); - if (parts.length != 2) { - return false; - } - return getMucServers().contains(parts[1]); - } - - public void startConversation(Contact contact, Account account, boolean muc) { - if (!contact.getOption(Contact.Options.IN_ROSTER)&&(!muc)) { - xmppConnectionService.createContact(contact); - } - Conversation conversation = xmppConnectionService - .findOrCreateConversation(account, contact.getJid(), muc); - - switchToConversation(conversation, null,false); - } - - @Override - void onBackendConnected() { - this.accounts = xmppConnectionService.getAccounts(); - if (Intent.ACTION_SENDTO.equals(getIntent().getAction())) { - getActionBar().setDisplayHomeAsUpEnabled(false); - getActionBar().setHomeButtonEnabled(false); - String jid; - try { - jid = URLDecoder.decode(getIntent().getData().getEncodedPath(), - "UTF-8").split("/")[1]; - } catch (UnsupportedEncodingException e) { - jid = null; - } - if (jid != null) { - final String finalJid = jid; - if (this.accounts.size() > 1) { - getAccountChooser(new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - Conversation conversation = xmppConnectionService - .findOrCreateConversation( - accounts.get(which), finalJid, - false); - switchToConversation(conversation, null,false); - finish(); - } - }).show(); - } else { - Conversation conversation = xmppConnectionService - .findOrCreateConversation(this.accounts.get(0), - jid, false); - switchToConversation(conversation, null,false); - finish(); - } - } - } - - if (xmppConnectionService.getConversationCount() == 0) { - getActionBar().setDisplayHomeAsUpEnabled(false); - getActionBar().setHomeButtonEnabled(false); - } - this.rosterContacts.clear(); - for(Account account : accounts) { - if (account.getStatus() != Account.STATUS_DISABLED) { - rosterContacts.addAll(account.getRoster().getContacts()); - } - } - updateAggregatedContacts(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. - getMenuInflater().inflate(R.menu.newconversation, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - default: - break; - } - return super.onOptionsItemSelected(item); - } - - @Override - public void onActionModeStarted(ActionMode mode) { - super.onActionModeStarted(mode); - this.isActionMode = true; - search.setEnabled(false); - } - - @Override - public void onActionModeFinished(ActionMode mode) { - super.onActionModeFinished(mode); - if (inviteIntent) { - finish(); - } else { - this.isActionMode = false; - contactsView.clearChoices(); - contactsView.requestLayout(); - contactsView.post(new Runnable() { - @Override - public void run() { - contactsView.setChoiceMode(ListView.CHOICE_MODE_NONE); - } - }); - search.setEnabled(true); - } - } - -} diff --git a/src/eu/siacs/conversations/ui/ConversationActivity.java b/src/eu/siacs/conversations/ui/ConversationActivity.java index 03ced289..66db353b 100644 --- a/src/eu/siacs/conversations/ui/ConversationActivity.java +++ b/src/eu/siacs/conversations/ui/ConversationActivity.java @@ -10,6 +10,7 @@ import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.services.ImageProvider; +import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdate; import eu.siacs.conversations.utils.ExceptionHelper; import eu.siacs.conversations.utils.UIHelper; import android.net.Uri; @@ -83,10 +84,10 @@ public class ConversationActivity extends XmppActivity { private boolean showLastseen = false; private ArrayAdapter<Conversation> listAdapter; - private OnConversationListChangedListener onConvChanged = new OnConversationListChangedListener() { - + private OnConversationUpdate onConvChanged = new OnConversationUpdate() { + @Override - public void onConversationListChanged() { + public void onConversationUpdate() { runOnUiThread(new Runnable() { @Override @@ -97,7 +98,7 @@ public class ConversationActivity extends XmppActivity { swapConversationFragment(); } else { startActivity(new Intent(getApplicationContext(), - ContactsActivity.class)); + StartConversation.class)); finish(); } } @@ -419,37 +420,6 @@ public class ConversationActivity extends XmppActivity { selectPresenceToAttachFile(attachmentChoice); } else { selectPresenceToAttachFile(attachmentChoice); - /*AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(getString(R.string.otr_file_transfer)); - builder.setMessage(getString(R.string.otr_file_transfer_msg)); - builder.setNegativeButton(getString(R.string.cancel), null); - if (conversation.getContact().getPgpKeyId() == 0) { - builder.setPositiveButton(getString(R.string.send_unencrypted), - new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, - int which) { - conversation - .setNextEncryption(Message.ENCRYPTION_NONE); - attachFile(attachmentChoice); - } - }); - } else { - builder.setPositiveButton( - getString(R.string.use_pgp_encryption), - new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, - int which) { - conversation - .setNextEncryption(Message.ENCRYPTION_PGP); - attachFile(attachmentChoice); - } - }); - } - builder.create().show();*/ } } @@ -488,7 +458,7 @@ public class ConversationActivity extends XmppActivity { attachFilePopup.show(); break; case R.id.action_add: - startActivity(new Intent(this, ContactsActivity.class)); + startActivity(new Intent(this, StartConversation.class)); break; case R.id.action_archive: this.endConversation(getSelectedConversation()); @@ -496,12 +466,7 @@ public class ConversationActivity extends XmppActivity { case R.id.action_contact_details: Contact contact = this.getSelectedConversation().getContact(); if (contact.showInRoster()) { - Intent intent = new Intent(this, ContactDetailsActivity.class); - intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT); - intent.putExtra("account", this.getSelectedConversation() - .getAccount().getJid()); - intent.putExtra("contact", contact.getJid()); - startActivity(intent); + switchToContactDetails(contact); } else { showAddToRosterDialog(getSelectedConversation()); } @@ -513,11 +478,11 @@ public class ConversationActivity extends XmppActivity { startActivity(intent); break; case R.id.action_invite: - Intent inviteIntent = new Intent(getApplicationContext(), + /*Intent inviteIntent = new Intent(getApplicationContext(), ContactsActivity.class); inviteIntent.setAction("invite"); inviteIntent.putExtra("uuid", getSelectedConversation().getUuid()); - startActivity(inviteIntent); + startActivity(inviteIntent);*/ break; case R.id.action_security: final Conversation conversation = getSelectedConversation(); @@ -691,7 +656,7 @@ public class ConversationActivity extends XmppActivity { this.onBackendConnected(); } if (conversationList.size() >= 1) { - onConvChanged.onConversationListChanged(); + onConvChanged.onConversationUpdate(); } } @@ -734,7 +699,7 @@ public class ConversationActivity extends XmppActivity { finish(); } else if (conversationList.size() <= 0) { // add no history - startActivity(new Intent(this, ContactsActivity.class)); + startActivity(new Intent(this, StartConversation.class)); finish(); } else { spl.openPane(); @@ -768,7 +733,7 @@ public class ConversationActivity extends XmppActivity { ConversationFragment selectedFragment = (ConversationFragment) getFragmentManager() .findFragmentByTag("conversation"); if (selectedFragment != null) { - selectedFragment.hidePgpPassphraseBox(); + selectedFragment.hideSnackbar(); } } else if (requestCode == REQUEST_ATTACH_FILE_DIALOG) { attachImageToConversation(getSelectedConversation(), @@ -841,8 +806,7 @@ public class ConversationActivity extends XmppActivity { } public void updateConversationList() { - conversationList.clear(); - conversationList.addAll(xmppConnectionService.getConversations()); + xmppConnectionService.populateWithOrderedConversations(conversationList); listView.invalidateViews(); } diff --git a/src/eu/siacs/conversations/ui/ConversationFragment.java b/src/eu/siacs/conversations/ui/ConversationFragment.java index 1e703b48..01bab773 100644 --- a/src/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/eu/siacs/conversations/ui/ConversationFragment.java @@ -48,6 +48,7 @@ import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ImageButton; import android.widget.ImageView; +import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; @@ -60,11 +61,17 @@ public class ConversationFragment extends Fragment { protected ArrayAdapter<Message> messageListAdapter; protected Contact contact; protected BitmapCache mBitmapCache = new BitmapCache(); + + protected int mPrimaryTextColor; + protected int mSecondaryTextColor; protected String queuedPqpMessage = null; private EditText chatMsg; private String pastedText = null; + private RelativeLayout snackbar; + private TextView snackbarMessage; + private TextView snackbarAction; protected Bitmap selfBitmap; @@ -106,10 +113,7 @@ public class ConversationFragment extends Fragment { } } }; - - private LinearLayout pgpInfo; - private LinearLayout mucError; - private TextView mucErrorText; + private OnClickListener clickToMuc = new OnClickListener() { @Override @@ -149,10 +153,6 @@ public class ConversationFragment extends Fragment { private ConversationActivity activity; - public void hidePgpPassphraseBox() { - pgpInfo.setVisibility(View.GONE); - } - public void updateChatMsgHint() { switch (conversation.getNextEncryption()) { case Message.ENCRYPTION_NONE: @@ -177,6 +177,9 @@ public class ConversationFragment extends Fragment { this.inflater = inflater; + mPrimaryTextColor = getResources().getColor(R.color.primarytext); + mSecondaryTextColor = getResources().getColor(R.color.secondarytext); + final View view = inflater.inflate(R.layout.fragment_conversation, container, false); chatMsg = (EditText) view.findViewById(R.id.textinput); @@ -185,11 +188,9 @@ public class ConversationFragment extends Fragment { .findViewById(R.id.textSendButton); sendButton.setOnClickListener(this.sendMsgListener); - pgpInfo = (LinearLayout) view.findViewById(R.id.pgp_keyentry); - pgpInfo.setOnClickListener(clickToDecryptListener); - mucError = (LinearLayout) view.findViewById(R.id.muc_error); - mucError.setOnClickListener(clickToMuc); - mucErrorText = (TextView) view.findViewById(R.id.muc_error_msg); + snackbar = (RelativeLayout) view.findViewById(R.id.snackbar); + snackbarMessage = (TextView) view.findViewById(R.id.snackbar_message); + snackbarAction = (TextView) view.findViewById(R.id.snackbar_action); messagesView = (ListView) view.findViewById(R.id.messages_view); messagesView.setOnScrollListener(mOnScrollListener); @@ -264,7 +265,7 @@ public class ConversationFragment extends Fragment { if (error) { viewHolder.time.setTextColor(0xFFe92727); } else { - viewHolder.time.setTextColor(0xFF8e8e8e); + viewHolder.time.setTextColor(mSecondaryTextColor); } if (message.getEncryption() == Message.ENCRYPTION_NONE) { viewHolder.indicator.setVisibility(View.GONE); @@ -341,7 +342,7 @@ public class ConversationFragment extends Fragment { } else { viewHolder.messageBody.setText(""); } - viewHolder.messageBody.setTextColor(0xff333333); + viewHolder.messageBody.setTextColor(mPrimaryTextColor); viewHolder.messageBody.setTypeface(null, Typeface.NORMAL); viewHolder.messageBody.setTextIsSelectable(true); } @@ -682,7 +683,7 @@ public class ConversationFragment extends Fragment { @Override public void userInputRequried(PendingIntent pi, Message message) { askForPassphraseIntent = pi.getIntentSender(); - pgpInfo.setVisibility(View.VISIBLE); + showSnackbar(R.string.openpgp_messages_found,R.string.decrypt,clickToDecryptListener); } @Override @@ -698,8 +699,6 @@ public class ConversationFragment extends Fragment { // updateMessages(); } }); - } else { - pgpInfo.setVisibility(View.GONE); } } @@ -736,12 +735,10 @@ public class ConversationFragment extends Fragment { } } else { if (conversation.getMucOptions().getError() != 0) { - mucError.setVisibility(View.VISIBLE); + showSnackbar(R.string.nick_in_use, R.string.edit,clickToMuc); if (conversation.getMucOptions().getError() == MucOptions.ERROR_NICK_IN_USE) { - mucErrorText.setText(getString(R.string.nick_in_use)); + showSnackbar(R.string.nick_in_use, R.string.edit,clickToMuc); } - } else { - mucError.setVisibility(View.GONE); } } getActivity().invalidateOptionsMenu(); @@ -789,34 +786,37 @@ public class ConversationFragment extends Fragment { } protected void makeFingerprintWarning(int latestEncryption) { - final LinearLayout fingerprintWarning = (LinearLayout) getView() - .findViewById(R.id.new_fingerprint); Set<String> knownFingerprints = conversation.getContact() .getOtrFingerprints(); if ((latestEncryption == Message.ENCRYPTION_OTR) && (conversation.hasValidOtrSession() && (conversation.getOtrSession().getSessionStatus() == SessionStatus.ENCRYPTED) && (!knownFingerprints .contains(conversation.getOtrFingerprint())))) { - fingerprintWarning.setVisibility(View.VISIBLE); - TextView fingerprint = (TextView) getView().findViewById( - R.id.otr_fingerprint); - fingerprint.setText(conversation.getOtrFingerprint()); - fingerprintWarning.setOnClickListener(new OnClickListener() { + showSnackbar(R.string.unknown_otr_fingerprint, R.string.verify, new OnClickListener() { @Override public void onClick(View v) { if (conversation.getOtrFingerprint() != null) { AlertDialog dialog = UIHelper.getVerifyFingerprintDialog( (ConversationActivity) getActivity(), conversation, - fingerprintWarning); + snackbar); dialog.show(); } } }); - } else { - fingerprintWarning.setVisibility(View.GONE); } } + + protected void showSnackbar(int message, int action, OnClickListener clickListener) { + snackbar.setVisibility(View.VISIBLE); + snackbarMessage.setText(message); + snackbarAction.setText(action); + snackbarAction.setOnClickListener(clickListener); + } + + protected void hideSnackbar() { + snackbar.setVisibility(View.GONE); + } protected void sendPlainTextMessage(Message message) { ConversationActivity activity = (ConversationActivity) getActivity(); diff --git a/src/eu/siacs/conversations/ui/EditAccount.java b/src/eu/siacs/conversations/ui/EditAccount.java index e1bcaeb5..9a0b8d84 100644 --- a/src/eu/siacs/conversations/ui/EditAccount.java +++ b/src/eu/siacs/conversations/ui/EditAccount.java @@ -1,14 +1,19 @@ package eu.siacs.conversations.ui; +import java.util.List; + import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.utils.KnownHostsAdapter; import eu.siacs.conversations.utils.Validator; import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; +import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; +import android.widget.AutoCompleteTextView; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; @@ -19,6 +24,8 @@ import android.widget.TextView; public class EditAccount extends DialogFragment { protected Account account; + + protected AutoCompleteTextView mAccountJid; public void setAccount(Account account) { this.account = account; @@ -30,16 +37,28 @@ public class EditAccount extends DialogFragment { protected EditAccountListener listener = null; + private KnownHostsAdapter mKnownHostsAdapter; + public void setEditAccountListener(EditAccountListener listener) { this.listener = listener; } + + public void setKnownHosts(List<String> hosts, Context context) { + this.mKnownHostsAdapter = new KnownHostsAdapter(context, android.R.layout.simple_list_item_1, hosts); + if (this.mAccountJid != null) { + this.mAccountJid.setAdapter(this.mKnownHostsAdapter); + } + } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); LayoutInflater inflater = getActivity().getLayoutInflater(); View view = inflater.inflate(R.layout.edit_account_dialog, null); - final EditText jidText = (EditText) view.findViewById(R.id.account_jid); + mAccountJid = (AutoCompleteTextView) view.findViewById(R.id.account_jid); + if (this.mKnownHostsAdapter!=null) { + mAccountJid.setAdapter(this.mKnownHostsAdapter); + } final TextView confirmPwDesc = (TextView) view .findViewById(R.id.account_confirm_password_desc); @@ -51,7 +70,7 @@ public class EditAccount extends DialogFragment { .findViewById(R.id.edit_account_register_new); if (account != null) { - jidText.setText(account.getJid()); + mAccountJid.setText(account.getJid()); password.setText(account.getPassword()); if (account.isOptionSet(Account.OPTION_REGISTER)) { registerAccount.setChecked(true); diff --git a/src/eu/siacs/conversations/ui/ManageAccountActivity.java b/src/eu/siacs/conversations/ui/ManageAccountActivity.java index c52916a2..c3f1e105 100644 --- a/src/eu/siacs/conversations/ui/ManageAccountActivity.java +++ b/src/eu/siacs/conversations/ui/ManageAccountActivity.java @@ -5,6 +5,7 @@ import java.util.List; import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate; import eu.siacs.conversations.ui.EditAccount.EditAccountListener; import eu.siacs.conversations.xmpp.OnTLSExceptionReceived; import eu.siacs.conversations.xmpp.XmppConnection; @@ -31,21 +32,21 @@ import android.widget.ListView; import android.widget.TextView; public class ManageAccountActivity extends XmppActivity { - + protected boolean isActionMode = false; protected ActionMode actionMode; protected Account selectedAccountForActionMode = null; protected ManageAccountActivity activity = this; - + protected boolean firstrun = true; - + protected List<Account> accountList = new ArrayList<Account>(); protected ListView accountListView; protected ArrayAdapter<Account> accountListViewAdapter; - protected OnAccountListChangedListener accountChanged = new OnAccountListChangedListener() { + protected OnAccountUpdate accountChanged = new OnAccountUpdate() { @Override - public void onAccountListChangedListener() { + public void onAccountUpdate() { accountList.clear(); accountList.addAll(xmppConnectionService.getAccounts()); runOnUiThread(new Runnable() { @@ -57,47 +58,55 @@ public class ManageAccountActivity extends XmppActivity { }); } }; - + protected OnTLSExceptionReceived tlsExceptionReceived = new OnTLSExceptionReceived() { - + @Override - public void onTLSExceptionReceived(final String fingerprint, final Account account) { + public void onTLSExceptionReceived(final String fingerprint, + final Account account) { activity.runOnUiThread(new Runnable() { - + @Override public void run() { - AlertDialog.Builder builder = new AlertDialog.Builder(activity); + AlertDialog.Builder builder = new AlertDialog.Builder( + activity); builder.setTitle(getString(R.string.account_status_error)); builder.setIconAttribute(android.R.attr.alertDialogIcon); - View view = (View) getLayoutInflater().inflate(R.layout.cert_warning, null); + View view = (View) getLayoutInflater().inflate( + R.layout.cert_warning, null); TextView sha = (TextView) view.findViewById(R.id.sha); TextView hint = (TextView) view.findViewById(R.id.hint); StringBuilder humanReadableSha = new StringBuilder(); humanReadableSha.append(fingerprint); - for(int i = 2; i < 59; i += 3) { - if ((i==14)||(i==29)||(i==44)) { + for (int i = 2; i < 59; i += 3) { + if ((i == 14) || (i == 29) || (i == 44)) { humanReadableSha.insert(i, "\n"); } else { humanReadableSha.insert(i, ":"); } - + } - hint.setText(getString(R.string.untrusted_cert_hint,account.getServer())); + hint.setText(getString(R.string.untrusted_cert_hint, + account.getServer())); sha.setText(humanReadableSha.toString()); builder.setView(view); - builder.setNegativeButton(getString(R.string.certif_no_trust), null); - builder.setPositiveButton(getString(R.string.certif_trust), new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - account.setSSLCertFingerprint(fingerprint); - activity.xmppConnectionService.updateAccount(account); - } - }); + builder.setNegativeButton( + getString(R.string.certif_no_trust), null); + builder.setPositiveButton(getString(R.string.certif_trust), + new OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, + int which) { + account.setSSLCertFingerprint(fingerprint); + activity.xmppConnectionService + .updateAccount(account); + } + }); builder.create().show(); } }); - + } }; @@ -124,55 +133,68 @@ public class ManageAccountActivity extends XmppActivity { .findViewById(R.id.account_status); switch (account.getStatus()) { case Account.STATUS_DISABLED: - statusView.setText(getString(R.string.account_status_disabled)); + statusView + .setText(getString(R.string.account_status_disabled)); statusView.setTextColor(0xFF1da9da); break; case Account.STATUS_ONLINE: - statusView.setText(getString(R.string.account_status_online)); + statusView + .setText(getString(R.string.account_status_online)); statusView.setTextColor(0xFF83b600); break; case Account.STATUS_CONNECTING: - statusView.setText(getString(R.string.account_status_connecting)); + statusView + .setText(getString(R.string.account_status_connecting)); statusView.setTextColor(0xFF1da9da); break; case Account.STATUS_OFFLINE: - statusView.setText(getString(R.string.account_status_offline)); + statusView + .setText(getString(R.string.account_status_offline)); statusView.setTextColor(0xFFe92727); break; case Account.STATUS_UNAUTHORIZED: - statusView.setText(getString(R.string.account_status_unauthorized)); + statusView + .setText(getString(R.string.account_status_unauthorized)); statusView.setTextColor(0xFFe92727); break; case Account.STATUS_SERVER_NOT_FOUND: - statusView.setText(getString(R.string.account_status_not_found)); + statusView + .setText(getString(R.string.account_status_not_found)); statusView.setTextColor(0xFFe92727); break; case Account.STATUS_NO_INTERNET: - statusView.setText(getString(R.string.account_status_no_internet)); + statusView + .setText(getString(R.string.account_status_no_internet)); statusView.setTextColor(0xFFe92727); break; case Account.STATUS_SERVER_REQUIRES_TLS: - statusView.setText(getString(R.string.account_status_requires_tls)); + statusView + .setText(getString(R.string.account_status_requires_tls)); statusView.setTextColor(0xFFe92727); break; case Account.STATUS_TLS_ERROR: - statusView.setText(getString(R.string.account_status_error)); + statusView + .setText(getString(R.string.account_status_error)); statusView.setTextColor(0xFFe92727); break; case Account.STATUS_REGISTRATION_FAILED: - statusView.setText(getString(R.string.account_status_regis_fail)); + statusView + .setText(getString(R.string.account_status_regis_fail)); statusView.setTextColor(0xFFe92727); break; case Account.STATUS_REGISTRATION_CONFLICT: - statusView.setText(getString(R.string.account_status_regis_conflict)); + statusView + .setText(getString(R.string.account_status_regis_conflict)); statusView.setTextColor(0xFFe92727); break; - case Account.STATUS_REGISTRATION_SUCCESSFULL: - statusView.setText(getString(R.string.account_status_regis_success)); + case Account.STATUS_REGISTRATION_SUCCESSFULL: + statusView + .setText(getString(R.string.account_status_regis_success)); statusView.setTextColor(0xFF83b600); break; case Account.STATUS_REGISTRATION_NOT_SUPPORTED: - statusView.setText(getString(R.string.account_status_regis_not_sup)); + statusView + .setText(getString(R.string.account_status_regis_not_sup)); statusView.setTextColor(0xFFe92727); break; default: @@ -192,10 +214,14 @@ public class ManageAccountActivity extends XmppActivity { int position, long arg3) { if (!isActionMode) { Account account = accountList.get(position); - if ((account.getStatus() == Account.STATUS_OFFLINE)||(account.getStatus() == Account.STATUS_TLS_ERROR)) { - activity.xmppConnectionService.reconnectAccount(accountList.get(position),true); + if ((account.getStatus() == Account.STATUS_OFFLINE) + || (account.getStatus() == Account.STATUS_TLS_ERROR)) { + activity.xmppConnectionService.reconnectAccount( + accountList.get(position), true); } else if (account.getStatus() == Account.STATUS_ONLINE) { - activity.startActivity(new Intent(activity.getApplicationContext(),ContactsActivity.class)); + activity.startActivity(new Intent(activity + .getApplicationContext(), + StartConversation.class)); } else if (account.getStatus() != Account.STATUS_DISABLED) { editAccount(account); } @@ -205,159 +231,242 @@ public class ManageAccountActivity extends XmppActivity { } } }); - accountListView.setOnItemLongClickListener(new OnItemLongClickListener() { + accountListView + .setOnItemLongClickListener(new OnItemLongClickListener() { - @Override - public boolean onItemLongClick(AdapterView<?> arg0, View view, - int position, long arg3) { - if (!isActionMode) { - accountListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); - accountListView.setItemChecked(position,true); - selectedAccountForActionMode = accountList.get(position); - actionMode = activity.startActionMode((new ActionMode.Callback() { - - @Override - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - if (selectedAccountForActionMode.isOptionSet(Account.OPTION_DISABLED)) { - menu.findItem(R.id.mgmt_account_enable).setVisible(true); - menu.findItem(R.id.mgmt_account_disable).setVisible(false); - } else { - menu.findItem(R.id.mgmt_account_disable).setVisible(true); - menu.findItem(R.id.mgmt_account_enable).setVisible(false); - } - return true; - } - - @Override - public void onDestroyActionMode(ActionMode mode) { - // TODO Auto-generated method stub - - } - - @Override - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - MenuInflater inflater = mode.getMenuInflater(); - inflater.inflate(R.menu.manageaccounts_context, menu); - return true; - } - - @Override - public boolean onActionItemClicked(final ActionMode mode, MenuItem item) { - if (item.getItemId()==R.id.mgmt_account_edit) { - editAccount(selectedAccountForActionMode); - } else if (item.getItemId()==R.id.mgmt_account_disable) { - selectedAccountForActionMode.setOption(Account.OPTION_DISABLED, true); - xmppConnectionService.updateAccount(selectedAccountForActionMode); - mode.finish(); - } else if (item.getItemId()==R.id.mgmt_account_enable) { - selectedAccountForActionMode.setOption(Account.OPTION_DISABLED, false); - xmppConnectionService.updateAccount(selectedAccountForActionMode); - mode.finish(); - } else if (item.getItemId()==R.id.mgmt_account_delete) { - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setTitle(getString(R.string.mgmt_account_are_you_sure)); - builder.setIconAttribute(android.R.attr.alertDialogIcon); - builder.setMessage(getString(R.string.mgmt_account_delete_confirm_text)); - builder.setPositiveButton(getString(R.string.delete), new OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int which) { - xmppConnectionService.deleteAccount(selectedAccountForActionMode); - selectedAccountForActionMode = null; - mode.finish(); - } - }); - builder.setNegativeButton(getString(R.string.cancel),null); - builder.create().show(); - } else if (item.getItemId()==R.id.mgmt_account_announce_pgp) { - if (activity.hasPgp()) { - mode.finish(); - announcePgp(selectedAccountForActionMode,null); - } else { - activity.showInstallPgpDialog(); - } - } else if (item.getItemId() == R.id.mgmt_otr_key) { - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setTitle("OTR Fingerprint"); - String fingerprintTxt = selectedAccountForActionMode.getOtrFingerprint(getApplicationContext()); - View view = (View) getLayoutInflater().inflate(R.layout.otr_fingerprint, null); - if (fingerprintTxt!=null) { - TextView fingerprint = (TextView) view.findViewById(R.id.otr_fingerprint); - TextView noFingerprintView = (TextView) view.findViewById(R.id.otr_no_fingerprint); - fingerprint.setText(fingerprintTxt); - fingerprint.setVisibility(View.VISIBLE); - noFingerprintView.setVisibility(View.GONE); - } - builder.setView(view); - builder.setPositiveButton(getString(R.string.done), null); - builder.create().show(); - } else if (item.getItemId() == R.id.mgmt_account_info) { - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setTitle(getString(R.string.account_info)); - if (selectedAccountForActionMode.getStatus() == Account.STATUS_ONLINE) { - XmppConnection xmpp = selectedAccountForActionMode.getXmppConnection(); - long connectionAge = (SystemClock.elapsedRealtime() - xmpp.lastConnect) / 60000; - long sessionAge = (SystemClock.elapsedRealtime() - xmpp.lastSessionStarted) / 60000; - long connectionAgeHours = connectionAge / 60; - long sessionAgeHours = sessionAge / 60; - View view = (View) getLayoutInflater().inflate(R.layout.server_info, null); - TextView connection = (TextView) view.findViewById(R.id.connection); - TextView session = (TextView) view.findViewById(R.id.session); - TextView pcks_sent = (TextView) view.findViewById(R.id.pcks_sent); - TextView pcks_received = (TextView) view.findViewById(R.id.pcks_received); - TextView carbon = (TextView) view.findViewById(R.id.carbon); - TextView stream = (TextView) view.findViewById(R.id.stream); - TextView roster = (TextView) view.findViewById(R.id.roster); - TextView presences = (TextView) view.findViewById(R.id.number_presences); - presences.setText(selectedAccountForActionMode.countPresences()+""); - pcks_received.setText(""+xmpp.getReceivedStanzas()); - pcks_sent.setText(""+xmpp.getSentStanzas()); - if (connectionAgeHours >= 2) { - connection.setText(connectionAgeHours+" " + getString(R.string.hours)); - } else { - connection.setText(connectionAge+" " + getString(R.string.mins)); - } - if (xmpp.hasFeatureStreamManagment()) { - if (sessionAgeHours >= 2) { - session.setText(sessionAgeHours+" " + getString(R.string.hours)); - } else { - session.setText(sessionAge+" " + getString(R.string.mins)); + @Override + public boolean onItemLongClick(AdapterView<?> arg0, + View view, int position, long arg3) { + if (!isActionMode) { + accountListView + .setChoiceMode(ListView.CHOICE_MODE_SINGLE); + accountListView.setItemChecked(position, true); + selectedAccountForActionMode = accountList + .get(position); + actionMode = activity + .startActionMode((new ActionMode.Callback() { + + @Override + public boolean onPrepareActionMode( + ActionMode mode, Menu menu) { + if (selectedAccountForActionMode + .isOptionSet(Account.OPTION_DISABLED)) { + menu.findItem( + R.id.mgmt_account_enable) + .setVisible(true); + menu.findItem( + R.id.mgmt_account_disable) + .setVisible(false); + } else { + menu.findItem( + R.id.mgmt_account_disable) + .setVisible(true); + menu.findItem( + R.id.mgmt_account_enable) + .setVisible(false); + } + return true; } - stream.setText(getString(R.string.yes)); - } else { - stream.setText(getString(R.string.no)); - session.setText(connection.getText()); - } - if (xmpp.hasFeaturesCarbon()) { - carbon.setText(getString(R.string.yes)); - } else { - carbon.setText(getString(R.string.no)); - } - if (xmpp.hasFeatureRosterManagment()) { - roster.setText(getString(R.string.yes)); - } else { - roster.setText(getString(R.string.no)); - } - builder.setView(view); - } else { - builder.setMessage(getString(R.string.mgmt_account_account_offline)); - } - builder.setPositiveButton(getString(R.string.hide), null); - builder.create().show(); - } + + @Override + public void onDestroyActionMode( + ActionMode mode) { + // TODO Auto-generated method stub + + } + + @Override + public boolean onCreateActionMode( + ActionMode mode, Menu menu) { + MenuInflater inflater = mode + .getMenuInflater(); + inflater.inflate( + R.menu.manageaccounts_context, + menu); + return true; + } + + @Override + public boolean onActionItemClicked( + final ActionMode mode, + MenuItem item) { + if (item.getItemId() == R.id.mgmt_account_edit) { + editAccount(selectedAccountForActionMode); + } else if (item.getItemId() == R.id.mgmt_account_disable) { + selectedAccountForActionMode + .setOption( + Account.OPTION_DISABLED, + true); + xmppConnectionService + .updateAccount(selectedAccountForActionMode); + mode.finish(); + } else if (item.getItemId() == R.id.mgmt_account_enable) { + selectedAccountForActionMode + .setOption( + Account.OPTION_DISABLED, + false); + xmppConnectionService + .updateAccount(selectedAccountForActionMode); + mode.finish(); + } else if (item.getItemId() == R.id.mgmt_account_delete) { + AlertDialog.Builder builder = new AlertDialog.Builder( + activity); + builder.setTitle(getString(R.string.mgmt_account_are_you_sure)); + builder.setIconAttribute(android.R.attr.alertDialogIcon); + builder.setMessage(getString(R.string.mgmt_account_delete_confirm_text)); + builder.setPositiveButton( + getString(R.string.delete), + new OnClickListener() { + + @Override + public void onClick( + DialogInterface dialog, + int which) { + xmppConnectionService + .deleteAccount(selectedAccountForActionMode); + selectedAccountForActionMode = null; + mode.finish(); + } + }); + builder.setNegativeButton( + getString(R.string.cancel), + null); + builder.create().show(); + } else if (item.getItemId() == R.id.mgmt_account_announce_pgp) { + if (activity.hasPgp()) { + mode.finish(); + announcePgp( + selectedAccountForActionMode, + null); + } else { + activity.showInstallPgpDialog(); + } + } else if (item.getItemId() == R.id.mgmt_otr_key) { + AlertDialog.Builder builder = new AlertDialog.Builder( + activity); + builder.setTitle("OTR Fingerprint"); + String fingerprintTxt = selectedAccountForActionMode + .getOtrFingerprint(getApplicationContext()); + View view = (View) getLayoutInflater() + .inflate( + R.layout.otr_fingerprint, + null); + if (fingerprintTxt != null) { + TextView fingerprint = (TextView) view + .findViewById(R.id.otr_fingerprint); + TextView noFingerprintView = (TextView) view + .findViewById(R.id.otr_no_fingerprint); + fingerprint + .setText(fingerprintTxt); + fingerprint + .setVisibility(View.VISIBLE); + noFingerprintView + .setVisibility(View.GONE); + } + builder.setView(view); + builder.setPositiveButton( + getString(R.string.done), + null); + builder.create().show(); + } else if (item.getItemId() == R.id.mgmt_account_info) { + AlertDialog.Builder builder = new AlertDialog.Builder( + activity); + builder.setTitle(getString(R.string.account_info)); + if (selectedAccountForActionMode + .getStatus() == Account.STATUS_ONLINE) { + XmppConnection xmpp = selectedAccountForActionMode + .getXmppConnection(); + long connectionAge = (SystemClock + .elapsedRealtime() - xmpp.lastConnect) / 60000; + long sessionAge = (SystemClock + .elapsedRealtime() - xmpp.lastSessionStarted) / 60000; + long connectionAgeHours = connectionAge / 60; + long sessionAgeHours = sessionAge / 60; + View view = (View) getLayoutInflater() + .inflate( + R.layout.server_info, + null); + TextView connection = (TextView) view + .findViewById(R.id.connection); + TextView session = (TextView) view + .findViewById(R.id.session); + TextView pcks_sent = (TextView) view + .findViewById(R.id.pcks_sent); + TextView pcks_received = (TextView) view + .findViewById(R.id.pcks_received); + TextView carbon = (TextView) view + .findViewById(R.id.carbon); + TextView stream = (TextView) view + .findViewById(R.id.stream); + TextView roster = (TextView) view + .findViewById(R.id.roster); + TextView presences = (TextView) view + .findViewById(R.id.number_presences); + presences.setText(selectedAccountForActionMode + .countPresences() + + ""); + pcks_received.setText("" + + xmpp.getReceivedStanzas()); + pcks_sent.setText("" + + xmpp.getSentStanzas()); + if (connectionAgeHours >= 2) { + connection + .setText(connectionAgeHours + + " " + + getString(R.string.hours)); + } else { + connection + .setText(connectionAge + + " " + + getString(R.string.mins)); + } + if (xmpp.hasFeatureStreamManagment()) { + if (sessionAgeHours >= 2) { + session.setText(sessionAgeHours + + " " + + getString(R.string.hours)); + } else { + session.setText(sessionAge + + " " + + getString(R.string.mins)); + } + stream.setText(getString(R.string.yes)); + } else { + stream.setText(getString(R.string.no)); + session.setText(connection + .getText()); + } + if (xmpp.hasFeaturesCarbon()) { + carbon.setText(getString(R.string.yes)); + } else { + carbon.setText(getString(R.string.no)); + } + if (xmpp.hasFeatureRosterManagment()) { + roster.setText(getString(R.string.yes)); + } else { + roster.setText(getString(R.string.no)); + } + builder.setView(view); + } else { + builder.setMessage(getString(R.string.mgmt_account_account_offline)); + } + builder.setPositiveButton( + getString(R.string.hide), + null); + builder.create().show(); + } + return true; + } + + })); return true; + } else { + return false; } - - - })); - return true; - } else { - return false; - } - } - }); + } + }); } - + @Override protected void onStop() { if (xmppConnectionServiceBound) { @@ -370,11 +479,12 @@ public class ManageAccountActivity extends XmppActivity { @Override void onBackendConnected() { xmppConnectionService.setOnAccountListChangedListener(accountChanged); - xmppConnectionService.setOnTLSExceptionReceivedListener(tlsExceptionReceived); + xmppConnectionService + .setOnTLSExceptionReceivedListener(tlsExceptionReceived); this.accountList.clear(); this.accountList.addAll(xmppConnectionService.getAccounts()); accountListViewAdapter.notifyDataSetChanged(); - if ((this.accountList.size() == 0)&&(this.firstrun)) { + if ((this.accountList.size() == 0) && (this.firstrun)) { getActionBar().setDisplayHomeAsUpEnabled(false); getActionBar().setHomeButtonEnabled(false); addAccount(); @@ -403,14 +513,15 @@ public class ManageAccountActivity extends XmppActivity { @Override public boolean onNavigateUp() { if (xmppConnectionService.getConversations().size() == 0) { - Intent contactsIntent = new Intent(this, ContactsActivity.class); + Intent contactsIntent = new Intent(this, StartConversation.class); contactsIntent.setFlags( - // if activity exists in stack, pop the stack and go back to it + // if activity exists in stack, pop the stack and go back to it Intent.FLAG_ACTIVITY_CLEAR_TOP | // otherwise, make a new task for it - Intent.FLAG_ACTIVITY_NEW_TASK | - // don't use the new activity animation; finish animation runs instead - Intent.FLAG_ACTIVITY_NO_ANIMATION); + Intent.FLAG_ACTIVITY_NEW_TASK | + // don't use the new activity animation; finish + // animation runs instead + Intent.FLAG_ACTIVITY_NO_ANIMATION); startActivity(contactsIntent); finish(); return true; @@ -420,22 +531,23 @@ public class ManageAccountActivity extends XmppActivity { } private void editAccount(Account account) { - EditAccount dialog = new EditAccount(); - dialog.setAccount(account); - dialog.setEditAccountListener(new EditAccountListener() { + EditAccount dialog = new EditAccount(); + dialog.setAccount(account); + dialog.setEditAccountListener(new EditAccountListener() { - @Override - public void onAccountEdited(Account account) { - xmppConnectionService.updateAccount(account); - if (actionMode != null) { - actionMode.finish(); - } + @Override + public void onAccountEdited(Account account) { + xmppConnectionService.updateAccount(account); + if (actionMode != null) { + actionMode.finish(); } - }); - dialog.show(getFragmentManager(), "edit_account"); - + } + }); + dialog.show(getFragmentManager(), "edit_account"); + dialog.setKnownHosts(xmppConnectionService.getKnownHosts(), this); + } - + protected void addAccount() { final Activity activity = this; EditAccount dialog = new EditAccount(); @@ -449,15 +561,15 @@ public class ManageAccountActivity extends XmppActivity { } }); dialog.show(getFragmentManager(), "add_account"); + dialog.setKnownHosts(xmppConnectionService.getKnownHosts(), this); } - @Override public void onActionModeStarted(ActionMode mode) { super.onActionModeStarted(mode); this.isActionMode = true; } - + @Override public void onActionModeFinished(ActionMode mode) { super.onActionModeFinished(mode); @@ -465,20 +577,20 @@ public class ManageAccountActivity extends XmppActivity { accountListView.clearChoices(); accountListView.requestLayout(); accountListView.post(new Runnable() { - @Override - public void run() { - accountListView.setChoiceMode(ListView.CHOICE_MODE_NONE); - } - }); + @Override + public void run() { + accountListView.setChoiceMode(ListView.CHOICE_MODE_NONE); + } + }); } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (resultCode == RESULT_OK) { + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (resultCode == RESULT_OK) { if (requestCode == REQUEST_ANNOUNCE_PGP) { - announcePgp(selectedAccountForActionMode,null); + announcePgp(selectedAccountForActionMode, null); } - } - } + } + } } diff --git a/src/eu/siacs/conversations/ui/MucDetailsActivity.java b/src/eu/siacs/conversations/ui/MucDetailsActivity.java index ee6709b7..8226e381 100644 --- a/src/eu/siacs/conversations/ui/MucDetailsActivity.java +++ b/src/eu/siacs/conversations/ui/MucDetailsActivity.java @@ -11,9 +11,9 @@ import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.MucOptions.User; import eu.siacs.conversations.utils.UIHelper; +import eu.siacs.conversations.xmpp.stanzas.MessagePacket; import android.app.PendingIntent; import android.content.Context; -import android.content.Intent; import android.content.SharedPreferences; import android.content.IntentSender.SendIntentException; import android.os.Bundle; @@ -62,7 +62,8 @@ public class MucDetailsActivity extends XmppActivity { String subject = mSubject.getText().toString(); MucOptions options = conversation.getMucOptions(); if (!subject.equals(options.getSubject())) { - xmppConnectionService.sendConversationSubject(conversation,subject); + MessagePacket packet = xmppConnectionService.getMessageGenerator().conferenceSubject(conversation, subject); + xmppConnectionService.sendMessagePacket(conversation.getAccount(), packet); finish(); } } @@ -72,11 +73,11 @@ public class MucDetailsActivity extends XmppActivity { @Override public void onClick(View v) { - Intent intent = new Intent(getApplicationContext(), + /*Intent intent = new Intent(getApplicationContext(), ContactsActivity.class); intent.setAction("invite"); intent.putExtra("uuid",conversation.getUuid()); - startActivity(intent); + startActivity(intent);*/ } }; diff --git a/src/eu/siacs/conversations/ui/OnAccountListChangedListener.java b/src/eu/siacs/conversations/ui/OnAccountListChangedListener.java deleted file mode 100644 index 98ef445e..00000000 --- a/src/eu/siacs/conversations/ui/OnAccountListChangedListener.java +++ /dev/null @@ -1,5 +0,0 @@ -package eu.siacs.conversations.ui; - -public interface OnAccountListChangedListener { - public void onAccountListChangedListener(); -} diff --git a/src/eu/siacs/conversations/ui/OnConversationListChangedListener.java b/src/eu/siacs/conversations/ui/OnConversationListChangedListener.java deleted file mode 100644 index 2a922e21..00000000 --- a/src/eu/siacs/conversations/ui/OnConversationListChangedListener.java +++ /dev/null @@ -1,5 +0,0 @@ -package eu.siacs.conversations.ui; - -public interface OnConversationListChangedListener { - public void onConversationListChanged(); -} diff --git a/src/eu/siacs/conversations/ui/ShareWithActivity.java b/src/eu/siacs/conversations/ui/ShareWithActivity.java index 9fe5e500..461aaec4 100644 --- a/src/eu/siacs/conversations/ui/ShareWithActivity.java +++ b/src/eu/siacs/conversations/ui/ShareWithActivity.java @@ -20,7 +20,6 @@ import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceManager; -import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.ImageView; @@ -90,7 +89,8 @@ public class ShareWithActivity extends XmppActivity { Set<Contact> displayedContacts = new HashSet<Contact>(); conversations.removeAllViews(); - List<Conversation> convList = xmppConnectionService.getConversations(); + List<Conversation> convList = new ArrayList<Conversation>(); + xmppConnectionService.populateWithOrderedConversations(convList); Collections.sort(convList, new Comparator<Conversation>() { @Override public int compare(Conversation lhs, Conversation rhs) { diff --git a/src/eu/siacs/conversations/ui/StartConversation.java b/src/eu/siacs/conversations/ui/StartConversation.java new file mode 100644 index 00000000..c9166c39 --- /dev/null +++ b/src/eu/siacs/conversations/ui/StartConversation.java @@ -0,0 +1,548 @@ +package eu.siacs.conversations.ui; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import android.app.ActionBar; +import android.app.ActionBar.Tab; +import android.app.ActionBar.TabListener; +import android.app.AlertDialog; +import android.app.Fragment; +import android.app.FragmentTransaction; +import android.app.ListFragment; +import android.content.Context; +import android.os.Bundle; +import android.support.v13.app.FragmentPagerAdapter; +import android.support.v4.view.ViewPager; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; +import android.widget.AdapterView; +import android.widget.AdapterView.AdapterContextMenuInfo; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ArrayAdapter; +import android.widget.AutoCompleteTextView; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.Spinner; +import android.widget.TextView; +import eu.siacs.conversations.R; +import eu.siacs.conversations.entities.Account; +import eu.siacs.conversations.entities.Bookmark; +import eu.siacs.conversations.entities.Contact; +import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.ListItem; +import eu.siacs.conversations.utils.KnownHostsAdapter; +import eu.siacs.conversations.utils.UIHelper; +import eu.siacs.conversations.utils.Validator; + +public class StartConversation extends XmppActivity { + + private Tab mContactsTab; + private Tab mConferencesTab; + private ViewPager mViewPager; + + private MyListFragment mContactsListFragment = new MyListFragment(); + private List<ListItem> contacts = new ArrayList<ListItem>(); + private ArrayAdapter<ListItem> mContactsAdapter; + + private MyListFragment mConferenceListFragment = new MyListFragment(); + private List<ListItem> conferences = new ArrayList<ListItem>(); + private ArrayAdapter<ListItem> mConferenceAdapter; + + private List<String> mActivatedAccounts = new ArrayList<String>(); + private List<String> mKnownHosts; + private List<String> mKnownConferenceHosts; + + private EditText mSearchEditText; + + public int conference_context_id; + public int contact_context_id; + + private TabListener mTabListener = new TabListener() { + + @Override + public void onTabUnselected(Tab tab, FragmentTransaction ft) { + return; + } + + @Override + public void onTabSelected(Tab tab, FragmentTransaction ft) { + mViewPager.setCurrentItem(tab.getPosition()); + onTabChanged(); + } + + @Override + public void onTabReselected(Tab tab, FragmentTransaction ft) { + return; + } + }; + + private ViewPager.SimpleOnPageChangeListener mOnPageChangeListener = new ViewPager.SimpleOnPageChangeListener() { + @Override + public void onPageSelected(int position) { + getActionBar().setSelectedNavigationItem(position); + onTabChanged(); + } + }; + + private MenuItem.OnActionExpandListener mOnActionExpandListener = new MenuItem.OnActionExpandListener() { + + @Override + public boolean onMenuItemActionExpand(MenuItem item) { + mSearchEditText.post(new Runnable() { + + @Override + public void run() { + mSearchEditText.requestFocus(); + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(mSearchEditText, + InputMethodManager.SHOW_IMPLICIT); + } + }); + + return true; + } + + @Override + public boolean onMenuItemActionCollapse(MenuItem item) { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(), + InputMethodManager.HIDE_IMPLICIT_ONLY); + mSearchEditText.setText(""); + filter(null); + return true; + } + }; + private TextWatcher mSearchTextWatcher = new TextWatcher() { + + @Override + public void afterTextChanged(Editable editable) { + filter(editable.toString()); + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, + int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, + int count) { + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_start_conversation); + mViewPager = (ViewPager) findViewById(R.id.start_conversation_view_pager); + ActionBar actionBar = getActionBar(); + actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); + + mContactsTab = actionBar.newTab().setText(R.string.contacts) + .setTabListener(mTabListener); + mConferencesTab = actionBar.newTab().setText(R.string.conferences) + .setTabListener(mTabListener); + actionBar.addTab(mContactsTab); + actionBar.addTab(mConferencesTab); + + mViewPager.setOnPageChangeListener(mOnPageChangeListener); + mViewPager.setAdapter(new FragmentPagerAdapter(getFragmentManager()) { + + @Override + public int getCount() { + return 2; + } + + @Override + public Fragment getItem(int position) { + if (position == 0) { + return mContactsListFragment; + } else { + return mConferenceListFragment; + } + } + }); + + mConferenceAdapter = new ListItemAdapter(conferences); + mConferenceListFragment.setListAdapter(mConferenceAdapter); + mConferenceListFragment.setContextMenu(R.menu.conference_context); + mConferenceListFragment.setOnListItemClickListener(new OnItemClickListener() { + + @Override + public void onItemClick(AdapterView<?> arg0, View arg1, + int position, long arg3) { + openConversationForBookmark(position); + } + }); + + mContactsAdapter = new ListItemAdapter(contacts); + mContactsListFragment.setListAdapter(mContactsAdapter); + mContactsListFragment.setContextMenu(R.menu.contact_context); + mContactsListFragment + .setOnListItemClickListener(new OnItemClickListener() { + + @Override + public void onItemClick(AdapterView<?> arg0, View arg1, + int position, long arg3) { + openConversationForContact(position); + } + }); + + } + + protected void openConversationForContact(int position) { + Contact contact = (Contact) contacts.get(position); + Conversation conversation = xmppConnectionService + .findOrCreateConversation(contact.getAccount(), + contact.getJid(), false); + switchToConversation(conversation); + } + + protected void openConversationForContact() { + int position = contact_context_id; + openConversationForContact(position); + } + + protected void openConversationForBookmark() { + openConversationForBookmark(conference_context_id); + } + + protected void openConversationForBookmark(int position) { + Bookmark bookmark = (Bookmark) conferences.get(position); + Conversation conversation = xmppConnectionService.findOrCreateConversation(bookmark.getAccount(), bookmark.getJid(), true); + conversation.setBookmark(bookmark); + if (!bookmark.autojoin()) { + bookmark.setAutojoin(true); + xmppConnectionService.pushBookmarks(bookmark.getAccount()); + } + switchToConversation(conversation); + } + + protected void openDetailsForContact() { + int position = contact_context_id; + Contact contact = (Contact) contacts.get(position); + switchToContactDetails(contact); + } + + protected void deleteContact() { + int position = contact_context_id; + Contact contact = (Contact) contacts.get(position); + xmppConnectionService.deleteContactOnServer(contact); + filter(mSearchEditText.getText().toString()); + } + + protected void deleteConference() { + int position = contact_context_id; + Bookmark bookmark = (Bookmark) conferences.get(position); + Account account = bookmark.getAccount(); + account.getBookmarks().remove(bookmark); + xmppConnectionService.pushBookmarks(account); + filter(mSearchEditText.getText().toString()); + } + + protected void showCreateContactDialog() { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.create_contact); + View dialogView = getLayoutInflater().inflate( + R.layout.create_contact_dialog, null); + final Spinner spinner = (Spinner) dialogView.findViewById(R.id.account); + final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView + .findViewById(R.id.jid); + jid.setAdapter(new KnownHostsAdapter(this, + android.R.layout.simple_list_item_1, mKnownHosts)); + populateAccountSpinner(spinner); + builder.setView(dialogView); + builder.setNegativeButton(R.string.cancel, null); + builder.setPositiveButton(R.string.create, null); + final AlertDialog dialog = builder.create(); + dialog.show(); + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener( + new View.OnClickListener() { + + @Override + public void onClick(View v) { + if (Validator.isValidJid(jid.getText().toString())) { + String accountJid = (String) spinner + .getSelectedItem(); + String contactJid = jid.getText().toString(); + Account account = xmppConnectionService + .findAccountByJid(accountJid); + Contact contact = account.getRoster().getContact( + contactJid); + if (contact.showInRoster()) { + jid.setError(getString(R.string.contact_already_exists)); + } else { + xmppConnectionService.createContact(contact); + switchToConversation(contact); + dialog.dismiss(); + } + } else { + jid.setError(getString(R.string.invalid_jid)); + } + } + }); + + } + + protected void showJoinConferenceDialog() { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.join_conference); + View dialogView = getLayoutInflater().inflate( + R.layout.join_conference_dialog, null); + final Spinner spinner = (Spinner) dialogView.findViewById(R.id.account); + final AutoCompleteTextView jid = (AutoCompleteTextView) dialogView + .findViewById(R.id.jid); + jid.setAdapter(new KnownHostsAdapter(this, + android.R.layout.simple_list_item_1, mKnownConferenceHosts)); + populateAccountSpinner(spinner); + final CheckBox bookmarkCheckBox = (CheckBox) dialogView.findViewById(R.id.bookmark); + builder.setView(dialogView); + builder.setNegativeButton(R.string.cancel, null); + builder.setPositiveButton(R.string.join, null); + final AlertDialog dialog = builder.create(); + dialog.show(); + dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener( + new View.OnClickListener() { + + @Override + public void onClick(View v) { + if (Validator.isValidJid(jid.getText().toString())) { + String accountJid = (String) spinner + .getSelectedItem(); + String conferenceJid = jid.getText().toString(); + Account account = xmppConnectionService + .findAccountByJid(accountJid); + if (bookmarkCheckBox.isChecked()) { + if (account.hasBookmarkFor(conferenceJid)) { + jid.setError(getString(R.string.bookmark_already_exists)); + } else { + Bookmark bookmark = new Bookmark(account, conferenceJid); + bookmark.setAutojoin(true); + account.getBookmarks().add(bookmark); + xmppConnectionService.pushBookmarks(account); + Conversation conversation = xmppConnectionService + .findOrCreateConversation(account, + conferenceJid, true); + conversation.setBookmark(bookmark); + switchToConversation(conversation); + } + } else { + Conversation conversation = xmppConnectionService + .findOrCreateConversation(account, + conferenceJid, true); + switchToConversation(conversation); + } + } else { + jid.setError(getString(R.string.invalid_jid)); + } + } + }); + } + + protected void switchToConversation(Contact contact) { + Conversation conversation = xmppConnectionService + .findOrCreateConversation(contact.getAccount(), + contact.getJid(), false); + switchToConversation(conversation); + } + + private void populateAccountSpinner(Spinner spinner) { + ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, + android.R.layout.simple_spinner_item, mActivatedAccounts); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinner.setAdapter(adapter); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.start_conversation, menu); + MenuItem menuCreateContact = (MenuItem) menu + .findItem(R.id.action_create_contact); + MenuItem menuCreateConference = (MenuItem) menu + .findItem(R.id.action_join_conference); + MenuItem menuSearchView = (MenuItem) menu.findItem(R.id.action_search); + menuSearchView.setOnActionExpandListener(mOnActionExpandListener); + View mSearchView = menuSearchView.getActionView(); + mSearchEditText = (EditText) mSearchView + .findViewById(R.id.search_field); + mSearchEditText.addTextChangedListener(mSearchTextWatcher); + if (getActionBar().getSelectedNavigationIndex() == 0) { + menuCreateConference.setVisible(false); + } else { + menuCreateContact.setVisible(false); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_create_contact: + showCreateContactDialog(); + break; + case R.id.action_join_conference: + showJoinConferenceDialog(); + break; + } + return super.onOptionsItemSelected(item); + } + + @Override + void onBackendConnected() { + if (mSearchEditText != null) { + filter(mSearchEditText.getText().toString()); + } else { + filter(null); + } + this.mActivatedAccounts.clear(); + for (Account account : xmppConnectionService.getAccounts()) { + if (account.getStatus() != Account.STATUS_DISABLED) { + this.mActivatedAccounts.add(account.getJid()); + } + } + this.mKnownHosts = xmppConnectionService.getKnownHosts(); + this.mKnownConferenceHosts = xmppConnectionService + .getKnownConferenceHosts(); + } + + protected void filter(String needle) { + this.filterContacts(needle); + this.filterConferences(needle); + } + + protected void filterContacts(String needle) { + this.contacts.clear(); + for (Account account : xmppConnectionService.getAccounts()) { + if (account.getStatus() != Account.STATUS_DISABLED) { + for (Contact contact : account.getRoster().getContacts()) { + if (contact.showInRoster() && contact.match(needle)) { + this.contacts.add(contact); + } + } + } + } + Collections.sort(this.contacts); + mContactsAdapter.notifyDataSetChanged(); + } + + protected void filterConferences(String needle) { + this.conferences.clear(); + for (Account account : xmppConnectionService.getAccounts()) { + if (account.getStatus() != Account.STATUS_DISABLED) { + for(Bookmark bookmark : account.getBookmarks()) { + if (bookmark.match(needle)) { + this.conferences.add(bookmark); + } + } + } + } + Collections.sort(this.conferences); + mConferenceAdapter.notifyDataSetChanged(); + } + + private void onTabChanged() { + invalidateOptionsMenu(); + } + + private class ListItemAdapter extends ArrayAdapter<ListItem> { + + public ListItemAdapter(List<ListItem> objects) { + super(getApplicationContext(), 0, objects); + } + + @Override + public View getView(int position, View view, ViewGroup parent) { + LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); + ListItem item = getItem(position); + if (view == null) { + view = (View) inflater.inflate(R.layout.contact, null); + } + TextView name = (TextView) view + .findViewById(R.id.contact_display_name); + TextView jid = (TextView) view.findViewById(R.id.contact_jid); + ImageView picture = (ImageView) view + .findViewById(R.id.contact_photo); + + jid.setText(item.getJid()); + name.setText(item.getDisplayName()); + picture.setImageBitmap(item.getImage(48, getApplicationContext())); + return view; + } + + } + + public static class MyListFragment extends ListFragment { + private AdapterView.OnItemClickListener mOnItemClickListener; + private int mResContextMenu; + + public void setContextMenu(int res) { + this.mResContextMenu = res; + } + + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + if (mOnItemClickListener != null) { + mOnItemClickListener.onItemClick(l, v, position, id); + } + } + + public void setOnListItemClickListener(AdapterView.OnItemClickListener l) { + this.mOnItemClickListener = l; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + registerForContextMenu(getListView()); + } + + @Override + public void onCreateContextMenu(ContextMenu menu, View v, + ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + StartConversation activity = (StartConversation) getActivity(); + activity.getMenuInflater().inflate(mResContextMenu, + menu); + AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo; + if (mResContextMenu == R.menu.conference_context) { + activity.conference_context_id = acmi.position; + } else { + activity.contact_context_id = acmi.position; + } + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + StartConversation activity = (StartConversation) getActivity(); + switch (item.getItemId()) { + case R.id.context_start_conversation: + activity.openConversationForContact(); + break; + case R.id.context_contact_details: + activity.openDetailsForContact(); + break; + case R.id.context_delete_contact: + activity.deleteContact(); + break; + case R.id.context_join_conference: + activity.openConversationForBookmark(); + break; + case R.id.context_delete_conference: + activity.deleteConference(); + } + return true; + } + } +} diff --git a/src/eu/siacs/conversations/ui/XmppActivity.java b/src/eu/siacs/conversations/ui/XmppActivity.java index c95cbfec..217bae55 100644 --- a/src/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/eu/siacs/conversations/ui/XmppActivity.java @@ -149,6 +149,10 @@ public abstract class XmppActivity extends Activity { ExceptionHelper.init(getApplicationContext()); } + public void switchToConversation(Conversation conversation) { + switchToConversation(conversation, null, false); + } + public void switchToConversation(Conversation conversation, String text, boolean newTask) { Intent viewConversationIntent = new Intent(this, @@ -171,6 +175,14 @@ public abstract class XmppActivity extends Activity { startActivity(viewConversationIntent); } + public void switchToContactDetails(Contact contact) { + Intent intent = new Intent(this, ContactDetailsActivity.class); + intent.setAction(ContactDetailsActivity.ACTION_VIEW_CONTACT); + intent.putExtra("account", contact.getAccount().getJid()); + intent.putExtra("contact", contact.getJid()); + startActivity(intent); + } + protected void announcePgp(Account account, final Conversation conversation) { xmppConnectionService.getPgpEngine().generateSignature(account, "online", new UiCallback<Account>() { @@ -191,7 +203,9 @@ public abstract class XmppActivity extends Activity { public void success(Account account) { xmppConnectionService.databaseBackend .updateAccount(account); - xmppConnectionService.sendPresence(account); + xmppConnectionService.sendPresencePacket(account, + xmppConnectionService.getPresenceGenerator() + .sendPresence(account)); if (conversation != null) { conversation .setNextEncryption(Message.ENCRYPTION_PGP); @@ -221,7 +235,7 @@ public abstract class XmppActivity extends Activity { }); } - + protected void showAddToRosterDialog(final Conversation conversation) { String jid = conversation.getContactJid(); AlertDialog.Builder builder = new AlertDialog.Builder(this); @@ -241,7 +255,7 @@ public abstract class XmppActivity extends Activity { }); builder.create().show(); } - + public void selectPresence(final Conversation conversation, final OnPresenceSelected listener) { Contact contact = conversation.getContact(); diff --git a/src/eu/siacs/conversations/utils/KnownHostsAdapter.java b/src/eu/siacs/conversations/utils/KnownHostsAdapter.java new file mode 100644 index 00000000..a0a223dd --- /dev/null +++ b/src/eu/siacs/conversations/utils/KnownHostsAdapter.java @@ -0,0 +1,69 @@ +package eu.siacs.conversations.utils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import android.content.Context; +import android.widget.ArrayAdapter; +import android.widget.Filter; + +public class KnownHostsAdapter extends ArrayAdapter<String> { + private ArrayList<String> domains; + private Filter domainFilter = new Filter() { + + @Override + protected FilterResults performFiltering(CharSequence constraint) { + if (constraint != null) { + ArrayList<String> suggestions = new ArrayList<String>(); + final String[] split = constraint.toString().split("@"); + if (split.length == 1) { + for (String domain : domains) { + suggestions.add(split[0].toLowerCase(Locale.getDefault()) + "@" + domain); + } + } else if (split.length == 2) { + for (String domain : domains) { + if (domain.contains(split[1])) { + suggestions.add(split[0].toLowerCase(Locale.getDefault()) + "@" + domain); + } + } + } else { + return new FilterResults(); + } + FilterResults filterResults = new FilterResults(); + filterResults.values = suggestions; + filterResults.count = suggestions.size(); + return filterResults; + } else { + return new FilterResults(); + } + } + + @Override + protected void publishResults(CharSequence constraint, + FilterResults results) { + ArrayList<String> filteredList = ((ArrayList<String>) results.values); + if (results != null && results.count > 0) { + clear(); + for (String c : filteredList) { + add(c); + } + notifyDataSetChanged(); + } + } + }; + + public KnownHostsAdapter(Context context, int viewResourceId, + List<String> mKnownHosts) { + super(context, viewResourceId, mKnownHosts); + domains = new ArrayList<String>(mKnownHosts.size()); + for (String domain : mKnownHosts) { + domains.add(new String(domain)); + } + } + + @Override + public Filter getFilter() { + return domainFilter; + } +}
\ No newline at end of file diff --git a/src/eu/siacs/conversations/utils/UIHelper.java b/src/eu/siacs/conversations/utils/UIHelper.java index 529d2b4c..ede43830 100644 --- a/src/eu/siacs/conversations/utils/UIHelper.java +++ b/src/eu/siacs/conversations/utils/UIHelper.java @@ -13,11 +13,11 @@ import eu.siacs.conversations.R; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.entities.ListItem; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.MucOptions.User; import eu.siacs.conversations.ui.ConversationActivity; import eu.siacs.conversations.ui.ManageAccountActivity; - import android.app.Activity; import android.app.AlertDialog; import android.app.Notification; @@ -45,13 +45,12 @@ import android.text.Html; import android.util.DisplayMetrics; import android.view.LayoutInflater; import android.view.View; -import android.widget.LinearLayout; import android.widget.QuickContactBadge; import android.widget.TextView; public class UIHelper { private static final int BG_COLOR = 0xFF181818; - private static final int FG_COLOR = 0xFFE5E5E5; + private static final int FG_COLOR = 0xFFFAFAFA; private static final int TRANSPARENT = 0x00000000; private static final int DATE_NO_YEAR_FLAGS = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR | DateUtils.FORMAT_ABBREV_ALL; @@ -69,7 +68,7 @@ public class UIHelper { } else if (difference < 60 * 15) { return context.getString(R.string.minutes_ago, Math.round(difference / 60.0)); - } else if (today(date)) { + } else if (today(date) || difference < 6 * 60 * 60) { java.text.DateFormat df = DateFormat.getTimeFormat(context); return df.format(date); } else { @@ -120,8 +119,9 @@ public class UIHelper { } private static int getNameColor(String name) { - int holoColors[] = { 0xFF1da9da, 0xFFb368d9, 0xFF83b600, 0xFFffa713, - 0xFFe92727 }; + /*int holoColors[] = { 0xFF1da9da, 0xFFb368d9, 0xFF83b600, 0xFFffa713, + 0xFFe92727 };*/ + int holoColors[] = {0xFFe91e63, 0xFF9c27b0, 0xFF673ab7, 0xFF3f51b5, 0xFF5677fc, 0xFF03a9f4, 0xFF00bcd4, 0xFF009688, 0xFFff5722, 0xFF795548, 0xFF607d8b}; return holoColors[(int) ((name.hashCode() & 0xffffffffl) % holoColors.length)]; } @@ -165,7 +165,7 @@ public class UIHelper { if (names.length > 4) { letters[3] = "\u2026"; // Unicode ellipsis - colors[3] = 0xFF444444; + colors[3] = 0xFF202020; } } @@ -491,7 +491,7 @@ public class UIHelper { public static AlertDialog getVerifyFingerprintDialog( final ConversationActivity activity, - final Conversation conversation, final LinearLayout msg) { + final Conversation conversation, final View msg) { final Contact contact = conversation.getContact(); final Account account = conversation.getAccount(); diff --git a/src/eu/siacs/conversations/xmpp/XmppConnection.java b/src/eu/siacs/conversations/xmpp/XmppConnection.java index b2829c6e..72018394 100644 --- a/src/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/eu/siacs/conversations/xmpp/XmppConnection.java @@ -18,7 +18,6 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; -import java.util.Iterator; import java.util.List; import java.util.Map.Entry; @@ -793,20 +792,10 @@ public class XmppConnection implements Runnable { this.sendPacket(packet, null); } - public void sendMessagePacket(MessagePacket packet, - OnMessagePacketReceived callback) { - this.sendPacket(packet, callback); - } - public void sendPresencePacket(PresencePacket packet) { this.sendPacket(packet, null); } - - public void sendPresencePacket(PresencePacket packet, - OnPresencePacketReceived callback) { - this.sendPacket(packet, callback); - } - + private synchronized void sendPacket(final AbstractStanza packet, PacketReceived callback) { // TODO dont increment stanza count if packet = request packet or ack; @@ -926,14 +915,10 @@ public class XmppConnection implements Runnable { public List<String> findDiscoItemsByFeature(String feature) { List<String> items = new ArrayList<String>(); - Iterator<Entry<String, List<String>>> it = this.disco.entrySet() - .iterator(); - while (it.hasNext()) { - Entry<String, List<String>> pairs = it.next(); - if (pairs.getValue().contains(feature)) { - items.add(pairs.getKey()); + for (Entry<String, List<String>> cursor : disco.entrySet()) { + if (cursor.getValue().contains(feature)) { + items.add(cursor.getKey()); } - it.remove(); } return items; } diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java b/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java index 1f287194..f1a0373c 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java +++ b/src/eu/siacs/conversations/xmpp/jingle/JingleConnection.java @@ -83,7 +83,7 @@ public class JingleConnection { sendSuccess(); if (acceptedAutomatically) { message.markUnread(); - JingleConnection.this.mXmppConnectionService.updateUi(message.getConversation(), true); + JingleConnection.this.mXmppConnectionService.notifyUi(message.getConversation(), true); } BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; @@ -277,7 +277,7 @@ public class JingleConnection { } else { message.markUnread(); Log.d("xmppService","not auto accepting new file offer with size: "+size+" allowed size:"+this.mJingleConnectionManager.getAutoAcceptFileSize()); - this.mXmppConnectionService.updateUi(conversation, true); + this.mXmppConnectionService.notifyUi(conversation, true); } this.file = this.mXmppConnectionService.getFileBackend().getJingleFile(message,false); if (message.getEncryption() == Message.ENCRYPTION_OTR) { @@ -507,10 +507,8 @@ public class JingleConnection { private JingleSocks5Transport chooseConnection() { JingleSocks5Transport connection = null; - Iterator<Entry<String, JingleSocks5Transport>> it = this.connections.entrySet().iterator(); - while (it.hasNext()) { - Entry<String, JingleSocks5Transport> pairs = it.next(); - JingleSocks5Transport currentConnection = pairs.getValue(); + for (Entry<String, JingleSocks5Transport> cursor : connections.entrySet()) { + JingleSocks5Transport currentConnection = cursor.getValue(); //Log.d("xmppService","comparing candidate: "+currentConnection.getCandidate().toString()); if (currentConnection.isEstablished()&&(currentConnection.getCandidate().isUsedByCounterpart()||(!currentConnection.getCandidate().isOurs()))) { //Log.d("xmppService","is usable"); @@ -533,7 +531,6 @@ public class JingleConnection { } } } - it.remove(); } return connection; } diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index d4af624a..f01d7fa9 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -2,12 +2,11 @@ package eu.siacs.conversations.xmpp.jingle; import java.math.BigInteger; import java.security.SecureRandom; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; - +import java.util.concurrent.CopyOnWriteArrayList; +import android.annotation.SuppressLint; import android.util.Log; - import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.services.XmppConnectionService; @@ -20,10 +19,11 @@ public class JingleConnectionManager { private XmppConnectionService xmppConnectionService; - private List<JingleConnection> connections = new ArrayList<JingleConnection>(); + private List<JingleConnection> connections = new CopyOnWriteArrayList<JingleConnection>(); private HashMap<String, JingleCandidate> primaryCandidates = new HashMap<String, JingleCandidate>(); + @SuppressLint("TrulyRandom") private SecureRandom random = new SecureRandom(); public JingleConnectionManager(XmppConnectionService service) { diff --git a/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java b/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java index f9de6a6a..d2c84325 100644 --- a/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java +++ b/src/eu/siacs/conversations/xmpp/jingle/JingleSocks5Transport.java @@ -5,7 +5,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; -import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; |