store disco features after fetching them
This commit is contained in:
parent
1b438117a3
commit
6458c6e9f9
|
@ -2,7 +2,7 @@
|
|||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "afc5b1df44123031e340e1a3db15396d",
|
||||
"identityHash": "adc70f7066828bb6cf1fc32aa3a24b2f",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "account",
|
||||
|
@ -248,7 +248,7 @@
|
|||
},
|
||||
{
|
||||
"tableName": "disco",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `accountId` INTEGER NOT NULL, `capsHash` BLOB, `caps2Hash` BLOB, `caps2Algorithm` TEXT, FOREIGN KEY(`accountId`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `accountId` INTEGER NOT NULL, `capsHash` BLOB, `caps2HashSha256` BLOB, FOREIGN KEY(`accountId`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
|
@ -269,16 +269,10 @@
|
|||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "caps2Hash",
|
||||
"columnName": "caps2Hash",
|
||||
"fieldPath": "caps2HashSha256",
|
||||
"columnName": "caps2HashSha256",
|
||||
"affinity": "BLOB",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "caps2Algorithm",
|
||||
"columnName": "caps2Algorithm",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
|
@ -289,13 +283,24 @@
|
|||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_disco_accountId",
|
||||
"name": "index_disco_accountId_capsHash",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"accountId"
|
||||
"accountId",
|
||||
"capsHash"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_disco_accountId` ON `${TABLE_NAME}` (`accountId`)"
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_disco_accountId_capsHash` ON `${TABLE_NAME}` (`accountId`, `capsHash`)"
|
||||
},
|
||||
{
|
||||
"name": "index_disco_accountId_caps2HashSha256",
|
||||
"unique": true,
|
||||
"columnNames": [
|
||||
"accountId",
|
||||
"caps2HashSha256"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_disco_accountId_caps2HashSha256` ON `${TABLE_NAME}` (`accountId`, `caps2HashSha256`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
|
@ -1299,7 +1304,7 @@
|
|||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'afc5b1df44123031e340e1a3db15396d')"
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'adc70f7066828bb6cf1fc32aa3a24b2f')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -1,12 +1,26 @@
|
|||
package im.conversations.android.database.dao;
|
||||
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.Transaction;
|
||||
import androidx.room.Upsert;
|
||||
import com.google.common.collect.Collections2;
|
||||
import eu.siacs.conversations.xmpp.Jid;
|
||||
import im.conversations.android.database.entity.DiscoEntity;
|
||||
import im.conversations.android.database.entity.DiscoExtensionEntity;
|
||||
import im.conversations.android.database.entity.DiscoExtensionFieldEntity;
|
||||
import im.conversations.android.database.entity.DiscoExtensionFieldValueEntity;
|
||||
import im.conversations.android.database.entity.DiscoFeatureEntity;
|
||||
import im.conversations.android.database.entity.DiscoIdentityEntity;
|
||||
import im.conversations.android.database.entity.DiscoItemEntity;
|
||||
import im.conversations.android.database.model.Account;
|
||||
import im.conversations.android.xmpp.model.data.Data;
|
||||
import im.conversations.android.xmpp.model.data.Field;
|
||||
import im.conversations.android.xmpp.model.data.Value;
|
||||
import im.conversations.android.xmpp.model.disco.info.Feature;
|
||||
import im.conversations.android.xmpp.model.disco.info.Identity;
|
||||
import im.conversations.android.xmpp.model.disco.info.InfoQuery;
|
||||
import im.conversations.android.xmpp.model.disco.items.Item;
|
||||
import java.util.Collection;
|
||||
|
||||
|
@ -14,16 +28,78 @@ import java.util.Collection;
|
|||
public abstract class DiscoDao {
|
||||
|
||||
@Upsert(entity = DiscoItemEntity.class)
|
||||
protected abstract void setDiscoItems(Collection<DiscoItemWithParent> items);
|
||||
protected abstract void insertDiscoItems(Collection<DiscoItemWithParent> items);
|
||||
|
||||
@Insert
|
||||
protected abstract void insertDiscoIdentities(Collection<DiscoIdentityEntity> identities);
|
||||
|
||||
@Insert
|
||||
protected abstract void insertDiscoFeatures(Collection<DiscoFeatureEntity> features);
|
||||
|
||||
@Insert
|
||||
protected abstract void insertDiscoFieldValues(
|
||||
Collection<DiscoExtensionFieldValueEntity> value);
|
||||
|
||||
@Upsert(entity = DiscoItemEntity.class)
|
||||
protected abstract void insert(DiscoItemWithDiscoId item);
|
||||
|
||||
@Insert
|
||||
protected abstract long insert(DiscoEntity entity);
|
||||
|
||||
@Insert
|
||||
protected abstract long insert(DiscoExtensionEntity entity);
|
||||
|
||||
@Insert
|
||||
protected abstract long insert(DiscoExtensionFieldEntity entity);
|
||||
|
||||
@Transaction
|
||||
public void setDiscoItems(
|
||||
final Account account, final Jid parent, final Collection<Item> items) {
|
||||
public void set(final Account account, final Jid parent, final Collection<Item> items) {
|
||||
final var entities =
|
||||
Collections2.transform(items, i -> DiscoItemWithParent.of(account.id, parent, i));
|
||||
setDiscoItems(entities);
|
||||
insertDiscoItems(entities);
|
||||
}
|
||||
|
||||
@Transaction
|
||||
public void set(
|
||||
final Account account,
|
||||
final Jid address,
|
||||
final String node,
|
||||
final byte[] capsHash,
|
||||
final byte[] caps2HashSha256,
|
||||
final InfoQuery infoQuery) {
|
||||
|
||||
final Long existingDiscoId = getDiscoId(account.id, caps2HashSha256);
|
||||
if (existingDiscoId != null) {
|
||||
insert(DiscoItemWithDiscoId.of(account.id, address, node, existingDiscoId));
|
||||
return;
|
||||
}
|
||||
final long discoId = insert(DiscoEntity.of(account.id, capsHash, caps2HashSha256));
|
||||
|
||||
insertDiscoIdentities(
|
||||
Collections2.transform(
|
||||
infoQuery.getExtensions(Identity.class),
|
||||
i -> DiscoIdentityEntity.of(discoId, i)));
|
||||
|
||||
insertDiscoFeatures(
|
||||
Collections2.transform(
|
||||
infoQuery.getExtensions(Feature.class),
|
||||
f -> DiscoFeatureEntity.of(discoId, f.getVar())));
|
||||
for (final Data data : infoQuery.getExtensions(Data.class)) {
|
||||
final var extensionId = insert(DiscoExtensionEntity.of(discoId));
|
||||
for (final var field : data.getExtensions(Field.class)) {
|
||||
final var fieldId =
|
||||
insert(DiscoExtensionFieldEntity.of(extensionId, field.getFieldName()));
|
||||
insertDiscoFieldValues(
|
||||
Collections2.transform(
|
||||
field.getExtensions(Value.class),
|
||||
v -> DiscoExtensionFieldValueEntity.of(fieldId, v.getContent())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Query("SELECT id FROM disco WHERE accountId=:accountId AND caps2HashSha256=:caps2HashSha256")
|
||||
protected abstract Long getDiscoId(final long accountId, final byte[] caps2HashSha256);
|
||||
|
||||
public static class DiscoItemWithParent {
|
||||
public long accountId;
|
||||
public Jid address;
|
||||
|
@ -40,4 +116,21 @@ public abstract class DiscoDao {
|
|||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
public static class DiscoItemWithDiscoId {
|
||||
public long accountId;
|
||||
public Jid address;
|
||||
public String node;
|
||||
public long discoId;
|
||||
|
||||
public static DiscoItemWithDiscoId of(
|
||||
final long account, final Jid address, final String node, final long discoId) {
|
||||
final var entity = new DiscoItemWithDiscoId();
|
||||
entity.accountId = account;
|
||||
entity.address = address;
|
||||
entity.node = node;
|
||||
entity.discoId = discoId;
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,15 +14,28 @@ import androidx.room.PrimaryKey;
|
|||
parentColumns = {"id"},
|
||||
childColumns = {"accountId"},
|
||||
onDelete = ForeignKey.CASCADE),
|
||||
indices = {@Index(value = {"accountId"})})
|
||||
indices = {
|
||||
@Index(value = {"accountId", "capsHash"}),
|
||||
@Index(
|
||||
value = {"accountId", "caps2HashSha256"},
|
||||
unique = true)
|
||||
})
|
||||
public class DiscoEntity {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
public Long id;
|
||||
|
||||
@NonNull Long accountId;
|
||||
@NonNull public Long accountId;
|
||||
|
||||
public byte[] capsHash;
|
||||
public byte[] caps2Hash;
|
||||
public String caps2Algorithm;
|
||||
public byte[] caps2HashSha256;
|
||||
|
||||
public static DiscoEntity of(
|
||||
final long accountId, final byte[] capsHash, final byte[] caps2HashSha256) {
|
||||
final var entity = new DiscoEntity();
|
||||
entity.accountId = accountId;
|
||||
entity.capsHash = capsHash;
|
||||
entity.caps2HashSha256 = caps2HashSha256;
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,4 +21,10 @@ public class DiscoExtensionEntity {
|
|||
public Long id;
|
||||
|
||||
@NonNull public Long discoId;
|
||||
|
||||
public static DiscoExtensionEntity of(long discoId) {
|
||||
final var entity = new DiscoExtensionEntity();
|
||||
entity.discoId = discoId;
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,4 +23,11 @@ public class DiscoExtensionFieldEntity {
|
|||
@NonNull public Long extensionId;
|
||||
|
||||
public String field;
|
||||
|
||||
public static DiscoExtensionFieldEntity of(final long extensionId, final String fieldName) {
|
||||
final var entity = new DiscoExtensionFieldEntity();
|
||||
entity.extensionId = extensionId;
|
||||
entity.field = fieldName;
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,4 +23,8 @@ public class DiscoExtensionFieldValueEntity {
|
|||
@NonNull public Long fieldId;
|
||||
|
||||
public String value;
|
||||
|
||||
public static DiscoExtensionFieldValueEntity of(long fieldId, final String value) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,4 +23,11 @@ public class DiscoFeatureEntity {
|
|||
@NonNull public Long discoId;
|
||||
|
||||
@NonNull public String feature;
|
||||
|
||||
public static DiscoFeatureEntity of(final long discoId, final String feature) {
|
||||
final var entity = new DiscoFeatureEntity();
|
||||
entity.discoId = discoId;
|
||||
entity.feature = feature;
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.room.Entity;
|
|||
import androidx.room.ForeignKey;
|
||||
import androidx.room.Index;
|
||||
import androidx.room.PrimaryKey;
|
||||
import im.conversations.android.xmpp.model.disco.info.Identity;
|
||||
|
||||
@Entity(
|
||||
tableName = "disco_identity",
|
||||
|
@ -25,4 +26,13 @@ public class DiscoIdentityEntity {
|
|||
public String category;
|
||||
public String type;
|
||||
public String name;
|
||||
|
||||
public static DiscoIdentityEntity of(final long discoId, final Identity i) {
|
||||
final var entity = new DiscoIdentityEntity();
|
||||
entity.discoId = discoId;
|
||||
entity.category = i.getCategory();
|
||||
entity.type = i.getType();
|
||||
entity.name = i.getIdentityName();
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import com.google.common.base.Joiner;
|
|||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.hash.HashFunction;
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.common.primitives.Bytes;
|
||||
import im.conversations.android.xmpp.model.data.Data;
|
||||
|
@ -25,8 +26,12 @@ public class EntityCapabilities2 {
|
|||
private static final char FILE_SEPARATOR = 0x1c;
|
||||
|
||||
public static byte[] hash(final InfoQuery info) {
|
||||
return hash(Hashing.sha256(), info);
|
||||
}
|
||||
|
||||
public static byte[] hash(HashFunction hashFunction, final InfoQuery info) {
|
||||
final String algo = algorithm(info);
|
||||
return Hashing.sha256().hashString(algo, StandardCharsets.UTF_8).asBytes();
|
||||
return hashFunction.hashString(algo, StandardCharsets.UTF_8).asBytes();
|
||||
}
|
||||
|
||||
private static String asHex(final String message) {
|
||||
|
|
|
@ -7,6 +7,8 @@ import com.google.common.util.concurrent.ListenableFuture;
|
|||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import eu.siacs.conversations.xmpp.Jid;
|
||||
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
|
||||
import im.conversations.android.xmpp.EntityCapabilities;
|
||||
import im.conversations.android.xmpp.EntityCapabilities2;
|
||||
import im.conversations.android.xmpp.XmppConnection;
|
||||
import im.conversations.android.xmpp.model.disco.info.InfoQuery;
|
||||
import im.conversations.android.xmpp.model.disco.items.Item;
|
||||
|
@ -21,18 +23,34 @@ public class DiscoManager extends AbstractManager {
|
|||
}
|
||||
|
||||
public ListenableFuture<InfoQuery> info(final Jid entity) {
|
||||
final var iqPacket = new IqPacket(IqPacket.TYPE.GET);
|
||||
iqPacket.setTo(entity);
|
||||
iqPacket.addChild(new InfoQuery());
|
||||
final var future = connection.sendIqPacket(iqPacket);
|
||||
return info(entity, null);
|
||||
}
|
||||
|
||||
public ListenableFuture<InfoQuery> info(final Jid entity, final String node) {
|
||||
final var iqRequest = new IqPacket(IqPacket.TYPE.GET);
|
||||
iqRequest.setTo(entity);
|
||||
final var infoQueryRequest = new InfoQuery();
|
||||
if (node != null) {
|
||||
infoQueryRequest.setNode(node);
|
||||
}
|
||||
iqRequest.addChild(infoQueryRequest);
|
||||
final var future = connection.sendIqPacket(iqRequest);
|
||||
return Futures.transform(
|
||||
future,
|
||||
iqResult -> {
|
||||
final var infoQuery = iqResult.getExtension(InfoQuery.class);
|
||||
if (infoQuery == null) {
|
||||
throw new IllegalStateException();
|
||||
throw new IllegalStateException("Response did not have query child");
|
||||
}
|
||||
// TODO store query
|
||||
if (!Objects.equals(node, infoQuery.getNode())) {
|
||||
throw new IllegalStateException(
|
||||
"Node in response did not match node in request");
|
||||
}
|
||||
final byte[] caps = EntityCapabilities.hash(infoQuery);
|
||||
final byte[] caps2 = EntityCapabilities2.hash(infoQuery);
|
||||
getDatabase()
|
||||
.discoDao()
|
||||
.set(getAccount(), entity, node, caps, caps2, infoQuery);
|
||||
return infoQuery;
|
||||
},
|
||||
MoreExecutors.directExecutor());
|
||||
|
@ -53,7 +71,7 @@ public class DiscoManager extends AbstractManager {
|
|||
final var items = itemsQuery.getExtensions(Item.class);
|
||||
final var validItems =
|
||||
Collections2.filter(items, i -> Objects.nonNull(i.getJid()));
|
||||
getDatabase().discoDao().setDiscoItems(getAccount(), entity, validItems);
|
||||
getDatabase().discoDao().set(getAccount(), entity, validItems);
|
||||
return validItems;
|
||||
},
|
||||
MoreExecutors.directExecutor());
|
||||
|
|
|
@ -9,4 +9,12 @@ public class InfoQuery extends Extension {
|
|||
public InfoQuery() {
|
||||
super(InfoQuery.class);
|
||||
}
|
||||
|
||||
public void setNode(final String node) {
|
||||
this.setAttribute("node", node);
|
||||
}
|
||||
|
||||
public String getNode() {
|
||||
return this.getAttribute("node");
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue