use empty string instead of null for 'no node' and 'no resource'
This commit is contained in:
parent
90e613f94e
commit
1a09b3ed05
|
@ -2,7 +2,7 @@
|
||||||
"formatVersion": 1,
|
"formatVersion": 1,
|
||||||
"database": {
|
"database": {
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"identityHash": "a521ff7a3e16cca9f6cbfe51241ec021",
|
"identityHash": "b28e01dcbb5d9774a4b36783d0db6c73",
|
||||||
"entities": [
|
"entities": [
|
||||||
{
|
{
|
||||||
"tableName": "account",
|
"tableName": "account",
|
||||||
|
@ -427,7 +427,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tableName": "disco_ext_field_value",
|
"tableName": "disco_ext_field_value",
|
||||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `fieldId` INTEGER NOT NULL, `value` TEXT, FOREIGN KEY(`fieldId`) REFERENCES `disco_ext`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `fieldId` INTEGER NOT NULL, `value` TEXT, FOREIGN KEY(`fieldId`) REFERENCES `disco_ext_field`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldPath": "id",
|
"fieldPath": "id",
|
||||||
|
@ -467,7 +467,7 @@
|
||||||
],
|
],
|
||||||
"foreignKeys": [
|
"foreignKeys": [
|
||||||
{
|
{
|
||||||
"table": "disco_ext",
|
"table": "disco_ext_field",
|
||||||
"onDelete": "CASCADE",
|
"onDelete": "CASCADE",
|
||||||
"onUpdate": "NO ACTION",
|
"onUpdate": "NO ACTION",
|
||||||
"columns": [
|
"columns": [
|
||||||
|
@ -601,7 +601,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tableName": "disco_item",
|
"tableName": "disco_item",
|
||||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `accountId` INTEGER NOT NULL, `address` TEXT NOT NULL, `node` TEXT, `parent` TEXT, `discoId` INTEGER, FOREIGN KEY(`accountId`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`discoId`) REFERENCES `disco`(`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, `address` TEXT NOT NULL, `node` TEXT NOT NULL, `parent` TEXT, `discoId` INTEGER, FOREIGN KEY(`accountId`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`discoId`) REFERENCES `disco`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldPath": "id",
|
"fieldPath": "id",
|
||||||
|
@ -625,7 +625,7 @@
|
||||||
"fieldPath": "node",
|
"fieldPath": "node",
|
||||||
"columnName": "node",
|
"columnName": "node",
|
||||||
"affinity": "TEXT",
|
"affinity": "TEXT",
|
||||||
"notNull": false
|
"notNull": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldPath": "parent",
|
"fieldPath": "parent",
|
||||||
|
@ -975,7 +975,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tableName": "presence",
|
"tableName": "presence",
|
||||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `accountId` INTEGER NOT NULL, `address` TEXT NOT NULL, `resource` TEXT, `type` TEXT, `show` TEXT, `status` TEXT, `vCardPhoto` TEXT, `occupantId` TEXT, `mucUserAffiliation` TEXT, `mucUserRole` TEXT, `mucUserJid` TEXT, `mucUserSelf` INTEGER NOT NULL, 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, `address` TEXT NOT NULL, `resource` TEXT NOT NULL, `type` TEXT, `show` TEXT, `status` TEXT, `vCardPhoto` TEXT, `occupantId` TEXT, `mucUserAffiliation` TEXT, `mucUserRole` TEXT, `mucUserJid` TEXT, `mucUserSelf` INTEGER NOT NULL, `discoId` INTEGER, FOREIGN KEY(`accountId`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`discoId`) REFERENCES `disco`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldPath": "id",
|
"fieldPath": "id",
|
||||||
|
@ -999,7 +999,7 @@
|
||||||
"fieldPath": "resource",
|
"fieldPath": "resource",
|
||||||
"columnName": "resource",
|
"columnName": "resource",
|
||||||
"affinity": "TEXT",
|
"affinity": "TEXT",
|
||||||
"notNull": false
|
"notNull": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldPath": "type",
|
"fieldPath": "type",
|
||||||
|
@ -1054,6 +1054,12 @@
|
||||||
"columnName": "mucUserSelf",
|
"columnName": "mucUserSelf",
|
||||||
"affinity": "INTEGER",
|
"affinity": "INTEGER",
|
||||||
"notNull": true
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "discoId",
|
||||||
|
"columnName": "discoId",
|
||||||
|
"affinity": "INTEGER",
|
||||||
|
"notNull": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"primaryKey": {
|
"primaryKey": {
|
||||||
|
@ -1073,6 +1079,15 @@
|
||||||
],
|
],
|
||||||
"orders": [],
|
"orders": [],
|
||||||
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_presence_accountId_address_resource` ON `${TABLE_NAME}` (`accountId`, `address`, `resource`)"
|
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_presence_accountId_address_resource` ON `${TABLE_NAME}` (`accountId`, `address`, `resource`)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "index_presence_discoId",
|
||||||
|
"unique": false,
|
||||||
|
"columnNames": [
|
||||||
|
"discoId"
|
||||||
|
],
|
||||||
|
"orders": [],
|
||||||
|
"createSql": "CREATE INDEX IF NOT EXISTS `index_presence_discoId` ON `${TABLE_NAME}` (`discoId`)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"foreignKeys": [
|
"foreignKeys": [
|
||||||
|
@ -1086,6 +1101,17 @@
|
||||||
"referencedColumns": [
|
"referencedColumns": [
|
||||||
"id"
|
"id"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"table": "disco",
|
||||||
|
"onDelete": "CASCADE",
|
||||||
|
"onUpdate": "NO ACTION",
|
||||||
|
"columns": [
|
||||||
|
"discoId"
|
||||||
|
],
|
||||||
|
"referencedColumns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -1310,7 +1336,7 @@
|
||||||
"views": [],
|
"views": [],
|
||||||
"setupQueries": [
|
"setupQueries": [
|
||||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
"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, 'a521ff7a3e16cca9f6cbfe51241ec021')"
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b28e01dcbb5d9774a4b36783d0db6c73')"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -72,7 +72,7 @@ public class Element {
|
||||||
}
|
}
|
||||||
|
|
||||||
public <E extends Extension> E getExtension(final Class<E> clazz) {
|
public <E extends Extension> E getExtension(final Class<E> clazz) {
|
||||||
final var extension = Iterables.find(this.children, clazz::isInstance);
|
final var extension = Iterables.find(this.children, clazz::isInstance, null);
|
||||||
if (extension == null) {
|
if (extension == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
package im.conversations.android.database.dao;
|
package im.conversations.android.database.dao;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.room.Dao;
|
import androidx.room.Dao;
|
||||||
import androidx.room.Insert;
|
import androidx.room.Insert;
|
||||||
import androidx.room.Query;
|
import androidx.room.Query;
|
||||||
import androidx.room.Transaction;
|
import androidx.room.Transaction;
|
||||||
import androidx.room.Upsert;
|
import androidx.room.Upsert;
|
||||||
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.Collections2;
|
import com.google.common.collect.Collections2;
|
||||||
import eu.siacs.conversations.xmpp.Jid;
|
import eu.siacs.conversations.xmpp.Jid;
|
||||||
import im.conversations.android.database.entity.DiscoEntity;
|
import im.conversations.android.database.entity.DiscoEntity;
|
||||||
|
@ -15,6 +18,7 @@ import im.conversations.android.database.entity.DiscoFeatureEntity;
|
||||||
import im.conversations.android.database.entity.DiscoIdentityEntity;
|
import im.conversations.android.database.entity.DiscoIdentityEntity;
|
||||||
import im.conversations.android.database.entity.DiscoItemEntity;
|
import im.conversations.android.database.entity.DiscoItemEntity;
|
||||||
import im.conversations.android.database.model.Account;
|
import im.conversations.android.database.model.Account;
|
||||||
|
import im.conversations.android.xmpp.Entity;
|
||||||
import im.conversations.android.xmpp.EntityCapabilities;
|
import im.conversations.android.xmpp.EntityCapabilities;
|
||||||
import im.conversations.android.xmpp.EntityCapabilities2;
|
import im.conversations.android.xmpp.EntityCapabilities2;
|
||||||
import im.conversations.android.xmpp.model.data.Data;
|
import im.conversations.android.xmpp.model.data.Data;
|
||||||
|
@ -43,12 +47,18 @@ public abstract class DiscoDao {
|
||||||
protected abstract void deleteNonExistentDiscoItems(
|
protected abstract void deleteNonExistentDiscoItems(
|
||||||
final long account, final Jid parent, final Collection<Jid> existent);
|
final long account, final Jid parent, final Collection<Jid> existent);
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"UPDATE presence SET discoId=:discoId WHERE accountId=:account AND address=:address"
|
||||||
|
+ " AND resource=:resource")
|
||||||
|
protected abstract void updateDiscoIdInPresence(
|
||||||
|
long account, Jid address, String resource, long discoId);
|
||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
protected abstract void insertDiscoFieldValues(
|
protected abstract void insertDiscoFieldValues(
|
||||||
Collection<DiscoExtensionFieldValueEntity> value);
|
Collection<DiscoExtensionFieldValueEntity> value);
|
||||||
|
|
||||||
@Upsert(entity = DiscoItemEntity.class)
|
@Upsert(entity = DiscoItemEntity.class)
|
||||||
protected abstract void insert(DiscoItemWithDiscoId item);
|
protected abstract void updateDiscoIdInDiscoItem(DiscoItemWithDiscoId item);
|
||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
protected abstract long insert(DiscoEntity entity);
|
protected abstract long insert(DiscoEntity entity);
|
||||||
|
@ -60,19 +70,20 @@ public abstract class DiscoDao {
|
||||||
protected abstract long insert(DiscoExtensionFieldEntity entity);
|
protected abstract long insert(DiscoExtensionFieldEntity entity);
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
public void set(final Account account, final Jid parent, final Collection<Item> items) {
|
public void set(
|
||||||
|
final Account account, final Entity.DiscoItem parent, final Collection<Item> items) {
|
||||||
final var entities =
|
final var entities =
|
||||||
Collections2.transform(items, i -> DiscoItemWithParent.of(account.id, parent, i));
|
Collections2.transform(items, i -> DiscoItemWithParent.of(account.id, parent, i));
|
||||||
insertDiscoItems(entities);
|
insertDiscoItems(entities);
|
||||||
deleteNonExistentDiscoItems(
|
deleteNonExistentDiscoItems(
|
||||||
account.id, parent, Collections2.transform(items, Item::getJid));
|
account.id, parent.address, Collections2.transform(items, Item::getJid));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
public boolean set(
|
public boolean set(
|
||||||
final Account account,
|
final Account account,
|
||||||
final Jid address,
|
final Entity entity,
|
||||||
final String node,
|
@Nullable final String node,
|
||||||
final EntityCapabilities.Hash capsHash) {
|
final EntityCapabilities.Hash capsHash) {
|
||||||
final Long existingDiscoId;
|
final Long existingDiscoId;
|
||||||
if (capsHash instanceof EntityCapabilities2.EntityCaps2Hash) {
|
if (capsHash instanceof EntityCapabilities2.EntityCaps2Hash) {
|
||||||
|
@ -85,14 +96,31 @@ public abstract class DiscoDao {
|
||||||
if (existingDiscoId == null) {
|
if (existingDiscoId == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
insert(DiscoItemWithDiscoId.of(account.id, address, node, existingDiscoId));
|
updateDiscoId(account.id, entity, node, existingDiscoId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void updateDiscoId(
|
||||||
|
final long account,
|
||||||
|
final Entity entity,
|
||||||
|
@Nullable final String node,
|
||||||
|
final long discoId) {
|
||||||
|
if (entity instanceof Entity.DiscoItem) {
|
||||||
|
updateDiscoIdInDiscoItem(
|
||||||
|
DiscoItemWithDiscoId.of(account, (Entity.DiscoItem) entity, node, discoId));
|
||||||
|
} else if (entity instanceof Entity.Presence) {
|
||||||
|
updateDiscoIdInPresence(
|
||||||
|
account,
|
||||||
|
entity.address.asBareJid(),
|
||||||
|
Strings.nullToEmpty(entity.address.getResource()),
|
||||||
|
discoId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
public void set(
|
public void set(
|
||||||
final Account account,
|
final Account account,
|
||||||
final Jid address,
|
final Entity entity,
|
||||||
final String node,
|
final String node,
|
||||||
final byte[] capsHash,
|
final byte[] capsHash,
|
||||||
final byte[] caps2HashSha256,
|
final byte[] caps2HashSha256,
|
||||||
|
@ -100,7 +128,7 @@ public abstract class DiscoDao {
|
||||||
|
|
||||||
final Long existingDiscoId = getDiscoId(account.id, caps2HashSha256);
|
final Long existingDiscoId = getDiscoId(account.id, caps2HashSha256);
|
||||||
if (existingDiscoId != null) {
|
if (existingDiscoId != null) {
|
||||||
insert(DiscoItemWithDiscoId.of(account.id, address, node, existingDiscoId));
|
updateDiscoId(account.id, entity, node, existingDiscoId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final long discoId = insert(DiscoEntity.of(account.id, capsHash, caps2HashSha256));
|
final long discoId = insert(DiscoEntity.of(account.id, capsHash, caps2HashSha256));
|
||||||
|
@ -125,6 +153,7 @@ public abstract class DiscoDao {
|
||||||
v -> DiscoExtensionFieldValueEntity.of(fieldId, v.getContent())));
|
v -> DiscoExtensionFieldValueEntity.of(fieldId, v.getContent())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
updateDiscoId(account.id, entity, node, discoId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Query("SELECT id FROM disco WHERE accountId=:accountId AND caps2HashSha256=:caps2HashSha256")
|
@Query("SELECT id FROM disco WHERE accountId=:accountId AND caps2HashSha256=:caps2HashSha256")
|
||||||
|
@ -141,32 +170,35 @@ public abstract class DiscoDao {
|
||||||
|
|
||||||
public static class DiscoItemWithParent {
|
public static class DiscoItemWithParent {
|
||||||
public long accountId;
|
public long accountId;
|
||||||
public Jid address;
|
public @NonNull Jid address;
|
||||||
public String node;
|
public @NonNull String node;
|
||||||
public Jid parent;
|
public @Nullable Jid parent;
|
||||||
|
|
||||||
public static DiscoItemWithParent of(
|
public static DiscoItemWithParent of(
|
||||||
final long account, final Jid parent, final Item item) {
|
final long account, Entity.DiscoItem parent, final Item item) {
|
||||||
final var entity = new DiscoItemWithParent();
|
final var entity = new DiscoItemWithParent();
|
||||||
entity.accountId = account;
|
entity.accountId = account;
|
||||||
entity.address = item.getJid();
|
entity.address = item.getJid();
|
||||||
entity.node = item.getNode();
|
entity.node = Strings.nullToEmpty(item.getNode());
|
||||||
entity.parent = parent;
|
entity.parent = parent.address;
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class DiscoItemWithDiscoId {
|
public static class DiscoItemWithDiscoId {
|
||||||
public long accountId;
|
public long accountId;
|
||||||
public Jid address;
|
public @NonNull Jid address;
|
||||||
public String node;
|
public @NonNull String node;
|
||||||
public long discoId;
|
public long discoId;
|
||||||
|
|
||||||
public static DiscoItemWithDiscoId of(
|
public static DiscoItemWithDiscoId of(
|
||||||
final long account, final Jid address, final String node, final long discoId) {
|
final long account,
|
||||||
|
final Entity.DiscoItem discoItem,
|
||||||
|
@NonNull final String node,
|
||||||
|
final long discoId) {
|
||||||
final var entity = new DiscoItemWithDiscoId();
|
final var entity = new DiscoItemWithDiscoId();
|
||||||
entity.accountId = account;
|
entity.accountId = account;
|
||||||
entity.address = address;
|
entity.address = discoItem.address;
|
||||||
entity.node = node;
|
entity.node = node;
|
||||||
entity.discoId = discoId;
|
entity.discoId = discoId;
|
||||||
return entity;
|
return entity;
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package im.conversations.android.database.dao;
|
package im.conversations.android.database.dao;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.room.Dao;
|
import androidx.room.Dao;
|
||||||
import androidx.room.Query;
|
import androidx.room.Query;
|
||||||
import androidx.room.Upsert;
|
import androidx.room.Upsert;
|
||||||
|
@ -28,12 +30,12 @@ public abstract class PresenceDao {
|
||||||
abstract void insert(PresenceEntity entity);
|
abstract void insert(PresenceEntity entity);
|
||||||
|
|
||||||
public void set(
|
public void set(
|
||||||
Account account,
|
@NonNull final Account account,
|
||||||
Jid address,
|
@NonNull final Jid address,
|
||||||
String resource,
|
@Nullable final String resource,
|
||||||
PresenceType type,
|
@Nullable final PresenceType type,
|
||||||
PresenceShow show,
|
@Nullable final PresenceShow show,
|
||||||
String status) {
|
@Nullable final String status) {
|
||||||
if (resource == null
|
if (resource == null
|
||||||
&& Arrays.asList(PresenceType.ERROR, PresenceType.UNAVAILABLE).contains(type)) {
|
&& Arrays.asList(PresenceType.ERROR, PresenceType.UNAVAILABLE).contains(type)) {
|
||||||
deletePresences(account.id, address);
|
deletePresences(account.id, address);
|
||||||
|
|
|
@ -10,7 +10,7 @@ import androidx.room.PrimaryKey;
|
||||||
tableName = "disco_ext_field_value",
|
tableName = "disco_ext_field_value",
|
||||||
foreignKeys =
|
foreignKeys =
|
||||||
@ForeignKey(
|
@ForeignKey(
|
||||||
entity = DiscoExtensionEntity.class,
|
entity = DiscoExtensionFieldEntity.class,
|
||||||
parentColumns = {"id"},
|
parentColumns = {"id"},
|
||||||
childColumns = {"fieldId"},
|
childColumns = {"fieldId"},
|
||||||
onDelete = ForeignKey.CASCADE),
|
onDelete = ForeignKey.CASCADE),
|
||||||
|
@ -25,6 +25,9 @@ public class DiscoExtensionFieldValueEntity {
|
||||||
public String value;
|
public String value;
|
||||||
|
|
||||||
public static DiscoExtensionFieldValueEntity of(long fieldId, final String value) {
|
public static DiscoExtensionFieldValueEntity of(long fieldId, final String value) {
|
||||||
return null;
|
final var entity = new DiscoExtensionFieldValueEntity();
|
||||||
|
entity.fieldId = fieldId;
|
||||||
|
entity.value = value;
|
||||||
|
return entity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ public class DiscoItemEntity {
|
||||||
|
|
||||||
@NonNull Jid address;
|
@NonNull Jid address;
|
||||||
|
|
||||||
@Nullable public String node;
|
@NonNull public String node;
|
||||||
|
|
||||||
@Nullable public Jid parent;
|
@Nullable public Jid parent;
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ 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 com.google.common.base.Strings;
|
||||||
import eu.siacs.conversations.entities.MucOptions;
|
import eu.siacs.conversations.entities.MucOptions;
|
||||||
import eu.siacs.conversations.xmpp.Jid;
|
import eu.siacs.conversations.xmpp.Jid;
|
||||||
import im.conversations.android.database.model.PresenceShow;
|
import im.conversations.android.database.model.PresenceShow;
|
||||||
|
@ -13,16 +14,23 @@ import im.conversations.android.database.model.PresenceType;
|
||||||
|
|
||||||
@Entity(
|
@Entity(
|
||||||
tableName = "presence",
|
tableName = "presence",
|
||||||
foreignKeys =
|
foreignKeys = {
|
||||||
@ForeignKey(
|
@ForeignKey(
|
||||||
entity = AccountEntity.class,
|
entity = AccountEntity.class,
|
||||||
parentColumns = {"id"},
|
parentColumns = {"id"},
|
||||||
childColumns = {"accountId"},
|
childColumns = {"accountId"},
|
||||||
onDelete = ForeignKey.CASCADE),
|
onDelete = ForeignKey.CASCADE),
|
||||||
|
@ForeignKey(
|
||||||
|
entity = DiscoEntity.class,
|
||||||
|
parentColumns = {"id"},
|
||||||
|
childColumns = {"discoId"},
|
||||||
|
onDelete = ForeignKey.CASCADE)
|
||||||
|
},
|
||||||
indices = {
|
indices = {
|
||||||
@Index(
|
@Index(
|
||||||
value = {"accountId", "address", "resource"},
|
value = {"accountId", "address", "resource"},
|
||||||
unique = true)
|
unique = true),
|
||||||
|
@Index(value = {"discoId"})
|
||||||
})
|
})
|
||||||
public class PresenceEntity {
|
public class PresenceEntity {
|
||||||
|
|
||||||
|
@ -33,7 +41,7 @@ public class PresenceEntity {
|
||||||
|
|
||||||
@NonNull public Jid address;
|
@NonNull public Jid address;
|
||||||
|
|
||||||
@Nullable public String resource;
|
@NonNull public String resource;
|
||||||
|
|
||||||
@Nullable public PresenceType type;
|
@Nullable public PresenceType type;
|
||||||
|
|
||||||
|
@ -54,17 +62,19 @@ public class PresenceEntity {
|
||||||
// set to true if presence has status code 110 (this means we are online)
|
// set to true if presence has status code 110 (this means we are online)
|
||||||
public boolean mucUserSelf;
|
public boolean mucUserSelf;
|
||||||
|
|
||||||
|
public Long discoId;
|
||||||
|
|
||||||
public static PresenceEntity of(
|
public static PresenceEntity of(
|
||||||
long account,
|
long account,
|
||||||
Jid address,
|
Jid address,
|
||||||
String resource,
|
@Nullable String resource,
|
||||||
PresenceType type,
|
PresenceType type,
|
||||||
PresenceShow show,
|
PresenceShow show,
|
||||||
String status) {
|
String status) {
|
||||||
final var entity = new PresenceEntity();
|
final var entity = new PresenceEntity();
|
||||||
entity.accountId = account;
|
entity.accountId = account;
|
||||||
entity.address = address;
|
entity.address = address;
|
||||||
entity.resource = resource;
|
entity.resource = Strings.nullToEmpty(resource);
|
||||||
entity.type = type;
|
entity.type = type;
|
||||||
entity.show = show;
|
entity.show = show;
|
||||||
entity.status = status;
|
entity.status = status;
|
||||||
|
|
|
@ -1,4 +1,34 @@
|
||||||
package im.conversations.android.xmpp;
|
package im.conversations.android.xmpp;
|
||||||
|
|
||||||
public class Entity {
|
import eu.siacs.conversations.xmpp.Jid;
|
||||||
|
|
||||||
|
public abstract class Entity {
|
||||||
|
|
||||||
|
public final Jid address;
|
||||||
|
|
||||||
|
private Entity(final Jid address) {
|
||||||
|
this.address = address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DiscoItem extends Entity {
|
||||||
|
|
||||||
|
private DiscoItem(Jid address) {
|
||||||
|
super(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Presence extends Entity {
|
||||||
|
|
||||||
|
private Presence(Jid address) {
|
||||||
|
super(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Presence presence(final Jid address) {
|
||||||
|
return new Presence(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DiscoItem discoItem(final Jid address) {
|
||||||
|
return new DiscoItem(address);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1735,10 +1735,23 @@ public class XmppConnection implements Runnable {
|
||||||
final Element bind = packet.findChild("bind");
|
final Element bind = packet.findChild("bind");
|
||||||
if (bind != null && packet.getType() == IqPacket.TYPE.RESULT) {
|
if (bind != null && packet.getType() == IqPacket.TYPE.RESULT) {
|
||||||
isBound = true;
|
isBound = true;
|
||||||
final Element jid = bind.findChild("jid");
|
final String jid = bind.findChildContent("jid");
|
||||||
if (jid != null && jid.getContent() != null) {
|
if (Strings.isNullOrEmpty(jid)) {
|
||||||
|
throw new StateChangingError(ConnectionState.BIND_FAILURE);
|
||||||
|
}
|
||||||
|
final Jid assignedJid;
|
||||||
try {
|
try {
|
||||||
final Jid assignedJid = Jid.ofEscaped(jid.getContent());
|
assignedJid = Jid.ofEscaped(jid);
|
||||||
|
} catch (final IllegalArgumentException e) {
|
||||||
|
Log.d(
|
||||||
|
Config.LOGTAG,
|
||||||
|
account.address
|
||||||
|
+ ": server reported invalid jid ("
|
||||||
|
+ jid
|
||||||
|
+ ") on bind");
|
||||||
|
throw new StateChangingError(ConnectionState.BIND_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
if (!account.address.getDomain().equals(assignedJid.getDomain())) {
|
if (!account.address.getDomain().equals(assignedJid.getDomain())) {
|
||||||
Log.d(
|
Log.d(
|
||||||
Config.LOGTAG,
|
Config.LOGTAG,
|
||||||
|
@ -1749,29 +1762,13 @@ public class XmppConnection implements Runnable {
|
||||||
}
|
}
|
||||||
setConnectionAddress(assignedJid);
|
setConnectionAddress(assignedJid);
|
||||||
if (streamFeatures.hasChild("session")
|
if (streamFeatures.hasChild("session")
|
||||||
&& !streamFeatures
|
&& !streamFeatures.findChild("session").hasChild("optional")) {
|
||||||
.findChild("session")
|
|
||||||
.hasChild("optional")) {
|
|
||||||
sendStartSession();
|
sendStartSession();
|
||||||
} else {
|
} else {
|
||||||
enableStreamManagement();
|
enableStreamManagement();
|
||||||
sendPostBindInitialization(false);
|
sendPostBindInitialization(false);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} catch (final IllegalArgumentException e) {
|
|
||||||
Log.d(
|
|
||||||
Config.LOGTAG,
|
|
||||||
account.address
|
|
||||||
+ ": server reported invalid jid ("
|
|
||||||
+ jid.getContent()
|
|
||||||
+ ") on bind");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Log.d(
|
|
||||||
Config.LOGTAG,
|
|
||||||
account.address
|
|
||||||
+ ": disconnecting because of bind failure. (no jid)");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Log.d(
|
Log.d(
|
||||||
Config.LOGTAG,
|
Config.LOGTAG,
|
||||||
|
@ -1885,14 +1882,14 @@ public class XmppConnection implements Runnable {
|
||||||
final var discoManager = getManager(DiscoManager.class);
|
final var discoManager = getManager(DiscoManager.class);
|
||||||
|
|
||||||
final var nodeHash = this.streamFeatures.getCapabilities();
|
final var nodeHash = this.streamFeatures.getCapabilities();
|
||||||
|
final var domainDiscoItem = Entity.discoItem(account.address.getDomain());
|
||||||
if (nodeHash != null) {
|
if (nodeHash != null) {
|
||||||
discoFutures.add(
|
discoFutures.add(discoManager.info(domainDiscoItem, nodeHash.node, nodeHash.hash));
|
||||||
discoManager.info(account.address.getDomain(), nodeHash.node, nodeHash.hash));
|
|
||||||
} else {
|
} else {
|
||||||
discoFutures.add(discoManager.info(account.address.getDomain()));
|
discoFutures.add(discoManager.info(domainDiscoItem));
|
||||||
}
|
}
|
||||||
discoFutures.add(discoManager.info(account.address));
|
discoFutures.add(discoManager.info(Entity.discoItem(account.address)));
|
||||||
discoFutures.add(discoManager.itemsWithInfo(account.address.getDomain()));
|
discoFutures.add(discoManager.itemsWithInfo(domainDiscoItem));
|
||||||
|
|
||||||
final var discoFuture =
|
final var discoFuture =
|
||||||
Futures.withTimeout(
|
Futures.withTimeout(
|
||||||
|
@ -1912,6 +1909,7 @@ public class XmppConnection implements Runnable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(@NonNull Throwable t) {
|
public void onFailure(@NonNull Throwable t) {
|
||||||
|
Log.d(Config.LOGTAG, "unable to fetch disco foo " + t);
|
||||||
// TODO reset stream ID so we get a proper connect next time
|
// TODO reset stream ID so we get a proper connect next time
|
||||||
finalizeBind();
|
finalizeBind();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
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.xmpp.Entity;
|
||||||
import im.conversations.android.xmpp.EntityCapabilities;
|
import im.conversations.android.xmpp.EntityCapabilities;
|
||||||
import im.conversations.android.xmpp.EntityCapabilities2;
|
import im.conversations.android.xmpp.EntityCapabilities2;
|
||||||
import im.conversations.android.xmpp.XmppConnection;
|
import im.conversations.android.xmpp.XmppConnection;
|
||||||
|
@ -24,12 +25,12 @@ public class DiscoManager extends AbstractManager {
|
||||||
super(context, connection);
|
super(context, connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListenableFuture<InfoQuery> info(final Jid entity) {
|
public ListenableFuture<InfoQuery> info(final Entity entity) {
|
||||||
return info(entity, null);
|
return info(entity, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListenableFuture<Void> info(
|
public ListenableFuture<Void> info(
|
||||||
final Jid entity, @Nullable final String node, final EntityCapabilities.Hash hash) {
|
final Entity entity, @Nullable final String node, final EntityCapabilities.Hash hash) {
|
||||||
final String capabilityNode = hash.capabilityNode(node);
|
final String capabilityNode = hash.capabilityNode(node);
|
||||||
if (getDatabase().discoDao().set(getAccount(), entity, capabilityNode, hash)) {
|
if (getDatabase().discoDao().set(getAccount(), entity, capabilityNode, hash)) {
|
||||||
return Futures.immediateFuture(null);
|
return Futures.immediateFuture(null);
|
||||||
|
@ -38,9 +39,9 @@ public class DiscoManager extends AbstractManager {
|
||||||
info(entity, capabilityNode), f -> null, MoreExecutors.directExecutor());
|
info(entity, capabilityNode), f -> null, MoreExecutors.directExecutor());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListenableFuture<InfoQuery> info(final Jid entity, final String node) {
|
public ListenableFuture<InfoQuery> info(final Entity entity, final String node) {
|
||||||
final var iqRequest = new IqPacket(IqPacket.TYPE.GET);
|
final var iqRequest = new IqPacket(IqPacket.TYPE.GET);
|
||||||
iqRequest.setTo(entity);
|
iqRequest.setTo(entity.address);
|
||||||
final var infoQueryRequest = new InfoQuery();
|
final var infoQueryRequest = new InfoQuery();
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
infoQueryRequest.setNode(node);
|
infoQueryRequest.setNode(node);
|
||||||
|
@ -70,9 +71,9 @@ public class DiscoManager extends AbstractManager {
|
||||||
MoreExecutors.directExecutor());
|
MoreExecutors.directExecutor());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListenableFuture<Collection<Item>> items(final Jid entity) {
|
public ListenableFuture<Collection<Item>> items(final Entity.DiscoItem entity) {
|
||||||
final var iqPacket = new IqPacket(IqPacket.TYPE.GET);
|
final var iqPacket = new IqPacket(IqPacket.TYPE.GET);
|
||||||
iqPacket.setTo(entity);
|
iqPacket.setTo(entity.address);
|
||||||
iqPacket.addChild(new ItemsQuery());
|
iqPacket.addChild(new ItemsQuery());
|
||||||
final var future = connection.sendIqPacket(iqPacket);
|
final var future = connection.sendIqPacket(iqPacket);
|
||||||
return Futures.transform(
|
return Futures.transform(
|
||||||
|
@ -91,14 +92,16 @@ public class DiscoManager extends AbstractManager {
|
||||||
MoreExecutors.directExecutor());
|
MoreExecutors.directExecutor());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListenableFuture<List<InfoQuery>> itemsWithInfo(final Jid entity) {
|
public ListenableFuture<List<InfoQuery>> itemsWithInfo(final Entity.DiscoItem entity) {
|
||||||
final var itemsFutures = items(entity);
|
final var itemsFutures = items(entity);
|
||||||
return Futures.transformAsync(
|
return Futures.transformAsync(
|
||||||
itemsFutures,
|
itemsFutures,
|
||||||
items -> {
|
items -> {
|
||||||
// TODO filter out items with empty jid
|
final var filtered =
|
||||||
|
Collections2.filter(items, i -> Objects.nonNull(i.getJid()));
|
||||||
Collection<ListenableFuture<InfoQuery>> infoFutures =
|
Collection<ListenableFuture<InfoQuery>> infoFutures =
|
||||||
Collections2.transform(items, i -> info(i.getJid(), i.getNode()));
|
Collections2.transform(
|
||||||
|
filtered, i -> info(Entity.discoItem(i.getJid()), i.getNode()));
|
||||||
return Futures.allAsList(infoFutures);
|
return Futures.allAsList(infoFutures);
|
||||||
},
|
},
|
||||||
MoreExecutors.directExecutor());
|
MoreExecutors.directExecutor());
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package im.conversations.android.xmpp.model.disco.items;
|
package im.conversations.android.xmpp.model.disco.items;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import eu.siacs.conversations.xmpp.Jid;
|
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;
|
||||||
|
@ -14,7 +15,7 @@ public class Item extends Extension {
|
||||||
return getAttributeAsJid("jid");
|
return getAttributeAsJid("jid");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getNode() {
|
public @Nullable String getNode() {
|
||||||
return this.getAttribute("node");
|
return this.getAttribute("node");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.content.Context;
|
||||||
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
|
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
|
||||||
import im.conversations.android.database.model.PresenceShow;
|
import im.conversations.android.database.model.PresenceShow;
|
||||||
import im.conversations.android.database.model.PresenceType;
|
import im.conversations.android.database.model.PresenceType;
|
||||||
|
import im.conversations.android.xmpp.Entity;
|
||||||
import im.conversations.android.xmpp.XmppConnection;
|
import im.conversations.android.xmpp.XmppConnection;
|
||||||
import im.conversations.android.xmpp.manager.DiscoManager;
|
import im.conversations.android.xmpp.manager.DiscoManager;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
@ -41,7 +42,8 @@ public class PresenceProcessor extends XmppConnection.Delegate implements Consum
|
||||||
final var entity = presencePacket.getFrom();
|
final var entity = presencePacket.getFrom();
|
||||||
final var nodeHash = presencePacket.getCapabilities();
|
final var nodeHash = presencePacket.getCapabilities();
|
||||||
if (nodeHash != null) {
|
if (nodeHash != null) {
|
||||||
getManager(DiscoManager.class).info(entity, nodeHash.node, nodeHash.hash);
|
getManager(DiscoManager.class)
|
||||||
|
.info(Entity.presence(entity), nodeHash.node, nodeHash.hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue