Add support for XEP-0191 (Blocking command)

Fixes #791

Squash of commits:
534f25d7dae3ce6852243e28fdd0a69ac01e9463
808fdf5147f27a912a60bee39aa4bf1ddd4f43b4
1eaf8a8330710ad35ba7c368e04f909af623ae4c
31585242c2359efdcd0eeddb9745077f54dbc9eb
2e69bd0bd0286ed1e98a42f4c3421ba4d8cf524b
e904fb5015bf3a1904ab941a1957edf3b1e7abd2
eebbadf3b3816bbf8fcccb763e419fed252d266f
7c5b87724ce494e5a6e8026557ed50a8fd9f23e8
b0eaaf446937794fe19cbdb4f8309c3ff83d4e42
8c652f9e8bb3512958d9ad8c6f1326505f2d98c8
ad0ea1ad948ff6f8fde7b0b10f5163dc8852032f
f5d49897e0dba691ef53a0eddb9ed34d129ad442
a08fa64c505bd895b7c626cfad182380373be20b
de67079113e08394a276048c31f6b21baa300829
9069f342173ba30c2b20c67529c7ff497a6a257d
0169fa79d161ee898c4b6762e207087682a952d8
8585a5bd75a5d56927fed8317729bd15fffe4dcc
0053528a078369e0b65dcf71bda251072a1299c7
e901a9c3554bd7cca193e92919b463991eadfea7
c5c78257434813c69ab9b7558bcc8f7cbe858433
e905af348d46d77bc46b5f7211527684acc02fab
13a0f9a10c7892b0f90f5fabd2f2615701b0fd66
2cfba1e24b0139839e4453b92be7e20634d150cf
58e074fb5bb44b05a8104250fccd7c024c808c1a
0d6cf98fc8eab212d798ac79b336f9b70a14f06d
e23620f56b85bcab9f3b5d9ce1c01524cd9674dc
d72cd2fcc8d54176c3ff53411a69b9bb4642eff3
195143dff8836623a37094a6b8fa6aa01ef31580
5f5f3caf3a1e480a99d27ee5c34ba516419c52e4
1dee3d5861c9f9c710da4cbda3688d94c622ca93
23949b8aa32c78b27bab49bb3c4f3ff588925ce1
9bf97f8ae522796e0dacb7f6fe7a7f90f86a93a1
This commit is contained in:
Sam Whited 2014-12-21 15:43:58 -05:00
parent 9656970051
commit af7a64491f
44 changed files with 1485 additions and 904 deletions

View File

@ -50,9 +50,11 @@ run your own XMPP server for you and your friends. These XEP's are:
* XEP-0313: Message Archive Management synchronize message history with the * XEP-0313: Message Archive Management synchronize message history with the
server. Catch up with messages that were sent while Conversations was server. Catch up with messages that were sent while Conversations was
offline. offline.
* XEP-0352: Client State Indication let the server know whether or not * XEP-0352: Client State Indication lets the server know whether or not
Conversations is in the background. Allows the server to save bandwidth by Conversations is in the background. Allows the server to save bandwidth by
withholding unimportant packages. withholding unimportant packages.
* XEP-0191: Blocking command lets you blacklist spammers or block contacts
without removing them from your roster.
## Team ## Team

View File

@ -16,3 +16,4 @@
* XEP-0313: Message Archive Management * XEP-0313: Message Archive Management
* XEP-0333: Chat Markers * XEP-0333: Chat Markers
* XEP-0352: Client State Indication * XEP-0352: Client State Indication
* XEP-0191: Blocking command

View File

@ -76,6 +76,9 @@
<activity <activity
android:name=".ui.ChooseContactActivity" android:name=".ui.ChooseContactActivity"
android:label="@string/title_activity_choose_contact" /> android:label="@string/title_activity_choose_contact" />
<activity
android:name=".ui.BlocklistActivity"
android:label="@string/title_activity_block_list" />
<activity <activity
android:name=".ui.ManageAccountActivity" android:name=".ui.ManageAccountActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"

View File

@ -11,8 +11,10 @@ import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import java.security.interfaces.DSAPublicKey; import java.security.interfaces.DSAPublicKey;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
@ -56,7 +58,7 @@ public class Account extends AbstractEntity {
SECURITY_ERROR(true), SECURITY_ERROR(true),
INCOMPATIBLE_SERVER(true); INCOMPATIBLE_SERVER(true);
private boolean isError; private final boolean isError;
public boolean isError() { public boolean isError() {
return this.isError; return this.isError;
@ -120,6 +122,7 @@ public class Account extends AbstractEntity {
private String otrFingerprint; private String otrFingerprint;
private final Roster roster = new Roster(this); private final Roster roster = new Roster(this);
private List<Bookmark> bookmarks = new CopyOnWriteArrayList<>(); private List<Bookmark> bookmarks = new CopyOnWriteArrayList<>();
private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>();
public Account() { public Account() {
this.uuid = "0"; this.uuid = "0";
@ -279,7 +282,7 @@ public class Account extends AbstractEntity {
return values; return values;
} }
public void initOtrEngine(XmppConnectionService context) { public void initOtrEngine(final XmppConnectionService context) {
this.otrEngine = new OtrEngine(context, this); this.otrEngine = new OtrEngine(context, this);
} }
@ -291,7 +294,7 @@ public class Account extends AbstractEntity {
return this.xmppConnection; return this.xmppConnection;
} }
public void setXmppConnection(XmppConnection connection) { public void setXmppConnection(final XmppConnection connection) {
this.xmppConnection = connection; this.xmppConnection = connection;
} }
@ -323,7 +326,7 @@ public class Account extends AbstractEntity {
} }
} }
public void setRosterVersion(String version) { public void setRosterVersion(final String version) {
this.rosterVersion = version; this.rosterVersion = version;
} }
@ -351,7 +354,7 @@ public class Account extends AbstractEntity {
return this.bookmarks; return this.bookmarks;
} }
public void setBookmarks(List<Bookmark> bookmarks) { public void setBookmarks(final List<Bookmark> bookmarks) {
this.bookmarks = bookmarks; this.bookmarks = bookmarks;
} }
@ -399,4 +402,21 @@ public class Account extends AbstractEntity {
return "xmpp:" + this.getJid().toBareJid().toString(); return "xmpp:" + this.getJid().toBareJid().toString();
} }
} }
public boolean isBlocked(final ListItem contact) {
final Jid jid = contact.getJid();
return jid != null && (blocklist.contains(jid.toBareJid()) || blocklist.contains(jid.toDomainJid()));
}
public boolean isBlocked(final Jid jid) {
return jid != null && blocklist.contains(jid.toBareJid());
}
public Collection<Jid> getBlocklist() {
return this.blocklist;
}
public void clearBlocklist() {
getBlocklist().clear();
}
} }

View File

@ -0,0 +1,11 @@
package eu.siacs.conversations.entities;
import eu.siacs.conversations.xmpp.jid.Jid;
public interface Blockable {
public boolean isBlocked();
public boolean isDomainBlocked();
public Jid getBlockedJid();
public Jid getJid();
public Account getAccount();
}

View File

@ -6,7 +6,6 @@ import java.util.Locale;
import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;
public class Bookmark extends Element implements ListItem { public class Bookmark extends Element implements ListItem {

View File

@ -16,7 +16,7 @@ import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;
public class Contact implements ListItem { public class Contact implements ListItem, Blockable {
public static final String TABLENAME = "contacts"; public static final String TABLENAME = "contacts";
public static final String SYSTEMNAME = "systemname"; public static final String SYSTEMNAME = "systemname";
@ -122,11 +122,10 @@ public class Contact implements ListItem {
@Override @Override
public List<Tag> getTags() { public List<Tag> getTags() {
ArrayList<Tag> tags = new ArrayList<Tag>(); final ArrayList<Tag> tags = new ArrayList<>();
for (String group : getGroups()) { for (final String group : getGroups()) {
tags.add(new Tag(group, UIHelper.getColorForName(group))); tags.add(new Tag(group, UIHelper.getColorForName(group)));
} }
int status = getMostAvailableStatus();
switch (getMostAvailableStatus()) { switch (getMostAvailableStatus()) {
case Presences.CHAT: case Presences.CHAT:
case Presences.ONLINE: case Presences.ONLINE:
@ -142,6 +141,9 @@ public class Contact implements ListItem {
tags.add(new Tag("dnd", 0xffe51c23)); tags.add(new Tag("dnd", 0xffe51c23));
break; break;
} }
if (isBlocked()) {
tags.add(new Tag("blocked", 0xff2e2f3b));
}
return tags; return tags;
} }
@ -176,7 +178,7 @@ public class Contact implements ListItem {
} }
public ContentValues getContentValues() { public ContentValues getContentValues() {
ContentValues values = new ContentValues(); final ContentValues values = new ContentValues();
values.put(ACCOUNT, accountUuid); values.put(ACCOUNT, accountUuid);
values.put(SYSTEMNAME, systemName); values.put(SYSTEMNAME, systemName);
values.put(SERVERNAME, serverName); values.put(SERVERNAME, serverName);
@ -213,11 +215,11 @@ public class Contact implements ListItem {
this.presences = pres; this.presences = pres;
} }
public void updatePresence(String resource, int status) { public void updatePresence(final String resource, final int status) {
this.presences.updatePresence(resource, status); this.presences.updatePresence(resource, status);
} }
public void removePresence(String resource) { public void removePresence(final String resource) {
this.presences.removePresence(resource); this.presences.removePresence(resource);
} }
@ -457,22 +459,40 @@ public class Contact implements ListItem {
} }
} }
@Override
public boolean isBlocked() {
return getAccount().isBlocked(this);
}
@Override
public boolean isDomainBlocked() {
return getAccount().isBlocked(this.getJid().toDomainJid());
}
@Override
public Jid getBlockedJid() {
if (isDomainBlocked()) {
return getJid().toDomainJid();
} else {
return getJid();
}
}
public static class Lastseen { public static class Lastseen {
public long time; public long time;
public String presence; public String presence;
public Lastseen() { public Lastseen() {
time = 0; this(null, 0);
presence = null;
} }
public Lastseen(final String presence, final long time) { public Lastseen(final String presence, final long time) {
this.time = time;
this.presence = presence; this.presence = presence;
this.time = time;
} }
} }
public class Options { public final class Options {
public static final int TO = 0; public static final int TO = 0;
public static final int FROM = 1; public static final int FROM = 1;
public static final int ASKING = 2; public static final int ASKING = 2;

View File

@ -3,7 +3,6 @@ package eu.siacs.conversations.entities;
import android.content.ContentValues; import android.content.ContentValues;
import android.database.Cursor; import android.database.Cursor;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Log;
import net.java.otr4j.OtrException; import net.java.otr4j.OtrException;
import net.java.otr4j.crypto.OtrCryptoEngineImpl; import net.java.otr4j.crypto.OtrCryptoEngineImpl;
@ -25,7 +24,7 @@ import eu.siacs.conversations.Config;
import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;
public class Conversation extends AbstractEntity { public class Conversation extends AbstractEntity implements Blockable {
public static final String TABLENAME = "conversations"; public static final String TABLENAME = "conversations";
public static final int STATUS_AVAILABLE = 0; public static final int STATUS_AVAILABLE = 0;
@ -174,13 +173,29 @@ public class Conversation extends AbstractEntity {
return null; return null;
} }
public void populateWithMessages(List<Message> messages) { public void populateWithMessages(final List<Message> messages) {
synchronized (this.messages) { synchronized (this.messages) {
messages.clear(); messages.clear();
messages.addAll(this.messages); messages.addAll(this.messages);
} }
} }
@Override
public boolean isBlocked() {
return getContact().isBlocked();
}
@Override
public boolean isDomainBlocked() {
return getContact().isDomainBlocked();
}
@Override
public Jid getBlockedJid() {
return getContact().getBlockedJid();
}
public interface OnMessageFound { public interface OnMessageFound {
public void onMessageFound(final Message message); public void onMessageFound(final Message message);
} }
@ -267,7 +282,7 @@ public class Conversation extends AbstractEntity {
if (generatedName != null) { if (generatedName != null) {
return generatedName; return generatedName;
} else { } else {
return getContactJid().getLocalpart(); return getJid().getLocalpart();
} }
} }
} else { } else {
@ -287,11 +302,12 @@ public class Conversation extends AbstractEntity {
return this.account.getRoster().getContact(this.contactJid); return this.account.getRoster().getContact(this.contactJid);
} }
public void setAccount(Account account) { public void setAccount(final Account account) {
this.account = account; this.account = account;
} }
public Jid getContactJid() { @Override
public Jid getJid() {
return this.contactJid; return this.contactJid;
} }
@ -352,7 +368,7 @@ public class Conversation extends AbstractEntity {
if (this.otrSession != null) { if (this.otrSession != null) {
return this.otrSession; return this.otrSession;
} else { } else {
final SessionID sessionId = new SessionID(this.getContactJid().toBareJid().toString(), final SessionID sessionId = new SessionID(this.getJid().toBareJid().toString(),
presence, presence,
"xmpp"); "xmpp");
this.otrSession = new SessionImpl(sessionId, getAccount().getOtrEngine()); this.otrSession = new SessionImpl(sessionId, getAccount().getOtrEngine());

View File

@ -12,10 +12,10 @@ public interface ListItem extends Comparable<ListItem> {
public List<Tag> getTags(); public List<Tag> getTags();
public final class Tag { public final class Tag {
private String name; private final String name;
private int color; private final int color;
public Tag(String name, int color) { public Tag(final String name, final int color) {
this.name = name; this.name = name;
this.color = color; this.color = color;
} }
@ -28,4 +28,6 @@ public interface ListItem extends Comparable<ListItem> {
return this.name; return this.name;
} }
} }
public boolean match(final String needle);
} }

View File

@ -77,7 +77,7 @@ public class Message extends AbstractEntity {
public Message(Conversation conversation, String body, int encryption, int status) { public Message(Conversation conversation, String body, int encryption, int status) {
this(java.util.UUID.randomUUID().toString(), this(java.util.UUID.randomUUID().toString(),
conversation.getUuid(), conversation.getUuid(),
conversation.getContactJid() == null ? null : conversation.getContactJid().toBareJid(), conversation.getJid() == null ? null : conversation.getJid().toBareJid(),
null, null,
body, body,
System.currentTimeMillis(), System.currentTimeMillis(),

View File

@ -179,7 +179,7 @@ public class MucOptions {
user.setAffiliation(item.getAttribute("affiliation")); user.setAffiliation(item.getAttribute("affiliation"));
user.setRole(item.getAttribute("role")); user.setRole(item.getAttribute("role"));
user.setJid(item.getAttributeAsJid("jid")); user.setJid(item.getAttributeAsJid("jid"));
if (codes.contains(STATUS_CODE_SELF_PRESENCE) || packet.getFrom().equals(this.conversation.getContactJid())) { if (codes.contains(STATUS_CODE_SELF_PRESENCE) || packet.getFrom().equals(this.conversation.getJid())) {
this.isOnline = true; this.isOnline = true;
this.error = ERROR_NO_ERROR; this.error = ERROR_NO_ERROR;
self = user; self = user;
@ -211,7 +211,7 @@ public class MucOptions {
} }
} else if (type.equals("unavailable")) { } else if (type.equals("unavailable")) {
if (codes.contains(STATUS_CODE_SELF_PRESENCE) || if (codes.contains(STATUS_CODE_SELF_PRESENCE) ||
packet.getFrom().equals(this.conversation.getContactJid())) { packet.getFrom().equals(this.conversation.getJid())) {
if (codes.contains(STATUS_CODE_CHANGED_NICK)) { if (codes.contains(STATUS_CODE_CHANGED_NICK)) {
this.mNickChangingInProgress = true; this.mNickChangingInProgress = true;
} else if (codes.contains(STATUS_CODE_KICKED)) { } else if (codes.contains(STATUS_CODE_KICKED)) {
@ -282,8 +282,8 @@ public class MucOptions {
&& conversation.getBookmark().getNick() != null && conversation.getBookmark().getNick() != null
&& !conversation.getBookmark().getNick().isEmpty()) { && !conversation.getBookmark().getNick().isEmpty()) {
return conversation.getBookmark().getNick(); return conversation.getBookmark().getNick();
} else if (!conversation.getContactJid().isBareJid()) { } else if (!conversation.getJid().isBareJid()) {
return conversation.getContactJid().getResourcepart(); return conversation.getJid().getResourcepart();
} else { } else {
return account.getUsername(); return account.getUsername();
} }
@ -389,7 +389,7 @@ public class MucOptions {
public Jid createJoinJid(String nick) { public Jid createJoinJid(String nick) {
try { try {
return Jid.fromString(this.conversation.getContactJid().toBareJid().toString() + "/"+nick); return Jid.fromString(this.conversation.getJid().toBareJid().toString() + "/"+nick);
} catch (final InvalidJidException e) { } catch (final InvalidJidException e) {
return null; return null;
} }

View File

@ -7,7 +7,7 @@ import java.util.concurrent.ConcurrentHashMap;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;
public class Roster { public class Roster {
Account account; final Account account;
final ConcurrentHashMap<String, Contact> contacts = new ConcurrentHashMap<>(); final ConcurrentHashMap<String, Contact> contacts = new ConcurrentHashMap<>();
private String version = null; private String version = null;
@ -19,7 +19,7 @@ public class Roster {
if (jid == null) { if (jid == null) {
return null; return null;
} }
Contact contact = contacts.get(jid.toBareJid().toString()); final Contact contact = contacts.get(jid.toBareJid().toString());
if (contact != null && contact.showInRoster()) { if (contact != null && contact.showInRoster()) {
return contact; return contact;
} else { } else {
@ -32,7 +32,7 @@ public class Roster {
if (contacts.containsKey(bareJid.toString())) { if (contacts.containsKey(bareJid.toString())) {
return contacts.get(bareJid.toString()); return contacts.get(bareJid.toString());
} else { } else {
Contact contact = new Contact(bareJid); final Contact contact = new Contact(bareJid);
contact.setAccount(account); contact.setAccount(account);
contacts.put(bareJid.toString(), contact); contacts.put(bareJid.toString(), contact);
return contact; return contact;
@ -46,13 +46,13 @@ public class Roster {
} }
public void markAllAsNotInRoster() { public void markAllAsNotInRoster() {
for (Contact contact : getContacts()) { for (final Contact contact : getContacts()) {
contact.resetOption(Contact.Options.IN_ROSTER); contact.resetOption(Contact.Options.IN_ROSTER);
} }
} }
public void clearSystemAccounts() { public void clearSystemAccounts() {
for (Contact contact : getContacts()) { for (final Contact contact : getContacts()) {
contact.setPhotoUri(null); contact.setPhotoUri(null);
contact.setSystemName(null); contact.setSystemName(null);
contact.setSystemAccount(null); contact.setSystemAccount(null);

View File

@ -1,14 +1,12 @@
package eu.siacs.conversations.generator; package eu.siacs.conversations.generator;
import android.util.Log;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.services.MessageArchiveService; import eu.siacs.conversations.services.MessageArchiveService;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.Xmlns;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.forms.Data; import eu.siacs.conversations.xmpp.forms.Data;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;
@ -17,44 +15,44 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public class IqGenerator extends AbstractGenerator { public class IqGenerator extends AbstractGenerator {
public IqGenerator(XmppConnectionService service) { public IqGenerator(final XmppConnectionService service) {
super(service); super(service);
} }
public IqPacket discoResponse(IqPacket request) { public IqPacket discoResponse(final IqPacket request) {
IqPacket packet = new IqPacket(IqPacket.TYPE_RESULT); final IqPacket packet = new IqPacket(IqPacket.TYPE_RESULT);
packet.setId(request.getId()); packet.setId(request.getId());
packet.setTo(request.getFrom()); packet.setTo(request.getFrom());
Element query = packet.addChild("query", final Element query = packet.addChild("query",
"http://jabber.org/protocol/disco#info"); "http://jabber.org/protocol/disco#info");
query.setAttribute("node", request.query().getAttribute("node")); query.setAttribute("node", request.query().getAttribute("node"));
Element identity = query.addChild("identity"); final Element identity = query.addChild("identity");
identity.setAttribute("category", "client"); identity.setAttribute("category", "client");
identity.setAttribute("type", this.IDENTITY_TYPE); identity.setAttribute("type", this.IDENTITY_TYPE);
identity.setAttribute("name", IDENTITY_NAME); identity.setAttribute("name", IDENTITY_NAME);
List<String> features = Arrays.asList(FEATURES); final List<String> features = Arrays.asList(FEATURES);
Collections.sort(features); Collections.sort(features);
for (String feature : features) { for (final String feature : features) {
query.addChild("feature").setAttribute("var", feature); query.addChild("feature").setAttribute("var", feature);
} }
return packet; return packet;
} }
protected IqPacket publish(String node, Element item) { protected IqPacket publish(final String node, final Element item) {
IqPacket packet = new IqPacket(IqPacket.TYPE_SET); final IqPacket packet = new IqPacket(IqPacket.TYPE_SET);
Element pubsub = packet.addChild("pubsub", final Element pubsub = packet.addChild("pubsub",
"http://jabber.org/protocol/pubsub"); "http://jabber.org/protocol/pubsub");
Element publish = pubsub.addChild("publish"); final Element publish = pubsub.addChild("publish");
publish.setAttribute("node", node); publish.setAttribute("node", node);
publish.addChild(item); publish.addChild(item);
return packet; return packet;
} }
protected IqPacket retrieve(String node, Element item) { protected IqPacket retrieve(String node, Element item) {
IqPacket packet = new IqPacket(IqPacket.TYPE_GET); final IqPacket packet = new IqPacket(IqPacket.TYPE_GET);
Element pubsub = packet.addChild("pubsub", final Element pubsub = packet.addChild("pubsub",
"http://jabber.org/protocol/pubsub"); "http://jabber.org/protocol/pubsub");
Element items = pubsub.addChild("items"); final Element items = pubsub.addChild("items");
items.setAttribute("node", node); items.setAttribute("node", node);
if (item != null) { if (item != null) {
items.addChild(item); items.addChild(item);
@ -63,19 +61,19 @@ public class IqGenerator extends AbstractGenerator {
} }
public IqPacket publishAvatar(Avatar avatar) { public IqPacket publishAvatar(Avatar avatar) {
Element item = new Element("item"); final Element item = new Element("item");
item.setAttribute("id", avatar.sha1sum); item.setAttribute("id", avatar.sha1sum);
Element data = item.addChild("data", "urn:xmpp:avatar:data"); final Element data = item.addChild("data", "urn:xmpp:avatar:data");
data.setContent(avatar.image); data.setContent(avatar.image);
return publish("urn:xmpp:avatar:data", item); return publish("urn:xmpp:avatar:data", item);
} }
public IqPacket publishAvatarMetadata(Avatar avatar) { public IqPacket publishAvatarMetadata(final Avatar avatar) {
Element item = new Element("item"); final Element item = new Element("item");
item.setAttribute("id", avatar.sha1sum); item.setAttribute("id", avatar.sha1sum);
Element metadata = item final Element metadata = item
.addChild("metadata", "urn:xmpp:avatar:metadata"); .addChild("metadata", "urn:xmpp:avatar:metadata");
Element info = metadata.addChild("info"); final Element info = metadata.addChild("info");
info.setAttribute("bytes", avatar.size); info.setAttribute("bytes", avatar.size);
info.setAttribute("id", avatar.sha1sum); info.setAttribute("id", avatar.sha1sum);
info.setAttribute("height", avatar.height); info.setAttribute("height", avatar.height);
@ -84,10 +82,10 @@ public class IqGenerator extends AbstractGenerator {
return publish("urn:xmpp:avatar:metadata", item); return publish("urn:xmpp:avatar:metadata", item);
} }
public IqPacket retrieveAvatar(Avatar avatar) { public IqPacket retrieveAvatar(final Avatar avatar) {
Element item = new Element("item"); final Element item = new Element("item");
item.setAttribute("id", avatar.sha1sum); item.setAttribute("id", avatar.sha1sum);
IqPacket packet = retrieve("urn:xmpp:avatar:data", item); final IqPacket packet = retrieve("urn:xmpp:avatar:data", item);
packet.setTo(avatar.owner); packet.setTo(avatar.owner);
return packet; return packet;
} }
@ -100,11 +98,11 @@ public class IqGenerator extends AbstractGenerator {
return packet; return packet;
} }
public IqPacket queryMessageArchiveManagement(MessageArchiveService.Query mam) { public IqPacket queryMessageArchiveManagement(final MessageArchiveService.Query mam) {
final IqPacket packet = new IqPacket(IqPacket.TYPE_SET); final IqPacket packet = new IqPacket(IqPacket.TYPE_SET);
Element query = packet.query("urn:xmpp:mam:0"); final Element query = packet.query("urn:xmpp:mam:0");
query.setAttribute("queryid",mam.getQueryId()); query.setAttribute("queryid",mam.getQueryId());
Data data = new Data(); final Data data = new Data();
data.setFormType("urn:xmpp:mam:0"); data.setFormType("urn:xmpp:mam:0");
if (mam.getWith()!=null) { if (mam.getWith()!=null) {
data.put("with", mam.getWith().toString()); data.put("with", mam.getWith().toString());
@ -119,4 +117,25 @@ public class IqGenerator extends AbstractGenerator {
} }
return packet; return packet;
} }
public IqPacket generateGetBlockList() {
final IqPacket iq = new IqPacket(IqPacket.TYPE_GET);
iq.addChild("blocklist", Xmlns.BLOCKING);
return iq;
}
public IqPacket generateSetBlockRequest(final Jid jid) {
final IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
final Element block = iq.addChild("block", Xmlns.BLOCKING);
block.addChild("item").setAttribute("jid", jid.toBareJid().toString());
return iq;
}
public IqPacket generateSetUnblockRequest(final Jid jid) {
final IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
final Element block = iq.addChild("unblock", Xmlns.BLOCKING);
block.addChild("item").setAttribute("jid", jid.toBareJid().toString());
return iq;
}
} }

View File

@ -135,7 +135,7 @@ public class MessageGenerator extends AbstractGenerator {
String subject) { String subject) {
MessagePacket packet = new MessagePacket(); MessagePacket packet = new MessagePacket();
packet.setType(MessagePacket.TYPE_GROUPCHAT); packet.setType(MessagePacket.TYPE_GROUPCHAT);
packet.setTo(conversation.getContactJid().toBareJid()); packet.setTo(conversation.getJid().toBareJid());
Element subjectChild = new Element("subject"); Element subjectChild = new Element("subject");
subjectChild.setContent(subject); subjectChild.setContent(subject);
packet.addChild(subjectChild); packet.addChild(subjectChild);
@ -149,13 +149,13 @@ public class MessageGenerator extends AbstractGenerator {
packet.setTo(contact); packet.setTo(contact);
packet.setFrom(conversation.getAccount().getJid()); packet.setFrom(conversation.getAccount().getJid());
Element x = packet.addChild("x", "jabber:x:conference"); Element x = packet.addChild("x", "jabber:x:conference");
x.setAttribute("jid", conversation.getContactJid().toBareJid().toString()); x.setAttribute("jid", conversation.getJid().toBareJid().toString());
return packet; return packet;
} }
public MessagePacket invite(Conversation conversation, Jid contact) { public MessagePacket invite(Conversation conversation, Jid contact) {
MessagePacket packet = new MessagePacket(); MessagePacket packet = new MessagePacket();
packet.setTo(conversation.getContactJid().toBareJid()); packet.setTo(conversation.getJid().toBareJid());
packet.setFrom(conversation.getAccount().getJid()); packet.setFrom(conversation.getAccount().getJid());
Element x = new Element("x"); Element x = new Element("x");
x.setAttribute("xmlns", "http://jabber.org/protocol/muc#user"); x.setAttribute("xmlns", "http://jabber.org/protocol/muc#user");

View File

@ -53,10 +53,10 @@ public abstract class AbstractParser {
protected void updateLastseen(final Element packet, final Account account, protected void updateLastseen(final Element packet, final Account account,
final boolean presenceOverwrite) { final boolean presenceOverwrite) {
Jid from = packet.getAttributeAsJid("from"); final Jid from = packet.getAttributeAsJid("from");
String presence = from == null || from.isBareJid() ? "" : from.getResourcepart(); final String presence = from == null || from.isBareJid() ? "" : from.getResourcepart();
Contact contact = account.getRoster().getContact(from); final Contact contact = account.getRoster().getContact(from);
long timestamp = getTimestamp(packet); final long timestamp = getTimestamp(packet);
if (timestamp >= contact.lastseen.time) { if (timestamp >= contact.lastseen.time) {
contact.lastseen.time = timestamp; contact.lastseen.time = timestamp;
if (!presence.isEmpty() && presenceOverwrite) { if (!presence.isEmpty() && presenceOverwrite) {

View File

@ -2,36 +2,40 @@ package eu.siacs.conversations.parser;
import android.util.Log; import android.util.Log;
import java.util.ArrayList;
import java.util.Collection;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.Xmlns;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public class IqParser extends AbstractParser implements OnIqPacketReceived { public class IqParser extends AbstractParser implements OnIqPacketReceived {
public IqParser(XmppConnectionService service) { public IqParser(final XmppConnectionService service) {
super(service); super(service);
} }
public void rosterItems(Account account, Element query) { public void rosterItems(final Account account, final Element query) {
String version = query.getAttribute("ver"); final String version = query.getAttribute("ver");
if (version != null) { if (version != null) {
account.getRoster().setVersion(version); account.getRoster().setVersion(version);
} }
for (Element item : query.getChildren()) { for (final Element item : query.getChildren()) {
if (item.getName().equals("item")) { if (item.getName().equals("item")) {
final Jid jid = item.getAttributeAsJid("jid"); final Jid jid = item.getAttributeAsJid("jid");
if (jid == null) { if (jid == null) {
continue; continue;
} }
String name = item.getAttribute("name"); final String name = item.getAttribute("name");
String subscription = item.getAttribute("subscription"); final String subscription = item.getAttribute("subscription");
Contact contact = account.getRoster().getContact(jid); final Contact contact = account.getRoster().getContact(jid);
if (!contact.getOption(Contact.Options.DIRTY_PUSH)) { if (!contact.getOption(Contact.Options.DIRTY_PUSH)) {
contact.setServerName(name); contact.setServerName(name);
contact.parseGroupsFromElement(item); contact.parseGroupsFromElement(item);
@ -54,13 +58,13 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
mXmppConnectionService.updateRosterUi(); mXmppConnectionService.updateRosterUi();
} }
public String avatarData(IqPacket packet) { public String avatarData(final IqPacket packet) {
Element pubsub = packet.findChild("pubsub", final Element pubsub = packet.findChild("pubsub",
"http://jabber.org/protocol/pubsub"); "http://jabber.org/protocol/pubsub");
if (pubsub == null) { if (pubsub == null) {
return null; return null;
} }
Element items = pubsub.findChild("items"); final Element items = pubsub.findChild("items");
if (items == null) { if (items == null) {
return null; return null;
} }
@ -68,13 +72,76 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
} }
@Override @Override
public void onIqPacketReceived(Account account, IqPacket packet) { public void onIqPacketReceived(final Account account, final IqPacket packet) {
if (packet.hasChild("query", "jabber:iq:roster")) { if (packet.hasChild("query", "jabber:iq:roster")) {
final Jid from = packet.getFrom(); final Jid from = packet.getFrom();
if ((from == null) || (from.equals(account.getJid().toBareJid()))) { if ((from == null) || (from.equals(account.getJid().toBareJid()))) {
Element query = packet.findChild("query"); final Element query = packet.findChild("query");
this.rosterItems(account, query); this.rosterItems(account, query);
} }
} else if (packet.hasChild("block", Xmlns.BLOCKING) || packet.hasChild("blocklist", Xmlns.BLOCKING)) {
// Only accept block list changes from the server.
// The server should probably prevent other people from faking a blocklist push,
// but just in case let's prevent it client side as well.
final Jid from = packet.getFrom();
if (from == null || from.equals(account.getServer()) || from.equals(account.getJid().toBareJid())) {
Log.d(Config.LOGTAG, "Received blocklist update from server");
final Element blocklist = packet.findChild("blocklist", Xmlns.BLOCKING);
final Element block = packet.findChild("block", Xmlns.BLOCKING);
final Collection<Element> items = blocklist != null ? blocklist.getChildren() :
(block != null ? block.getChildren() : null);
// If this is a response to a blocklist query, clear the block list and replace with the new one.
// Otherwise, just update the existing blocklist.
if (packet.getType() == IqPacket.TYPE_RESULT) {
account.clearBlocklist();
}
if (items != null) {
final Collection<Jid> jids = new ArrayList<>(items.size());
// Create a collection of Jids from the packet
for (final Element item : items) {
if (item.getName().equals("item")) {
final Jid jid = item.getAttributeAsJid("jid");
if (jid != null) {
jids.add(jid);
}
}
}
account.getBlocklist().addAll(jids);
}
// Update the UI
mXmppConnectionService.updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
} else {
Log.d(Config.LOGTAG, "Received blocklist update from invalid jid: " + from.toString());
}
} else if (packet.hasChild("unblock", Xmlns.BLOCKING)) {
final Jid from = packet.getFrom();
if ((from == null || from.equals(account.getServer()) || from.equals(account.getJid().toBareJid())) &&
packet.getType() == IqPacket.TYPE_SET) {
Log.d(Config.LOGTAG, "Received unblock update from server");
final Collection<Element> items = packet.getChildren().get(0).getChildren();
if (items.size() == 0) {
// No children to unblock == unblock all
account.getBlocklist().clear();
} else {
final Collection<Jid> jids = new ArrayList<>(items.size());
for (final Element item : items) {
if (item.getName().equals("item")) {
final Jid jid = item.getAttributeAsJid("jid");
if (jid != null) {
jids.add(jid);
}
}
}
account.getBlocklist().removeAll(jids);
mXmppConnectionService.updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED);
}
} else {
if (packet.getType() == IqPacket.TYPE_SET) {
Log.d(Config.LOGTAG, "Received unblock update from invalid jid " + from.toString());
} else {
Log.d(Config.LOGTAG, "Received unblock update with invalid type " + packet.getType());
}
}
} else { } else {
if (packet.getFrom() == null) { if (packet.getFrom() == null) {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": received iq with invalid from "+packet.toString()); Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": received iq with invalid from "+packet.toString());
@ -84,17 +151,17 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
mXmppConnectionService.getJingleConnectionManager() mXmppConnectionService.getJingleConnectionManager()
.deliverIbbPacket(account, packet); .deliverIbbPacket(account, packet);
} else if (packet.hasChild("query", "http://jabber.org/protocol/disco#info")) { } else if (packet.hasChild("query", "http://jabber.org/protocol/disco#info")) {
IqPacket response = mXmppConnectionService.getIqGenerator() final IqPacket response = mXmppConnectionService.getIqGenerator()
.discoResponse(packet); .discoResponse(packet);
account.getXmppConnection().sendIqPacket(response, null); account.getXmppConnection().sendIqPacket(response, null);
} else if (packet.hasChild("ping", "urn:xmpp:ping")) { } else if (packet.hasChild("ping", "urn:xmpp:ping")) {
IqPacket response = packet.generateRespone(IqPacket.TYPE_RESULT); final IqPacket response = packet.generateRespone(IqPacket.TYPE_RESULT);
mXmppConnectionService.sendIqPacket(account, response, null); mXmppConnectionService.sendIqPacket(account, response, null);
} else { } else {
if ((packet.getType() == IqPacket.TYPE_GET) if ((packet.getType() == IqPacket.TYPE_GET)
|| (packet.getType() == IqPacket.TYPE_SET)) { || (packet.getType() == IqPacket.TYPE_SET)) {
IqPacket response = packet.generateRespone(IqPacket.TYPE_ERROR); final IqPacket response = packet.generateRespone(IqPacket.TYPE_ERROR);
Element error = response.addChild("error"); final Element error = response.addChild("error");
error.setAttribute("type", "cancel"); error.setAttribute("type", "cancel");
error.addChild("feature-not-implemented", error.addChild("feature-not-implemented",
"urn:ietf:params:xml:ns:xmpp-stanzas"); "urn:ietf:params:xml:ns:xmpp-stanzas");

View File

@ -228,9 +228,9 @@ public class DatabaseBackend extends SQLiteOpenHelper {
return conversation; return conversation;
} }
public void updateConversation(Conversation conversation) { public void updateConversation(final Conversation conversation) {
SQLiteDatabase db = this.getWritableDatabase(); final SQLiteDatabase db = this.getWritableDatabase();
String[] args = { conversation.getUuid() }; final String[] args = { conversation.getUuid() };
db.update(Conversation.TABLENAME, conversation.getContentValues(), db.update(Conversation.TABLENAME, conversation.getContentValues(),
Conversation.UUID + "=?", args); Conversation.UUID + "=?", args);
} }

View File

@ -236,7 +236,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
public Query(Conversation conversation, long start, long end) { public Query(Conversation conversation, long start, long end) {
this(conversation.getAccount(), start, end); this(conversation.getAccount(), start, end);
this.conversation = conversation; this.conversation = conversation;
this.with = conversation.getContactJid().toBareJid(); this.with = conversation.getJid().toBareJid();
} }
public Query(Conversation conversation, long start, long end, PagingOrder order) { public Query(Conversation conversation, long start, long end, PagingOrder order) {

View File

@ -35,11 +35,13 @@ import org.openintents.openpgp.util.OpenPgpServiceConnection;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import de.duenndns.ssl.MemorizingTrustManager; import de.duenndns.ssl.MemorizingTrustManager;
@ -47,11 +49,11 @@ import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Blockable;
import eu.siacs.conversations.entities.Bookmark; import eu.siacs.conversations.entities.Bookmark;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Downloadable; import eu.siacs.conversations.entities.Downloadable;
import eu.siacs.conversations.entities.DownloadableFile;
import eu.siacs.conversations.entities.DownloadablePlaceholder; import eu.siacs.conversations.entities.DownloadablePlaceholder;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.MucOptions;
@ -77,7 +79,11 @@ import eu.siacs.conversations.xmpp.OnBindListener;
import eu.siacs.conversations.xmpp.OnContactStatusChanged; import eu.siacs.conversations.xmpp.OnContactStatusChanged;
import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.OnMessageAcknowledged; import eu.siacs.conversations.xmpp.OnMessageAcknowledged;
import eu.siacs.conversations.xmpp.OnMessagePacketReceived;
import eu.siacs.conversations.xmpp.OnPresencePacketReceived;
import eu.siacs.conversations.xmpp.OnStatusChanged; import eu.siacs.conversations.xmpp.OnStatusChanged;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.PacketReceived;
import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.forms.Data; import eu.siacs.conversations.xmpp.forms.Data;
import eu.siacs.conversations.xmpp.forms.Field; import eu.siacs.conversations.xmpp.forms.Field;
@ -93,9 +99,10 @@ import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
public class XmppConnectionService extends Service implements OnPhoneContactsLoadedListener { public class XmppConnectionService extends Service implements OnPhoneContactsLoadedListener {
public static String ACTION_CLEAR_NOTIFICATION = "clear_notification"; public static final String ACTION_CLEAR_NOTIFICATION = "clear_notification";
private static String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts"; private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts";
public static String ACTION_DISABLE_FOREGROUND = "disable_foreground"; public static final String ACTION_DISABLE_FOREGROUND = "disable_foreground";
private ContentObserver contactObserver = new ContentObserver(null) { private ContentObserver contactObserver = new ContentObserver(null) {
@Override @Override
public void onChange(boolean selfChange) { public void onChange(boolean selfChange) {
@ -129,13 +136,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
private MemorizingTrustManager mMemorizingTrustManager; private MemorizingTrustManager mMemorizingTrustManager;
private NotificationService mNotificationService = new NotificationService( private NotificationService mNotificationService = new NotificationService(
this); this);
private MessageParser mMessageParser = new MessageParser(this); private OnMessagePacketReceived mMessageParser = new MessageParser(this);
private PresenceParser mPresenceParser = new PresenceParser(this); private OnPresencePacketReceived mPresenceParser = new PresenceParser(this);
private IqParser mIqParser = new IqParser(this); private IqParser mIqParser = new IqParser(this);
private MessageGenerator mMessageGenerator = new MessageGenerator(this); private MessageGenerator mMessageGenerator = new MessageGenerator(this);
private PresenceGenerator mPresenceGenerator = new PresenceGenerator(this); private PresenceGenerator mPresenceGenerator = new PresenceGenerator(this);
private List<Account> accounts; private List<Account> accounts;
private final CopyOnWriteArrayList<Conversation> conversations = new CopyOnWriteArrayList<Conversation>(); private final List<Conversation> conversations = new CopyOnWriteArrayList<>();
private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager( private JingleConnectionManager mJingleConnectionManager = new JingleConnectionManager(
this); this);
private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager( private HttpConnectionManager mHttpConnectionManager = new HttpConnectionManager(
@ -208,6 +215,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
private int accountChangedListenerCount = 0; private int accountChangedListenerCount = 0;
private OnRosterUpdate mOnRosterUpdate = null; private OnRosterUpdate mOnRosterUpdate = null;
private OnUpdateBlocklist mOnUpdateBlocklist = null;
private int updateBlocklistListenerCount = 0;
private int rosterChangedListenerCount = 0; private int rosterChangedListenerCount = 0;
private OnMucRosterUpdate mOnMucRosterUpdate = null; private OnMucRosterUpdate mOnMucRosterUpdate = null;
private int mucRosterChangedListenerCount = 0; private int mucRosterChangedListenerCount = 0;
@ -354,13 +363,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
@Override @Override
public void run() { public void run() {
try { try {
DownloadableFile file = getFileBackend().copyImageToPrivateStorage(message, uri); getFileBackend().copyImageToPrivateStorage(message, uri);
if (conversation.getNextEncryption(forceEncryption()) == Message.ENCRYPTION_PGP) { if (conversation.getNextEncryption(forceEncryption()) == Message.ENCRYPTION_PGP) {
getPgpEngine().encrypt(message, callback); getPgpEngine().encrypt(message, callback);
} else { } else {
callback.success(message); callback.success(message);
} }
} catch (FileBackend.FileCopyException e) { } catch (final FileBackend.FileCopyException e) {
callback.error(e.getResId(), message); callback.error(e.getResId(), message);
} }
} }
@ -573,11 +582,11 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} }
public XmppConnection createConnection(Account account) { public XmppConnection createConnection(final Account account) {
SharedPreferences sharedPref = getPreferences(); final SharedPreferences sharedPref = getPreferences();
account.setResource(sharedPref.getString("resource", "mobile") account.setResource(sharedPref.getString("resource", "mobile")
.toLowerCase(Locale.getDefault())); .toLowerCase(Locale.getDefault()));
XmppConnection connection = new XmppConnection(account, this); final XmppConnection connection = new XmppConnection(account, this);
connection.setOnMessagePacketReceivedListener(this.mMessageParser); connection.setOnMessagePacketReceivedListener(this.mMessageParser);
connection.setOnStatusChangedListener(this.statusListener); connection.setOnStatusChangedListener(this.statusListener);
connection.setOnPresencePacketReceivedListener(this.mPresenceParser); connection.setOnPresencePacketReceivedListener(this.mPresenceParser);
@ -589,10 +598,10 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return connection; return connection;
} }
public void sendMessage(Message message) { public void sendMessage(final Message message) {
Account account = message.getConversation().getAccount(); final Account account = message.getConversation().getAccount();
account.deactivateGracePeriod(); account.deactivateGracePeriod();
Conversation conv = message.getConversation(); final Conversation conv = message.getConversation();
MessagePacket packet = null; MessagePacket packet = null;
boolean saveInDb = true; boolean saveInDb = true;
boolean send = false; boolean send = false;
@ -694,7 +703,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
updateConversationUi(); updateConversationUi();
} }
private void sendUnsentMessages(Conversation conversation) { private void sendUnsentMessages(final Conversation conversation) {
conversation.findWaitingMessages(new Conversation.OnMessageFound() { conversation.findWaitingMessages(new Conversation.OnMessageFound() {
@Override @Override
@ -704,7 +713,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}); });
} }
private void resendMessage(Message message) { private void resendMessage(final Message message) {
Account account = message.getConversation().getAccount(); Account account = message.getConversation().getAccount();
MessagePacket packet = null; MessagePacket packet = null;
if (message.getEncryption() == Message.ENCRYPTION_OTR) { if (message.getEncryption() == Message.ENCRYPTION_OTR) {
@ -731,7 +740,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} else if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) { } else if (message.getType() == Message.TYPE_IMAGE || message.getType() == Message.TYPE_FILE) {
mJingleConnectionManager.createNewConnection(message); mJingleConnectionManager.createNewConnection(message);
} }
} catch (InvalidJidException e) { } catch (final InvalidJidException ignored) {
} }
} }
@ -774,8 +783,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} }
} }
public void fetchRosterFromServer(Account account) { public void fetchRosterFromServer(final Account account) {
IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET); final IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
if (!"".equals(account.getRosterVersion())) { if (!"".equals(account.getRosterVersion())) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ ": fetching roster version " + account.getRosterVersion()); + ": fetching roster version " + account.getRosterVersion());
@ -789,8 +798,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
@Override @Override
public void onIqPacketReceived(final Account account, public void onIqPacketReceived(final Account account,
IqPacket packet) { final IqPacket packet) {
Element query = packet.findChild("query"); final Element query = packet.findChild("query");
if (query != null) { if (query != null) {
account.getRoster().markAllAsNotInRoster(); account.getRoster().markAllAsNotInRoster();
mIqParser.rosterItems(account, query); mIqParser.rosterItems(account, query);
@ -799,22 +808,22 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}); });
} }
public void fetchBookmarks(Account account) { public void fetchBookmarks(final Account account) {
IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET); final IqPacket iqPacket = new IqPacket(IqPacket.TYPE_GET);
Element query = iqPacket.query("jabber:iq:private"); final Element query = iqPacket.query("jabber:iq:private");
query.addChild("storage", "storage:bookmarks"); query.addChild("storage", "storage:bookmarks");
OnIqPacketReceived callback = new OnIqPacketReceived() { final PacketReceived callback = new OnIqPacketReceived() {
@Override @Override
public void onIqPacketReceived(Account account, IqPacket packet) { public void onIqPacketReceived(final Account account, final IqPacket packet) {
Element query = packet.query(); final Element query = packet.query();
List<Bookmark> bookmarks = new CopyOnWriteArrayList<>(); final List<Bookmark> bookmarks = new CopyOnWriteArrayList<>();
Element storage = query.findChild("storage", final Element storage = query.findChild("storage",
"storage:bookmarks"); "storage:bookmarks");
if (storage != null) { if (storage != null) {
for (Element item : storage.getChildren()) { for (final Element item : storage.getChildren()) {
if (item.getName().equals("conference")) { if (item.getName().equals("conference")) {
Bookmark bookmark = Bookmark.parse(item, account); final Bookmark bookmark = Bookmark.parse(item, account);
bookmarks.add(bookmark); bookmarks.add(bookmark);
Conversation conversation = find(bookmark); Conversation conversation = find(bookmark);
if (conversation != null) { if (conversation != null) {
@ -832,7 +841,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} }
}; };
sendIqPacket(account, iqPacket, callback); sendIqPacket(account, iqPacket, callback);
} }
public void pushBookmarks(Account account) { public void pushBookmarks(Account account) {
@ -885,7 +893,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
private void initConversations() { private void initConversations() {
synchronized (this.conversations) { synchronized (this.conversations) {
Hashtable<String, Account> accountLookupTable = new Hashtable<>(); final Map<String, Account> accountLookupTable = new Hashtable<>();
for (Account account : this.accounts) { for (Account account : this.accounts) {
accountLookupTable.put(account.getUuid(), account); accountLookupTable.put(account.getUuid(), account);
} }
@ -992,8 +1000,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return this.accounts; return this.accounts;
} }
public Conversation find(List<Conversation> haystack, Contact contact) { public Conversation find(final Iterable<Conversation> haystack, final Contact contact) {
for (Conversation conversation : haystack) { for (final Conversation conversation : haystack) {
if (conversation.getContact() == contact) { if (conversation.getContact() == contact) {
return conversation; return conversation;
} }
@ -1001,15 +1009,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return null; return null;
} }
public Conversation find(final List<Conversation> haystack, public Conversation find(final Iterable<Conversation> haystack, final Account account, final Jid jid) {
final Account account,
final Jid jid) {
if (jid == null ) { if (jid == null ) {
return null; return null;
} }
for (Conversation conversation : haystack) { for (final Conversation conversation : haystack) {
if ((account == null || conversation.getAccount() == account) if ((account == null || conversation.getAccount() == account)
&& (conversation.getContactJid().toBareJid().equals(jid.toBareJid()))) { && (conversation.getJid().toBareJid().equals(jid.toBareJid()))) {
return conversation; return conversation;
} }
} }
@ -1177,7 +1183,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} }
} }
public void setOnRosterUpdateListener(OnRosterUpdate listener) { public void setOnRosterUpdateListener(final OnRosterUpdate listener) {
synchronized (this) { synchronized (this) {
if (checkListeners()) { if (checkListeners()) {
switchToForeground(); switchToForeground();
@ -1202,6 +1208,31 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} }
} }
public void setOnUpdateBlocklistListener(final OnUpdateBlocklist listener) {
synchronized (this) {
if (checkListeners()) {
switchToForeground();
}
this.mOnUpdateBlocklist = listener;
if (this.updateBlocklistListenerCount < 2) {
this.updateBlocklistListenerCount++;
}
}
}
public void removeOnUpdateBlocklistListener() {
synchronized (this) {
this.updateBlocklistListenerCount--;
if (this.updateBlocklistListenerCount <= 0) {
this.updateBlocklistListenerCount = 0;
this.mOnUpdateBlocklist = null;
if (checkListeners()) {
switchToBackground();
}
}
}
}
public void setOnMucRosterUpdateListener(OnMucRosterUpdate listener) { public void setOnMucRosterUpdateListener(OnMucRosterUpdate listener) {
synchronized (this) { synchronized (this) {
if (checkListeners()) { if (checkListeners()) {
@ -1293,7 +1324,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
packet.addChild("x", "jabber:x:signed").setContent(sig); packet.addChild("x", "jabber:x:signed").setContent(sig);
} }
sendPresencePacket(account, packet); sendPresencePacket(account, packet);
if (!joinJid.equals(conversation.getContactJid())) { if (!joinJid.equals(conversation.getJid())) {
conversation.setContactJid(joinJid); conversation.setContactJid(joinJid);
databaseBackend.updateConversation(conversation); databaseBackend.updateConversation(conversation);
} }
@ -1369,14 +1400,14 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
account.pendingConferenceLeaves.remove(conversation); account.pendingConferenceLeaves.remove(conversation);
if (account.getStatus() == Account.State.ONLINE) { if (account.getStatus() == Account.State.ONLINE) {
PresencePacket packet = new PresencePacket(); PresencePacket packet = new PresencePacket();
packet.setTo(conversation.getContactJid()); packet.setTo(conversation.getJid());
packet.setFrom(conversation.getAccount().getJid()); packet.setFrom(conversation.getAccount().getJid());
packet.setAttribute("type", "unavailable"); packet.setAttribute("type", "unavailable");
sendPresencePacket(conversation.getAccount(), packet); sendPresencePacket(conversation.getAccount(), packet);
conversation.getMucOptions().setOffline(); conversation.getMucOptions().setOffline();
conversation.deregisterWithBookmark(); conversation.deregisterWithBookmark();
Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid()
+ ": leaving muc " + conversation.getContactJid()); + ": leaving muc " + conversation.getJid());
} else { } else {
account.pendingConferenceLeaves.add(conversation); account.pendingConferenceLeaves.add(conversation);
} }
@ -1401,7 +1432,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return null; return null;
} }
public void createAdhocConference(final Account account, final List<Jid> jids, final UiCallback<Conversation> callback) { public void createAdhocConference(final Account account, final Iterable<Jid> jids, final UiCallback<Conversation> callback) {
Log.d(Config.LOGTAG,account.getJid().toBareJid().toString()+": creating adhoc conference with "+ jids.toString()); Log.d(Config.LOGTAG,account.getJid().toBareJid().toString()+": creating adhoc conference with "+ jids.toString());
if (account.getStatus() == Account.State.ONLINE) { if (account.getStatus() == Account.State.ONLINE) {
try { try {
@ -1454,7 +1485,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public void pushConferenceConfiguration(final Conversation conversation,final Bundle options, final OnConferenceOptionsPushed callback) { public void pushConferenceConfiguration(final Conversation conversation,final Bundle options, final OnConferenceOptionsPushed callback) {
IqPacket request = new IqPacket(IqPacket.TYPE_GET); IqPacket request = new IqPacket(IqPacket.TYPE_GET);
request.setTo(conversation.getContactJid().toBareJid()); request.setTo(conversation.getJid().toBareJid());
request.query("http://jabber.org/protocol/muc#owner"); request.query("http://jabber.org/protocol/muc#owner");
sendIqPacket(conversation.getAccount(),request,new OnIqPacketReceived() { sendIqPacket(conversation.getAccount(),request,new OnIqPacketReceived() {
@Override @Override
@ -1468,7 +1499,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} }
data.submit(); data.submit();
IqPacket set = new IqPacket(IqPacket.TYPE_SET); IqPacket set = new IqPacket(IqPacket.TYPE_SET);
set.setTo(conversation.getContactJid().toBareJid()); set.setTo(conversation.getJid().toBareJid());
set.query("http://jabber.org/protocol/muc#owner").addChild(data); set.query("http://jabber.org/protocol/muc#owner").addChild(data);
sendIqPacket(account, set, new OnIqPacketReceived() { sendIqPacket(account, set, new OnIqPacketReceived() {
@Override @Override
@ -1506,7 +1537,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
if (conversation.endOtrIfNeeded()) { if (conversation.endOtrIfNeeded()) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ ": ended otr session with " + ": ended otr session with "
+ conversation.getContactJid()); + conversation.getJid());
} }
} }
} }
@ -1552,7 +1583,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
final Session otrSession = conversation.getOtrSession(); final Session otrSession = conversation.getOtrSession();
Log.d(Config.LOGTAG, Log.d(Config.LOGTAG,
account.getJid().toBareJid() + " otr session established with " account.getJid().toBareJid() + " otr session established with "
+ conversation.getContactJid() + "/" + conversation.getJid() + "/"
+ otrSession.getSessionID().getUserID()); + otrSession.getSessionID().getUserID());
conversation.findUnsentMessagesWithOtrEncryption(new Conversation.OnMessageFound() { conversation.findUnsentMessagesWithOtrEncryption(new Conversation.OnMessageFound() {
@ -1848,7 +1879,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return false; return false;
} else { } else {
for (Conversation conversation : getConversations()) { for (Conversation conversation : getConversations()) {
if (conversation.getContactJid().equals(recipient) if (conversation.getJid().equals(recipient)
&& conversation.getAccount().equals(account)) { && conversation.getAccount().equals(account)) {
return markMessage(conversation, uuid, status); return markMessage(conversation, uuid, status);
} }
@ -1922,6 +1953,12 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} }
} }
public void updateBlocklistUi(final OnUpdateBlocklist.Status status) {
if (mOnUpdateBlocklist != null) {
mOnUpdateBlocklist.OnUpdateBlocklist(status);
}
}
public void updateMucRosterUi() { public void updateMucRosterUi() {
if (mOnMucRosterUpdate != null) { if (mOnMucRosterUpdate != null) {
mOnMucRosterUpdate.onMucRosterUpdate(); mOnMucRosterUpdate.onMucRosterUpdate();
@ -2034,9 +2071,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} }
} }
public void sendIqPacket(Account account, IqPacket packet, public void sendIqPacket(final Account account, final IqPacket packet, final PacketReceived callback) {
OnIqPacketReceived callback) { final XmppConnection connection = account.getXmppConnection();
XmppConnection connection = account.getXmppConnection();
if (connection != null) { if (connection != null) {
connection.sendIqPacket(packet, callback); connection.sendIqPacket(packet, callback);
} }
@ -2054,6 +2090,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return this.mIqGenerator; return this.mIqGenerator;
} }
public IqParser getIqParser() { return this.mIqParser; }
public JingleConnectionManager getJingleConnectionManager() { public JingleConnectionManager getJingleConnectionManager() {
return this.mJingleConnectionManager; return this.mJingleConnectionManager;
} }
@ -2083,8 +2121,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return this.mHttpConnectionManager; return this.mHttpConnectionManager;
} }
public void resendFailedMessages(Message message) { public void resendFailedMessages(final Message message) {
List<Message> messages = new ArrayList<>(); final Collection<Message> messages = new ArrayList<>();
Message current = message; Message current = message;
while (current.getStatus() == Message.STATUS_SEND_FAILED) { while (current.getStatus() == Message.STATUS_SEND_FAILED) {
messages.add(current); messages.add(current);
@ -2094,7 +2132,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
break; break;
} }
} }
for (Message msg : messages) { for (final Message msg : messages) {
markMessage(msg, Message.STATUS_WAITING); markMessage(msg, Message.STATUS_WAITING);
this.resendMessage(msg); this.resendMessage(msg);
} }
@ -2136,4 +2174,35 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return XmppConnectionService.this; return XmppConnectionService.this;
} }
} }
public void sendBlockRequest(final Blockable blockable) {
if (blockable != null && blockable.getBlockedJid() != null) {
final Jid jid = blockable.getBlockedJid();
this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetBlockRequest(jid), new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(final Account account, final IqPacket packet) {
if (packet.getType() == IqPacket.TYPE_RESULT) {
account.getBlocklist().add(jid);
updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
}
}
});
}
}
public void sendUnblockRequest(final Blockable blockable) {
if (blockable != null && blockable.getJid() != null) {
final Jid jid = blockable.getBlockedJid();
this.sendIqPacket(blockable.getAccount(), getIqGenerator().generateSetUnblockRequest(jid), new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(final Account account, final IqPacket packet) {
if (packet.getType() == IqPacket.TYPE_RESULT) {
account.getBlocklist().remove(jid);
updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED);
}
}
});
}
}
} }

View File

@ -0,0 +1,124 @@
package eu.siacs.conversations.ui;
import android.content.Context;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import java.util.ArrayList;
import java.util.List;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.ui.adapter.ListItemAdapter;
public abstract class AbstractSearchableListItemActivity extends XmppActivity {
private ListView mListView;
private final List<ListItem> listItems = new ArrayList<>();
private ArrayAdapter<ListItem> mListItemsAdapter;
private EditText mSearchEditText;
private final MenuItem.OnActionExpandListener mOnActionExpandListener = new MenuItem.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(final MenuItem item) {
mSearchEditText.post(new Runnable() {
@Override
public void run() {
mSearchEditText.requestFocus();
final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(mSearchEditText,
InputMethodManager.SHOW_IMPLICIT);
}
});
return true;
}
@Override
public boolean onMenuItemActionCollapse(final MenuItem item) {
final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(),
InputMethodManager.HIDE_IMPLICIT_ONLY);
mSearchEditText.setText("");
filterContacts();
return true;
}
};
private final TextWatcher mSearchTextWatcher = new TextWatcher() {
@Override
public void afterTextChanged(final Editable editable) {
filterContacts(editable.toString());
}
@Override
public void beforeTextChanged(final CharSequence s, final int start, final int count,
final int after) {
}
@Override
public void onTextChanged(final CharSequence s, final int start, final int before,
final int count) {
}
};
public ListView getListView() {
return mListView;
}
public List<ListItem> getListItems() {
return listItems;
}
public EditText getSearchEditText() {
return mSearchEditText;
}
public ArrayAdapter<ListItem> getListItemAdapter() {
return mListItemsAdapter;
}
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_choose_contact);
mListView = (ListView) findViewById(R.id.choose_contact_list);
mListView.setFastScrollEnabled(true);
mListItemsAdapter = new ListItemAdapter(this, listItems);
mListView.setAdapter(mListItemsAdapter);
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.choose_contact, menu);
final MenuItem menuSearchView = menu.findItem(R.id.action_search);
final View mSearchView = menuSearchView.getActionView();
mSearchEditText = (EditText) mSearchView
.findViewById(R.id.search_field);
mSearchEditText.addTextChangedListener(mSearchTextWatcher);
menuSearchView.setOnActionExpandListener(mOnActionExpandListener);
return true;
}
protected void filterContacts() {
filterContacts(null);
}
protected abstract void filterContacts(final String needle);
@Override
void onBackendConnected() {
filterContacts();
}
}

View File

@ -0,0 +1,41 @@
package eu.siacs.conversations.ui;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Blockable;
import eu.siacs.conversations.services.XmppConnectionService;
public final class BlockContactDialog {
public static void show(final Context context,
final XmppConnectionService xmppConnectionService,
final Blockable blockable) {
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
final boolean isBlocked = blockable.isBlocked();
builder.setNegativeButton(R.string.cancel, null);
if (blockable.getJid().isDomainJid() || blockable.getAccount().isBlocked(blockable.getJid().toDomainJid())) {
builder.setTitle(isBlocked ? R.string.action_unblock_domain : R.string.action_block_domain);
builder.setMessage(context.getResources().getString(isBlocked ? R.string.unblock_domain_text : R.string.block_domain_text,
blockable.getJid().toDomainJid()));
} else {
builder.setTitle(isBlocked ? R.string.action_unblock_contact : R.string.action_block_contact);
builder.setMessage(context.getResources().getString(isBlocked ? R.string.unblock_contact_text : R.string.block_contact_text,
blockable.getJid().toBareJid()));
}
builder.setPositiveButton(isBlocked ? R.string.unblock : R.string.block, new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface dialog, final int which) {
if (isBlocked) {
xmppConnectionService.sendUnblockRequest(blockable);
} else {
xmppConnectionService.sendBlockRequest(blockable);
}
}
});
builder.create().show();
}
}

View File

@ -0,0 +1,75 @@
package eu.siacs.conversations.ui;
import android.os.Bundle;
import android.text.Editable;
import android.view.View;
import android.widget.AdapterView;
import java.util.Collections;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.jid.Jid;
public class BlocklistActivity extends AbstractSearchableListItemActivity implements OnUpdateBlocklist {
private Account account = null;
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(final AdapterView<?> parent,
final View view,
final int position,
final long id) {
BlockContactDialog.show(parent.getContext(), xmppConnectionService,(Contact) getListItems().get(position));
return true;
}
});
}
@Override
public void onBackendConnected() {
for (final Account account : xmppConnectionService.getAccounts()) {
if (account.getJid().toString().equals(getIntent().getStringExtra("account"))) {
this.account = account;
break;
}
}
filterContacts();
}
@Override
protected void filterContacts(final String needle) {
getListItems().clear();
if (account != null) {
for (final Jid jid : account.getBlocklist()) {
final Contact contact = account.getRoster().getContact(jid);
if (contact.match(needle) && contact.isBlocked()) {
getListItems().add(contact);
}
}
Collections.sort(getListItems());
}
runOnUiThread(new Runnable() {
@Override
public void run() {
getListItemAdapter().notifyDataSetChanged();
}
});
}
@Override
public void OnUpdateBlocklist(final OnUpdateBlocklist.Status status) {
final Editable editable = getSearchEditText().getText();
if (editable != null) {
filterContacts(editable.toString());
} else {
filterContacts();
}
}
}

View File

@ -1,101 +1,33 @@
package eu.siacs.conversations.ui; package eu.siacs.conversations.ui;
import java.util.ArrayList;
import java.util.Collections;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText; import java.util.Collections;
import android.widget.ListView;
import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.ListItem; import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.ui.adapter.ListItemAdapter;
public class ChooseContactActivity extends XmppActivity {
private ListView mListView;
private ArrayList<ListItem> contacts = new ArrayList<>();
private ArrayAdapter<ListItem> mContactsAdapter;
private EditText mSearchEditText;
private TextWatcher mSearchTextWatcher = new TextWatcher() {
public class ChooseContactActivity extends AbstractSearchableListItemActivity {
@Override @Override
public void afterTextChanged(Editable editable) { public void onCreate(final Bundle savedInstanceState) {
filterContacts(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) {
}
};
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("");
filterContacts(null);
return true;
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_choose_contact); getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
mListView = (ListView) findViewById(R.id.choose_contact_list);
mListView.setFastScrollEnabled(true);
mContactsAdapter = new ListItemAdapter(this, contacts);
mListView.setAdapter(mContactsAdapter);
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override @Override
public void onItemClick(AdapterView<?> arg0, View arg1, public void onItemClick(final AdapterView<?> parent, final View view,
int position, long arg3) { final int position, final long id) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(), imm.hideSoftInputFromWindow(getSearchEditText().getWindowToken(),
InputMethodManager.HIDE_IMPLICIT_ONLY); InputMethodManager.HIDE_IMPLICIT_ONLY);
Intent request = getIntent(); final Intent request = getIntent();
Intent data = new Intent(); final Intent data = new Intent();
ListItem mListItem = contacts.get(position); final ListItem mListItem = getListItems().get(position);
data.putExtra("contact", mListItem.getJid().toString()); data.putExtra("contact", mListItem.getJid().toString());
String account = request.getStringExtra("account"); String account = request.getStringExtra("account");
if (account == null && mListItem instanceof Contact) { if (account == null && mListItem instanceof Contact) {
@ -108,38 +40,21 @@ public class ChooseContactActivity extends XmppActivity {
finish(); finish();
} }
}); });
} }
@Override protected void filterContacts(final String needle) {
public boolean onCreateOptionsMenu(Menu menu) { getListItems().clear();
getMenuInflater().inflate(R.menu.choose_contact, menu); for (final Account account : xmppConnectionService.getAccounts()) {
MenuItem menuSearchView = menu.findItem(R.id.action_search);
View mSearchView = menuSearchView.getActionView();
mSearchEditText = (EditText) mSearchView
.findViewById(R.id.search_field);
mSearchEditText.addTextChangedListener(mSearchTextWatcher);
menuSearchView.setOnActionExpandListener(mOnActionExpandListener);
return true;
}
@Override
void onBackendConnected() {
filterContacts(null);
}
protected void filterContacts(String needle) {
this.contacts.clear();
for (Account account : xmppConnectionService.getAccounts()) {
if (account.getStatus() != Account.State.DISABLED) { if (account.getStatus() != Account.State.DISABLED) {
for (Contact contact : account.getRoster().getContacts()) { for (final Contact contact : account.getRoster().getContacts()) {
if (contact.showInRoster() && contact.match(needle)) { if (contact.showInRoster() && contact.match(needle)) {
this.contacts.add(contact); getListItems().add(contact);
} }
} }
} }
} }
Collections.sort(this.contacts); Collections.sort(getListItems());
mContactsAdapter.notifyDataSetChanged(); getListItemAdapter().notifyDataSetChanged();
} }
} }

View File

@ -191,7 +191,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
@Override @Override
protected String getShareableUri() { protected String getShareableUri() {
if (mConversation != null) { if (mConversation != null) {
return "xmpp:" + mConversation.getContactJid().toBareJid().toString() + "?join"; return "xmpp:" + mConversation.getJid().toBareJid().toString() + "?join";
} else { } else {
return ""; return "";
} }
@ -202,7 +202,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
MenuItem menuItemSaveBookmark = menu.findItem(R.id.action_save_as_bookmark); MenuItem menuItemSaveBookmark = menu.findItem(R.id.action_save_as_bookmark);
MenuItem menuItemDeleteBookmark = menu.findItem(R.id.action_delete_bookmark); MenuItem menuItemDeleteBookmark = menu.findItem(R.id.action_delete_bookmark);
Account account = mConversation.getAccount(); Account account = mConversation.getAccount();
if (account.hasBookmarkFor(mConversation.getContactJid().toBareJid())) { if (account.hasBookmarkFor(mConversation.getJid().toBareJid())) {
menuItemSaveBookmark.setVisible(false); menuItemSaveBookmark.setVisible(false);
menuItemDeleteBookmark.setVisible(true); menuItemDeleteBookmark.setVisible(true);
} else { } else {
@ -263,9 +263,9 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
protected void saveAsBookmark() { protected void saveAsBookmark() {
Account account = mConversation.getAccount(); Account account = mConversation.getAccount();
Bookmark bookmark = new Bookmark(account, mConversation.getContactJid().toBareJid()); Bookmark bookmark = new Bookmark(account, mConversation.getJid().toBareJid());
if (!mConversation.getContactJid().isBareJid()) { if (!mConversation.getJid().isBareJid()) {
bookmark.setNick(mConversation.getContactJid().getResourcepart()); bookmark.setNick(mConversation.getJid().getResourcepart());
} }
bookmark.setAutojoin(true); bookmark.setAutojoin(true);
account.getBookmarks().add(bookmark); account.getBookmarks().add(bookmark);
@ -301,7 +301,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
mYourPhoto.setImageBitmap(avatarService().get( mYourPhoto.setImageBitmap(avatarService().get(
mConversation.getAccount(), getPixel(48))); mConversation.getAccount(), getPixel(48)));
setTitle(mConversation.getName()); setTitle(mConversation.getName());
mFullJid.setText(mConversation.getContactJid().toBareJid().toString()); mFullJid.setText(mConversation.getJid().toBareJid().toString());
mYourNick.setText(mConversation.getMucOptions().getActualNick()); mYourNick.setText(mConversation.getMucOptions().getActualNick());
mRoleAffiliaton = (TextView) findViewById(R.id.muc_role); mRoleAffiliaton = (TextView) findViewById(R.id.muc_role);
if (mConversation.getMucOptions().online()) { if (mConversation.getMucOptions().online()) {

View File

@ -38,10 +38,11 @@ import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate; import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate; import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;
public class ContactDetailsActivity extends XmppActivity implements OnAccountUpdate, OnRosterUpdate { public class ContactDetailsActivity extends XmppActivity implements OnAccountUpdate, OnRosterUpdate, OnUpdateBlocklist {
public static final String ACTION_VIEW_CONTACT = "view_contact"; public static final String ACTION_VIEW_CONTACT = "view_contact";
private Contact contact; private Contact contact;
@ -166,7 +167,7 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
} }
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
if (getIntent().getAction().equals(ACTION_VIEW_CONTACT)) { if (getIntent().getAction().equals(ACTION_VIEW_CONTACT)) {
try { try {
@ -188,15 +189,17 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
badge = (QuickContactBadge) findViewById(R.id.details_contact_badge); badge = (QuickContactBadge) findViewById(R.id.details_contact_badge);
keys = (LinearLayout) findViewById(R.id.details_contact_keys); keys = (LinearLayout) findViewById(R.id.details_contact_keys);
tags = (LinearLayout) findViewById(R.id.tags); tags = (LinearLayout) findViewById(R.id.tags);
if (getActionBar() != null) {
getActionBar().setHomeButtonEnabled(true); getActionBar().setHomeButtonEnabled(true);
getActionBar().setDisplayHomeAsUpEnabled(true); getActionBar().setDisplayHomeAsUpEnabled(true);
}
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
this.showDynamicTags = preferences.getBoolean("show_dynamic_tags",false); this.showDynamicTags = preferences.getBoolean("show_dynamic_tags",false);
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem menuItem) { public boolean onOptionsItemSelected(final MenuItem menuItem) {
AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setNegativeButton(getString(R.string.cancel), null); builder.setNegativeButton(getString(R.string.cancel), null);
switch (menuItem.getItemId()) { switch (menuItem.getItemId()) {
@ -363,8 +366,8 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
} else { } else {
tags.setVisibility(View.VISIBLE); tags.setVisibility(View.VISIBLE);
tags.removeAllViewsInLayout(); tags.removeAllViewsInLayout();
for(ListItem.Tag tag : tagList) { for(final ListItem.Tag tag : tagList) {
TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag,tags,false); final TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag,tags,false);
tv.setText(tag.getName()); tv.setText(tag.getName());
tv.setBackgroundColor(tag.getColor()); tv.setBackgroundColor(tag.getColor());
tags.addView(tv); tags.addView(tv);
@ -414,4 +417,15 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd
populateView(); populateView();
} }
} }
@Override
public void OnUpdateBlocklist(final Status status) {
runOnUiThread(new Runnable() {
@Override
public void run() {
populateView();
}
});
}
} }

View File

@ -15,7 +15,6 @@ import android.os.SystemClock;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.support.v4.widget.SlidingPaneLayout; import android.support.v4.widget.SlidingPaneLayout;
import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener; import android.support.v4.widget.SlidingPaneLayout.PanelSlideListener;
import android.view.KeyEvent;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
@ -32,6 +31,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Blockable;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Message; import eu.siacs.conversations.entities.Message;
@ -40,9 +40,10 @@ import eu.siacs.conversations.services.XmppConnectionService.OnConversationUpdat
import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate; import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
import eu.siacs.conversations.ui.adapter.ConversationAdapter; import eu.siacs.conversations.ui.adapter.ConversationAdapter;
import eu.siacs.conversations.utils.ExceptionHelper; import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
public class ConversationActivity extends XmppActivity implements public class ConversationActivity extends XmppActivity
OnAccountUpdate, OnConversationUpdate, OnRosterUpdate { implements OnAccountUpdate, OnConversationUpdate, OnRosterUpdate, OnUpdateBlocklist {
public static final String VIEW_CONVERSATION = "viewConversation"; public static final String VIEW_CONVERSATION = "viewConversation";
public static final String CONVERSATION = "conversationUuid"; public static final String CONVERSATION = "conversationUuid";
@ -244,7 +245,7 @@ public class ConversationActivity extends XmppActivity implements
if (conversation.getMode() == Conversation.MODE_SINGLE || useSubjectToIdentifyConference()) { if (conversation.getMode() == Conversation.MODE_SINGLE || useSubjectToIdentifyConference()) {
ab.setTitle(conversation.getName()); ab.setTitle(conversation.getName());
} else { } else {
ab.setTitle(conversation.getContactJid().toBareJid().toString()); ab.setTitle(conversation.getJid().toBareJid().toString());
} }
} else { } else {
ab.setDisplayHomeAsUpEnabled(false); ab.setDisplayHomeAsUpEnabled(false);
@ -269,17 +270,18 @@ public class ConversationActivity extends XmppActivity implements
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.conversations, menu); getMenuInflater().inflate(R.menu.conversations, menu);
MenuItem menuSecure = menu.findItem(R.id.action_security); final MenuItem menuSecure = menu.findItem(R.id.action_security);
MenuItem menuArchive = menu.findItem(R.id.action_archive); final MenuItem menuArchive = menu.findItem(R.id.action_archive);
MenuItem menuMucDetails = menu.findItem(R.id.action_muc_details); final MenuItem menuMucDetails = menu.findItem(R.id.action_muc_details);
MenuItem menuContactDetails = menu final MenuItem menuContactDetails = menu.findItem(R.id.action_contact_details);
.findItem(R.id.action_contact_details); final MenuItem menuAttach = menu.findItem(R.id.action_attach_file);
MenuItem menuAttach = menu.findItem(R.id.action_attach_file); final MenuItem menuClearHistory = menu.findItem(R.id.action_clear_history);
MenuItem menuClearHistory = menu.findItem(R.id.action_clear_history); final MenuItem menuAdd = menu.findItem(R.id.action_add);
MenuItem menuAdd = menu.findItem(R.id.action_add); final MenuItem menuInviteContact = menu.findItem(R.id.action_invite);
MenuItem menuInviteContact = menu.findItem(R.id.action_invite); final MenuItem menuMute = menu.findItem(R.id.action_mute);
MenuItem menuMute = menu.findItem(R.id.action_mute); final MenuItem menuUnmute = menu.findItem(R.id.action_unmute);
MenuItem menuUnmute = menu.findItem(R.id.action_unmute); final MenuItem menuBlock = menu.findItem(R.id.action_block);
final MenuItem menuUnblock = menu.findItem(R.id.action_unblock);
if (isConversationsOverviewVisable() if (isConversationsOverviewVisable()
&& isConversationsOverviewHideable()) { && isConversationsOverviewHideable()) {
@ -292,6 +294,8 @@ public class ConversationActivity extends XmppActivity implements
menuClearHistory.setVisible(false); menuClearHistory.setVisible(false);
menuMute.setVisible(false); menuMute.setVisible(false);
menuUnmute.setVisible(false); menuUnmute.setVisible(false);
menuBlock.setVisible(false);
menuUnblock.setVisible(false);
} else { } else {
menuAdd.setVisible(!isConversationsOverviewHideable()); menuAdd.setVisible(!isConversationsOverviewHideable());
if (this.getSelectedConversation() != null) { if (this.getSelectedConversation() != null) {
@ -302,9 +306,20 @@ public class ConversationActivity extends XmppActivity implements
if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) { if (this.getSelectedConversation().getMode() == Conversation.MODE_MULTI) {
menuContactDetails.setVisible(false); menuContactDetails.setVisible(false);
menuAttach.setVisible(false); menuAttach.setVisible(false);
menuBlock.setVisible(false);
menuUnblock.setVisible(false);
} else { } else {
menuMucDetails.setVisible(false); menuMucDetails.setVisible(false);
menuInviteContact.setTitle(R.string.conference_with); menuInviteContact.setTitle(R.string.conference_with);
if (this.getSelectedConversation().isBlocked()) {
menuBlock.setVisible(false);
} else {
menuUnblock.setVisible(false);
}
if (!this.getSelectedConversation().getAccount().getXmppConnection().getFeatures().blocking()) {
menuBlock.setVisible(false);
menuUnblock.setVisible(false);
}
} }
if (this.getSelectedConversation().isMuted()) { if (this.getSelectedConversation().isMuted()) {
menuMute.setVisible(false); menuMute.setVisible(false);
@ -410,7 +425,7 @@ public class ConversationActivity extends XmppActivity implements
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(final MenuItem item) {
if (item.getItemId() == android.R.id.home) { if (item.getItemId() == android.R.id.home) {
showConversationsOverview(); showConversationsOverview();
return true; return true;
@ -455,6 +470,12 @@ public class ConversationActivity extends XmppActivity implements
case R.id.action_unmute: case R.id.action_unmute:
unmuteConversation(getSelectedConversation()); unmuteConversation(getSelectedConversation());
break; break;
case R.id.action_block:
BlockContactDialog.show(this, xmppConnectionService, getSelectedConversation());
break;
case R.id.action_unblock:
BlockContactDialog.show(this, xmppConnectionService, getSelectedConversation());
break;
default: default:
break; break;
} }
@ -619,8 +640,8 @@ public class ConversationActivity extends XmppActivity implements
new OnClickListener() { new OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(final DialogInterface dialog, final int which) {
long till; final long till;
if (durations[which] == -1) { if (durations[which] == -1) {
till = Long.MAX_VALUE; till = Long.MAX_VALUE;
} else { } else {
@ -983,4 +1004,23 @@ public class ConversationActivity extends XmppActivity implements
} }
}); });
} }
@Override
public void OnUpdateBlocklist(Status status) {
invalidateOptionsMenu();
runOnUiThread(new Runnable() {
@Override
public void run() {
ConversationActivity.this.mConversationFragment.updateMessages();
}
});
}
public void unblockConversation(final Blockable conversation) {
xmppConnectionService.sendUnblockRequest(conversation);
}
public void blockConversation(final Blockable conversation) {
xmppConnectionService.sendBlockRequest(conversation);
}
} }

View File

@ -9,7 +9,6 @@ import android.content.Intent;
import android.content.IntentSender; import android.content.IntentSender;
import android.content.IntentSender.SendIntentException; import android.content.IntentSender.SendIntentException;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextMenu.ContextMenuInfo;
import android.view.Gravity; import android.view.Gravity;
@ -39,7 +38,6 @@ import java.util.List;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.crypto.PgpEngine;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
@ -583,12 +581,30 @@ public class ConversationFragment extends Fragment {
final ConversationActivity activity = (ConversationActivity) getActivity(); final ConversationActivity activity = (ConversationActivity) getActivity();
if (this.conversation != null) { if (this.conversation != null) {
final Contact contact = this.conversation.getContact(); final Contact contact = this.conversation.getContact();
if (this.conversation.isMuted()) { if (this.conversation.isBlocked()) {
showSnackbar(R.string.contact_blocked, R.string.unblock,
new OnClickListener() {
@Override
public void onClick(final View v) {
v.post(new Runnable() {
@Override
public void run() {
v.setVisibility(View.INVISIBLE);
}
});
if (conversation.isDomainBlocked()) {
BlockContactDialog.show(getActivity(), ((ConversationActivity) getActivity()).xmppConnectionService, conversation);
} else {
((ConversationActivity) getActivity()).unblockConversation(conversation);
}
}
});
} else if (this.conversation.isMuted()) {
showSnackbar(R.string.notifications_disabled, R.string.enable, showSnackbar(R.string.notifications_disabled, R.string.enable,
new OnClickListener() { new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(final View v) {
activity.unmuteConversation(conversation); activity.unmuteConversation(conversation);
} }
}); });
@ -787,12 +803,13 @@ public class ConversationFragment extends Fragment {
} }
} }
protected void showSnackbar(int message, int action, protected void showSnackbar(final int message, final int action,
OnClickListener clickListener) { final OnClickListener clickListener) {
snackbar.setVisibility(View.VISIBLE); snackbar.setVisibility(View.VISIBLE);
snackbar.setOnClickListener(null); snackbar.setOnClickListener(null);
snackbarMessage.setText(message); snackbarMessage.setText(message);
snackbarMessage.setOnClickListener(null); snackbarMessage.setOnClickListener(null);
snackbarAction.setVisibility(View.VISIBLE);
snackbarAction.setText(action); snackbarAction.setText(action);
snackbarAction.setOnClickListener(clickListener); snackbarAction.setOnClickListener(clickListener);
} }

View File

@ -319,12 +319,16 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
} }
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(final Menu menu) {
super.onCreateOptionsMenu(menu); super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.editaccount, menu); getMenuInflater().inflate(R.menu.editaccount, menu);
MenuItem showQrCode = menu.findItem(R.id.action_show_qr_code); final MenuItem showQrCode = menu.findItem(R.id.action_show_qr_code);
final MenuItem showBlocklist = menu.findItem(R.id.action_show_block_list);
if (mAccount == null) { if (mAccount == null) {
showQrCode.setVisible(false); showQrCode.setVisible(false);
showBlocklist.setVisible(false);
} else if (!mAccount.getXmppConnection().getFeatures().blocking()) {
showBlocklist.setVisible(false);
} }
return true; return true;
} }
@ -340,25 +344,31 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
} }
if (this.jidToEdit != null) { if (this.jidToEdit != null) {
this.mRegisterNew.setVisibility(View.GONE); this.mRegisterNew.setVisibility(View.GONE);
if (getActionBar() != null) {
getActionBar().setTitle(getString(R.string.account_details)); getActionBar().setTitle(getString(R.string.account_details));
}
} else { } else {
this.mAvatar.setVisibility(View.GONE); this.mAvatar.setVisibility(View.GONE);
if (getActionBar() != null) {
getActionBar().setTitle(R.string.action_add_account); getActionBar().setTitle(R.string.action_add_account);
} }
} }
} }
}
@Override @Override
protected void onBackendConnected() { protected void onBackendConnected() {
KnownHostsAdapter mKnownHostsAdapter = new KnownHostsAdapter(this, final KnownHostsAdapter mKnownHostsAdapter = new KnownHostsAdapter(this,
android.R.layout.simple_list_item_1, android.R.layout.simple_list_item_1,
xmppConnectionService.getKnownHosts()); xmppConnectionService.getKnownHosts());
if (this.jidToEdit != null) { if (this.jidToEdit != null) {
this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit); this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit);
updateAccountInformation(); updateAccountInformation();
} else if (this.xmppConnectionService.getAccounts().size() == 0) { } else if (this.xmppConnectionService.getAccounts().size() == 0) {
if (getActionBar() != null) {
getActionBar().setDisplayHomeAsUpEnabled(false); getActionBar().setDisplayHomeAsUpEnabled(false);
getActionBar().setDisplayShowHomeEnabled(false); getActionBar().setDisplayShowHomeEnabled(false);
}
this.mCancelButton.setEnabled(false); this.mCancelButton.setEnabled(false);
this.mCancelButton.setTextColor(getSecondaryTextColor()); this.mCancelButton.setTextColor(getSecondaryTextColor());
} }
@ -366,6 +376,18 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
updateSaveButton(); updateSaveButton();
} }
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case R.id.action_show_block_list:
final Intent intent = new Intent(this, BlocklistActivity.class);
intent.putExtra("account", mAccount.getJid().toString());
startActivity(intent);
break;
}
return super.onOptionsItemSelected(item);
}
private void updateAccountInformation() { private void updateAccountInformation() {
this.mAccountJid.setText(this.mAccount.getJid().toBareJid().toString()); this.mAccountJid.setText(this.mAccount.getJid().toBareJid().toString());
this.mPassword.setText(this.mAccount.getPassword()); this.mPassword.setText(this.mAccount.getPassword());
@ -387,7 +409,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate
this.mSessionEst.setText(UIHelper.readableTimeDifferenceFull( this.mSessionEst.setText(UIHelper.readableTimeDifferenceFull(
getApplicationContext(), this.mAccount.getXmppConnection() getApplicationContext(), this.mAccount.getXmppConnection()
.getLastSessionEstablished())); .getLastSessionEstablished()));
Features features = this.mAccount.getXmppConnection().getFeatures(); final Features features = this.mAccount.getXmppConnection().getFeatures();
if (features.carbons()) { if (features.carbons()) {
this.mServerInfoCarbons.setText(R.string.server_info_available); this.mServerInfoCarbons.setText(R.string.server_info_available);
} else { } else {

View File

@ -117,10 +117,10 @@ public class ShareWithActivity extends XmppActivity {
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.action_add: case R.id.action_add:
Intent intent = new Intent(getApplicationContext(), final Intent intent = new Intent(getApplicationContext(),
ChooseContactActivity.class); ChooseContactActivity.class);
startActivityForResult(intent, REQUEST_START_NEW_CONVERSATION); startActivityForResult(intent, REQUEST_START_NEW_CONVERSATION);
return true; return true;

View File

@ -53,19 +53,21 @@ import java.util.List;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Blockable;
import eu.siacs.conversations.entities.Bookmark; import eu.siacs.conversations.entities.Bookmark;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.ListItem; import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate; import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
import eu.siacs.conversations.ui.adapter.KnownHostsAdapter; import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
import eu.siacs.conversations.ui.adapter.ListItemAdapter; import eu.siacs.conversations.ui.adapter.ListItemAdapter;
import eu.siacs.conversations.utils.Validator; import eu.siacs.conversations.utils.Validator;
import eu.siacs.conversations.utils.XmppUri;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;
public class StartConversationActivity extends XmppActivity implements OnRosterUpdate { public class StartConversationActivity extends XmppActivity implements OnRosterUpdate, OnUpdateBlocklist {
public int conference_context_id; public int conference_context_id;
public int contact_context_id; public int contact_context_id;
@ -133,7 +135,9 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
private ViewPager.SimpleOnPageChangeListener mOnPageChangeListener = new ViewPager.SimpleOnPageChangeListener() { private ViewPager.SimpleOnPageChangeListener mOnPageChangeListener = new ViewPager.SimpleOnPageChangeListener() {
@Override @Override
public void onPageSelected(int position) { public void onPageSelected(int position) {
if (getActionBar() != null) {
getActionBar().setSelectedNavigationItem(position); getActionBar().setSelectedNavigationItem(position);
}
onTabChanged(); onTabChanged();
} }
}; };
@ -270,10 +274,15 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
switchToContactDetails(contact); switchToContactDetails(contact);
} }
protected void toggleContactBlock() {
final int position = contact_context_id;
BlockContactDialog.show(this, xmppConnectionService, (Contact)contacts.get(position));
}
protected void deleteContact() { protected void deleteContact() {
int position = contact_context_id; final int position = contact_context_id;
final Contact contact = (Contact) contacts.get(position); final Contact contact = (Contact) contacts.get(position);
AlertDialog.Builder builder = new AlertDialog.Builder(this); final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setNegativeButton(R.string.cancel, null); builder.setNegativeButton(R.string.cancel, null);
builder.setTitle(R.string.action_delete_contact); builder.setTitle(R.string.action_delete_contact);
builder.setMessage(getString(R.string.remove_contact_text, builder.setMessage(getString(R.string.remove_contact_text,
@ -287,7 +296,6 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
} }
}); });
builder.create().show(); builder.create().show();
} }
protected void deleteConference() { protected void deleteConference() {
@ -685,16 +693,29 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
invalidateOptionsMenu(); invalidateOptionsMenu();
} }
@Override
public void OnUpdateBlocklist(final Status status) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (mSearchEditText != null) {
filter(mSearchEditText.getText().toString());
}
}
});
}
public static class MyListFragment extends ListFragment { public static class MyListFragment extends ListFragment {
private AdapterView.OnItemClickListener mOnItemClickListener; private AdapterView.OnItemClickListener mOnItemClickListener;
private int mResContextMenu; private int mResContextMenu;
public void setContextMenu(int res) { public void setContextMenu(final int res) {
this.mResContextMenu = res; this.mResContextMenu = res;
} }
@Override @Override
public void onListItemClick(ListView l, View v, int position, long id) { public void onListItemClick(final ListView l, final View v, final int position, final long id) {
if (mOnItemClickListener != null) { if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(l, v, position, id); mOnItemClickListener.onItemClick(l, v, position, id);
} }
@ -705,28 +726,38 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
} }
@Override @Override
public void onViewCreated(View view, Bundle savedInstanceState) { public void onViewCreated(final View view, final Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
registerForContextMenu(getListView()); registerForContextMenu(getListView());
getListView().setFastScrollEnabled(true); getListView().setFastScrollEnabled(true);
} }
@Override @Override
public void onCreateContextMenu(ContextMenu menu, View v, public void onCreateContextMenu(final ContextMenu menu, final View v,
ContextMenuInfo menuInfo) { final ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo); super.onCreateContextMenu(menu, v, menuInfo);
StartConversationActivity activity = (StartConversationActivity) getActivity(); final StartConversationActivity activity = (StartConversationActivity) getActivity();
activity.getMenuInflater().inflate(mResContextMenu, menu); activity.getMenuInflater().inflate(mResContextMenu, menu);
AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo; final AdapterView.AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
if (mResContextMenu == R.menu.conference_context) { if (mResContextMenu == R.menu.conference_context) {
activity.conference_context_id = acmi.position; activity.conference_context_id = acmi.position;
} else { } else {
activity.contact_context_id = acmi.position; activity.contact_context_id = acmi.position;
final Blockable contact = (Contact) activity.contacts.get(acmi.position);
final MenuItem blockUnblockItem = menu.findItem(R.id.context_contact_block_unblock);
if (blockUnblockItem != null) {
if (contact.isBlocked()) {
blockUnblockItem.setTitle(R.string.unblock_contact);
} else {
blockUnblockItem.setTitle(R.string.block_contact);
}
}
} }
} }
@Override @Override
public boolean onContextItemSelected(MenuItem item) { public boolean onContextItemSelected(final MenuItem item) {
StartConversationActivity activity = (StartConversationActivity) getActivity(); StartConversationActivity activity = (StartConversationActivity) getActivity();
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.context_start_conversation: case R.id.context_start_conversation:
@ -735,6 +766,9 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
case R.id.context_contact_details: case R.id.context_contact_details:
activity.openDetailsForContact(); activity.openDetailsForContact();
break; break;
case R.id.context_contact_block_unblock:
activity.toggleContactBlock();
break;
case R.id.context_delete_contact: case R.id.context_delete_contact:
activity.deleteContact(); activity.deleteContact();
break; break;
@ -750,11 +784,11 @@ public class StartConversationActivity extends XmppActivity implements OnRosterU
private class Invite extends XmppUri { private class Invite extends XmppUri {
public Invite(Uri uri) { public Invite(final Uri uri) {
super(uri); super(uri);
} }
public Invite(String uri) { public Invite(final String uri) {
super(uri); super(uri);
} }

View File

@ -71,6 +71,7 @@ import eu.siacs.conversations.services.AvatarService;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinder; import eu.siacs.conversations.services.XmppConnectionService.XmppConnectionBinder;
import eu.siacs.conversations.utils.ExceptionHelper; import eu.siacs.conversations.utils.ExceptionHelper;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;
@ -245,6 +246,9 @@ public abstract class XmppActivity extends Activity {
if (this instanceof XmppConnectionService.OnMucRosterUpdate) { if (this instanceof XmppConnectionService.OnMucRosterUpdate) {
this.xmppConnectionService.setOnMucRosterUpdateListener((XmppConnectionService.OnMucRosterUpdate) this); this.xmppConnectionService.setOnMucRosterUpdateListener((XmppConnectionService.OnMucRosterUpdate) this);
} }
if (this instanceof OnUpdateBlocklist) {
this.xmppConnectionService.setOnUpdateBlocklistListener((OnUpdateBlocklist) this);
}
} }
protected void unregisterListeners() { protected void unregisterListeners() {
@ -260,9 +264,13 @@ public abstract class XmppActivity extends Activity {
if (this instanceof XmppConnectionService.OnMucRosterUpdate) { if (this instanceof XmppConnectionService.OnMucRosterUpdate) {
this.xmppConnectionService.removeOnMucRosterUpdateListener(); this.xmppConnectionService.removeOnMucRosterUpdateListener();
} }
if (this instanceof OnUpdateBlocklist) {
this.xmppConnectionService.removeOnUpdateBlocklistListener();
}
} }
public boolean onOptionsItemSelected(MenuItem item) { @Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.action_settings: case R.id.action_settings:
startActivity(new Intent(this, SettingsActivity.class)); startActivity(new Intent(this, SettingsActivity.class));
@ -420,7 +428,7 @@ public abstract class XmppActivity extends Activity {
} }
protected void showAddToRosterDialog(final Conversation conversation) { protected void showAddToRosterDialog(final Conversation conversation) {
final Jid jid = conversation.getContactJid(); final Jid jid = conversation.getJid();
AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(jid.toString()); builder.setTitle(jid.toString());
builder.setMessage(getString(R.string.not_in_roster)); builder.setMessage(getString(R.string.not_in_roster));
@ -430,7 +438,7 @@ public abstract class XmppActivity extends Activity {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
final Jid jid = conversation.getContactJid(); final Jid jid = conversation.getJid();
Account account = conversation.getAccount(); Account account = conversation.getAccount();
Contact contact = account.getRoster().getContact(jid); Contact contact = account.getRoster().getContact(jid);
xmppConnectionService.createContact(contact); xmppConnectionService.createContact(contact);
@ -613,7 +621,7 @@ public abstract class XmppActivity extends Activity {
xmppConnectionService.invite(conversation, jid); xmppConnectionService.invite(conversation, jid);
} else { } else {
List<Jid> jids = new ArrayList<Jid>(); List<Jid> jids = new ArrayList<Jid>();
jids.add(conversation.getContactJid().toBareJid()); jids.add(conversation.getJid().toBareJid());
jids.add(jid); jids.add(jid);
xmppConnectionService.createAdhocConference(conversation.getAccount(), jids, adhocCallback); xmppConnectionService.createAdhocConference(conversation.getAccount(), jids, adhocCallback);
} }

View File

@ -58,7 +58,7 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
|| activity.useSubjectToIdentifyConference()) { || activity.useSubjectToIdentifyConference()) {
convName.setText(conversation.getName()); convName.setText(conversation.getName());
} else { } else {
convName.setText(conversation.getContactJid().toBareJid().toString()); convName.setText(conversation.getJid().toBareJid().toString());
} }
TextView mLastMessage = (TextView) view TextView mLastMessage = (TextView) view
.findViewById(R.id.conversation_lastmsg); .findViewById(R.id.conversation_lastmsg);

View File

@ -0,0 +1,5 @@
package eu.siacs.conversations.utils;
public final class Xmlns {
public static final String BLOCKING = "urn:xmpp:blocking";
}

View File

@ -5,7 +5,6 @@ import android.net.Uri;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;

View File

@ -67,11 +67,11 @@ public class Element {
return null; return null;
} }
public boolean hasChild(String name) { public boolean hasChild(final String name) {
return findChild(name) != null; return findChild(name) != null;
} }
public boolean hasChild(String name, String xmlns) { public boolean hasChild(final String name, final String xmlns) {
return findChild(name, xmlns) != null; return findChild(name, xmlns) != null;
} }

View File

@ -3,5 +3,5 @@ package eu.siacs.conversations.xmpp;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
public interface OnContactStatusChanged { public interface OnContactStatusChanged {
public void onContactStatusChanged(Contact contact, boolean online); public void onContactStatusChanged(final Contact contact, final boolean online);
} }

View File

@ -0,0 +1,13 @@
package eu.siacs.conversations.xmpp;
public interface OnUpdateBlocklist {
// Use an enum instead of a boolean to make sure we don't run into the boolean trap
// (`onUpdateBlocklist(true)' doesn't read well, and could be confusing).
public static enum Status {
BLOCKED,
UNBLOCKED
}
@SuppressWarnings("MethodNameSameAsClassName")
public void OnUpdateBlocklist(final Status status);
}

View File

@ -30,10 +30,12 @@ import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HostnameVerifier;
@ -48,8 +50,10 @@ import eu.siacs.conversations.crypto.sasl.Plain;
import eu.siacs.conversations.crypto.sasl.SaslMechanism; import eu.siacs.conversations.crypto.sasl.SaslMechanism;
import eu.siacs.conversations.crypto.sasl.ScramSha1; import eu.siacs.conversations.crypto.sasl.ScramSha1;
import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.generator.IqGenerator;
import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.utils.DNSHelper; import eu.siacs.conversations.utils.DNSHelper;
import eu.siacs.conversations.utils.Xmlns;
import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Tag; import eu.siacs.conversations.xml.Tag;
import eu.siacs.conversations.xml.TagWriter; import eu.siacs.conversations.xml.TagWriter;
@ -76,19 +80,19 @@ public class XmppConnection implements Runnable {
private static final int PACKET_PRESENCE = 2; private static final int PACKET_PRESENCE = 2;
private final Context applicationContext; private final Context applicationContext;
protected Account account; protected Account account;
private WakeLock wakeLock; private final WakeLock wakeLock;
private Socket socket; private Socket socket;
private XmlReader tagReader; private XmlReader tagReader;
private TagWriter tagWriter; private TagWriter tagWriter;
private Features features = new Features(this); private final Features features = new Features(this);
private boolean shouldBind = true; private boolean shouldBind = true;
private boolean shouldAuthenticate = true; private boolean shouldAuthenticate = true;
private Element streamFeatures; private Element streamFeatures;
private HashMap<String, List<String>> disco = new HashMap<>(); private final HashMap<String, List<String>> disco = new HashMap<>();
private String streamId = null; private String streamId = null;
private int smVersion = 3; private int smVersion = 3;
private SparseArray<String> messageReceipts = new SparseArray<>(); private final SparseArray<String> messageReceipts = new SparseArray<>();
private boolean enabledEncryption = false; private boolean enabledEncryption = false;
private boolean enabledCarbons = false; private boolean enabledCarbons = false;
@ -100,20 +104,20 @@ public class XmppConnection implements Runnable {
private long lastConnect = 0; private long lastConnect = 0;
private long lastSessionStarted = 0; private long lastSessionStarted = 0;
private int attempt = 0; private int attempt = 0;
private Hashtable<String, PacketReceived> packetCallbacks = new Hashtable<>(); private final Map<String, PacketReceived> packetCallbacks = new Hashtable<>();
private OnPresencePacketReceived presenceListener = null; private OnPresencePacketReceived presenceListener = null;
private OnJinglePacketReceived jingleListener = null; private OnJinglePacketReceived jingleListener = null;
private OnIqPacketReceived unregisteredIqListener = null; private OnIqPacketReceived unregisteredIqListener = null;
private OnMessagePacketReceived messageListener = null; private OnMessagePacketReceived messageListener = null;
private OnStatusChanged statusListener = null; private OnStatusChanged statusListener = null;
private OnBindListener bindListener = null; private OnBindListener bindListener = null;
private ArrayList<OnAdvancedStreamFeaturesLoaded> advancedStreamFeaturesLoadedListeners = new ArrayList<>(); private final ArrayList<OnAdvancedStreamFeaturesLoaded> advancedStreamFeaturesLoadedListeners = new ArrayList<>();
private OnMessageAcknowledged acknowledgedListener = null; private OnMessageAcknowledged acknowledgedListener = null;
private XmppConnectionService mXmppConnectionService = null; private XmppConnectionService mXmppConnectionService = null;
private SaslMechanism saslMechanism; private SaslMechanism saslMechanism;
public XmppConnection(Account account, XmppConnectionService service) { public XmppConnection(final Account account, final XmppConnectionService service) {
this.account = account; this.account = account;
this.wakeLock = service.getPowerManager().newWakeLock( this.wakeLock = service.getPowerManager().newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, account.getJid().toBareJid().toString()); PowerManager.PARTIAL_WAKE_LOCK, account.getJid().toBareJid().toString());
@ -153,15 +157,15 @@ public class XmppConnection implements Runnable {
tagWriter = new TagWriter(); tagWriter = new TagWriter();
packetCallbacks.clear(); packetCallbacks.clear();
this.changeStatus(Account.State.CONNECTING); this.changeStatus(Account.State.CONNECTING);
Bundle result = DNSHelper.getSRVRecord(account.getServer()); final Bundle result = DNSHelper.getSRVRecord(account.getServer());
ArrayList<Parcelable> values = result.getParcelableArrayList("values"); final ArrayList<Parcelable> values = result.getParcelableArrayList("values");
if ("timeout".equals(result.getString("error"))) { if ("timeout".equals(result.getString("error"))) {
throw new IOException("timeout in dns"); throw new IOException("timeout in dns");
} else if (values != null) { } else if (values != null) {
int i = 0; int i = 0;
boolean socketError = true; boolean socketError = true;
while (socketError && values.size() > i) { while (socketError && values.size() > i) {
Bundle namePort = (Bundle) values.get(i); final Bundle namePort = (Bundle) values.get(i);
try { try {
String srvRecordServer; String srvRecordServer;
try { try {
@ -170,9 +174,9 @@ public class XmppConnection implements Runnable {
// TODO: Handle me?` // TODO: Handle me?`
srvRecordServer = ""; srvRecordServer = "";
} }
int srvRecordPort = namePort.getInt("port"); final int srvRecordPort = namePort.getInt("port");
String srvIpServer = namePort.getString("ip"); final String srvIpServer = namePort.getString("ip");
InetSocketAddress addr; final InetSocketAddress addr;
if (srvIpServer != null) { if (srvIpServer != null) {
addr = new InetSocketAddress(srvIpServer, srvRecordPort); addr = new InetSocketAddress(srvIpServer, srvRecordPort);
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
@ -187,10 +191,10 @@ public class XmppConnection implements Runnable {
socket = new Socket(); socket = new Socket();
socket.connect(addr, 20000); socket.connect(addr, 20000);
socketError = false; socketError = false;
} catch (UnknownHostException e) { } catch (final UnknownHostException e) {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
i++; i++;
} catch (IOException e) { } catch (final IOException e) {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
i++; i++;
} }
@ -204,9 +208,9 @@ public class XmppConnection implements Runnable {
} else { } else {
throw new IOException("timeout in dns"); throw new IOException("timeout in dns");
} }
OutputStream out = socket.getOutputStream(); final OutputStream out = socket.getOutputStream();
tagWriter.setOutputStream(out); tagWriter.setOutputStream(out);
InputStream in = socket.getInputStream(); final InputStream in = socket.getInputStream();
tagReader.setInputStream(in); tagReader.setInputStream(in);
tagWriter.beginDocument(); tagWriter.beginDocument();
sendStartStream(); sendStartStream();
@ -222,14 +226,9 @@ public class XmppConnection implements Runnable {
if (socket.isConnected()) { if (socket.isConnected()) {
socket.close(); socket.close();
} }
} catch (UnknownHostException e) { } catch (final UnknownHostException | ConnectException e) {
this.changeStatus(Account.State.SERVER_NOT_FOUND); this.changeStatus(Account.State.SERVER_NOT_FOUND);
} catch (final ConnectException e) { } catch (final IOException | XmlPullParserException | NoSuchAlgorithmException e) {
this.changeStatus(Account.State.SERVER_NOT_FOUND);
} catch (final IOException | XmlPullParserException e) {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
this.changeStatus(Account.State.OFFLINE);
} catch (NoSuchAlgorithmException e) {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage()); Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": " + e.getMessage());
this.changeStatus(Account.State.OFFLINE); this.changeStatus(Account.State.OFFLINE);
} finally { } finally {
@ -289,7 +288,7 @@ public class XmppConnection implements Runnable {
} }
tagWriter.writeElement(response); tagWriter.writeElement(response);
} else if (nextTag.isStart("enabled")) { } else if (nextTag.isStart("enabled")) {
Element enabled = tagReader.readElement(nextTag); final Element enabled = tagReader.readElement(nextTag);
if ("true".equals(enabled.getAttribute("resume"))) { if ("true".equals(enabled.getAttribute("resume"))) {
this.streamId = enabled.getAttribute("id"); this.streamId = enabled.getAttribute("id");
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
@ -301,14 +300,14 @@ public class XmppConnection implements Runnable {
} }
this.lastSessionStarted = SystemClock.elapsedRealtime(); this.lastSessionStarted = SystemClock.elapsedRealtime();
this.stanzasReceived = 0; this.stanzasReceived = 0;
RequestPacket r = new RequestPacket(smVersion); final RequestPacket r = new RequestPacket(smVersion);
tagWriter.writeStanzaAsync(r); tagWriter.writeStanzaAsync(r);
} else if (nextTag.isStart("resumed")) { } else if (nextTag.isStart("resumed")) {
lastPaketReceived = SystemClock.elapsedRealtime(); lastPaketReceived = SystemClock.elapsedRealtime();
Element resumed = tagReader.readElement(nextTag); final Element resumed = tagReader.readElement(nextTag);
String h = resumed.getAttribute("h"); final String h = resumed.getAttribute("h");
try { try {
int serverCount = Integer.parseInt(h); final int serverCount = Integer.parseInt(h);
if (serverCount != stanzasSent) { if (serverCount != stanzasSent) {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
+ ": session resumed with lost packages"); + ": session resumed with lost packages");
@ -327,20 +326,19 @@ public class XmppConnection implements Runnable {
} }
messageReceipts.clear(); messageReceipts.clear();
} catch (final NumberFormatException ignored) { } catch (final NumberFormatException ignored) {
} }
sendServiceDiscoveryInfo(account.getServer()); sendServiceDiscoveryInfo(account.getServer());
sendServiceDiscoveryItems(account.getServer()); sendServiceDiscoveryItems(account.getServer());
sendInitialPing(); sendInitialPing();
} else if (nextTag.isStart("r")) { } else if (nextTag.isStart("r")) {
tagReader.readElement(nextTag); tagReader.readElement(nextTag);
AckPacket ack = new AckPacket(this.stanzasReceived, smVersion); final AckPacket ack = new AckPacket(this.stanzasReceived, smVersion);
tagWriter.writeStanzaAsync(ack); tagWriter.writeStanzaAsync(ack);
} else if (nextTag.isStart("a")) { } else if (nextTag.isStart("a")) {
Element ack = tagReader.readElement(nextTag); final Element ack = tagReader.readElement(nextTag);
lastPaketReceived = SystemClock.elapsedRealtime(); lastPaketReceived = SystemClock.elapsedRealtime();
int serverSequence = Integer.parseInt(ack.getAttribute("h")); final int serverSequence = Integer.parseInt(ack.getAttribute("h"));
String msgId = this.messageReceipts.get(serverSequence); final String msgId = this.messageReceipts.get(serverSequence);
if (msgId != null) { if (msgId != null) {
if (this.acknowledgedListener != null) { if (this.acknowledgedListener != null) {
this.acknowledgedListener.onMessageAcknowledged( this.acknowledgedListener.onMessageAcknowledged(
@ -374,13 +372,12 @@ public class XmppConnection implements Runnable {
private void sendInitialPing() { private void sendInitialPing() {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": sending intial ping"); Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": sending intial ping");
IqPacket iq = new IqPacket(IqPacket.TYPE_GET); final IqPacket iq = new IqPacket(IqPacket.TYPE_GET);
iq.setFrom(account.getJid()); iq.setFrom(account.getJid());
iq.addChild("ping", "urn:xmpp:ping"); iq.addChild("ping", "urn:xmpp:ping");
this.sendIqPacket(iq, new OnIqPacketReceived() { this.sendIqPacket(iq, new OnIqPacketReceived() {
@Override @Override
public void onIqPacketReceived(Account account, IqPacket packet) { public void onIqPacketReceived(final Account account, final IqPacket packet) {
Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() Log.d(Config.LOGTAG, account.getJid().toBareJid().toString()
+ ": online with resource " + account.getResource()); + ": online with resource " + account.getResource());
changeStatus(Account.State.ONLINE); changeStatus(Account.State.ONLINE);
@ -388,7 +385,7 @@ public class XmppConnection implements Runnable {
}); });
} }
private Element processPacket(Tag currentTag, int packetType) private Element processPacket(final Tag currentTag, final int packetType)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
Element element; Element element;
switch (packetType) { switch (packetType) {
@ -411,8 +408,8 @@ public class XmppConnection implements Runnable {
} }
while (!nextTag.isEnd(element.getName())) { while (!nextTag.isEnd(element.getName())) {
if (!nextTag.isNo()) { if (!nextTag.isNo()) {
Element child = tagReader.readElement(nextTag); final Element child = tagReader.readElement(nextTag);
String type = currentTag.getAttribute("type"); final String type = currentTag.getAttribute("type");
if (packetType == PACKET_IQ if (packetType == PACKET_IQ
&& "jingle".equals(child.getName()) && "jingle".equals(child.getName())
&& ("set".equalsIgnoreCase(type) || "get" && ("set".equalsIgnoreCase(type) || "get"
@ -432,9 +429,9 @@ public class XmppConnection implements Runnable {
return element; return element;
} }
private void processIq(Tag currentTag) throws XmlPullParserException, private void processIq(final Tag currentTag) throws XmlPullParserException,
IOException { IOException {
IqPacket packet = (IqPacket) processPacket(currentTag, PACKET_IQ); final IqPacket packet = (IqPacket) processPacket(currentTag, PACKET_IQ);
if (packet.getId() == null) { if (packet.getId() == null) {
return; // an iq packet without id is definitely invalid return; // an iq packet without id is definitely invalid
@ -461,11 +458,11 @@ public class XmppConnection implements Runnable {
} }
} }
private void processMessage(Tag currentTag) throws XmlPullParserException, private void processMessage(final Tag currentTag) throws XmlPullParserException,
IOException { IOException {
MessagePacket packet = (MessagePacket) processPacket(currentTag, final MessagePacket packet = (MessagePacket) processPacket(currentTag,
PACKET_MESSAGE); PACKET_MESSAGE);
String id = packet.getAttribute("id"); final String id = packet.getAttribute("id");
if ((id != null) && (packetCallbacks.containsKey(id))) { if ((id != null) && (packetCallbacks.containsKey(id))) {
if (packetCallbacks.get(id) instanceof OnMessagePacketReceived) { if (packetCallbacks.get(id) instanceof OnMessagePacketReceived) {
((OnMessagePacketReceived) packetCallbacks.get(id)) ((OnMessagePacketReceived) packetCallbacks.get(id))
@ -477,11 +474,11 @@ public class XmppConnection implements Runnable {
} }
} }
private void processPresence(Tag currentTag) throws XmlPullParserException, private void processPresence(final Tag currentTag) throws XmlPullParserException,
IOException { IOException {
PresencePacket packet = (PresencePacket) processPacket(currentTag, PresencePacket packet = (PresencePacket) processPacket(currentTag,
PACKET_PRESENCE); PACKET_PRESENCE);
String id = packet.getAttribute("id"); final String id = packet.getAttribute("id");
if ((id != null) && (packetCallbacks.containsKey(id))) { if ((id != null) && (packetCallbacks.containsKey(id))) {
if (packetCallbacks.get(id) instanceof OnPresencePacketReceived) { if (packetCallbacks.get(id) instanceof OnPresencePacketReceived) {
((OnPresencePacketReceived) packetCallbacks.get(id)) ((OnPresencePacketReceived) packetCallbacks.get(id))
@ -494,7 +491,7 @@ public class XmppConnection implements Runnable {
} }
private void sendStartTLS() throws IOException { private void sendStartTLS() throws IOException {
Tag startTLS = Tag.empty("starttls"); final Tag startTLS = Tag.empty("starttls");
startTLS.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-tls"); startTLS.setAttribute("xmlns", "urn:ietf:params:xml:ns:xmpp-tls");
tagWriter.writeTag(startTLS); tagWriter.writeTag(startTLS);
} }
@ -512,11 +509,11 @@ public class XmppConnection implements Runnable {
IOException { IOException {
tagReader.readTag(); tagReader.readTag();
try { try {
SSLContext sc = SSLContext.getInstance("TLS"); final SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, sc.init(null,
new X509TrustManager[]{this.mXmppConnectionService.getMemorizingTrustManager()}, new X509TrustManager[]{this.mXmppConnectionService.getMemorizingTrustManager()},
mXmppConnectionService.getRNG()); mXmppConnectionService.getRNG());
SSLSocketFactory factory = sc.getSocketFactory(); final SSLSocketFactory factory = sc.getSocketFactory();
if (factory == null) { if (factory == null) {
throw new IOException("SSLSocketFactory was null"); throw new IOException("SSLSocketFactory was null");
@ -541,7 +538,7 @@ public class XmppConnection implements Runnable {
if (enableLegacySSL()) { if (enableLegacySSL()) {
supportProtocols = sslSocket.getSupportedProtocols(); supportProtocols = sslSocket.getSupportedProtocols();
} else { } else {
final List<String> supportedProtocols = new LinkedList<>( final Collection<String> supportedProtocols = new LinkedList<>(
Arrays.asList(sslSocket.getSupportedProtocols())); Arrays.asList(sslSocket.getSupportedProtocols()));
supportedProtocols.remove("SSLv3"); supportedProtocols.remove("SSLv3");
supportProtocols = new String[supportedProtocols.size()]; supportProtocols = new String[supportedProtocols.size()];
@ -569,7 +566,7 @@ public class XmppConnection implements Runnable {
} }
} }
private void processStreamFeatures(Tag currentTag) private void processStreamFeatures(final Tag currentTag)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
this.streamFeatures = tagReader.readElement(currentTag); this.streamFeatures = tagReader.readElement(currentTag);
if (this.streamFeatures.hasChild("starttls") && !enabledEncryption) { if (this.streamFeatures.hasChild("starttls") && !enabledEncryption) {
@ -618,7 +615,7 @@ public class XmppConnection implements Runnable {
} else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:" } else if (this.streamFeatures.hasChild("sm", "urn:xmpp:sm:"
+ smVersion) + smVersion)
&& streamId != null) { && streamId != null) {
ResumePacket resume = new ResumePacket(this.streamId, final ResumePacket resume = new ResumePacket(this.streamId,
stanzasReceived, smVersion); stanzasReceived, smVersion);
this.tagWriter.writeStanzaAsync(resume); this.tagWriter.writeStanzaAsync(resume);
} else if (this.streamFeatures.hasChild("bind") && shouldBind) { } else if (this.streamFeatures.hasChild("bind") && shouldBind) {
@ -629,38 +626,37 @@ public class XmppConnection implements Runnable {
} }
} }
private List<String> extractMechanisms(Element stream) { private List<String> extractMechanisms(final Element stream) {
ArrayList<String> mechanisms = new ArrayList<>(stream final ArrayList<String> mechanisms = new ArrayList<>(stream
.getChildren().size()); .getChildren().size());
for (Element child : stream.getChildren()) { for (final Element child : stream.getChildren()) {
mechanisms.add(child.getContent()); mechanisms.add(child.getContent());
} }
return mechanisms; return mechanisms;
} }
private void sendRegistryRequest() { private void sendRegistryRequest() {
IqPacket register = new IqPacket(IqPacket.TYPE_GET); final IqPacket register = new IqPacket(IqPacket.TYPE_GET);
register.query("jabber:iq:register"); register.query("jabber:iq:register");
register.setTo(account.getServer()); register.setTo(account.getServer());
sendIqPacket(register, new OnIqPacketReceived() { sendIqPacket(register, new OnIqPacketReceived() {
@Override @Override
public void onIqPacketReceived(Account account, IqPacket packet) { public void onIqPacketReceived(final Account account, final IqPacket packet) {
Element instructions = packet.query().findChild("instructions"); final Element instructions = packet.query().findChild("instructions");
if (packet.query().hasChild("username") if (packet.query().hasChild("username")
&& (packet.query().hasChild("password"))) { && (packet.query().hasChild("password"))) {
IqPacket register = new IqPacket(IqPacket.TYPE_SET); final IqPacket register = new IqPacket(IqPacket.TYPE_SET);
Element username = new Element("username") final Element username = new Element("username")
.setContent(account.getUsername()); .setContent(account.getUsername());
Element password = new Element("password") final Element password = new Element("password")
.setContent(account.getPassword()); .setContent(account.getPassword());
register.query("jabber:iq:register").addChild(username); register.query("jabber:iq:register").addChild(username);
register.query().addChild(password); register.query().addChild(password);
sendIqPacket(register, new OnIqPacketReceived() { sendIqPacket(register, new OnIqPacketReceived() {
@Override @Override
public void onIqPacketReceived(Account account, public void onIqPacketReceived(final Account account, final IqPacket packet) {
IqPacket packet) {
if (packet.getType() == IqPacket.TYPE_RESULT) { if (packet.getType() == IqPacket.TYPE_RESULT) {
account.setOption(Account.OPTION_REGISTER, account.setOption(Account.OPTION_REGISTER,
false); false);
@ -687,14 +683,14 @@ public class XmppConnection implements Runnable {
}); });
} }
private void sendBindRequest() throws IOException { private void sendBindRequest() {
IqPacket iq = new IqPacket(IqPacket.TYPE_SET); final IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
iq.addChild("bind", "urn:ietf:params:xml:ns:xmpp-bind") iq.addChild("bind", "urn:ietf:params:xml:ns:xmpp-bind")
.addChild("resource").setContent(account.getResource()); .addChild("resource").setContent(account.getResource());
this.sendUnboundIqPacket(iq, new OnIqPacketReceived() { this.sendUnboundIqPacket(iq, new OnIqPacketReceived() {
@Override @Override
public void onIqPacketReceived(Account account, IqPacket packet) { public void onIqPacketReceived(final Account account, final IqPacket packet) {
Element bind = packet.findChild("bind"); final Element bind = packet.findChild("bind");
if (bind != null) { if (bind != null) {
final Element jid = bind.findChild("jid"); final Element jid = bind.findChild("jid");
if (jid != null && jid.getContent() != null) { if (jid != null && jid.getContent() != null) {
@ -705,14 +701,14 @@ public class XmppConnection implements Runnable {
} }
if (streamFeatures.hasChild("sm", "urn:xmpp:sm:3")) { if (streamFeatures.hasChild("sm", "urn:xmpp:sm:3")) {
smVersion = 3; smVersion = 3;
EnablePacket enable = new EnablePacket(smVersion); final EnablePacket enable = new EnablePacket(smVersion);
tagWriter.writeStanzaAsync(enable); tagWriter.writeStanzaAsync(enable);
stanzasSent = 0; stanzasSent = 0;
messageReceipts.clear(); messageReceipts.clear();
} else if (streamFeatures.hasChild("sm", } else if (streamFeatures.hasChild("sm",
"urn:xmpp:sm:2")) { "urn:xmpp:sm:2")) {
smVersion = 2; smVersion = 2;
EnablePacket enable = new EnablePacket(smVersion); final EnablePacket enable = new EnablePacket(smVersion);
tagWriter.writeStanzaAsync(enable); tagWriter.writeStanzaAsync(enable);
stanzasSent = 0; stanzasSent = 0;
messageReceipts.clear(); messageReceipts.clear();
@ -736,7 +732,7 @@ public class XmppConnection implements Runnable {
if (this.streamFeatures.hasChild("session")) { if (this.streamFeatures.hasChild("session")) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ ": sending deprecated session"); + ": sending deprecated session");
IqPacket startSession = new IqPacket(IqPacket.TYPE_SET); final IqPacket startSession = new IqPacket(IqPacket.TYPE_SET);
startSession.addChild("session", startSession.addChild("session",
"urn:ietf:params:xml:ns:xmpp-session"); "urn:ietf:params:xml:ns:xmpp-session");
this.sendUnboundIqPacket(startSession, null); this.sendUnboundIqPacket(startSession, null);
@ -755,10 +751,10 @@ public class XmppConnection implements Runnable {
this.sendIqPacket(iq, new OnIqPacketReceived() { this.sendIqPacket(iq, new OnIqPacketReceived() {
@Override @Override
public void onIqPacketReceived(Account account, IqPacket packet) { public void onIqPacketReceived(final Account account, final IqPacket packet) {
final List<Element> elements = packet.query().getChildren(); final List<Element> elements = packet.query().getChildren();
final List<String> features = new ArrayList<>(); final List<String> features = new ArrayList<>();
for (Element element : elements) { for (final Element element : elements) {
if (element.getName().equals("identity")) { if (element.getName().equals("identity")) {
if ("irc".equals(element.getAttribute("type"))) { if ("irc".equals(element.getAttribute("type"))) {
//add fake feature to not confuse irc and real muc //add fake feature to not confuse irc and real muc
@ -772,7 +768,7 @@ public class XmppConnection implements Runnable {
if (account.getServer().equals(server.toDomainJid())) { if (account.getServer().equals(server.toDomainJid())) {
enableAdvancedStreamFeatures(); enableAdvancedStreamFeatures();
for(OnAdvancedStreamFeaturesLoaded listener : advancedStreamFeaturesLoadedListeners) { for (final OnAdvancedStreamFeaturesLoaded listener : advancedStreamFeaturesLoadedListeners) {
listener.onAdvancedStreamFeaturesAvailable(account); listener.onAdvancedStreamFeaturesAvailable(account);
} }
} }
@ -787,6 +783,10 @@ public class XmppConnection implements Runnable {
sendEnableCarbons(); sendEnableCarbons();
} }
} }
if (getFeatures().blocking()) {
Log.d(Config.LOGTAG, "Requesting block list");
this.sendIqPacket(getIqGenerator().generateGetBlockList(), mXmppConnectionService.getIqParser());
}
} }
private void sendServiceDiscoveryItems(final Jid server) { private void sendServiceDiscoveryItems(final Jid server) {
@ -796,9 +796,9 @@ public class XmppConnection implements Runnable {
this.sendIqPacket(iq, new OnIqPacketReceived() { this.sendIqPacket(iq, new OnIqPacketReceived() {
@Override @Override
public void onIqPacketReceived(Account account, IqPacket packet) { public void onIqPacketReceived(final Account account, final IqPacket packet) {
List<Element> elements = packet.query().getChildren(); final List<Element> elements = packet.query().getChildren();
for (Element element : elements) { for (final Element element : elements) {
if (element.getName().equals("item")) { if (element.getName().equals("item")) {
final Jid jid = element.getAttributeAsJid("jid"); final Jid jid = element.getAttributeAsJid("jid");
if (jid != null && !jid.equals(account.getServer())) { if (jid != null && !jid.equals(account.getServer())) {
@ -811,12 +811,12 @@ public class XmppConnection implements Runnable {
} }
private void sendEnableCarbons() { private void sendEnableCarbons() {
IqPacket iq = new IqPacket(IqPacket.TYPE_SET); final IqPacket iq = new IqPacket(IqPacket.TYPE_SET);
iq.addChild("enable", "urn:xmpp:carbons:2"); iq.addChild("enable", "urn:xmpp:carbons:2");
this.sendIqPacket(iq, new OnIqPacketReceived() { this.sendIqPacket(iq, new OnIqPacketReceived() {
@Override @Override
public void onIqPacketReceived(Account account, IqPacket packet) { public void onIqPacketReceived(final Account account, final IqPacket packet) {
if (!packet.hasChild("error")) { if (!packet.hasChild("error")) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() Log.d(Config.LOGTAG, account.getJid().toBareJid()
+ ": successfully enabled carbons"); + ": successfully enabled carbons");
@ -829,9 +829,9 @@ public class XmppConnection implements Runnable {
}); });
} }
private void processStreamError(Tag currentTag) private void processStreamError(final Tag currentTag)
throws XmlPullParserException, IOException { throws XmlPullParserException, IOException {
Element streamError = tagReader.readElement(currentTag); final Element streamError = tagReader.readElement(currentTag);
if (streamError != null && streamError.hasChild("conflict")) { if (streamError != null && streamError.hasChild("conflict")) {
final String resource = account.getResource().split("\\.")[0]; final String resource = account.getResource().split("\\.")[0];
account.setResource(resource + "." + nextRandomId()); account.setResource(resource + "." + nextRandomId());
@ -842,7 +842,7 @@ public class XmppConnection implements Runnable {
} }
private void sendStartStream() throws IOException { private void sendStartStream() throws IOException {
Tag stream = Tag.start("stream:stream"); final Tag stream = Tag.start("stream:stream");
stream.setAttribute("from", account.getJid().toBareJid().toString()); stream.setAttribute("from", account.getJid().toBareJid().toString());
stream.setAttribute("to", account.getServer().toString()); stream.setAttribute("to", account.getServer().toString());
stream.setAttribute("version", "1.0"); stream.setAttribute("version", "1.0");
@ -856,33 +856,32 @@ public class XmppConnection implements Runnable {
return new BigInteger(50, mXmppConnectionService.getRNG()).toString(32); return new BigInteger(50, mXmppConnectionService.getRNG()).toString(32);
} }
public void sendIqPacket(IqPacket packet, OnIqPacketReceived callback) { public void sendIqPacket(final IqPacket packet, final PacketReceived callback) {
if (packet.getId() == null) { if (packet.getId() == null) {
String id = nextRandomId(); final String id = nextRandomId();
packet.setAttribute("id", id); packet.setAttribute("id", id);
} }
packet.setFrom(account.getJid()); packet.setFrom(account.getJid());
this.sendPacket(packet, callback); this.sendPacket(packet, callback);
} }
public void sendUnboundIqPacket(IqPacket packet, OnIqPacketReceived callback) { public void sendUnboundIqPacket(final IqPacket packet, final PacketReceived callback) {
if (packet.getId() == null) { if (packet.getId() == null) {
String id = nextRandomId(); final String id = nextRandomId();
packet.setAttribute("id", id); packet.setAttribute("id", id);
} }
this.sendPacket(packet, callback); this.sendPacket(packet, callback);
} }
public void sendMessagePacket(MessagePacket packet) { public void sendMessagePacket(final MessagePacket packet) {
this.sendPacket(packet, null); this.sendPacket(packet, null);
} }
public void sendPresencePacket(PresencePacket packet) { public void sendPresencePacket(final PresencePacket packet) {
this.sendPacket(packet, null); this.sendPacket(packet, null);
} }
private synchronized void sendPacket(final AbstractStanza packet, private synchronized void sendPacket(final AbstractStanza packet, final PacketReceived callback) {
PacketReceived callback) {
if (packet.getName().equals("iq") || packet.getName().equals("message") if (packet.getName().equals("iq") || packet.getName().equals("message")
|| packet.getName().equals("presence")) { || packet.getName().equals("presence")) {
++stanzasSent; ++stanzasSent;
@ -907,7 +906,7 @@ public class XmppConnection implements Runnable {
if (streamFeatures.hasChild("sm")) { if (streamFeatures.hasChild("sm")) {
tagWriter.writeStanzaAsync(new RequestPacket(smVersion)); tagWriter.writeStanzaAsync(new RequestPacket(smVersion));
} else { } else {
IqPacket iq = new IqPacket(IqPacket.TYPE_GET); final IqPacket iq = new IqPacket(IqPacket.TYPE_GET);
iq.setFrom(account.getJid()); iq.setFrom(account.getJid());
iq.addChild("ping", "urn:xmpp:ping"); iq.addChild("ping", "urn:xmpp:ping");
this.sendIqPacket(iq, null); this.sendIqPacket(iq, null);
@ -916,44 +915,44 @@ public class XmppConnection implements Runnable {
} }
public void setOnMessagePacketReceivedListener( public void setOnMessagePacketReceivedListener(
OnMessagePacketReceived listener) { final OnMessagePacketReceived listener) {
this.messageListener = listener; this.messageListener = listener;
} }
public void setOnUnregisteredIqPacketReceivedListener( public void setOnUnregisteredIqPacketReceivedListener(
OnIqPacketReceived listener) { final OnIqPacketReceived listener) {
this.unregisteredIqListener = listener; this.unregisteredIqListener = listener;
} }
public void setOnPresencePacketReceivedListener( public void setOnPresencePacketReceivedListener(
OnPresencePacketReceived listener) { final OnPresencePacketReceived listener) {
this.presenceListener = listener; this.presenceListener = listener;
} }
public void setOnJinglePacketReceivedListener( public void setOnJinglePacketReceivedListener(
OnJinglePacketReceived listener) { final OnJinglePacketReceived listener) {
this.jingleListener = listener; this.jingleListener = listener;
} }
public void setOnStatusChangedListener(OnStatusChanged listener) { public void setOnStatusChangedListener(final OnStatusChanged listener) {
this.statusListener = listener; this.statusListener = listener;
} }
public void setOnBindListener(OnBindListener listener) { public void setOnBindListener(final OnBindListener listener) {
this.bindListener = listener; this.bindListener = listener;
} }
public void setOnMessageAcknowledgeListener(OnMessageAcknowledged listener) { public void setOnMessageAcknowledgeListener(final OnMessageAcknowledged listener) {
this.acknowledgedListener = listener; this.acknowledgedListener = listener;
} }
public void addOnAdvancedStreamFeaturesAvailableListener(OnAdvancedStreamFeaturesLoaded listener) { public void addOnAdvancedStreamFeaturesAvailableListener(final OnAdvancedStreamFeaturesLoaded listener) {
if (!this.advancedStreamFeaturesLoadedListeners.contains(listener)) { if (!this.advancedStreamFeaturesLoadedListeners.contains(listener)) {
this.advancedStreamFeaturesLoadedListeners.add(listener); this.advancedStreamFeaturesLoadedListeners.add(listener);
} }
} }
public void disconnect(boolean force) { public void disconnect(final boolean force) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting"); Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": disconnecting");
try { try {
if (force) { if (force) {
@ -973,23 +972,23 @@ public class XmppConnection implements Runnable {
} }
tagWriter.writeTag(Tag.end("stream:stream")); tagWriter.writeTag(Tag.end("stream:stream"));
socket.close(); socket.close();
} catch (IOException e) { } catch (final IOException e) {
Log.d(Config.LOGTAG, Log.d(Config.LOGTAG,
"io exception during disconnect"); "io exception during disconnect");
} catch (InterruptedException e) { } catch (final InterruptedException e) {
Log.d(Config.LOGTAG, "interrupted"); Log.d(Config.LOGTAG, "interrupted");
} }
} }
} }
}).start(); }).start();
} catch (IOException e) { } catch (final IOException e) {
Log.d(Config.LOGTAG, "io exception during disconnect"); Log.d(Config.LOGTAG, "io exception during disconnect");
} }
} }
public List<String> findDiscoItemsByFeature(String feature) { public List<String> findDiscoItemsByFeature(final String feature) {
final List<String> items = new ArrayList<>(); final List<String> items = new ArrayList<>();
for (Entry<String, List<String>> cursor : disco.entrySet()) { for (final Entry<String, List<String>> cursor : disco.entrySet()) {
if (cursor.getValue().contains(feature)) { if (cursor.getValue().contains(feature)) {
items.add(cursor.getKey()); items.add(cursor.getKey());
} }
@ -997,8 +996,8 @@ public class XmppConnection implements Runnable {
return items; return items;
} }
public String findDiscoItemByFeature(String feature) { public String findDiscoItemByFeature(final String feature) {
List<String> items = findDiscoItemsByFeature(feature); final List<String> items = findDiscoItemsByFeature(feature);
if (items.size() >= 1) { if (items.size() >= 1) {
return items.get(0); return items.get(0);
} }
@ -1010,8 +1009,7 @@ public class XmppConnection implements Runnable {
} }
public String getMucServer() { public String getMucServer() {
final List<String> items = new ArrayList<>(); for (final Entry<String, List<String>> cursor : disco.entrySet()) {
for (Entry<String, List<String>> cursor : disco.entrySet()) {
final List<String> value = cursor.getValue(); final List<String> value = cursor.getValue();
if (value.contains("http://jabber.org/protocol/muc") && !value.contains("jabber:iq:gateway") && !value.contains("siacs:no:muc")) { if (value.contains("http://jabber.org/protocol/muc") && !value.contains("jabber:iq:gateway") && !value.contains("siacs:no:muc")) {
return cursor.getKey(); return cursor.getKey();
@ -1021,8 +1019,8 @@ public class XmppConnection implements Runnable {
} }
public int getTimeToNextAttempt() { public int getTimeToNextAttempt() {
int interval = (int) (25 * Math.pow(1.5, attempt)); final int interval = (int) (25 * Math.pow(1.5, attempt));
int secondsSinceLast = (int) ((SystemClock.elapsedRealtime() - this.lastConnect) / 1000); final int secondsSinceLast = (int) ((SystemClock.elapsedRealtime() - this.lastConnect) / 1000);
return interval - secondsSinceLast; return interval - secondsSinceLast;
} }
@ -1035,7 +1033,7 @@ public class XmppConnection implements Runnable {
} }
public long getLastSessionEstablished() { public long getLastSessionEstablished() {
long diff; final long diff;
if (this.lastSessionStarted == 0) { if (this.lastSessionStarted == 0) {
diff = SystemClock.elapsedRealtime() - this.lastConnect; diff = SystemClock.elapsedRealtime() - this.lastConnect;
} else { } else {
@ -1067,7 +1065,7 @@ public class XmppConnection implements Runnable {
public class Features { public class Features {
XmppConnection connection; XmppConnection connection;
public Features(XmppConnection connection) { public Features(final XmppConnection connection) {
this.connection = connection; this.connection = connection;
} }
@ -1080,6 +1078,10 @@ public class XmppConnection implements Runnable {
return hasDiscoFeature(account.getServer(), "urn:xmpp:carbons:2"); return hasDiscoFeature(account.getServer(), "urn:xmpp:carbons:2");
} }
public boolean blocking() {
return hasDiscoFeature(account.getServer(), Xmlns.BLOCKING);
}
public boolean sm() { public boolean sm() {
return streamId != null; return streamId != null;
} }
@ -1110,4 +1112,8 @@ public class XmppConnection implements Runnable {
.findDiscoItemByFeature("http://jabber.org/protocol/bytestreams") != null; .findDiscoItemByFeature("http://jabber.org/protocol/bytestreams") != null;
} }
} }
private IqGenerator getIqGenerator() {
return mXmppConnectionService.getIqGenerator();
}
} }

View File

@ -32,7 +32,7 @@ public final class Jid {
return resourcepart; return resourcepart;
} }
public static Jid fromSessionID(SessionID id) throws InvalidJidException{ public static Jid fromSessionID(final SessionID id) throws InvalidJidException{
if (id.getUserID().isEmpty()) { if (id.getUserID().isEmpty()) {
return Jid.fromString(id.getAccountID()); return Jid.fromString(id.getAccountID());
} else { } else {
@ -190,4 +190,8 @@ public final class Jid {
public boolean isBareJid() { public boolean isBareJid() {
return this.resourcepart.isEmpty(); return this.resourcepart.isEmpty();
} }
public boolean isDomainJid() {
return !this.hasLocalpart();
}
} }

View File

@ -9,11 +9,11 @@ public class IqPacket extends AbstractStanza {
public static final int TYPE_RESULT = 1; public static final int TYPE_RESULT = 1;
public static final int TYPE_GET = 2; public static final int TYPE_GET = 2;
private IqPacket(String name) { private IqPacket(final String name) {
super(name); super(name);
} }
public IqPacket(int type) { public IqPacket(final int type) {
super("iq"); super("iq");
switch (type) { switch (type) {
case TYPE_SET: case TYPE_SET:
@ -45,29 +45,30 @@ public class IqPacket extends AbstractStanza {
return query; return query;
} }
public Element query(String xmlns) { public Element query(final String xmlns) {
Element query = query(); final Element query = query();
query.setAttribute("xmlns", xmlns); query.setAttribute("xmlns", xmlns);
return query(); return query();
} }
public int getType() { public int getType() {
String type = getAttribute("type"); final String type = getAttribute("type");
if ("error".equals(type)) { switch (type) {
case "error":
return TYPE_ERROR; return TYPE_ERROR;
} else if ("result".equals(type)) { case "result":
return TYPE_RESULT; return TYPE_RESULT;
} else if ("set".equals(type)) { case "set":
return TYPE_SET; return TYPE_SET;
} else if ("get".equals(type)) { case "get":
return TYPE_GET; return TYPE_GET;
} else { default:
return 1000; return 1000;
} }
} }
public IqPacket generateRespone(int type) { public IqPacket generateRespone(final int type) {
IqPacket packet = new IqPacket(type); final IqPacket packet = new IqPacket(type);
packet.setTo(this.getFrom()); packet.setTo(this.getFrom());
packet.setId(this.getId()); packet.setId(this.getId());
return packet; return packet;

View File

@ -7,6 +7,9 @@
<item <item
android:id="@+id/context_contact_details" android:id="@+id/context_contact_details"
android:title="@string/view_contact_details"/> android:title="@string/view_contact_details"/>
<item
android:id="@+id/context_contact_block_unblock"
android:title="@string/block_contact"/>
<item <item
android:id="@+id/context_delete_contact" android:id="@+id/context_delete_contact"
android:title="@string/delete_contact"/> android:title="@string/delete_contact"/>

View File

@ -56,6 +56,18 @@
android:showAsAction="never" android:showAsAction="never"
android:title="@string/enable_notifications"/> android:title="@string/enable_notifications"/>
<item
android:id="@+id/action_block"
android:orderInCategory="72"
android:showAsAction="never"
android:title="@string/action_block_contact"/>
<item
android:id="@+id/action_unblock"
android:orderInCategory="73"
android:showAsAction="never"
android:title="@string/action_unblock_contact"/>
<item <item
android:id="@+id/action_accounts" android:id="@+id/action_accounts"
android:orderInCategory="90" android:orderInCategory="90"

View File

@ -4,5 +4,9 @@
android:id="@+id/action_show_qr_code" android:id="@+id/action_show_qr_code"
android:title="@string/show_qr_code" android:title="@string/show_qr_code"
android:showAsAction="never" /> android:showAsAction="never" />
<item
android:id="@+id/action_show_block_list"
android:title="@string/show_block_list"
android:showAsAction="never" />
</menu> </menu>

View File

@ -13,6 +13,10 @@
<string name="action_edit_contact">Edit name</string> <string name="action_edit_contact">Edit name</string>
<string name="action_add_phone_book">Add to phone book</string> <string name="action_add_phone_book">Add to phone book</string>
<string name="action_delete_contact">Delete from roster</string> <string name="action_delete_contact">Delete from roster</string>
<string name="action_block_contact">Block contact</string>
<string name="action_unblock_contact">Unblock contact</string>
<string name="action_block_domain">Block domain</string>
<string name="action_unblock_domain">Unblock domain</string>
<string name="title_activity_manage_accounts">Manage Accounts</string> <string name="title_activity_manage_accounts">Manage Accounts</string>
<string name="title_activity_settings">Settings</string> <string name="title_activity_settings">Settings</string>
<string name="title_activity_conference_details">Conference Details</string> <string name="title_activity_conference_details">Conference Details</string>
@ -21,6 +25,7 @@
<string name="title_activity_sharewith">Share with Conversation</string> <string name="title_activity_sharewith">Share with Conversation</string>
<string name="title_activity_start_conversation">Start Conversation</string> <string name="title_activity_start_conversation">Start Conversation</string>
<string name="title_activity_choose_contact">Choose contact</string> <string name="title_activity_choose_contact">Choose contact</string>
<string name="title_activity_block_list">Block list</string>
<string name="just_now">just now</string> <string name="just_now">just now</string>
<string name="minute_ago">1 min ago</string> <string name="minute_ago">1 min ago</string>
<string name="minutes_ago">%d mins ago</string> <string name="minutes_ago">%d mins ago</string>
@ -34,6 +39,11 @@
<string name="participant">Participant</string> <string name="participant">Participant</string>
<string name="visitor">Visitor</string> <string name="visitor">Visitor</string>
<string name="remove_contact_text">Would you like to remove %s from your roster? The conversation associated with this contact will not be removed.</string> <string name="remove_contact_text">Would you like to remove %s from your roster? The conversation associated with this contact will not be removed.</string>
<string name="block_contact_text">Would you like to block %s from sending you messages?</string>
<string name="unblock_contact_text">Would you like to unblock %s and allow them to send you messages?</string>
<string name="block_domain_text">Block all contacts from %s?</string>
<string name="unblock_domain_text">Unblock all contacts from %s?</string>
<string name="contact_blocked">Contact blocked</string>
<string name="remove_bookmark_text">Would you like to remove %s as a bookmark? The conversation associated with this bookmark will not be removed.</string> <string name="remove_bookmark_text">Would you like to remove %s as a bookmark? The conversation associated with this bookmark will not be removed.</string>
<string name="register_account">Register new account on server</string> <string name="register_account">Register new account on server</string>
<string name="share_with">Share with</string> <string name="share_with">Share with</string>
@ -45,6 +55,8 @@
<string name="add">Add</string> <string name="add">Add</string>
<string name="edit">Edit</string> <string name="edit">Edit</string>
<string name="delete">Delete</string> <string name="delete">Delete</string>
<string name="block">Block</string>
<string name="unblock">Unblock</string>
<string name="save">Save</string> <string name="save">Save</string>
<string name="ok">OK</string> <string name="ok">OK</string>
<string name="crash_report_title">Conversations has crashed</string> <string name="crash_report_title">Conversations has crashed</string>
@ -202,6 +214,8 @@
<string name="join_conference">Join Conference</string> <string name="join_conference">Join Conference</string>
<string name="delete_contact">Delete Contact</string> <string name="delete_contact">Delete Contact</string>
<string name="view_contact_details">View contact details</string> <string name="view_contact_details">View contact details</string>
<string name="block_contact">Block contact</string>
<string name="unblock_contact">Unblock contact</string>
<string name="create">Create</string> <string name="create">Create</string>
<string name="contact_already_exists">The contact already exists</string> <string name="contact_already_exists">The contact already exists</string>
<string name="join">Join</string> <string name="join">Join</string>
@ -318,6 +332,7 @@
<string name="image_transmission_failed">Image transmission failed</string> <string name="image_transmission_failed">Image transmission failed</string>
<string name="scan_qr_code">Scan QR code</string> <string name="scan_qr_code">Scan QR code</string>
<string name="show_qr_code">Show QR code</string> <string name="show_qr_code">Show QR code</string>
<string name="show_block_list">Show block list</string>
<string name="account_details">Account details</string> <string name="account_details">Account details</string>
<string name="verify_otr">Verify OTR</string> <string name="verify_otr">Verify OTR</string>
<string name="remote_fingerprint">Remote Fingerprint</string> <string name="remote_fingerprint">Remote Fingerprint</string>