fetch roster. process result

This commit is contained in:
Daniel Gultsch 2023-01-14 13:31:47 +01:00
parent 9e7bbcc272
commit 6b232f7a5a
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2
8 changed files with 132 additions and 33 deletions

View file

@ -1,6 +1,7 @@
package eu.siacs.conversations.xml; package eu.siacs.conversations.xml;
import com.google.common.base.Optional; import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2; import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
@ -177,15 +178,15 @@ public class Element {
public Jid getAttributeAsJid(String name) { public Jid getAttributeAsJid(String name) {
final String jid = this.getAttribute(name); final String jid = this.getAttribute(name);
if (jid != null && !jid.isEmpty()) { if (Strings.isNullOrEmpty(jid)) {
return null;
}
try { try {
return Jid.ofEscaped(jid); return Jid.ofEscaped(jid);
} catch (final IllegalArgumentException e) { } catch (final IllegalArgumentException e) {
return InvalidJid.of(jid, this instanceof MessagePacket); return InvalidJid.of(jid, this instanceof MessagePacket);
} }
} }
return null;
}
public Hashtable<String, String> getAttributes() { public Hashtable<String, String> getAttributes() {
return this.attributes; return this.attributes;
@ -215,6 +216,7 @@ public class Element {
return elementOutput.toString(); return elementOutput.toString();
} }
// TODO should ultimately be removed once everything is an extension
public final String getName() { public final String getName() {
return name; return name;
} }

View file

@ -8,6 +8,7 @@ import androidx.room.TypeConverters;
import im.conversations.android.database.dao.AccountDao; import im.conversations.android.database.dao.AccountDao;
import im.conversations.android.database.dao.MessageDao; import im.conversations.android.database.dao.MessageDao;
import im.conversations.android.database.dao.PresenceDao; import im.conversations.android.database.dao.PresenceDao;
import im.conversations.android.database.dao.RosterDao;
import im.conversations.android.database.entity.AccountEntity; import im.conversations.android.database.entity.AccountEntity;
import im.conversations.android.database.entity.BlockedItemEntity; import im.conversations.android.database.entity.BlockedItemEntity;
import im.conversations.android.database.entity.ChatEntity; import im.conversations.android.database.entity.ChatEntity;
@ -71,4 +72,6 @@ public abstract class ConversationsDatabase extends RoomDatabase {
public abstract PresenceDao presenceDao(); public abstract PresenceDao presenceDao();
public abstract MessageDao messageDao(); public abstract MessageDao messageDao();
public abstract RosterDao rosterDao();
} }

View file

@ -0,0 +1,32 @@
package im.conversations.android.database.dao;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Transaction;
import im.conversations.android.database.entity.RosterItemEntity;
import im.conversations.android.database.model.Account;
import java.util.Collection;
@Dao
public abstract class RosterDao {
@Insert
protected abstract void insert(Collection<RosterItemEntity> rosterItems);
@Query("DELETE FROM roster WHERE accountId=:account")
protected abstract void clear(final long account);
@Query("UPDATE account SET rosterVersion=:version WHERE id=:account")
protected abstract void setRosterVersion(final long account, final String version);
@Transaction
public void setRoster(
final Account account,
final String version,
final Collection<RosterItemEntity> rosterItems) {
clear(account.id);
insert(rosterItems);
setRosterVersion(account.id, version);
}
}

View file

@ -5,7 +5,10 @@ import androidx.room.Entity;
import androidx.room.ForeignKey; import androidx.room.ForeignKey;
import androidx.room.Index; import androidx.room.Index;
import androidx.room.PrimaryKey; import androidx.room.PrimaryKey;
import im.conversations.android.database.model.Subscription; import com.google.common.collect.Collections2;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.xmpp.model.roster.Item;
import java.util.Collection;
@Entity( @Entity(
tableName = "roster", tableName = "roster",
@ -27,11 +30,26 @@ public class RosterItemEntity {
@NonNull public Long accountId; @NonNull public Long accountId;
@NonNull public String address; @NonNull public Jid address;
public Subscription subscription; public Item.Subscription subscription;
public boolean ask; public boolean isPendingOut;
public String name; public String name;
public static RosterItemEntity of(final long accountId, final Item item) {
final var entity = new RosterItemEntity();
entity.accountId = accountId;
entity.address = item.getJid();
entity.subscription = item.getSubscription();
entity.isPendingOut = item.isPendingOut();
entity.name = item.getItemName();
return entity;
}
public static Collection<RosterItemEntity> of(
final long accountId, final Collection<Item> items) {
return Collections2.transform(items, i -> of(accountId, i));
}
} }

View file

@ -1,8 +0,0 @@
package im.conversations.android.database.model;
public enum Subscription {
NONE,
TO,
FROM,
BOTH
}

View file

@ -1,13 +1,49 @@
package im.conversations.android.xmpp.model.roster; package im.conversations.android.xmpp.model.roster;
import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.annotation.XmlElement; import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension; import im.conversations.android.xmpp.model.Extension;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
@XmlElement @XmlElement
public class Item extends Extension { public class Item extends Extension {
public static final List<Subscription> RESULT_SUBSCRIPTIONS =
Arrays.asList(Subscription.NONE, Subscription.TO, Subscription.FROM, Subscription.BOTH);
public Item() { public Item() {
super("item", Namespace.ROSTER); super("item", Namespace.ROSTER);
} }
public Jid getJid() {
return getAttributeAsJid("jid");
}
public String getItemName() {
return this.getAttribute("name");
}
public boolean isPendingOut() {
return "subscribe".equalsIgnoreCase(this.getAttribute("ask"));
}
public Subscription getSubscription() {
final String value = this.getAttribute("subscription");
try {
return value == null ? null : Subscription.valueOf(value.toLowerCase(Locale.ROOT));
} catch (final IllegalArgumentException e) {
return null;
}
}
public enum Subscription {
NONE,
TO,
FROM,
BOTH,
REMOVE
}
} }

View file

@ -14,4 +14,8 @@ public class Query extends Extension {
public void setVersion(final String rosterVersion) { public void setVersion(final String rosterVersion) {
this.setAttribute("ver", rosterVersion); this.setAttribute("ver", rosterVersion);
} }
public String getVersion() {
return this.getAttribute("ver");
}
} }

View file

@ -3,9 +3,11 @@ package im.conversations.android.xmpp.processor;
import android.content.Context; import android.content.Context;
import android.util.Log; import android.util.Log;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import im.conversations.android.database.entity.RosterItemEntity;
import im.conversations.android.xmpp.XmppConnection; import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.model.roster.Item; import im.conversations.android.xmpp.model.roster.Item;
import im.conversations.android.xmpp.model.roster.Query; import im.conversations.android.xmpp.model.roster.Query;
@ -52,9 +54,10 @@ public class BindProcessor extends AbstractBaseProcessor implements Consumer<Jid
Log.d(Config.LOGTAG, account.address + ": fetching roster version " + rosterVersion); Log.d(Config.LOGTAG, account.address + ": fetching roster version " + rosterVersion);
rosterQuery.setVersion(rosterVersion); rosterQuery.setVersion(rosterVersion);
} }
connection.sendIqPacket( connection.sendIqPacket(iqPacket, this::handleFetchRosterResult);
iqPacket, }
result -> {
private void handleFetchRosterResult(final IqPacket result) {
if (result.getType() != IqPacket.TYPE.RESULT) { if (result.getType() != IqPacket.TYPE.RESULT) {
return; return;
} }
@ -63,8 +66,17 @@ public class BindProcessor extends AbstractBaseProcessor implements Consumer<Jid
// No query in result means further modifications are sent via pushes // No query in result means further modifications are sent via pushes
return; return;
} }
// TODO delete entire roster final var account = getAccount();
for (final Item item : query.getExtensions(Item.class)) {} 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 -> i != null && Item.RESULT_SUBSCRIPTIONS.contains(i.getSubscription()));
final var entities = RosterItemEntity.of(account.id, validItems);
database.rosterDao().setRoster(account, version, entities);
} }
} }