mirror of
https://codeberg.org/monocles/monocles_chat.git
synced 2025-01-15 14:12:21 +01:00
join MultiUserChats on bind
This commit is contained in:
parent
de63e5e290
commit
c0eb99c3d5
14 changed files with 347 additions and 58 deletions
|
@ -2,7 +2,7 @@
|
|||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "b5e8a59bbd86e133c0bc2edd303ad2a0",
|
||||
"identityHash": "9620a1b63d595091a2b463e89b504eb7",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "account",
|
||||
|
@ -1009,7 +1009,7 @@
|
|||
},
|
||||
{
|
||||
"tableName": "chat",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `accountId` INTEGER NOT NULL, `address` TEXT NOT NULL, `type` TEXT, `archived` 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, `type` TEXT, `archived` INTEGER NOT NULL, `mucState` TEXT, `errorCondition` TEXT, FOREIGN KEY(`accountId`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
|
@ -1040,6 +1040,18 @@
|
|||
"columnName": "archived",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mucState",
|
||||
"columnName": "mucState",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "errorCondition",
|
||||
"columnName": "errorCondition",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
|
@ -2007,6 +2019,60 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "muc_status_code",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `chatId` INTEGER NOT NULL, `code` INTEGER NOT NULL, FOREIGN KEY(`chatId`) REFERENCES `chat`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "chatId",
|
||||
"columnName": "chatId",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "code",
|
||||
"columnName": "code",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
},
|
||||
"indices": [
|
||||
{
|
||||
"name": "index_muc_status_code_chatId",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"chatId"
|
||||
],
|
||||
"orders": [],
|
||||
"createSql": "CREATE INDEX IF NOT EXISTS `index_muc_status_code_chatId` ON `${TABLE_NAME}` (`chatId`)"
|
||||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "chat",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"chatId"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tableName": "nick",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `accountId` INTEGER NOT NULL, `address` TEXT NOT NULL, `nick` TEXT, FOREIGN KEY(`accountId`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
|
@ -2528,7 +2594,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, 'b5e8a59bbd86e133c0bc2edd303ad2a0')"
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '9620a1b63d595091a2b463e89b504eb7')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -46,6 +46,7 @@ import im.conversations.android.database.entity.MessageEntity;
|
|||
import im.conversations.android.database.entity.MessageReactionEntity;
|
||||
import im.conversations.android.database.entity.MessageStateEntity;
|
||||
import im.conversations.android.database.entity.MessageVersionEntity;
|
||||
import im.conversations.android.database.entity.MucStatusCodeEntity;
|
||||
import im.conversations.android.database.entity.NickEntity;
|
||||
import im.conversations.android.database.entity.PresenceEntity;
|
||||
import im.conversations.android.database.entity.RosterItemEntity;
|
||||
|
@ -81,6 +82,7 @@ import im.conversations.android.database.entity.ServiceRecordCacheEntity;
|
|||
MessageStateEntity.class,
|
||||
MessageContentEntity.class,
|
||||
MessageVersionEntity.class,
|
||||
MucStatusCodeEntity.class,
|
||||
NickEntity.class,
|
||||
PresenceEntity.class,
|
||||
MessageReactionEntity.class,
|
||||
|
|
|
@ -7,13 +7,16 @@ import androidx.room.Query;
|
|||
import androidx.room.Transaction;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import im.conversations.android.database.entity.ChatEntity;
|
||||
import im.conversations.android.database.entity.MucStatusCodeEntity;
|
||||
import im.conversations.android.database.model.Account;
|
||||
import im.conversations.android.database.model.ChatIdentifier;
|
||||
import im.conversations.android.database.model.ChatType;
|
||||
import im.conversations.android.database.model.GroupIdentifier;
|
||||
import im.conversations.android.database.model.MucState;
|
||||
import im.conversations.android.database.model.MucWithNick;
|
||||
import im.conversations.android.xmpp.model.stanza.Message;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.jxmpp.jid.Jid;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -59,6 +62,12 @@ public abstract class ChatDao {
|
|||
+ " address=:address")
|
||||
protected abstract ChatIdentifier get(final long accountId, final Jid address);
|
||||
|
||||
@Query(
|
||||
"SELECT id,address,type,archived FROM chat WHERE accountId=:accountId AND"
|
||||
+ " address=:address AND type=:chatType")
|
||||
public abstract ChatIdentifier get(
|
||||
final long accountId, final Jid address, final ChatType chatType);
|
||||
|
||||
@Insert
|
||||
protected abstract long insert(ChatEntity chatEntity);
|
||||
|
||||
|
@ -125,13 +134,56 @@ public abstract class ChatDao {
|
|||
+ " chat.accountId=:account AND chat.type=:chatType AND archived=0")
|
||||
protected abstract List<Jid> getBookmarksNotInChats(long account, ChatType chatType);
|
||||
|
||||
// TODO get pending only
|
||||
@Query(
|
||||
"SELECT chat.address,bookmark.nick as nickBookmark,nick.nick as nickAccount FROM chat"
|
||||
+ " LEFT JOIN bookmark ON chat.accountId=bookmark.accountId AND"
|
||||
+ " chat.address=bookmark.address JOIN account ON account.id=chat.accountId LEFT"
|
||||
+ " JOIN nick ON nick.accountId=chat.accountId AND nick.address=account.address"
|
||||
+ " WHERE chat.accountId=:account AND chat.type=:chatType AND chat.archived=0")
|
||||
"SELECT chat.id as chatId,chat.address,bookmark.nick as nickBookmark,nick.nick as"
|
||||
+ " nickAccount FROM chat LEFT JOIN bookmark ON chat.accountId=bookmark.accountId"
|
||||
+ " AND chat.address=bookmark.address JOIN account ON account.id=chat.accountId"
|
||||
+ " LEFT JOIN nick ON nick.accountId=chat.accountId AND"
|
||||
+ " nick.address=account.address WHERE chat.accountId=:account AND"
|
||||
+ " chat.type=:chatType AND chat.archived=0 AND chat.mucState IS NULL")
|
||||
public abstract ListenableFuture<List<MucWithNick>> getMultiUserChats(
|
||||
final long account, final ChatType chatType);
|
||||
|
||||
@Query("UPDATE chat SET mucState=:mucState, errorCondition=:errorCondition WHERE id=:chatId")
|
||||
protected abstract void setMucStateInternal(
|
||||
final long chatId, final MucState mucState, final String errorCondition);
|
||||
|
||||
@Transaction
|
||||
public void setMucState(
|
||||
final long chatId, final MucState mucState, final String errorCondition) {
|
||||
setMucStateInternal(chatId, mucState, errorCondition);
|
||||
deleteStatusCodes(chatId);
|
||||
}
|
||||
|
||||
@Transaction
|
||||
public void setMucState(
|
||||
final long chatId, final MucState mucState, final Collection<Integer> statusCodes) {
|
||||
setMucStateInternal(chatId, mucState, null);
|
||||
deleteStatusCodes(chatId);
|
||||
insertStatusCode(MucStatusCodeEntity.of(chatId, statusCodes));
|
||||
}
|
||||
|
||||
@Transaction
|
||||
public void setMucState(final long chatId, final MucState mucState) {
|
||||
setMucStateInternal(chatId, mucState, null);
|
||||
deleteStatusCodes(chatId);
|
||||
}
|
||||
|
||||
@Transaction
|
||||
public void resetMucStates() {
|
||||
this.nullMucStates();
|
||||
this.deleteStatusCodes();
|
||||
}
|
||||
|
||||
@Insert
|
||||
protected abstract void insertStatusCode(final Collection<MucStatusCodeEntity> entities);
|
||||
|
||||
@Query("UPDATE chat SET mucState=null,errorCondition=null")
|
||||
protected abstract void nullMucStates();
|
||||
|
||||
@Query("DELETE FROM muc_status_code")
|
||||
protected abstract void deleteStatusCodes();
|
||||
|
||||
@Query("DELETE FROM muc_status_code WHERE chatId=:chatId")
|
||||
protected abstract void deleteStatusCodes(final long chatId);
|
||||
}
|
||||
|
|
|
@ -21,8 +21,6 @@ import im.conversations.android.xmpp.EntityCapabilities;
|
|||
import im.conversations.android.xmpp.EntityCapabilities2;
|
||||
import im.conversations.android.xmpp.model.data.Data;
|
||||
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;
|
||||
|
@ -156,13 +154,11 @@ public abstract class DiscoDao {
|
|||
|
||||
insertDiscoIdentities(
|
||||
Collections2.transform(
|
||||
infoQuery.getExtensions(Identity.class),
|
||||
i -> DiscoIdentityEntity.of(discoId, i)));
|
||||
infoQuery.getIdentities(), i -> DiscoIdentityEntity.of(discoId, i)));
|
||||
|
||||
insertDiscoFeatures(
|
||||
Collections2.transform(
|
||||
infoQuery.getExtensions(Feature.class),
|
||||
f -> DiscoFeatureEntity.of(discoId, f.getVar())));
|
||||
infoQuery.getFeatures(), f -> DiscoFeatureEntity.of(discoId, f.getVar())));
|
||||
for (final Data data : infoQuery.getExtensions(Data.class)) {
|
||||
final var extensionId = insert(DiscoExtensionEntity.of(discoId, data.getFormType()));
|
||||
for (final var field : data.getFields()) {
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package im.conversations.android.database.entity;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.ForeignKey;
|
||||
import androidx.room.Index;
|
||||
import androidx.room.PrimaryKey;
|
||||
import im.conversations.android.database.model.ChatType;
|
||||
import im.conversations.android.database.model.MucState;
|
||||
|
||||
@Entity(
|
||||
tableName = "chat",
|
||||
|
@ -32,4 +34,7 @@ public class ChatEntity {
|
|||
public ChatType type;
|
||||
|
||||
public boolean archived;
|
||||
|
||||
@Nullable public MucState mucState;
|
||||
@Nullable public String errorCondition;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package im.conversations.android.database.entity;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.ForeignKey;
|
||||
import androidx.room.Index;
|
||||
import androidx.room.PrimaryKey;
|
||||
import com.google.common.collect.Collections2;
|
||||
import java.util.Collection;
|
||||
|
||||
@Entity(
|
||||
tableName = "muc_status_code",
|
||||
foreignKeys = {
|
||||
@ForeignKey(
|
||||
entity = ChatEntity.class,
|
||||
parentColumns = {"id"},
|
||||
childColumns = {"chatId"},
|
||||
onDelete = ForeignKey.CASCADE)
|
||||
},
|
||||
indices = {@Index(value = "chatId")})
|
||||
public class MucStatusCodeEntity {
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
public Long id;
|
||||
|
||||
@NonNull public Long chatId;
|
||||
|
||||
@NonNull public Integer code;
|
||||
|
||||
public static Collection<MucStatusCodeEntity> of(
|
||||
final long chatId, final Collection<Integer> codes) {
|
||||
return Collections2.transform(codes, c -> of(chatId, c));
|
||||
}
|
||||
|
||||
private static MucStatusCodeEntity of(final long chatId, final int code) {
|
||||
final var entity = new MucStatusCodeEntity();
|
||||
entity.chatId = chatId;
|
||||
entity.code = code;
|
||||
return entity;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package im.conversations.android.database.model;
|
||||
|
||||
public enum MucState {
|
||||
JOINING,
|
||||
AVAILABLE,
|
||||
ERROR_PRESENCE,
|
||||
ERROR_IQ,
|
||||
UNAVAILABLE,
|
||||
NOT_A_MUC
|
||||
}
|
|
@ -5,12 +5,18 @@ import org.jxmpp.jid.BareJid;
|
|||
import org.jxmpp.jid.parts.Resourcepart;
|
||||
|
||||
public class MucWithNick {
|
||||
public final long chatId;
|
||||
|
||||
public final BareJid address;
|
||||
private final String nickBookmark;
|
||||
private final String nickAccount;
|
||||
|
||||
public MucWithNick(BareJid address, String nickBookmark, String nickAccount) {
|
||||
public MucWithNick(
|
||||
final long chatId,
|
||||
final BareJid address,
|
||||
final String nickBookmark,
|
||||
final String nickAccount) {
|
||||
this.chatId = chatId;
|
||||
this.address = address;
|
||||
this.nickBookmark = nickBookmark;
|
||||
this.nickAccount = nickAccount;
|
||||
|
|
|
@ -37,7 +37,7 @@ public final class EntityCapabilities {
|
|||
blankNull(a.getIdentityName()),
|
||||
blankNull(b.getIdentityName()))
|
||||
.result())
|
||||
.sortedCopy(info.getExtensions(Identity.class));
|
||||
.sortedCopy(info.getIdentities());
|
||||
|
||||
for (final Identity id : orderedIdentities) {
|
||||
s.append(blankNull(id.getCategory()))
|
||||
|
@ -52,9 +52,7 @@ public final class EntityCapabilities {
|
|||
|
||||
final List<String> features =
|
||||
Ordering.natural()
|
||||
.sortedCopy(
|
||||
Collections2.transform(
|
||||
info.getExtensions(Feature.class), Feature::getVar));
|
||||
.sortedCopy(Collections2.transform(info.getFeatures(), Feature::getVar));
|
||||
for (final String feature : features) {
|
||||
s.append(clean(feature)).append("<");
|
||||
}
|
||||
|
|
|
@ -62,8 +62,8 @@ public class EntityCapabilities2 {
|
|||
}
|
||||
|
||||
private static String algorithm(final InfoQuery infoQuery) {
|
||||
return features(infoQuery.getExtensions(Feature.class))
|
||||
+ identities(infoQuery.getExtensions(Identity.class))
|
||||
return features(infoQuery.getFeatures())
|
||||
+ identities(infoQuery.getIdentities())
|
||||
+ extensions(infoQuery.getExtensions(Data.class));
|
||||
}
|
||||
|
||||
|
|
|
@ -5,12 +5,19 @@ import androidx.annotation.NonNull;
|
|||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import im.conversations.android.database.model.ChatIdentifier;
|
||||
import im.conversations.android.database.model.ChatType;
|
||||
import im.conversations.android.database.model.MucState;
|
||||
import im.conversations.android.database.model.MucWithNick;
|
||||
import im.conversations.android.xml.Namespace;
|
||||
import im.conversations.android.xmpp.Entity;
|
||||
import im.conversations.android.xmpp.IqErrorException;
|
||||
import im.conversations.android.xmpp.XmppConnection;
|
||||
import im.conversations.android.xmpp.model.disco.info.InfoQuery;
|
||||
import im.conversations.android.xmpp.model.error.Condition;
|
||||
import im.conversations.android.xmpp.model.error.Error;
|
||||
import im.conversations.android.xmpp.model.muc.History;
|
||||
import im.conversations.android.xmpp.model.muc.MultiUserChat;
|
||||
import im.conversations.android.xmpp.model.muc.user.MucUser;
|
||||
|
@ -29,47 +36,41 @@ public class MultiUserChatManager extends AbstractManager {
|
|||
super(context, connection);
|
||||
}
|
||||
|
||||
public void joinMultiUserChats() {
|
||||
final var future = getDatabase().chatDao().getMultiUserChats(getAccount().id, ChatType.MUC);
|
||||
Futures.addCallback(
|
||||
future,
|
||||
new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(List<MucWithNick> result) {
|
||||
for (final MucWithNick mucWithNick : result) {
|
||||
enter(mucWithNick);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
LOGGER.info("Could not query db", t);
|
||||
}
|
||||
},
|
||||
public ListenableFuture<Integer> joinMultiUserChats() {
|
||||
LOGGER.info("joining multi user chats. start");
|
||||
return Futures.transform(
|
||||
getDatabase().chatDao().getMultiUserChats(getAccount().id, ChatType.MUC),
|
||||
this::joinMultiUserChats,
|
||||
MoreExecutors.directExecutor());
|
||||
}
|
||||
|
||||
public void enter(final MucWithNick mucWithNick) {
|
||||
private int joinMultiUserChats(final List<MucWithNick> chats) {
|
||||
LOGGER.info("joining {} chats", chats.size());
|
||||
for (final MucWithNick chat : chats) {
|
||||
this.enterExisting(chat);
|
||||
}
|
||||
return chats.size();
|
||||
}
|
||||
|
||||
public void enterExisting(final MucWithNick mucWithNick) {
|
||||
getDatabase().chatDao().setMucState(mucWithNick.chatId, MucState.JOINING);
|
||||
final var discoInfoFuture =
|
||||
getManager(DiscoManager.class).info(Entity.discoItem(mucWithNick.address));
|
||||
// TODO catching
|
||||
Futures.addCallback(
|
||||
discoInfoFuture,
|
||||
new FutureCallback<>() {
|
||||
@Override
|
||||
public void onSuccess(final InfoQuery result) {
|
||||
enter(mucWithNick, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Throwable throwable) {
|
||||
// TODO mark chat as ERROR_IQ
|
||||
}
|
||||
},
|
||||
new ExistingMucJoiner(mucWithNick),
|
||||
MoreExecutors.directExecutor());
|
||||
}
|
||||
|
||||
public void enter(final MucWithNick mucWithNick, final InfoQuery infoQuery) {
|
||||
private void enterExisting(final MucWithNick mucWithNick, final InfoQuery infoQuery) {
|
||||
if (infoQuery.hasFeature(Namespace.MUC)) {
|
||||
sendJoinPresence(mucWithNick);
|
||||
} else {
|
||||
getDatabase().chatDao().setMucState(mucWithNick.chatId, MucState.NOT_A_MUC);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendJoinPresence(final MucWithNick mucWithNick) {
|
||||
final var nick = mucWithNick.nick();
|
||||
final Jid to;
|
||||
if (nick != null) {
|
||||
|
@ -86,16 +87,106 @@ public class MultiUserChatManager extends AbstractManager {
|
|||
connection.sendPresencePacket(presence);
|
||||
}
|
||||
|
||||
public void handleSelfPresence(final Presence presencePacket) {
|
||||
public void handleSelfPresenceAvailable(final Presence presencePacket) {
|
||||
final MucUser mucUser = presencePacket.getExtension(MucUser.class);
|
||||
Preconditions.checkArgument(
|
||||
mucUser.getStatus().contains(MucUser.STATUS_CODE_SELF_PRESENCE));
|
||||
|
||||
// TODO flag chat as joined
|
||||
LOGGER.info("Received self presence for {}", presencePacket.getFrom());
|
||||
final var database = getDatabase();
|
||||
database.runInTransaction(
|
||||
() -> {
|
||||
final ChatIdentifier chatIdentifier =
|
||||
database.chatDao()
|
||||
.get(
|
||||
getAccount().id,
|
||||
presencePacket.getFrom().asBareJid(),
|
||||
ChatType.MUC);
|
||||
if (chatIdentifier == null || chatIdentifier.archived) {
|
||||
LOGGER.info(
|
||||
"Available presence received for archived or non existent chat");
|
||||
return;
|
||||
}
|
||||
// TODO set status codes
|
||||
database.chatDao()
|
||||
.setMucState(
|
||||
chatIdentifier.id, MucState.AVAILABLE, mucUser.getStatus());
|
||||
});
|
||||
}
|
||||
|
||||
public void handleSelfPresenceUnavailable(final Presence presencePacket) {
|
||||
final MucUser mucUser = presencePacket.getExtension(MucUser.class);
|
||||
Preconditions.checkArgument(
|
||||
mucUser.getStatus().contains(MucUser.STATUS_CODE_SELF_PRESENCE));
|
||||
final var database = getDatabase();
|
||||
database.runInTransaction(
|
||||
() -> {
|
||||
final ChatIdentifier chatIdentifier =
|
||||
database.chatDao()
|
||||
.get(
|
||||
getAccount().id,
|
||||
presencePacket.getFrom().asBareJid(),
|
||||
ChatType.MUC);
|
||||
if (chatIdentifier == null) {
|
||||
LOGGER.error("Unavailable presence received for non existent chat");
|
||||
} else if (chatIdentifier.archived) {
|
||||
database.chatDao().setMucState(chatIdentifier.id, null);
|
||||
} else {
|
||||
// TODO set status codes
|
||||
database.chatDao().setMucState(chatIdentifier.id, MucState.UNAVAILABLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void handleErrorPresence(final Presence presencePacket) {
|
||||
LOGGER.info("Received error presence from {}", presencePacket.getFrom());
|
||||
final var database = getDatabase();
|
||||
database.runInTransaction(
|
||||
() -> {
|
||||
final ChatIdentifier chatIdentifier =
|
||||
database.chatDao()
|
||||
.get(
|
||||
getAccount().id,
|
||||
presencePacket.getFrom().asBareJid(),
|
||||
ChatType.MUC);
|
||||
if (chatIdentifier == null) {
|
||||
// this is fine. error is simply not for a MUC
|
||||
return;
|
||||
}
|
||||
final Error error = presencePacket.getError();
|
||||
final Condition condition = error == null ? null : error.getCondition();
|
||||
final String errorCondition = condition == null ? null : condition.getName();
|
||||
database.chatDao()
|
||||
.setMucState(
|
||||
chatIdentifier.id, MucState.ERROR_PRESENCE, errorCondition);
|
||||
});
|
||||
}
|
||||
|
||||
private class ExistingMucJoiner implements FutureCallback<InfoQuery> {
|
||||
|
||||
private final MucWithNick chat;
|
||||
|
||||
private ExistingMucJoiner(final MucWithNick chat) {
|
||||
this.chat = chat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(final InfoQuery result) {
|
||||
enterExisting(chat, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull final Throwable throwable) {
|
||||
final String errorCondition;
|
||||
if (throwable instanceof IqErrorException) {
|
||||
final var iqErrorException = (IqErrorException) throwable;
|
||||
final Error error = iqErrorException.getError();
|
||||
final Condition condition = error == null ? null : error.getCondition();
|
||||
errorCondition = condition == null ? null : condition.getName();
|
||||
} else {
|
||||
errorCondition = null;
|
||||
}
|
||||
getDatabase().chatDao().setMucState(chat.chatId, MucState.ERROR_IQ, errorCondition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package im.conversations.android.xmpp.model.disco.info;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import im.conversations.android.annotation.XmlElement;
|
||||
import im.conversations.android.xmpp.model.Extension;
|
||||
import java.util.Collection;
|
||||
|
||||
@XmlElement(name = "query")
|
||||
public class InfoQuery extends Extension {
|
||||
|
@ -17,4 +19,16 @@ public class InfoQuery extends Extension {
|
|||
public String getNode() {
|
||||
return this.getAttribute("node");
|
||||
}
|
||||
|
||||
public Collection<Feature> getFeatures() {
|
||||
return this.getExtensions(Feature.class);
|
||||
}
|
||||
|
||||
public boolean hasFeature(final String feature) {
|
||||
return Iterables.any(getFeatures(), f -> feature.equals(f.getVar()));
|
||||
}
|
||||
|
||||
public Collection<Identity> getIdentities() {
|
||||
return this.getExtensions(Identity.class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,7 +29,11 @@ public class BindProcessor extends XmppConnection.Delegate implements Consumer<J
|
|||
final var database = getDatabase();
|
||||
|
||||
// TODO reset errorCondition; mucState in chats
|
||||
database.presenceDao().deletePresences(account.id);
|
||||
database.runInTransaction(
|
||||
() -> {
|
||||
database.chatDao().resetMucStates();
|
||||
database.presenceDao().deletePresences(account.id);
|
||||
});
|
||||
|
||||
getManager(RosterManager.class).fetch();
|
||||
|
||||
|
|
|
@ -73,11 +73,16 @@ public class PresenceProcessor extends XmppConnection.Delegate implements Consum
|
|||
occupantId,
|
||||
muc);
|
||||
|
||||
final var mucManager = getManager(MultiUserChatManager.class);
|
||||
if (muc != null && muc.getStatus().contains(MucUser.STATUS_CODE_SELF_PRESENCE)) {
|
||||
getManager(MultiUserChatManager.class).handleSelfPresence(presencePacket);
|
||||
if (type == null) {
|
||||
mucManager.handleSelfPresenceAvailable(presencePacket);
|
||||
} else if (type == PresenceType.UNAVAILABLE) {
|
||||
mucManager.handleSelfPresenceUnavailable(presencePacket);
|
||||
}
|
||||
}
|
||||
if (type == PresenceType.ERROR) {
|
||||
getManager(MultiUserChatManager.class).handleErrorPresence(presencePacket);
|
||||
mucManager.handleErrorPresence(presencePacket);
|
||||
}
|
||||
|
||||
// TODO do this only for contacts?
|
||||
|
|
Loading…
Reference in a new issue