introduce Manager concept to bundle functionality like roster, blocking, …
This commit is contained in:
parent
20962554a4
commit
07c1669813
23
src/main/java/im/conversations/android/xmpp/Managers.java
Normal file
23
src/main/java/im/conversations/android/xmpp/Managers.java
Normal file
|
@ -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<AbstractManager> initialize(
|
||||
final Context context, final XmppConnection connection) {
|
||||
return new ImmutableClassToInstanceMap.Builder<AbstractManager>()
|
||||
.put(BlockingManager.class, new BlockingManager(context, connection))
|
||||
.put(BookmarkManager.class, new BookmarkManager(context, connection))
|
||||
.put(RosterManager.class, new RosterManager(context, connection))
|
||||
.build();
|
||||
}
|
||||
}
|
|
@ -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<MessagePacket> messagePacketConsumer;
|
||||
private final BiFunction<Jid, String, Boolean> messageAcknowledgeProcessor;
|
||||
private final Consumer<Jid> bindConsumer;
|
||||
private final ClassToInstanceMap<AbstractManager> managers;
|
||||
private Consumer<XmppConnection> 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 extends AbstractManager> T getManager(Class<T> 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 extends AbstractManager> T getManager(Class<T> type) {
|
||||
return connection.managers.getInstance(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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() {}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<Jid> {
|
||||
public class BindProcessor extends XmppConnection.Delegate implements Consumer<Jid> {
|
||||
|
||||
public BindProcessor(final Context context, final XmppConnection connection) {
|
||||
super(context, connection);
|
||||
|
@ -30,79 +24,18 @@ public class BindProcessor extends AbstractBaseProcessor implements Consumer<Jid
|
|||
|
||||
if (firstLogin) {
|
||||
// TODO publish display name if this is the first attempt
|
||||
// iirc this is used when the display name is set from a certificate or something
|
||||
// IIRC this is used when the display name is set from a certificate or something
|
||||
}
|
||||
|
||||
database.presenceDao().deletePresences(account.id);
|
||||
|
||||
fetchRoster();
|
||||
getManager(RosterManager.class).fetch();
|
||||
|
||||
// TODO check feature
|
||||
fetchBlocklist();
|
||||
// TODO check feature before fetching
|
||||
getManager(BlockingManager.class).fetch();
|
||||
|
||||
// TODO fetch bookmarks
|
||||
getManager(BookmarkManager.class).fetch();
|
||||
|
||||
// TODO send initial presence
|
||||
}
|
||||
|
||||
private void fetchRoster() {
|
||||
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::handleFetchRosterResult);
|
||||
}
|
||||
|
||||
private void handleFetchRosterResult(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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<IqPacket> {
|
||||
public class IqProcessor extends XmppConnection.Delegate implements Consumer<IqPacket> {
|
||||
|
||||
public IqProcessor(final Context context, final XmppConnection connection) {
|
||||
super(context, connection);
|
||||
|
@ -22,11 +26,21 @@ public class IqProcessor extends AbstractBaseProcessor implements Consumer<IqPac
|
|||
if (type == IqPacket.TYPE.SET
|
||||
&& connection.fromAccount(packet)
|
||||
&& packet.hasExtension(Query.class)) {
|
||||
handleRosterPush(packet.getExtension(Query.class));
|
||||
getManager(RosterManager.class).handlePush(packet.getExtension(Query.class));
|
||||
return;
|
||||
}
|
||||
if (type == IqPacket.TYPE.SET
|
||||
&& connection.fromAccount(packet)
|
||||
&& packet.hasExtension(Block.class)) {
|
||||
getManager(BlockingManager.class).handlePush(packet.getExtension(Block.class));
|
||||
return;
|
||||
}
|
||||
|
||||
private void handleRosterPush(final Query query) {
|
||||
final String version = query.getVersion();
|
||||
if (type == IqPacket.TYPE.SET
|
||||
&& connection.fromAccount(packet)
|
||||
&& packet.hasExtension(Unblock.class)) {
|
||||
getManager(BlockingManager.class).handlePush(packet.getExtension(Unblock.class));
|
||||
return;
|
||||
}
|
||||
// TODO return feature not implemented
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import eu.siacs.conversations.xmpp.Jid;
|
|||
import im.conversations.android.xmpp.XmppConnection;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public class MessageAcknowledgeProcessor extends AbstractBaseProcessor
|
||||
public class MessageAcknowledgeProcessor extends XmppConnection.Delegate
|
||||
implements BiFunction<Jid, String, Boolean> {
|
||||
|
||||
public MessageAcknowledgeProcessor(final Context context, final XmppConnection connection) {
|
||||
|
|
Loading…
Reference in a new issue