Detailed changes
@@ -1,11 +1,17 @@
package eu.siacs.conversations.entities;
+import androidx.annotation.NonNull;
import eu.siacs.conversations.xmpp.Jid;
public interface Blockable {
- boolean isBlocked();
- boolean isDomainBlocked();
- Jid getBlockedJid();
- Jid getJid();
- Account getAccount();
+ boolean isBlocked();
+
+ boolean isDomainBlocked();
+
+ @NonNull
+ Jid getBlockedJid();
+
+ Jid getJid();
+
+ Account getAccount();
}
@@ -316,7 +316,7 @@ public class Contact implements ListItem, Blockable {
this.systemAccount = lookupUri;
}
- private Collection<String> getGroups(final boolean unique) {
+ public Collection<String> getGroups(final boolean unique) {
final Collection<String> groups = unique ? new HashSet<>() : new ArrayList<>();
for (int i = 0; i < this.groups.length(); ++i) {
try {
@@ -428,18 +428,6 @@ public class Contact implements ListItem, Blockable {
}
}
- public Element asElement() {
- final Element item = new Element("item");
- item.setAttribute("jid", this.jid);
- if (this.serverName != null) {
- item.setAttribute("name", this.serverName);
- }
- for (String group : getGroups(false)) {
- item.addChild("group").setContent(group);
- }
- return item;
- }
-
@Override
public int compareTo(@NonNull final ListItem another) {
return this.getDisplayName().compareToIgnoreCase(another.getDisplayName());
@@ -490,6 +478,7 @@ public class Contact implements ListItem, Blockable {
}
@Override
+ @NonNull
public Jid getBlockedJid() {
if (isDomainBlocked()) {
return getJid().getDomain();
@@ -533,6 +533,7 @@ public class Conversation extends AbstractEntity
}
@Override
+ @NonNull
public Jid getBlockedJid() {
return getContact().getBlockedJid();
}
@@ -2,6 +2,7 @@ package eu.siacs.conversations.entities;
import android.content.Context;
import android.text.TextUtils;
+import androidx.annotation.NonNull;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.Jid;
import java.util.Collections;
@@ -13,7 +14,7 @@ public class RawBlockable implements ListItem, Blockable {
private final Account account;
private final Jid jid;
- public RawBlockable(Account account, Jid jid) {
+ public RawBlockable(@NonNull Account account, @NonNull Jid jid) {
this.account = account;
this.jid = jid;
}
@@ -29,6 +30,7 @@ public class RawBlockable implements ListItem, Blockable {
}
@Override
+ @NonNull
public Jid getBlockedJid() {
return this.jid;
}
@@ -316,31 +316,6 @@ public class IqGenerator extends AbstractGenerator {
return packet;
}
- public Iq generateSetBlockRequest(
- final Jid jid, final boolean reportSpam, final String serverMsgId) {
- final Iq iq = new Iq(Iq.Type.SET);
- final Element block = iq.addChild("block", Namespace.BLOCKING);
- final Element item = block.addChild("item").setAttribute("jid", jid);
- if (reportSpam) {
- final Element report = item.addChild("report", Namespace.REPORTING);
- report.setAttribute("reason", Namespace.REPORTING_REASON_SPAM);
- if (serverMsgId != null) {
- final Element stanzaId = report.addChild("stanza-id", Namespace.STANZA_IDS);
- stanzaId.setAttribute("by", jid);
- stanzaId.setAttribute("id", serverMsgId);
- }
- }
- Log.d(Config.LOGTAG, iq.toString());
- return iq;
- }
-
- public Iq generateSetUnblockRequest(final Jid jid) {
- final Iq iq = new Iq(Iq.Type.SET);
- final Element block = iq.addChild("unblock", Namespace.BLOCKING);
- block.addChild("item").setAttribute("jid", jid);
- return iq;
- }
-
public Iq generateSetPassword(final Account account, final String newPassword) {
final Iq packet = new Iq(Iq.Type.SET);
packet.setTo(account.getDomain());
@@ -1,11 +1,8 @@
package eu.siacs.conversations.generator;
-import android.text.TextUtils;
import eu.siacs.conversations.entities.Account;
-import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.services.XmppConnectionService;
-import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.manager.PresenceManager;
import im.conversations.android.xmpp.model.stanza.Presence;
@@ -15,50 +12,6 @@ public class PresenceGenerator extends AbstractGenerator {
super(service);
}
- private im.conversations.android.xmpp.model.stanza.Presence subscription(
- String type, Contact contact) {
- im.conversations.android.xmpp.model.stanza.Presence packet =
- new im.conversations.android.xmpp.model.stanza.Presence();
- packet.setAttribute("type", type);
- packet.setTo(contact.getJid());
- packet.setFrom(contact.getAccount().getJid().asBareJid());
- return packet;
- }
-
- public im.conversations.android.xmpp.model.stanza.Presence requestPresenceUpdatesFrom(
- final Contact contact) {
- return requestPresenceUpdatesFrom(contact, null);
- }
-
- public im.conversations.android.xmpp.model.stanza.Presence requestPresenceUpdatesFrom(
- final Contact contact, final String preAuth) {
- im.conversations.android.xmpp.model.stanza.Presence packet =
- subscription("subscribe", contact);
- String displayName = contact.getAccount().getDisplayName();
- if (!TextUtils.isEmpty(displayName)) {
- packet.addChild("nick", Namespace.NICK).setContent(displayName);
- }
- if (preAuth != null) {
- packet.addChild("preauth", Namespace.PARS).setAttribute("token", preAuth);
- }
- return packet;
- }
-
- public im.conversations.android.xmpp.model.stanza.Presence stopPresenceUpdatesFrom(
- Contact contact) {
- return subscription("unsubscribe", contact);
- }
-
- public im.conversations.android.xmpp.model.stanza.Presence stopPresenceUpdatesTo(
- Contact contact) {
- return subscription("unsubscribed", contact);
- }
-
- public im.conversations.android.xmpp.model.stanza.Presence sendPresenceUpdatesTo(
- Contact contact) {
- return subscription("subscribed", contact);
- }
-
public im.conversations.android.xmpp.model.stanza.Presence selfPresence(
Account account, Presence.Availability status) {
return selfPresence(account, status, true);
@@ -24,6 +24,7 @@ import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.manager.DiscoManager;
+import eu.siacs.conversations.xmpp.manager.PresenceManager;
import eu.siacs.conversations.xmpp.manager.RosterManager;
import eu.siacs.conversations.xmpp.pep.Avatar;
import im.conversations.android.xmpp.Entity;
@@ -445,8 +446,9 @@ public class PresenceParser extends AbstractParser
mXmppConnectionService.getAvatarService().clear(contact);
}
if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) {
- mXmppConnectionService.sendPresencePacket(
- account, mPresenceGenerator.sendPresenceUpdatesTo(contact));
+ connection
+ .getManager(PresenceManager.class)
+ .subscribed(contact.getJid().asBareJid());
} else {
contact.setOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST);
final Conversation conversation =
@@ -137,7 +137,9 @@ import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection;
import eu.siacs.conversations.xmpp.jingle.Media;
import eu.siacs.conversations.xmpp.jingle.RtpEndUserState;
import eu.siacs.conversations.xmpp.mam.MamReference;
+import eu.siacs.conversations.xmpp.manager.BlockingManager;
import eu.siacs.conversations.xmpp.manager.DiscoManager;
+import eu.siacs.conversations.xmpp.manager.PresenceManager;
import eu.siacs.conversations.xmpp.manager.RosterManager;
import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.pep.PublishOptions;
@@ -1902,7 +1904,7 @@ public class XmppConnectionService extends Service {
+ ": adding "
+ contact.getJid()
+ " on sending message");
- createContact(contact, true);
+ createContact(contact);
}
}
@@ -3022,10 +3024,13 @@ public class XmppConnectionService extends Service {
}
}
- public void stopPresenceUpdatesTo(Contact contact) {
+ public void stopPresenceUpdatesTo(final Contact contact) {
Log.d(Config.LOGTAG, "Canceling presence request from " + contact.getJid().toString());
- sendPresencePacket(contact.getAccount(), mPresenceGenerator.stopPresenceUpdatesTo(contact));
contact.resetOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST);
+ contact.getAccount()
+ .getXmppConnection()
+ .getManager(PresenceManager.class)
+ .unsubscribed(contact.getJid().asBareJid());
}
public void createAccount(final Account account) {
@@ -4690,57 +4695,20 @@ public class XmppConnectionService extends Service {
updateConversationUi();
}
- // TODO move this to RosterManager
- public void syncDirtyContacts(Account account) {
- for (Contact contact : account.getRoster().getContacts()) {
- if (contact.getOption(Contact.Options.DIRTY_PUSH)) {
- pushContactToServer(contact);
- }
- if (contact.getOption(Contact.Options.DIRTY_DELETE)) {
- deleteContactOnServer(contact);
- }
- }
+ public void createContact(final Contact contact) {
+ createContact(contact, null);
}
- public void createContact(final Contact contact, final boolean autoGrant) {
- createContact(contact, autoGrant, null);
+ public void createContact(final Contact contact, final String preAuth) {
+ contact.setOption(Contact.Options.PREEMPTIVE_GRANT);
+ contact.setOption(Contact.Options.ASKING);
+ final var connection = contact.getAccount().getXmppConnection();
+ connection.getManager(RosterManager.class).addRosterItem(contact, preAuth);
}
- public void createContact(
- final Contact contact, final boolean autoGrant, final String preAuth) {
- if (autoGrant) {
- contact.setOption(Contact.Options.PREEMPTIVE_GRANT);
- contact.setOption(Contact.Options.ASKING);
- }
- pushContactToServer(contact, preAuth);
- }
-
- public void pushContactToServer(final Contact contact) {
- pushContactToServer(contact, null);
- }
-
- private void pushContactToServer(final Contact contact, final String preAuth) {
- contact.resetOption(Contact.Options.DIRTY_DELETE);
- contact.setOption(Contact.Options.DIRTY_PUSH);
- final Account account = contact.getAccount();
- if (account.getStatus() == Account.State.ONLINE) {
- final boolean ask = contact.getOption(Contact.Options.ASKING);
- final boolean sendUpdates =
- contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)
- && contact.getOption(Contact.Options.PREEMPTIVE_GRANT);
- final Iq iq = new Iq(Iq.Type.SET);
- iq.query(Namespace.ROSTER).addChild(contact.asElement());
- account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler);
- if (sendUpdates) {
- sendPresencePacket(account, mPresenceGenerator.sendPresenceUpdatesTo(contact));
- }
- if (ask) {
- sendPresencePacket(
- account, mPresenceGenerator.requestPresenceUpdatesFrom(contact, preAuth));
- }
- } else {
- account.getXmppConnection().getManager(RosterManager.class).writeToDatabaseAsync();
- }
+ public void deleteContactOnServer(final Contact contact) {
+ final var connection = contact.getAccount().getXmppConnection();
+ connection.getManager(RosterManager.class).deleteRosterItem(contact);
}
public void publishMucAvatar(
@@ -5312,20 +5280,6 @@ public class XmppConnectionService extends Service {
}
}
- public void deleteContactOnServer(Contact contact) {
- contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
- contact.resetOption(Contact.Options.DIRTY_PUSH);
- contact.setOption(Contact.Options.DIRTY_DELETE);
- Account account = contact.getAccount();
- if (account.getStatus() == Account.State.ONLINE) {
- final Iq iq = new Iq(Iq.Type.SET);
- Element item = iq.query(Namespace.ROSTER).addChild("item");
- item.setAttribute("jid", contact.getJid());
- item.setAttribute("subscription", "remove");
- account.getXmppConnection().sendIqPacket(iq, mDefaultIqHandler);
- }
- }
-
public void updateConversation(final Conversation conversation) {
mDatabaseWriterExecutor.execute(() -> databaseBackend.updateConversation(conversation));
}
@@ -6145,29 +6099,11 @@ public class XmppConnectionService extends Service {
public boolean sendBlockRequest(
final Blockable blockable, final boolean reportSpam, final String serverMsgId) {
- if (blockable != null && blockable.getBlockedJid() != null) {
- final var account = blockable.getAccount();
- final Jid jid = blockable.getBlockedJid();
- this.sendIqPacket(
- account,
- getIqGenerator().generateSetBlockRequest(jid, reportSpam, serverMsgId),
- (response) -> {
- if (response.getType() == Iq.Type.RESULT) {
- account.getBlocklist().add(jid);
- updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
- }
- });
- if (blockable.getBlockedJid().isFullJid()) {
- return false;
- } else if (removeBlockedConversations(blockable.getAccount(), jid)) {
- updateConversationUi();
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
+ final var account = blockable.getAccount();
+ final var connection = account.getXmppConnection();
+ return connection
+ .getManager(BlockingManager.class)
+ .block(blockable, reportSpam, serverMsgId);
}
public boolean removeBlockedConversations(final Account account, final Jid blockedJid) {
@@ -6202,19 +6138,9 @@ public class XmppConnectionService extends Service {
}
public void sendUnblockRequest(final Blockable blockable) {
- if (blockable != null && blockable.getJid() != null) {
- final var account = blockable.getAccount();
- final Jid jid = blockable.getBlockedJid();
- this.sendIqPacket(
- account,
- getIqGenerator().generateSetUnblockRequest(jid),
- response -> {
- if (response.getType() == Iq.Type.RESULT) {
- account.getBlocklist().remove(jid);
- updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED);
- }
- });
- }
+ final var account = blockable.getAccount();
+ final var connection = account.getXmppConnection();
+ connection.getManager(BlockingManager.class).unblock(blockable);
}
public void publishDisplayName(final Account account) {
@@ -70,6 +70,8 @@ import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
import eu.siacs.conversations.xmpp.XmppConnection;
+import eu.siacs.conversations.xmpp.manager.PresenceManager;
+import eu.siacs.conversations.xmpp.manager.RosterManager;
import im.conversations.android.xmpp.model.stanza.Presence;
import java.util.Collection;
import java.util.Collections;
@@ -109,11 +111,10 @@ public class ContactDetailsActivity extends OmemoActivity
}
} else {
contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
- xmppConnectionService.sendPresencePacket(
- contact.getAccount(),
- xmppConnectionService
- .getPresenceGenerator()
- .stopPresenceUpdatesTo(contact));
+ final var connection = contact.getAccount().getXmppConnection();
+ connection
+ .getManager(PresenceManager.class)
+ .unsubscribed(contact.getJid().asBareJid());
}
}
};
@@ -122,18 +123,15 @@ public class ContactDetailsActivity extends OmemoActivity
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ final var connection = contact.getAccount().getXmppConnection();
if (isChecked) {
- xmppConnectionService.sendPresencePacket(
- contact.getAccount(),
- xmppConnectionService
- .getPresenceGenerator()
- .requestPresenceUpdatesFrom(contact));
+ connection
+ .getManager(PresenceManager.class)
+ .subscribe(contact.getJid().asBareJid());
} else {
- xmppConnectionService.sendPresencePacket(
- contact.getAccount(),
- xmppConnectionService
- .getPresenceGenerator()
- .stopPresenceUpdatesFrom(contact));
+ connection
+ .getManager(PresenceManager.class)
+ .unsubscribe(contact.getJid().asBareJid());
}
}
};
@@ -343,8 +341,10 @@ public class ContactDetailsActivity extends OmemoActivity
R.string.contact_name,
value -> {
contact.setServerName(value);
- ContactDetailsActivity.this.xmppConnectionService
- .pushContactToServer(contact);
+ final var connection = contact.getAccount().getXmppConnection();
+ connection
+ .getManager(RosterManager.class)
+ .addRosterItem(contact, null);
populateView();
return null;
},
@@ -127,6 +127,7 @@ import eu.siacs.conversations.xmpp.jingle.JingleFileTransferConnection;
import eu.siacs.conversations.xmpp.jingle.Media;
import eu.siacs.conversations.xmpp.jingle.OngoingRtpSession;
import eu.siacs.conversations.xmpp.jingle.RtpCapability;
+import eu.siacs.conversations.xmpp.manager.PresenceManager;
import im.conversations.android.xmpp.model.stanza.Presence;
import java.util.ArrayList;
import java.util.Arrays;
@@ -446,7 +447,7 @@ public class ConversationFragment extends XmppFragment
public void onClick(View v) {
final Contact contact = conversation == null ? null : conversation.getContact();
if (contact != null) {
- activity.xmppConnectionService.createContact(contact, true);
+ activity.xmppConnectionService.createContact(contact);
activity.switchToContactDetails(contact);
}
}
@@ -458,11 +459,10 @@ public class ConversationFragment extends XmppFragment
public void onClick(View v) {
final Contact contact = conversation == null ? null : conversation.getContact();
if (contact != null) {
- activity.xmppConnectionService.sendPresencePacket(
- contact.getAccount(),
- activity.xmppConnectionService
- .getPresenceGenerator()
- .sendPresenceUpdatesTo(contact));
+ final var connection = contact.getAccount().getXmppConnection();
+ connection
+ .getManager(PresenceManager.class)
+ .subscribed(contact.getJid().asBareJid());
hideSnackbar();
}
}
@@ -647,7 +647,7 @@ public class StartConversationActivity extends XmppActivity
invite == null
? null
: invite.getParameter(XmppUri.PARAMETER_PRE_AUTH);
- xmppConnectionService.createContact(contact, true, preAuth);
+ xmppConnectionService.createContact(contact, preAuth);
if (invite != null && invite.hasFingerprints()) {
xmppConnectionService.verifyFingerprints(
contact, invite.getFingerprints());
@@ -85,6 +85,7 @@ import eu.siacs.conversations.utils.SignupUtils;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
+import eu.siacs.conversations.xmpp.manager.PresenceManager;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -894,7 +895,7 @@ public abstract class XmppActivity extends ActionBarActivity {
builder.setNegativeButton(getString(R.string.cancel), null);
builder.setPositiveButton(
getString(R.string.add_contact),
- (dialog, which) -> xmppConnectionService.createContact(contact, true));
+ (dialog, which) -> xmppConnectionService.createContact(contact));
builder.create().show();
}
@@ -906,13 +907,10 @@ public abstract class XmppActivity extends ActionBarActivity {
builder.setPositiveButton(
R.string.request_now,
(dialog, which) -> {
- if (xmppConnectionServiceBound) {
- xmppConnectionService.sendPresencePacket(
- contact.getAccount(),
- xmppConnectionService
- .getPresenceGenerator()
- .requestPresenceUpdatesFrom(contact));
- }
+ final var connection = contact.getAccount().getXmppConnection();
+ connection
+ .getManager(PresenceManager.class)
+ .subscribe(contact.getJid().asBareJid());
});
builder.create().show();
}
@@ -3071,7 +3071,7 @@ public class XmppConnection implements Runnable {
}
public boolean blocking() {
- return hasDiscoFeature(account.getDomain(), Namespace.BLOCKING);
+ return connection.getManager(BlockingManager.class).hasFeature();
}
public boolean spamReporting() {
@@ -2,11 +2,13 @@ package eu.siacs.conversations.xmpp.manager;
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;
import eu.siacs.conversations.Config;
+import eu.siacs.conversations.entities.Blockable;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid;
@@ -18,7 +20,9 @@ import im.conversations.android.xmpp.model.blocking.Item;
import im.conversations.android.xmpp.model.blocking.Unblock;
import im.conversations.android.xmpp.model.error.Condition;
import im.conversations.android.xmpp.model.error.Error;
+import im.conversations.android.xmpp.model.reporting.Report;
import im.conversations.android.xmpp.model.stanza.Iq;
+import im.conversations.android.xmpp.model.unique.StanzaId;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@@ -141,4 +145,85 @@ public class BlockingManager extends AbstractManager {
}
return builder.build();
}
+
+ public boolean block(
+ @NonNull final Blockable blockable,
+ final boolean reportSpam,
+ @Nullable final String serverMsgId) {
+ final var address = blockable.getBlockedJid();
+ final var iq = new Iq(Iq.Type.SET);
+ final var block = iq.addExtension(new Block());
+ final var item = block.addExtension(new Item());
+ item.setJid(address);
+ if (reportSpam) {
+ final var report = item.addExtension(new Report());
+ report.setReason(Namespace.REPORTING_REASON_SPAM);
+ if (serverMsgId != null) {
+ // XEP has a 'by' attribute that is the same as reported jid but that doesn't make
+ // sense this the 'by' attribute in the stanza-id refers to the arriving entity
+ // (usually the account or the MUC)
+ report.addExtension(new StanzaId(serverMsgId));
+ }
+ }
+ final var future = this.connection.sendIqPacket(iq);
+ Futures.addCallback(
+ future,
+ new FutureCallback<>() {
+ @Override
+ public void onSuccess(Iq result) {
+ synchronized (blocklist) {
+ blocklist.add(address);
+ }
+ service.updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
+ }
+
+ @Override
+ public void onFailure(@NonNull Throwable throwable) {
+ Log.d(
+ Config.LOGTAG,
+ getAccount().getJid().asBareJid() + ": could not block " + address,
+ throwable);
+ }
+ },
+ MoreExecutors.directExecutor());
+ if (address.isFullJid()) {
+ return false;
+ } else if (service.removeBlockedConversations(getAccount(), address)) {
+ service.updateConversationUi();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public void unblock(@NonNull final Blockable blockable) {
+ final var address = blockable.getBlockedJid();
+ final var iq = new Iq(Iq.Type.SET);
+ final var unblock = iq.addExtension(new Unblock());
+ final var item = unblock.addExtension(new Item());
+ item.setJid(address);
+ final var future = this.connection.sendIqPacket(iq);
+ Futures.addCallback(
+ future,
+ new FutureCallback<Iq>() {
+ @Override
+ public void onSuccess(Iq result) {
+ synchronized (blocklist) {
+ blocklist.remove(address);
+ }
+ service.updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED);
+ }
+
+ @Override
+ public void onFailure(@NonNull Throwable t) {
+ Log.d(
+ Config.LOGTAG,
+ getAccount().getJid().asBareJid()
+ + ": could not unblock "
+ + address,
+ t);
+ }
+ },
+ MoreExecutors.directExecutor());
+ }
}
@@ -1,12 +1,16 @@
package eu.siacs.conversations.xmpp.manager;
import android.content.Context;
+import com.google.common.base.Strings;
+import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.XmppConnection;
import im.conversations.android.xmpp.EntityCapabilities;
import im.conversations.android.xmpp.EntityCapabilities2;
import im.conversations.android.xmpp.ServiceDescription;
import im.conversations.android.xmpp.model.capabilties.Capabilities;
import im.conversations.android.xmpp.model.capabilties.LegacyCapabilities;
+import im.conversations.android.xmpp.model.nick.Nick;
+import im.conversations.android.xmpp.model.pars.PreAuth;
import im.conversations.android.xmpp.model.pgp.Signed;
import im.conversations.android.xmpp.model.stanza.Presence;
import java.util.HashMap;
@@ -21,6 +25,43 @@ public class PresenceManager extends AbstractManager {
super(context, connection);
}
+ public void subscribe(final Jid address) {
+ subscribe(address, null);
+ }
+
+ public void subscribe(final Jid address, final String preAuth) {
+
+ var presence = new Presence(Presence.Type.SUBSCRIBE);
+ presence.setTo(address);
+
+ final var displayName = getAccount().getDisplayName();
+ if (!Strings.isNullOrEmpty(displayName)) {
+ presence.addExtension(new Nick(displayName));
+ }
+ if (preAuth != null) {
+ presence.addExtension(new PreAuth()).setToken(preAuth);
+ }
+ this.connection.sendPresencePacket(presence);
+ }
+
+ public void unsubscribe(final Jid address) {
+ var presence = new Presence(Presence.Type.UNSUBSCRIBE);
+ presence.setTo(address);
+ this.connection.sendPresencePacket(presence);
+ }
+
+ public void unsubscribed(final Jid address) {
+ var presence = new Presence(Presence.Type.UNSUBSCRIBED);
+ presence.setTo(address);
+ this.connection.sendPresencePacket(presence);
+ }
+
+ public void subscribed(final Jid address) {
+ var presence = new Presence(Presence.Type.SUBSCRIBED);
+ presence.setTo(address);
+ this.connection.sendPresencePacket(presence);
+ }
+
public Presence getPresence(final Presence.Availability availability, final boolean personal) {
final var account = connection.getAccount();
final var serviceDiscoveryFeatures = getManager(DiscoManager.class).getServiceDescription();
@@ -212,7 +212,7 @@ public class RosterManager extends AbstractManager implements Roster {
public Contact getContactFromContactList(@NonNull final Jid jid) {
synchronized (this.contacts) {
final var contact =
- Iterables.find(this.contacts, c -> c.getJid().equals(jid.asBareJid()));
+ Iterables.find(this.contacts, c -> c.getJid().equals(jid.asBareJid()), null);
if (contact != null && contact.showInContactList()) {
return contact;
} else {
@@ -273,4 +273,105 @@ public class RosterManager extends AbstractManager implements Roster {
}
getDatabase().writeRoster(account, version, contacts);
}
+
+ public void syncDirtyContacts() {
+ synchronized (this.contacts) {
+ for (final var contact : this.contacts) {
+ if (contact.getOption(Contact.Options.DIRTY_PUSH)) {
+ addRosterItem(contact, null);
+ }
+ if (contact.getOption(Contact.Options.DIRTY_DELETE)) {
+ deleteRosterItem(contact);
+ }
+ }
+ }
+ }
+
+ public void addRosterItem(final Contact contact, final String preAuth) {
+ final var address = contact.getJid().asBareJid();
+ contact.resetOption(Contact.Options.DIRTY_DELETE);
+ contact.setOption(Contact.Options.DIRTY_PUSH);
+ // sync the 'dirty push' flag to disk in case we are offline
+ this.writeToDatabaseAsync();
+ final boolean ask = contact.getOption(Contact.Options.ASKING);
+ final boolean sendUpdates =
+ contact.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)
+ && contact.getOption(Contact.Options.PREEMPTIVE_GRANT);
+ final Iq iq = new Iq(Iq.Type.SET);
+ final var query = iq.addExtension(new Query());
+ final var item = query.addExtension(new Item());
+ item.setJid(address);
+ final var serverName = contact.getServerName();
+ if (serverName != null) {
+ item.setItemName(serverName);
+ }
+ item.setGroups(contact.getGroups(false));
+ final var future = this.connection.sendIqPacket(iq);
+ Futures.addCallback(
+ future,
+ new FutureCallback<Iq>() {
+ @Override
+ public void onSuccess(Iq result) {
+ Log.d(
+ Config.LOGTAG,
+ getAccount().getJid().asBareJid()
+ + ": pushed roster item "
+ + address);
+ }
+
+ @Override
+ public void onFailure(@NonNull Throwable t) {
+ Log.d(
+ Config.LOGTAG,
+ getAccount().getJid().asBareJid()
+ + ": could not push roster item "
+ + address,
+ t);
+ }
+ },
+ MoreExecutors.directExecutor());
+ if (sendUpdates) {
+ getManager(PresenceManager.class).subscribed(contact.getJid().asBareJid());
+ }
+ if (ask) {
+ getManager(PresenceManager.class).subscribe(contact.getJid().asBareJid(), preAuth);
+ }
+ }
+
+ public void deleteRosterItem(final Contact contact) {
+ final var address = contact.getJid().asBareJid();
+ contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
+ contact.resetOption(Contact.Options.DIRTY_PUSH);
+ contact.setOption(Contact.Options.DIRTY_DELETE);
+ this.writeToDatabaseAsync();
+ final Iq iq = new Iq(Iq.Type.SET);
+ final var query = iq.addExtension(new Query());
+ final var item = query.addExtension(new Item());
+ item.setJid(address);
+ item.setSubscription(Item.Subscription.REMOVE);
+ final var future = this.connection.sendIqPacket(iq);
+ Futures.addCallback(
+ future,
+ new FutureCallback<Iq>() {
+ @Override
+ public void onSuccess(final Iq result) {
+ Log.d(
+ Config.LOGTAG,
+ getAccount().getJid().asBareJid()
+ + ": removed roster item "
+ + address);
+ }
+
+ @Override
+ public void onFailure(final @NonNull Throwable t) {
+ Log.d(
+ Config.LOGTAG,
+ getAccount().getJid().asBareJid()
+ + ": could not remove roster item "
+ + address,
+ t);
+ }
+ },
+ MoreExecutors.directExecutor());
+ }
}
@@ -14,4 +14,8 @@ public class Item extends Extension {
public Jid getJid() {
return getAttributeAsJid("jid");
}
+
+ public void setJid(final Jid address) {
+ this.setAttribute("jid", address);
+ }
}
@@ -10,4 +10,9 @@ public class Nick extends Extension {
public Nick() {
super(Nick.class);
}
+
+ public Nick(final String nick) {
+ this();
+ this.setContent(nick);
+ }
}
@@ -0,0 +1,16 @@
+package im.conversations.android.xmpp.model.reporting;
+
+import eu.siacs.conversations.xml.Namespace;
+import im.conversations.android.annotation.XmlElement;
+import im.conversations.android.xmpp.model.Extension;
+
+@XmlElement(namespace = Namespace.REPORTING)
+public class Report extends Extension {
+ public Report() {
+ super(Report.class);
+ }
+
+ public void setReason(final String reason) {
+ this.setAttribute("reason", reason);
+ }
+}
@@ -9,4 +9,9 @@ public class Group extends Extension {
public Group() {
super(Group.class);
}
+
+ public Group(final String group) {
+ this();
+ this.setContent(group);
+ }
}
@@ -1,13 +1,10 @@
package im.conversations.android.xmpp.model.roster;
import com.google.common.collect.Collections2;
-
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.Jid;
-
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
-
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@@ -28,10 +25,18 @@ public class Item extends Extension {
return getAttributeAsJid("jid");
}
+ public void setJid(final Jid jid) {
+ this.setAttribute("jid", jid);
+ }
+
public String getItemName() {
return this.getAttribute("name");
}
+ public void setItemName(final String serverName) {
+ this.setAttribute("name", serverName);
+ }
+
public boolean isPendingOut() {
return "subscribe".equalsIgnoreCase(this.getAttribute("ask"));
}
@@ -45,12 +50,26 @@ public class Item extends Extension {
}
}
+ public void setSubscription(final Subscription subscription) {
+ if (subscription == null) {
+ this.removeAttribute("subscription");
+ } else {
+ this.setAttribute("subscription", subscription.toString().toLowerCase(Locale.ROOT));
+ }
+ }
+
public Collection<String> getGroups() {
return Collections2.filter(
Collections2.transform(getExtensions(Group.class), Element::getContent),
Objects::nonNull);
}
+ public void setGroups(final Collection<String> groups) {
+ for (final String group : groups) {
+ this.addExtension(new Group());
+ }
+ }
+
public enum Subscription {
NONE,
TO,
@@ -5,6 +5,7 @@ import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.capabilties.EntityCapabilities;
import im.conversations.android.xmpp.model.jabber.Show;
import im.conversations.android.xmpp.model.jabber.Status;
+import java.util.Locale;
@XmlElement
public class Presence extends Stanza implements EntityCapabilities {
@@ -13,6 +14,11 @@ public class Presence extends Stanza implements EntityCapabilities {
super(Presence.class);
}
+ public Presence(final Type type) {
+ this();
+ this.setType(type);
+ }
+
public Availability getAvailability() {
final var show = getExtension(Show.class);
if (show == null) {
@@ -28,6 +34,18 @@ public class Presence extends Stanza implements EntityCapabilities {
this.addExtension(new Show()).setContent(availability.toShowString());
}
+ public void setType(final Type type) {
+ if (type == null) {
+ this.removeAttribute("type");
+ } else {
+ this.setAttribute("type", type.toString().toLowerCase(Locale.ROOT));
+ }
+ }
+
+ public Type getType() {
+ return Type.valueOfOrNull(this.getAttribute("type"));
+ }
+
public void setStatus(final String status) {
if (Strings.isNullOrEmpty(status)) {
return;
@@ -40,6 +58,27 @@ public class Presence extends Stanza implements EntityCapabilities {
return status == null ? null : status.getContent();
}
+ public enum Type {
+ ERROR,
+ PROBE,
+ SUBSCRIBE,
+ SUBSCRIBED,
+ UNAVAILABLE,
+ UNSUBSCRIBE,
+ UNSUBSCRIBED;
+
+ public static Type valueOfOrNull(final String type) {
+ if (Strings.isNullOrEmpty(type)) {
+ return null;
+ }
+ try {
+ return valueOf(type.toUpperCase(Locale.ROOT));
+ } catch (final IllegalArgumentException e) {
+ return null;
+ }
+ }
+ }
+
public enum Availability {
CHAT,
ONLINE,
@@ -13,6 +13,11 @@ public class StanzaId extends Extension {
super(StanzaId.class);
}
+ public StanzaId(final String id) {
+ this();
+ this.setAttribute("id", id);
+ }
+
public Jid getBy() {
return this.getAttributeAsJid("by");
}
@@ -98,7 +98,7 @@ public class BindProcessor extends XmppConnection.Delegate implements Runnable {
service.getPushManagementService().registerPushTokenOnServer(account);
}
service.connectMultiModeConversations(account);
- service.syncDirtyContacts(account);
+ connection.getManager(RosterManager.class).syncDirtyContacts();
service.getUnifiedPushBroker().renewUnifiedPushEndpointsOnBind(account);
}