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;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.google.common.primitives.Ints;
@ -177,15 +178,15 @@ public class Element {
public Jid getAttributeAsJid(String name) {
final String jid = this.getAttribute(name);
if (jid != null && !jid.isEmpty()) {
if (Strings.isNullOrEmpty(jid)) {
return null;
}
try {
return Jid.ofEscaped(jid);
} catch (final IllegalArgumentException e) {
return InvalidJid.of(jid, this instanceof MessagePacket);
}
}
return null;
}
public Hashtable<String, String> getAttributes() {
return this.attributes;
@ -215,6 +216,7 @@ public class Element {
return elementOutput.toString();
}
// TODO should ultimately be removed once everything is an extension
public final String getName() {
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.MessageDao;
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.BlockedItemEntity;
import im.conversations.android.database.entity.ChatEntity;
@ -71,4 +72,6 @@ public abstract class ConversationsDatabase extends RoomDatabase {
public abstract PresenceDao presenceDao();
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.Index;
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(
tableName = "roster",
@ -27,11 +30,26 @@ public class RosterItemEntity {
@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 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;
import eu.siacs.conversations.xml.Namespace;
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.List;
import java.util.Locale;
@XmlElement
public class Item extends Extension {
public static final List<Subscription> RESULT_SUBSCRIPTIONS =
Arrays.asList(Subscription.NONE, Subscription.TO, Subscription.FROM, Subscription.BOTH);
public Item() {
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) {
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.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.database.entity.RosterItemEntity;
import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.model.roster.Item;
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);
rosterQuery.setVersion(rosterVersion);
}
connection.sendIqPacket(
iqPacket,
result -> {
connection.sendIqPacket(iqPacket, this::handleFetchRosterResult);
}
private void handleFetchRosterResult(final IqPacket result) {
if (result.getType() != IqPacket.TYPE.RESULT) {
return;
}
@ -63,8 +66,17 @@ public class BindProcessor extends AbstractBaseProcessor implements Consumer<Jid
// No query in result means further modifications are sent via pushes
return;
}
// TODO delete entire roster
for (final Item item : query.getExtensions(Item.class)) {}
});
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 -> i != null && Item.RESULT_SUBSCRIPTIONS.contains(i.getSubscription()));
final var entities = RosterItemEntity.of(account.id, validItems);
database.rosterDao().setRoster(account, version, entities);
}
}