diff --git a/src/main/java/im/conversations/android/xmpp/Managers.java b/src/main/java/im/conversations/android/xmpp/Managers.java new file mode 100644 index 000000000..6a2c64765 --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/Managers.java @@ -0,0 +1,23 @@ +package im.conversations.android.xmpp; + +import android.content.Context; +import com.google.common.collect.ClassToInstanceMap; +import com.google.common.collect.ImmutableClassToInstanceMap; +import im.conversations.android.xmpp.manager.AbstractManager; +import im.conversations.android.xmpp.manager.BlockingManager; +import im.conversations.android.xmpp.manager.BookmarkManager; +import im.conversations.android.xmpp.manager.RosterManager; + +public final class Managers { + + private Managers() {} + + public static ClassToInstanceMap initialize( + final Context context, final XmppConnection connection) { + return new ImmutableClassToInstanceMap.Builder() + .put(BlockingManager.class, new BlockingManager(context, connection)) + .put(BookmarkManager.class, new BookmarkManager(context, connection)) + .put(RosterManager.class, new RosterManager(context, connection)) + .build(); + } +} diff --git a/src/main/java/im/conversations/android/xmpp/XmppConnection.java b/src/main/java/im/conversations/android/xmpp/XmppConnection.java index cf48e2210..27f5858bb 100644 --- a/src/main/java/im/conversations/android/xmpp/XmppConnection.java +++ b/src/main/java/im/conversations/android/xmpp/XmppConnection.java @@ -16,6 +16,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.google.common.base.Optional; import com.google.common.base.Strings; +import com.google.common.collect.ClassToInstanceMap; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.XmppDomainVerifier; @@ -60,6 +61,7 @@ import im.conversations.android.database.CredentialStore; import im.conversations.android.database.model.Account; import im.conversations.android.database.model.Connection; import im.conversations.android.database.model.Credential; +import im.conversations.android.xmpp.manager.AbstractManager; import im.conversations.android.xmpp.processor.BindProcessor; import im.conversations.android.xmpp.processor.IqProcessor; import im.conversations.android.xmpp.processor.JingleProcessor; @@ -155,6 +157,7 @@ public class XmppConnection implements Runnable { private final Consumer messagePacketConsumer; private final BiFunction messageAcknowledgeProcessor; private final Consumer bindConsumer; + private final ClassToInstanceMap managers; private Consumer statusListener = null; private SaslMechanism saslMechanism; private HashedToken.Mechanism hashTokenRequest; @@ -178,12 +181,17 @@ public class XmppConnection implements Runnable { this.jinglePacketConsumer = new JingleProcessor(context, this); this.messageAcknowledgeProcessor = new MessageAcknowledgeProcessor(context, this); this.bindConsumer = new BindProcessor(context, this); + this.managers = Managers.initialize(context, this); } public Account getAccount() { return account; } + public T getManager(Class type) { + return this.managers.getInstance(type); + } + private String fixResource(final String resource) { if (Strings.isNullOrEmpty(resource)) { return null; @@ -2778,4 +2786,27 @@ public class XmppConnection implements Runnable { account.address.getDomain(), Namespace.EXTERNAL_SERVICE_DISCOVERY); } } + + public abstract static class Delegate { + + protected final Context context; + protected final XmppConnection connection; + + protected Delegate(final Context context, final XmppConnection connection) { + this.context = context; + this.connection = connection; + } + + protected Account getAccount() { + return connection.getAccount(); + } + + protected ConversationsDatabase getDatabase() { + return ConversationsDatabase.getInstance(context); + } + + public T getManager(Class type) { + return connection.managers.getInstance(type); + } + } } diff --git a/src/main/java/im/conversations/android/xmpp/manager/AbstractManager.java b/src/main/java/im/conversations/android/xmpp/manager/AbstractManager.java new file mode 100644 index 000000000..207602334 --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/manager/AbstractManager.java @@ -0,0 +1,11 @@ +package im.conversations.android.xmpp.manager; + +import android.content.Context; +import im.conversations.android.xmpp.XmppConnection; + +public class AbstractManager extends XmppConnection.Delegate { + + protected AbstractManager(final Context context, final XmppConnection connection) { + super(context, connection); + } +} diff --git a/src/main/java/im/conversations/android/xmpp/manager/BlockingManager.java b/src/main/java/im/conversations/android/xmpp/manager/BlockingManager.java new file mode 100644 index 000000000..dcf30ef42 --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/manager/BlockingManager.java @@ -0,0 +1,42 @@ +package im.conversations.android.xmpp.manager; + +import android.content.Context; +import com.google.common.collect.Collections2; +import eu.siacs.conversations.xmpp.stanzas.IqPacket; +import im.conversations.android.xmpp.XmppConnection; +import im.conversations.android.xmpp.model.blocking.Block; +import im.conversations.android.xmpp.model.blocking.Blocklist; +import im.conversations.android.xmpp.model.blocking.Unblock; +import java.util.Objects; + +public class BlockingManager extends AbstractManager { + + public BlockingManager(Context context, XmppConnection connection) { + super(context, connection); + } + + public void handlePush(final Block block) {} + + public void handlePush(final Unblock unblock) {} + + public void fetch() { + final IqPacket iqPacket = new IqPacket(IqPacket.TYPE.GET); + iqPacket.addChild(new Blocklist()); + connection.sendIqPacket(iqPacket, this::handleFetchResult); + } + + private void handleFetchResult(final IqPacket result) { + if (result.getType() != IqPacket.TYPE.RESULT) { + return; + } + final var blocklist = result.getExtension(Blocklist.class); + if (blocklist == null) { + return; + } + final var account = getAccount(); + final var items = + blocklist.getExtensions(im.conversations.android.xmpp.model.blocking.Item.class); + final var filteredItems = Collections2.filter(items, i -> Objects.nonNull(i.getJid())); + getDatabase().blockingDao().setBlocklist(account, filteredItems); + } +} diff --git a/src/main/java/im/conversations/android/xmpp/manager/BookmarkManager.java b/src/main/java/im/conversations/android/xmpp/manager/BookmarkManager.java new file mode 100644 index 000000000..615ed5bcb --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/manager/BookmarkManager.java @@ -0,0 +1,12 @@ +package im.conversations.android.xmpp.manager; + +import android.content.Context; +import im.conversations.android.xmpp.XmppConnection; + +public class BookmarkManager extends AbstractManager { + public BookmarkManager(Context context, XmppConnection connection) { + super(context, connection); + } + + public void fetch() {} +} diff --git a/src/main/java/im/conversations/android/xmpp/manager/RosterManager.java b/src/main/java/im/conversations/android/xmpp/manager/RosterManager.java new file mode 100644 index 000000000..dd3389ba8 --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/manager/RosterManager.java @@ -0,0 +1,65 @@ +package im.conversations.android.xmpp.manager; + +import android.content.Context; +import android.util.Log; +import com.google.common.base.Strings; +import com.google.common.collect.Collections2; +import eu.siacs.conversations.Config; +import eu.siacs.conversations.xmpp.stanzas.IqPacket; +import im.conversations.android.xmpp.XmppConnection; +import im.conversations.android.xmpp.model.roster.Item; +import im.conversations.android.xmpp.model.roster.Query; +import java.util.Objects; + +public class RosterManager extends AbstractManager { + + public RosterManager(final Context context, final XmppConnection connection) { + super(context, connection); + } + + public void handlePush(final Query query) { + final var version = query.getVersion(); + final var items = query.getExtensions(Item.class); + getDatabase().rosterDao().update(getAccount(), version, items); + } + + public void fetch() { + final var account = getAccount(); + final var database = getDatabase(); + final String rosterVersion = database.accountDao().getRosterVersion(account.id); + final IqPacket iqPacket = new IqPacket(IqPacket.TYPE.GET); + final Query rosterQuery = new Query(); + iqPacket.addChild(rosterQuery); + if (Strings.isNullOrEmpty(rosterVersion)) { + Log.d(Config.LOGTAG, account.address + ": fetching roster"); + } else { + Log.d(Config.LOGTAG, account.address + ": fetching roster version " + rosterVersion); + rosterQuery.setVersion(rosterVersion); + } + connection.sendIqPacket(iqPacket, this::handleFetchResult); + } + + private void handleFetchResult(final IqPacket result) { + if (result.getType() != IqPacket.TYPE.RESULT) { + return; + } + final var query = result.getExtension(Query.class); + if (query == null) { + // No query in result means further modifications are sent via pushes + return; + } + final var account = getAccount(); + final var database = getDatabase(); + final var version = query.getVersion(); + final var items = query.getExtensions(Item.class); + // In a roster result (Section 2.1.4), the client MUST ignore values of the c'subscription' + // attribute other than "none", "to", "from", or "both". + final var validItems = + Collections2.filter( + items, + i -> + Item.RESULT_SUBSCRIPTIONS.contains(i.getSubscription()) + && Objects.nonNull(i.getJid())); + database.rosterDao().set(account, version, validItems); + } +} diff --git a/src/main/java/im/conversations/android/xmpp/processor/AbstractBaseProcessor.java b/src/main/java/im/conversations/android/xmpp/processor/AbstractBaseProcessor.java deleted file mode 100644 index 65ed31050..000000000 --- a/src/main/java/im/conversations/android/xmpp/processor/AbstractBaseProcessor.java +++ /dev/null @@ -1,25 +0,0 @@ -package im.conversations.android.xmpp.processor; - -import android.content.Context; -import im.conversations.android.database.ConversationsDatabase; -import im.conversations.android.database.model.Account; -import im.conversations.android.xmpp.XmppConnection; - -abstract class AbstractBaseProcessor { - - protected final Context context; - protected final XmppConnection connection; - - AbstractBaseProcessor(final Context context, final XmppConnection connection) { - this.context = context; - this.connection = connection; - } - - protected Account getAccount() { - return connection.getAccount(); - } - - protected ConversationsDatabase getDatabase() { - return ConversationsDatabase.getInstance(context); - } -} diff --git a/src/main/java/im/conversations/android/xmpp/processor/BindProcessor.java b/src/main/java/im/conversations/android/xmpp/processor/BindProcessor.java index 1e3d05b16..d719ee48f 100644 --- a/src/main/java/im/conversations/android/xmpp/processor/BindProcessor.java +++ b/src/main/java/im/conversations/android/xmpp/processor/BindProcessor.java @@ -1,20 +1,14 @@ package im.conversations.android.xmpp.processor; import android.content.Context; -import android.util.Log; -import com.google.common.base.Strings; -import com.google.common.collect.Collections2; -import eu.siacs.conversations.Config; import eu.siacs.conversations.xmpp.Jid; -import eu.siacs.conversations.xmpp.stanzas.IqPacket; import im.conversations.android.xmpp.XmppConnection; -import im.conversations.android.xmpp.model.blocking.Blocklist; -import im.conversations.android.xmpp.model.roster.Item; -import im.conversations.android.xmpp.model.roster.Query; -import java.util.Objects; +import im.conversations.android.xmpp.manager.BlockingManager; +import im.conversations.android.xmpp.manager.BookmarkManager; +import im.conversations.android.xmpp.manager.RosterManager; import java.util.function.Consumer; -public class BindProcessor extends AbstractBaseProcessor implements Consumer { +public class BindProcessor extends XmppConnection.Delegate implements Consumer { public BindProcessor(final Context context, final XmppConnection connection) { super(context, connection); @@ -30,79 +24,18 @@ public class BindProcessor extends AbstractBaseProcessor implements Consumer - Item.RESULT_SUBSCRIPTIONS.contains(i.getSubscription()) - && Objects.nonNull(i.getJid())); - database.rosterDao().set(account, version, validItems); - } - - private void fetchBlocklist() { - final IqPacket iqPacket = new IqPacket(IqPacket.TYPE.GET); - iqPacket.addChild(new Blocklist()); - connection.sendIqPacket(iqPacket, this::handleFetchBlocklistResult); - } - - private void handleFetchBlocklistResult(final IqPacket result) { - if (result.getType() != IqPacket.TYPE.RESULT) { - return; - } - final var blocklist = result.getExtension(Blocklist.class); - if (blocklist == null) { - return; - } - final var account = getAccount(); - final var items = - blocklist.getExtensions(im.conversations.android.xmpp.model.blocking.Item.class); - final var filteredItems = Collections2.filter(items, i -> Objects.nonNull(i.getJid())); - getDatabase().blockingDao().setBlocklist(account, filteredItems); - } } diff --git a/src/main/java/im/conversations/android/xmpp/processor/IqProcessor.java b/src/main/java/im/conversations/android/xmpp/processor/IqProcessor.java index d782d2079..2db4bd074 100644 --- a/src/main/java/im/conversations/android/xmpp/processor/IqProcessor.java +++ b/src/main/java/im/conversations/android/xmpp/processor/IqProcessor.java @@ -4,11 +4,15 @@ import android.content.Context; import com.google.common.base.Preconditions; import eu.siacs.conversations.xmpp.stanzas.IqPacket; import im.conversations.android.xmpp.XmppConnection; +import im.conversations.android.xmpp.manager.BlockingManager; +import im.conversations.android.xmpp.manager.RosterManager; +import im.conversations.android.xmpp.model.blocking.Block; +import im.conversations.android.xmpp.model.blocking.Unblock; import im.conversations.android.xmpp.model.roster.Query; import java.util.Arrays; import java.util.function.Consumer; -public class IqProcessor extends AbstractBaseProcessor implements Consumer { +public class IqProcessor extends XmppConnection.Delegate implements Consumer { public IqProcessor(final Context context, final XmppConnection connection) { super(context, connection); @@ -22,11 +26,21 @@ public class IqProcessor extends AbstractBaseProcessor implements Consumer { public MessageAcknowledgeProcessor(final Context context, final XmppConnection connection) {