store presence in DB
This commit is contained in:
parent
26bff8028a
commit
944c48e00b
|
@ -2,7 +2,7 @@
|
||||||
"formatVersion": 1,
|
"formatVersion": 1,
|
||||||
"database": {
|
"database": {
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"identityHash": "adc70f7066828bb6cf1fc32aa3a24b2f",
|
"identityHash": "a521ff7a3e16cca9f6cbfe51241ec021",
|
||||||
"entities": [
|
"entities": [
|
||||||
{
|
{
|
||||||
"tableName": "account",
|
"tableName": "account",
|
||||||
|
@ -319,7 +319,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"tableName": "disco_ext",
|
"tableName": "disco_ext",
|
||||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `discoId` INTEGER NOT NULL, 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, `discoId` INTEGER NOT NULL, `type` TEXT, FOREIGN KEY(`discoId`) REFERENCES `disco`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldPath": "id",
|
"fieldPath": "id",
|
||||||
|
@ -332,6 +332,12 @@
|
||||||
"columnName": "discoId",
|
"columnName": "discoId",
|
||||||
"affinity": "INTEGER",
|
"affinity": "INTEGER",
|
||||||
"notNull": true
|
"notNull": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldPath": "type",
|
||||||
|
"columnName": "type",
|
||||||
|
"affinity": "TEXT",
|
||||||
|
"notNull": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"primaryKey": {
|
"primaryKey": {
|
||||||
|
@ -1304,7 +1310,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, 'adc70f7066828bb6cf1fc32aa3a24b2f')"
|
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a521ff7a3e16cca9f6cbfe51241ec021')"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -18,7 +18,6 @@ import im.conversations.android.database.model.Account;
|
||||||
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;
|
||||||
import im.conversations.android.xmpp.model.data.Field;
|
|
||||||
import im.conversations.android.xmpp.model.data.Value;
|
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.Feature;
|
||||||
import im.conversations.android.xmpp.model.disco.info.Identity;
|
import im.conversations.android.xmpp.model.disco.info.Identity;
|
||||||
|
@ -38,6 +37,12 @@ public abstract class DiscoDao {
|
||||||
@Insert
|
@Insert
|
||||||
protected abstract void insertDiscoFeatures(Collection<DiscoFeatureEntity> features);
|
protected abstract void insertDiscoFeatures(Collection<DiscoFeatureEntity> features);
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"DELETE FROM disco_item WHERE accountId=:account AND parent=:parent AND address NOT"
|
||||||
|
+ " IN(:existent)")
|
||||||
|
protected abstract void deleteNonExistentDiscoItems(
|
||||||
|
final long account, final Jid parent, final Collection<Jid> existent);
|
||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
protected abstract void insertDiscoFieldValues(
|
protected abstract void insertDiscoFieldValues(
|
||||||
Collection<DiscoExtensionFieldValueEntity> value);
|
Collection<DiscoExtensionFieldValueEntity> value);
|
||||||
|
@ -59,6 +64,8 @@ public abstract class DiscoDao {
|
||||||
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(
|
||||||
|
account.id, parent, Collections2.transform(items, Item::getJid));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
|
@ -108,8 +115,8 @@ public abstract class DiscoDao {
|
||||||
infoQuery.getExtensions(Feature.class),
|
infoQuery.getExtensions(Feature.class),
|
||||||
f -> DiscoFeatureEntity.of(discoId, f.getVar())));
|
f -> DiscoFeatureEntity.of(discoId, f.getVar())));
|
||||||
for (final Data data : infoQuery.getExtensions(Data.class)) {
|
for (final Data data : infoQuery.getExtensions(Data.class)) {
|
||||||
final var extensionId = insert(DiscoExtensionEntity.of(discoId));
|
final var extensionId = insert(DiscoExtensionEntity.of(discoId, data.getFormType()));
|
||||||
for (final var field : data.getExtensions(Field.class)) {
|
for (final var field : data.getFields()) {
|
||||||
final var fieldId =
|
final var fieldId =
|
||||||
insert(DiscoExtensionFieldEntity.of(extensionId, field.getFieldName()));
|
insert(DiscoExtensionFieldEntity.of(extensionId, field.getFieldName()));
|
||||||
insertDiscoFieldValues(
|
insertDiscoFieldValues(
|
||||||
|
|
|
@ -2,10 +2,50 @@ package im.conversations.android.database.dao;
|
||||||
|
|
||||||
import androidx.room.Dao;
|
import androidx.room.Dao;
|
||||||
import androidx.room.Query;
|
import androidx.room.Query;
|
||||||
|
import androidx.room.Upsert;
|
||||||
|
import eu.siacs.conversations.xmpp.Jid;
|
||||||
|
import im.conversations.android.database.entity.PresenceEntity;
|
||||||
|
import im.conversations.android.database.model.Account;
|
||||||
|
import im.conversations.android.database.model.PresenceShow;
|
||||||
|
import im.conversations.android.database.model.PresenceType;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
public interface PresenceDao {
|
public abstract class PresenceDao {
|
||||||
|
|
||||||
@Query("DELETE FROM presence WHERE accountId=:account")
|
@Query("DELETE FROM presence WHERE accountId=:account")
|
||||||
void deletePresences(long account);
|
public abstract void deletePresences(long account);
|
||||||
|
|
||||||
|
@Query("DELETE FROM presence WHERE accountId=:account AND address=:address")
|
||||||
|
abstract void deletePresences(long account, Jid address);
|
||||||
|
|
||||||
|
@Query(
|
||||||
|
"DELETE FROM presence WHERE accountId=:account AND address=:address AND"
|
||||||
|
+ " resource=:resource")
|
||||||
|
abstract void deletePresence(long account, Jid address, String resource);
|
||||||
|
|
||||||
|
@Upsert
|
||||||
|
abstract void insert(PresenceEntity entity);
|
||||||
|
|
||||||
|
public void set(
|
||||||
|
Account account,
|
||||||
|
Jid address,
|
||||||
|
String resource,
|
||||||
|
PresenceType type,
|
||||||
|
PresenceShow show,
|
||||||
|
String status) {
|
||||||
|
if (resource == null
|
||||||
|
&& Arrays.asList(PresenceType.ERROR, PresenceType.UNAVAILABLE).contains(type)) {
|
||||||
|
deletePresences(account.id, address);
|
||||||
|
}
|
||||||
|
if (type == PresenceType.UNAVAILABLE) {
|
||||||
|
if (resource != null) {
|
||||||
|
deletePresence(account.id, address, resource);
|
||||||
|
}
|
||||||
|
// unavailable presence only delete previous nothing left to do
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final var entity = PresenceEntity.of(account.id, address, resource, type, show, status);
|
||||||
|
insert(entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package im.conversations.android.database.entity;
|
package im.conversations.android.database.entity;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.room.Entity;
|
import androidx.room.Entity;
|
||||||
import androidx.room.ForeignKey;
|
import androidx.room.ForeignKey;
|
||||||
import androidx.room.Index;
|
import androidx.room.Index;
|
||||||
|
@ -22,9 +23,12 @@ public class DiscoExtensionEntity {
|
||||||
|
|
||||||
@NonNull public Long discoId;
|
@NonNull public Long discoId;
|
||||||
|
|
||||||
public static DiscoExtensionEntity of(long discoId) {
|
@Nullable public String type;
|
||||||
|
|
||||||
|
public static DiscoExtensionEntity of(long discoId, final String type) {
|
||||||
final var entity = new DiscoExtensionEntity();
|
final var entity = new DiscoExtensionEntity();
|
||||||
entity.discoId = discoId;
|
entity.discoId = discoId;
|
||||||
|
entity.type = type;
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ public class PresenceEntity {
|
||||||
|
|
||||||
@NonNull public Long accountId;
|
@NonNull public Long accountId;
|
||||||
|
|
||||||
@NonNull public String address;
|
@NonNull public Jid address;
|
||||||
|
|
||||||
@Nullable public String resource;
|
@Nullable public String resource;
|
||||||
|
|
||||||
|
@ -53,4 +53,21 @@ 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 static PresenceEntity of(
|
||||||
|
long account,
|
||||||
|
Jid address,
|
||||||
|
String resource,
|
||||||
|
PresenceType type,
|
||||||
|
PresenceShow show,
|
||||||
|
String status) {
|
||||||
|
final var entity = new PresenceEntity();
|
||||||
|
entity.accountId = account;
|
||||||
|
entity.address = address;
|
||||||
|
entity.resource = resource;
|
||||||
|
entity.type = type;
|
||||||
|
entity.show = show;
|
||||||
|
entity.status = status;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,18 @@
|
||||||
package im.conversations.android.database.model;
|
package im.conversations.android.database.model;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
public enum PresenceShow {
|
public enum PresenceShow {
|
||||||
CHAT,
|
CHAT,
|
||||||
AWAY,
|
AWAY,
|
||||||
XA,
|
XA,
|
||||||
DND
|
DND;
|
||||||
|
|
||||||
|
public static PresenceShow of(final String value) {
|
||||||
|
try {
|
||||||
|
return value == null ? null : valueOf(value.toUpperCase(Locale.ROOT));
|
||||||
|
} catch (final IllegalArgumentException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,17 @@
|
||||||
package im.conversations.android.database.model;
|
package im.conversations.android.database.model;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
public enum PresenceType {
|
public enum PresenceType {
|
||||||
UNAVAILABLE,
|
UNAVAILABLE,
|
||||||
ERROR,
|
ERROR,
|
||||||
SUBSCRIBE
|
SUBSCRIBE;
|
||||||
|
|
||||||
|
public static PresenceType of(@Nullable String typeAttribute) {
|
||||||
|
if (typeAttribute == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return of(typeAttribute.toUpperCase(Locale.ROOT));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,8 @@ public class DiscoManager extends AbstractManager {
|
||||||
}
|
}
|
||||||
iqRequest.addChild(infoQueryRequest);
|
iqRequest.addChild(infoQueryRequest);
|
||||||
final var future = connection.sendIqPacket(iqRequest);
|
final var future = connection.sendIqPacket(iqRequest);
|
||||||
|
// TODO we need to remove the disco info associated with $entity in case of failure
|
||||||
|
// this might happen in (rather unlikely) scenarios where an item no longer speaks disco
|
||||||
return Futures.transform(
|
return Futures.transform(
|
||||||
future,
|
future,
|
||||||
iqResult -> {
|
iqResult -> {
|
||||||
|
|
|
@ -2,6 +2,8 @@ package im.conversations.android.xmpp.processor;
|
||||||
|
|
||||||
import android.content.Context;
|
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.PresenceType;
|
||||||
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;
|
||||||
|
@ -14,6 +16,23 @@ public class PresenceProcessor extends XmppConnection.Delegate implements Consum
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accept(final PresencePacket presencePacket) {
|
public void accept(final PresencePacket presencePacket) {
|
||||||
|
final var from = presencePacket.getFrom();
|
||||||
|
final var address = from == null ? null : from.asBareJid();
|
||||||
|
final var resource = from == null ? null : from.getResource();
|
||||||
|
final var typeAttribute = presencePacket.getAttribute("type");
|
||||||
|
final PresenceType type;
|
||||||
|
try {
|
||||||
|
type = PresenceType.of(typeAttribute);
|
||||||
|
} catch (final IllegalArgumentException e) {
|
||||||
|
// log we don’t parse presence of type $type
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final var show = PresenceShow.of(presencePacket.findChildContent("show"));
|
||||||
|
final var status = presencePacket.findChildContent("status");
|
||||||
|
getDatabase().presenceDao().set(getAccount(), address, resource, type, show, status);
|
||||||
|
|
||||||
|
// TODO store presence info
|
||||||
|
|
||||||
// TODO do this only for contacts?
|
// TODO do this only for contacts?
|
||||||
fetchCapabilities(presencePacket);
|
fetchCapabilities(presencePacket);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue