mirror of
https://codeberg.org/Mercury-IM/Mercury-IM
synced 2024-06-22 11:34:50 +02:00
experimental MAM and improved rx repos
This commit is contained in:
parent
93bcda8480
commit
939ce748ef
|
@ -8,11 +8,9 @@ import android.view.MenuItem;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.android.material.navigation.NavigationView;
|
||||
|
||||
import org.mercury_im.messenger.MercuryImApplication;
|
||||
|
|
|
@ -126,9 +126,9 @@ public class ChatActivity extends AppCompatActivity
|
|||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_debug:
|
||||
chatRepository.getOrCreateChatWith(accountId, JidCreate.entityBareFromOrThrowUnchecked("alice@wonderland.lit"))
|
||||
Log.d("CHATACTIVITY", "Fetch MAM messages!");
|
||||
chatViewModel.requestMamMessages()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe();
|
||||
break;
|
||||
|
||||
|
|
|
@ -6,8 +6,11 @@ import androidx.lifecycle.ViewModel;
|
|||
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.mercury_im.messenger.MercuryImApplication;
|
||||
import org.mercury_im.messenger.core.centers.ConnectionCenter;
|
||||
import org.mercury_im.messenger.persistence.model.ChatModel;
|
||||
import org.mercury_im.messenger.persistence.model.ContactModel;
|
||||
import org.mercury_im.messenger.persistence.model.MessageModel;
|
||||
import org.mercury_im.messenger.persistence.repository.ChatRepository;
|
||||
import org.mercury_im.messenger.persistence.repository.MessageRepository;
|
||||
import org.mercury_im.messenger.persistence.repository.RosterRepository;
|
||||
|
||||
|
@ -15,9 +18,10 @@ import java.util.List;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import io.reactivex.Scheduler;
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.functions.Action;
|
||||
import io.reactivex.functions.Consumer;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
|
@ -31,12 +35,19 @@ public class ChatViewModel extends ViewModel {
|
|||
@Inject
|
||||
RosterRepository rosterRepository;
|
||||
|
||||
@Inject
|
||||
ChatRepository chatRepository;
|
||||
|
||||
@Inject
|
||||
ConnectionCenter connectionCenter;
|
||||
|
||||
private long accountId;
|
||||
private EntityBareJid jid;
|
||||
|
||||
private MutableLiveData<ContactModel> contact = new MutableLiveData<>();
|
||||
private MutableLiveData<List<MessageModel>> messages = new MutableLiveData<>();
|
||||
private MutableLiveData<String> contactDisplayName = new MutableLiveData<>();
|
||||
private MutableLiveData<ChatModel> chat = new MutableLiveData<>();
|
||||
|
||||
public ChatViewModel() {
|
||||
super();
|
||||
|
@ -66,6 +77,12 @@ public class ChatViewModel extends ViewModel {
|
|||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe((Consumer<List<MessageModel>>)
|
||||
messages -> ChatViewModel.this.messages.setValue(messages)));
|
||||
|
||||
disposable.add(chatRepository.getOrCreateChatWith(accountId, jid)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe((Consumer<ChatModel>)
|
||||
chatModel -> this.chat.setValue(chatModel)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -101,4 +118,18 @@ public class ChatViewModel extends ViewModel {
|
|||
messages.setValue(o);
|
||||
}));
|
||||
}
|
||||
|
||||
public Completable requestMamMessages() {
|
||||
return Completable.fromAction(new Action() {
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
ChatModel chatModel = ChatViewModel.this.chat.getValue();
|
||||
if (chatModel == null) {
|
||||
return;
|
||||
}
|
||||
connectionCenter.requestMamMessagesFor(chatModel);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.mercury_im.messenger;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mercury_im.messenger.core.connection.ConnectionState;
|
||||
|
||||
import io.reactivex.observers.TestObserver;
|
||||
|
|
|
@ -7,12 +7,16 @@ import org.jivesoftware.smack.tcp.XMPPTCPConnection;
|
|||
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
|
||||
import org.jivesoftware.smackx.caps.EntityCapsManager;
|
||||
import org.jivesoftware.smackx.csi.ClientStateIndicationManager;
|
||||
import org.jivesoftware.smackx.mam.MamManager;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.mercury_im.messenger.core.stores.EntityCapsStore;
|
||||
import org.mercury_im.messenger.core.connection.MercuryConfiguration;
|
||||
import org.mercury_im.messenger.core.connection.MercuryConnection;
|
||||
import org.mercury_im.messenger.core.stores.PlainMessageStore;
|
||||
import org.mercury_im.messenger.core.stores.RosterStore;
|
||||
import org.mercury_im.messenger.persistence.model.AccountModel;
|
||||
import org.mercury_im.messenger.persistence.model.ChatModel;
|
||||
import org.mercury_im.messenger.persistence.model.EntityModel;
|
||||
import org.mercury_im.messenger.persistence.repository.AccountRepository;
|
||||
import org.mercury_im.messenger.persistence.repository.RosterRepository;
|
||||
|
||||
|
@ -213,4 +217,23 @@ public class ConnectionCenter {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void requestMamMessagesFor(ChatModel chat) {
|
||||
disposable.add(rosterRepository.getEntity(chat.getPeerEntityId())
|
||||
.subscribe((Consumer<EntityModel>) entity -> {
|
||||
MercuryConnection connection = connectionMap.get(entity.getAccountId());
|
||||
if (connection == null) return;
|
||||
MamManager mamManager = MamManager.getInstanceFor(connection.getConnection());
|
||||
MamManager.MamQuery query;
|
||||
//if (chat.getEarliestMamMessageId() == null) {
|
||||
query = mamManager.queryMostRecentPage(entity.getJid(), 100);
|
||||
//} else {
|
||||
//MamManager.MamQueryArgs queryArgs = MamManager.MamQueryArgs.builder()
|
||||
// .beforeUid()
|
||||
// .build();
|
||||
//query = mamManager.queryArchive()
|
||||
//}
|
||||
messageStore.onMamResult(entity.getAccountId(), entity.getJid(), query);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,12 @@ package org.mercury_im.messenger.core.centers;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class NotificationCenter {
|
||||
public class MessageCenter {
|
||||
|
||||
private final ConnectionCenter connectionCenter;
|
||||
|
||||
@Inject
|
||||
public NotificationCenter(ConnectionCenter connectionCenter) {
|
||||
public MessageCenter(ConnectionCenter connectionCenter) {
|
||||
this.connectionCenter = connectionCenter;
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import org.jivesoftware.smackx.carbons.CarbonManager;
|
|||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
||||
import org.jivesoftware.smackx.iqversion.VersionManager;
|
||||
import org.jivesoftware.smackx.mam.MamManager;
|
||||
import org.jivesoftware.smackx.sid.StableUniqueStanzaIdManager;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -36,6 +37,7 @@ public class MercuryConnection {
|
|||
protected final CarbonManager carbonManager;
|
||||
protected final StableUniqueStanzaIdManager stanzaIdManager;
|
||||
protected final ServiceDiscoveryManager serviceDiscoveryManager;
|
||||
protected final MamManager mamManager;
|
||||
|
||||
BehaviorSubject<ConnectionState> state = BehaviorSubject.createDefault(ConnectionState.DISCONNECTED);
|
||||
|
||||
|
@ -59,6 +61,8 @@ public class MercuryConnection {
|
|||
VersionManager.setAutoAppendSmackVersion(false);
|
||||
VersionManager.getInstanceFor(connection).setVersion("Mercury", "0.0.1-stealth", "Android");
|
||||
serviceDiscoveryManager.setIdentity(new DiscoverInfo.Identity("client", "Mercury", "phone"));
|
||||
|
||||
mamManager = MamManager.getInstanceFor(connection);
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
|
@ -120,6 +124,8 @@ public class MercuryConnection {
|
|||
exception.printStackTrace();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,8 +16,10 @@ import java.util.logging.Logger;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.functions.Consumer;
|
||||
import io.reactivex.observers.DisposableSingleObserver;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
|
@ -29,7 +31,7 @@ public class EntityCapsStore implements EntityCapsPersistentCache {
|
|||
private final Map<String, DiscoverInfo> discoverInfoMap = new HashMap<>();
|
||||
|
||||
private final CompositeDisposable disposable = new CompositeDisposable();
|
||||
private Single<List<EntityCapsModel>> allEntityCaps;
|
||||
private Observable<List<EntityCapsModel>> allEntityCaps;
|
||||
|
||||
@Inject
|
||||
public EntityCapsStore(EntityCapsRepository repository) {
|
||||
|
@ -41,40 +43,30 @@ public class EntityCapsStore implements EntityCapsPersistentCache {
|
|||
allEntityCaps = entityCapsRepository.getAllEntityCaps();
|
||||
disposable.add(allEntityCaps.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribeWith(new DisposableSingleObserver<List<? extends EntityCapsModel>>() {
|
||||
@Override
|
||||
public void onSuccess(List<? extends EntityCapsModel> entityCapsModels) {
|
||||
discoverInfoMap.clear();
|
||||
for (EntityCapsModel c : entityCapsModels) {
|
||||
DiscoverInfo info;
|
||||
try {
|
||||
XmlPullParser parser = PacketParserUtils.getParserFor(new StringReader(c.getXml()));
|
||||
info = (DiscoverInfo) PacketParserUtils.parseIQ(parser);
|
||||
discoverInfoMap.put(c.getNodeVer(), info);
|
||||
} catch (Exception e) {
|
||||
LOGGER.log(Level.SEVERE, "Error parsing EntityCaps: ", e);
|
||||
}
|
||||
.subscribe(entityCapsModels -> {
|
||||
discoverInfoMap.clear();
|
||||
for (EntityCapsModel c : entityCapsModels) {
|
||||
DiscoverInfo info;
|
||||
try {
|
||||
XmlPullParser parser = PacketParserUtils.getParserFor(new StringReader(c.getXml()));
|
||||
info = (DiscoverInfo) PacketParserUtils.parseIQ(parser);
|
||||
discoverInfoMap.put(c.getNodeVer(), info);
|
||||
} catch (Exception e) {
|
||||
LOGGER.log(Level.SEVERE, "Error parsing EntityCaps: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Throwable e) {
|
||||
LOGGER.log(Level.SEVERE, "Error processing Caps update from store.", e);
|
||||
}
|
||||
}));
|
||||
}, throwable -> LOGGER.log(Level.SEVERE, "Error accessing database", throwable)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addDiscoverInfoByNodePersistent(String nodeVer, DiscoverInfo info) {
|
||||
discoverInfoMap.put(nodeVer, info);
|
||||
EntityCapsModel model = entityCapsRepository.newEntityCapsModel(nodeVer);
|
||||
CharSequence xml = info.toXML();
|
||||
LOGGER.log(Level.INFO, "Persisting entry:" + xml);
|
||||
String string = xml.toString();
|
||||
model.setXml(string);
|
||||
entityCapsRepository.insertOrReplaceEntityCaps(model)
|
||||
disposable.add(entityCapsRepository.insertOrReplaceEntityCaps(model)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe();
|
||||
.subscribe());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -5,16 +5,21 @@ import org.jivesoftware.smack.chat2.ChatManager;
|
|||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smackx.carbons.CarbonManager;
|
||||
import org.jivesoftware.smackx.carbons.packet.CarbonExtension;
|
||||
import org.jivesoftware.smackx.delay.packet.DelayInformation;
|
||||
import org.jivesoftware.smackx.mam.MamManager;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.mercury_im.messenger.core.connection.MercuryConnection;
|
||||
import org.mercury_im.messenger.persistence.model.MessageModel;
|
||||
import org.mercury_im.messenger.persistence.repository.MessageRepository;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import io.reactivex.functions.Consumer;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
public class PlainMessageStore {
|
||||
|
@ -102,4 +107,29 @@ public class PlainMessageStore {
|
|||
public void dispose() {
|
||||
disposable.clear();
|
||||
}
|
||||
|
||||
public void onMamResult(long accountId, EntityBareJid peerJid, MamManager.MamQuery query) {
|
||||
List<MessageModel> messageModels = new ArrayList<>();
|
||||
for (Message message : query.getMessages()) {
|
||||
Date date = new Date();
|
||||
DelayInformation delay = DelayInformation.from(message);
|
||||
if (delay != null) {
|
||||
date = delay.getStamp();
|
||||
}
|
||||
|
||||
MessageModel messageModel = messageRepository.newMessageModel();
|
||||
messageModel.setAccountId(accountId);
|
||||
messageModel.setBody(message.getBody());
|
||||
messageModel.setFrom(message.getFrom().asEntityBareJidOrThrow());
|
||||
messageModel.setTo(message.getTo().asEntityBareJidOrThrow());
|
||||
messageModel.setIncoming(peerJid.equals(message.getFrom().asEntityBareJidOrThrow()));
|
||||
messageModel.setSendDate(date);
|
||||
messageModels.add(messageModel);
|
||||
}
|
||||
|
||||
disposable.add(
|
||||
messageRepository.insertMessages(messageModels)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ public class RosterStore implements org.jivesoftware.smack.roster.rosterstore.Ro
|
|||
LOGGER.log(Level.INFO, "Add entry " + item.toXML().toString());
|
||||
// Update database
|
||||
ContactModel contact = toModel(item);
|
||||
rosterRepository.updateOrInsertContact(contact)
|
||||
rosterRepository.upsertContact(contact)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.computation())
|
||||
.subscribe();
|
||||
|
@ -122,7 +122,7 @@ public class RosterStore implements org.jivesoftware.smack.roster.rosterstore.Ro
|
|||
// Update database
|
||||
for (RosterPacket.Item item : items) {
|
||||
ContactModel model = toModel(item);
|
||||
rosterRepository.updateOrInsertContact(model)
|
||||
rosterRepository.upsertContact(model)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe();
|
||||
}
|
||||
|
@ -174,7 +174,6 @@ public class RosterStore implements org.jivesoftware.smack.roster.rosterstore.Ro
|
|||
|
||||
public ContactModel toModel(RosterPacket.Item item) {
|
||||
ContactModel contact = rosterRepository.newContactModel();
|
||||
contact.setAccountId(accountId);
|
||||
|
||||
contact.setRosterName(item.getName());
|
||||
if (item.getItemType() != null) {
|
||||
|
|
|
@ -41,15 +41,16 @@ dependencies {
|
|||
implementation "com.google.dagger:dagger:$daggerVersion"
|
||||
annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
|
||||
|
||||
testImplementation "junit:junit:$junitVersion"
|
||||
androidTestImplementation "androidx.test:runner:$andxTestRunnerVersion"
|
||||
androidTestImplementation "androidx.test.espresso:espresso-core:$andxTestEspressoVersion"
|
||||
androidTestImplementation "androidx.test:core:$andxTestCoreVersion"
|
||||
androidTestImplementation "androidx.test.ext:junit:$andxTestJunitVersion"
|
||||
|
||||
// Room
|
||||
api "androidx.room:room-runtime:$roomVersion"
|
||||
annotationProcessor "androidx.room:room-compiler:$roomVersion"
|
||||
implementation "androidx.room:room-rxjava2:$roomRxJavaVersion"
|
||||
api "io.reactivex.rxjava2:rxandroid:$rxAndroidVersion"
|
||||
|
||||
// Test
|
||||
testImplementation "junit:junit:$junitVersion"
|
||||
androidTestImplementation "androidx.test:runner:$andxTestRunnerVersion"
|
||||
androidTestImplementation "androidx.test.espresso:espresso-core:$andxTestEspressoVersion"
|
||||
androidTestImplementation "androidx.test:core:$andxTestCoreVersion"
|
||||
androidTestImplementation "androidx.test.ext:junit:$andxTestJunitVersion"
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "343b0cb91d977d378c17c577a4dc231d",
|
||||
"identityHash": "aa41a1d6727ebded374388bdd6cf8456",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "accounts",
|
||||
|
@ -121,7 +121,7 @@
|
|||
},
|
||||
{
|
||||
"tableName": "contacts",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`pk_contact_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `fk_account_id` INTEGER NOT NULL, `fk_entity_id` INTEGER NOT NULL, `rostername` TEXT, `nickname` TEXT, `direction` TEXT, `sub_pending` INTEGER NOT NULL, `approved` INTEGER NOT NULL, FOREIGN KEY(`fk_account_id`) REFERENCES `accounts`(`pk_account_id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`fk_entity_id`) REFERENCES `entities`(`pk_entity_id`) ON UPDATE NO ACTION ON DELETE RESTRICT )",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`pk_contact_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `fk_entity_id` INTEGER NOT NULL, `rostername` TEXT, `nickname` TEXT, `direction` TEXT, `sub_pending` INTEGER NOT NULL, `approved` INTEGER NOT NULL, FOREIGN KEY(`fk_entity_id`) REFERENCES `entities`(`pk_entity_id`) ON UPDATE NO ACTION ON DELETE RESTRICT )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
|
@ -129,12 +129,6 @@
|
|||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "accountId",
|
||||
"columnName": "fk_account_id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "entityId",
|
||||
"columnName": "fk_entity_id",
|
||||
|
@ -187,14 +181,6 @@
|
|||
],
|
||||
"createSql": "CREATE INDEX `index_contacts_pk_contact_id` ON `${TABLE_NAME}` (`pk_contact_id`)"
|
||||
},
|
||||
{
|
||||
"name": "index_contacts_fk_account_id",
|
||||
"unique": false,
|
||||
"columnNames": [
|
||||
"fk_account_id"
|
||||
],
|
||||
"createSql": "CREATE INDEX `index_contacts_fk_account_id` ON `${TABLE_NAME}` (`fk_account_id`)"
|
||||
},
|
||||
{
|
||||
"name": "index_contacts_fk_entity_id",
|
||||
"unique": true,
|
||||
|
@ -214,17 +200,6 @@
|
|||
}
|
||||
],
|
||||
"foreignKeys": [
|
||||
{
|
||||
"table": "accounts",
|
||||
"onDelete": "CASCADE",
|
||||
"onUpdate": "NO ACTION",
|
||||
"columns": [
|
||||
"fk_account_id"
|
||||
],
|
||||
"referencedColumns": [
|
||||
"pk_account_id"
|
||||
]
|
||||
},
|
||||
{
|
||||
"table": "entities",
|
||||
"onDelete": "RESTRICT",
|
||||
|
@ -275,7 +250,7 @@
|
|||
},
|
||||
{
|
||||
"tableName": "chats",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`pk_chat_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `fk_entity_id` INTEGER NOT NULL, `active` INTEGER NOT NULL, FOREIGN KEY(`fk_entity_id`) REFERENCES `entities`(`pk_entity_id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`pk_chat_id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `fk_entity_id` INTEGER NOT NULL, `active` INTEGER NOT NULL, `last_read_message` INTEGER NOT NULL, `most_recent_mam_msg` TEXT, `earliest_mam_msg` TEXT, FOREIGN KEY(`fk_entity_id`) REFERENCES `entities`(`pk_entity_id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
|
@ -294,6 +269,24 @@
|
|||
"columnName": "active",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastReadMessageId",
|
||||
"columnName": "last_read_message",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "mostRecentMamMessage",
|
||||
"columnName": "most_recent_mam_msg",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
},
|
||||
{
|
||||
"fieldPath": "earliestMamMsg",
|
||||
"columnName": "earliest_mam_msg",
|
||||
"affinity": "TEXT",
|
||||
"notNull": false
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
|
@ -525,7 +518,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, '343b0cb91d977d378c17c577a4dc231d')"
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'aa41a1d6727ebded374388bdd6cf8456')"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package org.mercury_im.messenger.persistence.room;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.room.Room;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
import org.mercury_im.messenger.persistence.room.repository.IAccountRepository;
|
||||
import org.mercury_im.messenger.persistence.room.repository.IAvatarRepository;
|
||||
import org.mercury_im.messenger.persistence.room.repository.IChatRepository;
|
||||
import org.mercury_im.messenger.persistence.room.repository.IEntityCapsRepository;
|
||||
import org.mercury_im.messenger.persistence.room.repository.IMessageRepository;
|
||||
import org.mercury_im.messenger.persistence.room.repository.IRosterRepository;
|
||||
|
||||
public abstract class AbstractDatabaseTest {
|
||||
|
||||
protected AppDatabase db;
|
||||
protected IAccountRepository accountRepository;
|
||||
protected IRosterRepository rosterRepository;
|
||||
protected IMessageRepository messageRepository;
|
||||
protected IChatRepository chatRepository;
|
||||
protected IEntityCapsRepository capsRepository;
|
||||
protected IAvatarRepository avatarRepository;
|
||||
|
||||
protected final EntityBareJid TEST_JID_JULIET = JidCreate.entityBareFromOrThrowUnchecked("juliet@capulet.lit");
|
||||
protected final EntityBareJid TEST_JID_ROMEO = JidCreate.entityBareFromOrThrowUnchecked("romeo@montague.lit");
|
||||
protected final EntityBareJid TEST_JID_MERCUTIO = JidCreate.entityBareFromOrThrowUnchecked("mercutio@montague.lit");
|
||||
|
||||
|
||||
@Before
|
||||
public void createDb() {
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
db = Room.inMemoryDatabaseBuilder(context, AppDatabase.class).build();
|
||||
accountRepository = new IAccountRepository(db.accountDao());
|
||||
rosterRepository = new IRosterRepository(
|
||||
db.entityDao(), db.contactDao(), db.rosterInformationDao());
|
||||
messageRepository = new IMessageRepository(db.messageDao());
|
||||
chatRepository = new IChatRepository(db.chatDao(), rosterRepository);
|
||||
capsRepository = new IEntityCapsRepository(db.entityCapsDao());
|
||||
avatarRepository = new IAvatarRepository(db.avatarDao());
|
||||
}
|
||||
|
||||
@After
|
||||
public void closeDb() {
|
||||
db.close();
|
||||
}
|
||||
}
|
|
@ -1,24 +1,18 @@
|
|||
package org.mercury_im.messenger.persistence.room;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
import org.mercury_im.messenger.persistence.room.model.RoomAccountModel;
|
||||
import org.mercury_im.messenger.persistence.room.repository.IAccountRepository;
|
||||
import org.mercury_im.messenger.persistence.room.repository.IRosterRepository;
|
||||
import org.mercury_im.messenger.persistence.room.repository.IMessageRepository;
|
||||
|
||||
import java.util.List;
|
||||
import org.mercury_im.messenger.persistence.room.model.RoomChatModel;
|
||||
import org.mercury_im.messenger.persistence.room.model.RoomContactModel;
|
||||
import org.mercury_im.messenger.persistence.room.model.RoomEntityModel;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.functions.Predicate;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
|
@ -26,33 +20,67 @@ import static org.junit.Assert.assertEquals;
|
|||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
public class ExampleInstrumentedTest extends AbstractDatabaseTest {
|
||||
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test
|
||||
public void testUpsertContact() {
|
||||
RoomAccountModel accountModel = new RoomAccountModel();
|
||||
accountModel.setJid(TEST_JID_JULIET);
|
||||
accountRepository.insertAccount(accountModel).test().assertValue(1L).dispose();
|
||||
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
RoomContactModel contactModel = new RoomContactModel();
|
||||
RoomEntityModel entityModel = new RoomEntityModel();
|
||||
entityModel.setAccountId(1);
|
||||
entityModel.setJid(TEST_JID_ROMEO);
|
||||
contactModel.setEntity(entityModel);
|
||||
|
||||
AppDatabase appDatabase = AppDatabase.getDatabase(context);
|
||||
IAccountRepository accountRepository = new IAccountRepository(appDatabase.accountDao());
|
||||
IRosterRepository rosterRepository = new IRosterRepository(appDatabase.rosterEntryDao());
|
||||
IMessageRepository messageRepository = new IMessageRepository(appDatabase.messageDao());
|
||||
rosterRepository.upsertContact(contactModel).test().assertValue(1L).dispose();
|
||||
rosterRepository.getContact(1L).subscribe(contact -> {
|
||||
System.out.println(contact.getEntity().getAccountId() + " " + contact.getEntity().getJid().toString());
|
||||
}).dispose();
|
||||
|
||||
Observable<List<RoomAccountModel>> accounts = accountRepository.getAllAccounts();
|
||||
rosterRepository.upsertContact(contactModel).test().assertValue(1L).dispose();
|
||||
rosterRepository.getContact(1L).subscribe(contact -> {
|
||||
System.out.println(contact.getEntity().getAccountId() + " " + contact.getEntity().getJid().toString());
|
||||
}).dispose();
|
||||
}
|
||||
|
||||
final RoomAccountModel a1 = new RoomAccountModel();
|
||||
a1.setJid(JidCreate.entityBareFromOrThrowUnchecked("alice@wonderland.lit"));
|
||||
a1.setPassword("5w0rdf1sh");
|
||||
a1.setEnabled(false);
|
||||
accountRepository.insertAccount(a1);
|
||||
@Test
|
||||
public void testGetOrCreateEntity() {
|
||||
RoomAccountModel accountModel = new RoomAccountModel();
|
||||
accountModel.setJid(TEST_JID_JULIET);
|
||||
accountRepository.insertAccount(accountModel).test().assertValue(1L).dispose();
|
||||
|
||||
accounts.test()
|
||||
.assertValue(new Predicate<List<RoomAccountModel>>() {
|
||||
@Override
|
||||
public boolean test(List<RoomAccountModel> roomAccountModels) throws Exception {
|
||||
return roomAccountModels.size() == 1;
|
||||
}
|
||||
});
|
||||
RoomEntityModel romeo = rosterRepository.getOrCreateEntityForAccountAndJid(accountModel, TEST_JID_ROMEO).blockingGet();
|
||||
RoomEntityModel mercu = rosterRepository.getOrCreateEntityForAccountAndJid(accountModel, TEST_JID_MERCUTIO).blockingGet();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testObservabilityOfChat() throws InterruptedException {
|
||||
RoomAccountModel accountModel = new RoomAccountModel();
|
||||
accountModel.setJid(TEST_JID_JULIET);
|
||||
accountRepository.insertAccount(accountModel).test().assertValue(1L).dispose();
|
||||
|
||||
Observable<RoomChatModel> chat = chatRepository.getOrCreateChatWith(accountModel.getId(), TEST_JID_ROMEO);
|
||||
Disposable disposable = chat.subscribe(chatModel -> Log.d(AppDatabase.TAG, "onNext: " + chatModel));
|
||||
|
||||
|
||||
RoomChatModel chatModel = chatRepository.newChatModel();
|
||||
chatModel.setId(1L);
|
||||
chatModel.setPeerEntityId(1L);
|
||||
chatModel.setActive(true);
|
||||
chatRepository.updateChat(chatModel).blockingAwait();
|
||||
|
||||
chatModel.setActive(false);
|
||||
chatRepository.updateChat(chatModel).blockingAwait();
|
||||
|
||||
chatModel.setActive(true);
|
||||
chatRepository.updateChat(chatModel).blockingAwait();
|
||||
|
||||
Thread.sleep(100);
|
||||
|
||||
disposable.dispose();
|
||||
|
||||
Thread.sleep(100);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package org.mercury_im.messenger.persistence.room;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.room.Database;
|
||||
import androidx.room.Room;
|
||||
|
@ -43,6 +42,8 @@ public abstract class AppDatabase extends RoomDatabase {
|
|||
private static final String DB_NAME = "mercury_db";
|
||||
private static AppDatabase INSTANCE;
|
||||
|
||||
public static final String TAG = "PERSISTENCE_ROOM";
|
||||
|
||||
public static AppDatabase getDatabase(final Context context) {
|
||||
if (INSTANCE == null) {
|
||||
Logger.getLogger("DATABASE").log(Level.INFO, context.getApplicationContext().getDatabasePath(DB_NAME).getAbsolutePath());
|
||||
|
|
|
@ -3,9 +3,9 @@ package org.mercury_im.messenger.persistence.room;
|
|||
import org.mercury_im.messenger.persistence.repository.AccountRepository;
|
||||
import org.mercury_im.messenger.persistence.repository.AvatarRepository;
|
||||
import org.mercury_im.messenger.persistence.repository.ChatRepository;
|
||||
import org.mercury_im.messenger.persistence.repository.RosterRepository;
|
||||
import org.mercury_im.messenger.persistence.repository.EntityCapsRepository;
|
||||
import org.mercury_im.messenger.persistence.repository.MessageRepository;
|
||||
import org.mercury_im.messenger.persistence.repository.RosterRepository;
|
||||
import org.mercury_im.messenger.persistence.room.dao.AccountDao;
|
||||
import org.mercury_im.messenger.persistence.room.dao.AvatarDao;
|
||||
import org.mercury_im.messenger.persistence.room.dao.ChatDao;
|
||||
|
@ -17,9 +17,9 @@ import org.mercury_im.messenger.persistence.room.dao.RosterInformationDao;
|
|||
import org.mercury_im.messenger.persistence.room.repository.IAccountRepository;
|
||||
import org.mercury_im.messenger.persistence.room.repository.IAvatarRepository;
|
||||
import org.mercury_im.messenger.persistence.room.repository.IChatRepository;
|
||||
import org.mercury_im.messenger.persistence.room.repository.IRosterRepository;
|
||||
import org.mercury_im.messenger.persistence.room.repository.IEntityCapsRepository;
|
||||
import org.mercury_im.messenger.persistence.room.repository.IMessageRepository;
|
||||
import org.mercury_im.messenger.persistence.room.repository.IRosterRepository;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
|
@ -37,8 +37,8 @@ public class RoomRepositoryModule {
|
|||
|
||||
@Singleton
|
||||
@Provides
|
||||
ChatRepository provideChatRepository(ChatDao chatDao, EntityDao entityDao) {
|
||||
return new IChatRepository(chatDao, entityDao);
|
||||
ChatRepository provideChatRepository(ChatDao chatDao, RosterRepository rosterRepository) {
|
||||
return new IChatRepository(chatDao, rosterRepository);
|
||||
}
|
||||
|
||||
@Singleton
|
||||
|
|
|
@ -36,7 +36,10 @@ public interface AccountDao extends BaseDao<RoomAccountModel> {
|
|||
* @return account or null
|
||||
*/
|
||||
@Query("select * from accounts where pk_account_id = :id")
|
||||
Maybe<RoomAccountModel> getAccountById(long id);
|
||||
Maybe<RoomAccountModel> maybeGetAccountById(long id);
|
||||
|
||||
@Query("select * from accounts where pk_account_id = :id")
|
||||
Observable<RoomAccountModel> getAccountById(long id);
|
||||
|
||||
@Query("select * from accounts where jid = :jid")
|
||||
Maybe<RoomAccountModel> getAccountByJid(EntityBareJid jid);
|
||||
|
|
|
@ -2,16 +2,27 @@ package org.mercury_im.messenger.persistence.room.dao;
|
|||
|
||||
import androidx.room.Delete;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Transaction;
|
||||
import androidx.room.Update;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
|
||||
public interface BaseDao<T> {
|
||||
|
||||
@Insert
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
Single<Long> insert(T entity);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
Single<Long[]> insert(T[] entities);
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.IGNORE)
|
||||
Single<List<Long>> insert(List<T> entities);
|
||||
|
||||
@Update
|
||||
Completable update(T entity);
|
||||
|
||||
|
@ -23,4 +34,7 @@ public interface BaseDao<T> {
|
|||
|
||||
@Delete
|
||||
Completable delete(T[] entities);
|
||||
|
||||
@Delete
|
||||
Completable delete(List<T> entities);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import java.util.List;
|
|||
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
|
||||
@Dao
|
||||
@TypeConverters(EntityBareJidConverter.class)
|
||||
|
@ -22,26 +23,23 @@ public interface ChatDao extends BaseDao<RoomChatModel> {
|
|||
Observable<List<RoomChatModel>> getAllChats();
|
||||
|
||||
@Query("SELECT chats.* FROM chats JOIN entities WHERE fk_account_id = :accountId")
|
||||
Observable<List<RoomChatModel>> getAllChatsOf(long accountId);
|
||||
Observable<List<RoomChatModel>> getAllChatsOfAccount(long accountId);
|
||||
|
||||
@Query("SELECT * FROM chats WHERE fk_entity_id = :entityId")
|
||||
Maybe<RoomChatModel> getChatWithIdentity(long entityId);
|
||||
Maybe<RoomChatModel> maybeGetChatWithEntity(long entityId);
|
||||
|
||||
@Query("SELECT * FROM chats WHERE fk_entity_id = :entityId")
|
||||
Observable<RoomChatModel> getChatWithEntity(long entityId);
|
||||
|
||||
@Query("SELECT chats.* FROM chats JOIN entities WHERE fk_account_id = :accountId AND jid = :jid")
|
||||
Maybe<RoomChatModel> getChatWithJid(long accountId, EntityBareJid jid);
|
||||
Maybe<RoomChatModel> maybeGetChatWithJid(long accountId, EntityBareJid jid);
|
||||
|
||||
@Query("SELECT chats.* FROM chats JOIN entities WHERE fk_account_id = :accountId AND jid = :jid")
|
||||
Observable<RoomChatModel> getChatWithJid(long accountId, EntityBareJid jid);
|
||||
|
||||
@Query("SELECT chats.* FROM chats JOIN contacts WHERE contacts.pk_contact_id = :contactId")
|
||||
Maybe<RoomChatModel> getChatWithContact(long contactId);
|
||||
|
||||
@Query("SELECT chats.* FROM chats JOIN entities WHERE fk_account_id = :accountId AND jid = :jid")
|
||||
Observable<RoomChatModel> getObservableChatWithJid(long accountId, EntityBareJid jid);
|
||||
|
||||
@Query("SELECT " +
|
||||
"pk_chat_id as chatId, " +
|
||||
"fk_entity_id as entityId, " +
|
||||
"active, " +
|
||||
"jid " +
|
||||
"FROM chats INNER JOIN entities")
|
||||
Observable<Chat> getChat();
|
||||
Maybe<RoomChatModel> maybeGetChatWithContact(long contactId);
|
||||
|
||||
@Query("SELECT chats.* FROM chats JOIN contacts WHERE contacts.pk_contact_id = :contactId")
|
||||
Observable<RoomChatModel> getChatWithContact(long contactId);
|
||||
}
|
||||
|
|
|
@ -37,10 +37,11 @@ public interface ContactDao extends BaseDao<RoomContactModel> {
|
|||
Observable<List<RoomContactModel>> getAllContacts();
|
||||
|
||||
@Query("SELECT contacts.* FROM contacts JOIN entities " +
|
||||
"WHERE contacts.fk_account_id = :accountId AND jid = :jid")
|
||||
"WHERE entities.fk_account_id = :accountId AND jid = :jid")
|
||||
Maybe<RoomContactModel> getContactByJid(long accountId, EntityBareJid jid);
|
||||
|
||||
@Query("SELECT * FROM contacts WHERE fk_account_id = :accountId")
|
||||
@Query("SELECT contacts.* FROM contacts JOIN entities " +
|
||||
"WHERE entities.fk_account_id = :accountId")
|
||||
Observable<List<RoomContactModel>> getContactsForAccount(long accountId);
|
||||
|
||||
@Query("DELETE FROM contacts WHERE pk_contact_id = :id")
|
||||
|
@ -52,12 +53,15 @@ public interface ContactDao extends BaseDao<RoomContactModel> {
|
|||
@Query("DELETE FROM contacts")
|
||||
Completable deleteAll();
|
||||
|
||||
@Query("DELETE FROM contacts WHERE fk_account_id = :accountId")
|
||||
@Query("DELETE FROM contacts WHERE fk_entity_id IN " +
|
||||
"(SELECT pk_entity_id FROM entities " +
|
||||
"WHERE entities.fk_account_id = :accountId)")
|
||||
Completable deleteAllForAccount(long accountId);
|
||||
|
||||
@Query("DELETE FROM contacts WHERE pk_contact_id IN(:ids)")
|
||||
Completable deleteContacts(long[] ids);
|
||||
|
||||
@Query("SELECT entities.* FROM contacts INNER JOIN entities ON contacts.fk_entity_id = entities.pk_entity_id WHERE contacts.pk_contact_id = :contactId")
|
||||
@Query("SELECT entities.* FROM contacts INNER JOIN entities ON contacts.fk_entity_id = entities.pk_entity_id " +
|
||||
"WHERE contacts.pk_contact_id = :contactId")
|
||||
Single<RoomEntityModel> getEntityForContactId(long contactId);
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@ package org.mercury_im.messenger.persistence.room.dao;
|
|||
|
||||
import androidx.annotation.WorkerThread;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Delete;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.Query;
|
||||
|
||||
import org.mercury_im.messenger.persistence.room.model.RoomEntityCapsModel;
|
||||
|
@ -11,31 +9,18 @@ import org.mercury_im.messenger.persistence.room.model.RoomEntityCapsModel;
|
|||
import java.util.List;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
|
||||
import static androidx.room.OnConflictStrategy.REPLACE;
|
||||
|
||||
@Dao
|
||||
@WorkerThread
|
||||
public interface EntityCapsDao {
|
||||
public interface EntityCapsDao extends BaseDao<RoomEntityCapsModel> {
|
||||
|
||||
@Query("SELECT * FROM entity_caps WHERE pk_node_ver = :nodeVer")
|
||||
Single<RoomEntityCapsModel> getEntityCapsForNodeVer(String nodeVer);
|
||||
|
||||
@Query("SELECT * FROM entity_caps")
|
||||
Single<List<RoomEntityCapsModel>> getAllEntityCaps();
|
||||
|
||||
@Insert(onConflict = REPLACE)
|
||||
Completable insert(List<RoomEntityCapsModel> models);
|
||||
|
||||
@Insert(onConflict = REPLACE)
|
||||
Completable insert(RoomEntityCapsModel model);
|
||||
|
||||
@Delete
|
||||
Completable delete(List<RoomEntityCapsModel> models);
|
||||
|
||||
@Delete
|
||||
Completable delete(RoomEntityCapsModel model);
|
||||
Observable<List<RoomEntityCapsModel>> getAllEntityCaps();
|
||||
|
||||
@Query("DELETE FROM entity_caps")
|
||||
Completable deleteAllEntityCaps();
|
||||
|
|
|
@ -18,10 +18,6 @@ import static androidx.room.OnConflictStrategy.REPLACE;
|
|||
@TypeConverters(EntityBareJidConverter.class)
|
||||
public interface EntityDao extends BaseDao<RoomEntityModel> {
|
||||
|
||||
@Override
|
||||
@Insert(onConflict = REPLACE)
|
||||
Single<Long> insert(RoomEntityModel entity);
|
||||
|
||||
@Query("SELECT * FROM entities WHERE pk_entity_id = :id")
|
||||
Maybe<RoomEntityModel> getEntity(long id);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.mercury_im.messenger.persistence.room.model;
|
||||
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Index;
|
||||
|
@ -48,8 +49,9 @@ public class RoomAccountModel extends AbstractAccountModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setId(long id) {
|
||||
public RoomAccountModel setId(long id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -91,4 +93,16 @@ public class RoomAccountModel extends AbstractAccountModel {
|
|||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String toString() {
|
||||
return "AccountModel[" +
|
||||
KEY_ID + ": " + getId() + ", " +
|
||||
KEY_JID + ": " + (getJid() != null ? getJid().toString() : "null") + ", " +
|
||||
KEY_PASSWORD + ": " + getPassword() + ", " +
|
||||
KEY_ENABLED + ": " + getEnabled() + ", " +
|
||||
KEY_STATE + ": " +getState() +
|
||||
"]";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.mercury_im.messenger.persistence.room.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.ForeignKey;
|
||||
|
@ -30,6 +31,9 @@ public class RoomChatModel implements ChatModel {
|
|||
|
||||
public static final String KEY_ENTITY = "fk_entity_id";
|
||||
public static final String KEY_ACTIVE = "active";
|
||||
public static final String KEY_LAST_READ_MSG = "last_read_message";
|
||||
public static final String KEY_MOST_RECENT_MAM_MSG = "most_recent_mam_msg";
|
||||
public static final String KEY_EARLIEST_MAM_MSG = "earliest_mam_msg";
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ColumnInfo(name = KEY_ID)
|
||||
|
@ -41,6 +45,15 @@ public class RoomChatModel implements ChatModel {
|
|||
@ColumnInfo(name = KEY_ACTIVE)
|
||||
private boolean isActive;
|
||||
|
||||
@ColumnInfo(name = KEY_LAST_READ_MSG)
|
||||
private long lastReadMessageId;
|
||||
|
||||
@ColumnInfo(name = KEY_MOST_RECENT_MAM_MSG)
|
||||
private String mostRecentMamMessageId;
|
||||
|
||||
@ColumnInfo(name = KEY_EARLIEST_MAM_MSG)
|
||||
private String earliestMamMessageId;
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
|
@ -70,4 +83,44 @@ public class RoomChatModel implements ChatModel {
|
|||
public void setActive(boolean active) {
|
||||
this.isActive = active;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastReadMessageId() {
|
||||
return lastReadMessageId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastReadMessageId(long messageId) {
|
||||
this.lastReadMessageId = messageId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMostRecentMamMessageId() {
|
||||
return mostRecentMamMessageId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMostRecentMamMessageId(String uid) {
|
||||
this.mostRecentMamMessageId = uid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEarliestMamMessageId() {
|
||||
return earliestMamMessageId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEarliestMamMessageId(String uid) {
|
||||
this.earliestMamMessageId = uid;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String toString() {
|
||||
return "ChatModel[" +
|
||||
KEY_ID + ": " + getId() + ", " +
|
||||
KEY_ENTITY + ": " + getPeerEntityId() + ", " +
|
||||
KEY_ACTIVE + ": " + isActive() +
|
||||
"]";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.mercury_im.messenger.persistence.room.model;
|
||||
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.ForeignKey;
|
||||
|
@ -13,9 +14,7 @@ import org.mercury_im.messenger.persistence.model.ContactModel;
|
|||
import org.mercury_im.messenger.persistence.model.EntityModel;
|
||||
import org.mercury_im.messenger.persistence.room.type_converter.DirectionConverter;
|
||||
|
||||
import static androidx.room.ForeignKey.CASCADE;
|
||||
import static androidx.room.ForeignKey.RESTRICT;
|
||||
import static org.mercury_im.messenger.persistence.room.model.RoomContactModel.KEY_ACCOUNT_ID;
|
||||
import static org.mercury_im.messenger.persistence.room.model.RoomContactModel.KEY_ID;
|
||||
import static org.mercury_im.messenger.persistence.room.model.RoomContactModel.KEY_ENTITY_ID;
|
||||
import static org.mercury_im.messenger.persistence.room.model.RoomContactModel.TABLE;
|
||||
|
@ -23,15 +22,10 @@ import static org.mercury_im.messenger.persistence.room.model.RoomContactModel.T
|
|||
@Entity(tableName = TABLE,
|
||||
indices = {
|
||||
@Index(value = KEY_ID),
|
||||
@Index(value = KEY_ACCOUNT_ID),
|
||||
@Index(value = KEY_ENTITY_ID, unique = true),
|
||||
@Index(value = {KEY_ID, KEY_ENTITY_ID}, unique = true)
|
||||
},
|
||||
foreignKeys = {
|
||||
@ForeignKey(entity = RoomAccountModel.class,
|
||||
parentColumns = RoomAccountModel.KEY_ID,
|
||||
childColumns = KEY_ACCOUNT_ID,
|
||||
onDelete = CASCADE),
|
||||
@ForeignKey(entity = RoomEntityModel.class,
|
||||
parentColumns = RoomEntityModel.KEY_ID,
|
||||
childColumns = KEY_ENTITY_ID,
|
||||
|
@ -40,21 +34,17 @@ public class RoomContactModel implements ContactModel {
|
|||
|
||||
public static final String TABLE = "contacts";
|
||||
public static final String KEY_ID = "pk_contact_id";
|
||||
public static final String KEY_ACCOUNT_ID = "fk_account_id";
|
||||
public static final String KEY_ENTITY_ID = "fk_entity_id";
|
||||
public static final String KEY_ROSTER_NAME = "rostername";
|
||||
public static final String KEY_NICKNAME = "nickname";
|
||||
public static final String KEY_DIRECTION = "direction";
|
||||
public static final String KEY_SUB_DIRECTION = "direction";
|
||||
public static final String KEY_SUB_PENDING = "sub_pending";
|
||||
public static final String KEY_APPROVED = "approved";
|
||||
public static final String KEY_SUB_APPROVED = "approved";
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ColumnInfo(name = KEY_ID)
|
||||
private long id;
|
||||
|
||||
@ColumnInfo(name = KEY_ACCOUNT_ID)
|
||||
private long accountId;
|
||||
|
||||
@ColumnInfo(name = KEY_ENTITY_ID)
|
||||
private long entityId;
|
||||
|
||||
|
@ -64,14 +54,14 @@ public class RoomContactModel implements ContactModel {
|
|||
@ColumnInfo(name = KEY_NICKNAME)
|
||||
private String nickname;
|
||||
|
||||
@ColumnInfo(name = KEY_DIRECTION)
|
||||
@ColumnInfo(name = KEY_SUB_DIRECTION)
|
||||
@TypeConverters(DirectionConverter.class)
|
||||
private DIRECTION direction;
|
||||
|
||||
@ColumnInfo(name = KEY_SUB_PENDING)
|
||||
private boolean subscriptionPending;
|
||||
|
||||
@ColumnInfo(name = KEY_APPROVED)
|
||||
@ColumnInfo(name = KEY_SUB_APPROVED)
|
||||
private boolean approved;
|
||||
|
||||
/**
|
||||
|
@ -91,16 +81,6 @@ public class RoomContactModel implements ContactModel {
|
|||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getAccountId() {
|
||||
return accountId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAccountId(long accountId) {
|
||||
this.accountId = accountId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getEntityId() {
|
||||
return entityId;
|
||||
|
@ -169,5 +149,20 @@ public class RoomContactModel implements ContactModel {
|
|||
@Override
|
||||
public void setEntity(EntityModel entity) {
|
||||
this.entityModel = entity;
|
||||
this.entityId = entity.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String toString() {
|
||||
return "ContactModel[" +
|
||||
KEY_ID + ": " + getId() + ", " +
|
||||
KEY_ENTITY_ID + ": " + getEntityId() + ", " +
|
||||
KEY_ROSTER_NAME + ": " + getRosterName() + ", " +
|
||||
KEY_NICKNAME + ": " + getNickname() + ", " +
|
||||
KEY_SUB_DIRECTION + ": " + getDirection() + ", " +
|
||||
KEY_SUB_PENDING + ": " + isSubscriptionPending() + ", " +
|
||||
KEY_SUB_APPROVED + ": " + isApproved() +
|
||||
"]";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,6 @@ public class RoomEntityModel implements EntityModel {
|
|||
public static final String KEY_ID = "pk_entity_id";
|
||||
public static final String KEY_ACCOUNT_ID = "fk_account_id";
|
||||
public static final String KEY_JID = "jid";
|
||||
public static final String KEY_AVATAR = "avatar";
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ColumnInfo(name = KEY_ID)
|
||||
|
@ -82,4 +81,14 @@ public class RoomEntityModel implements EntityModel {
|
|||
public void setAccountId(long accountId) {
|
||||
this.accountId = accountId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String toString() {
|
||||
return "EntityModel[" +
|
||||
KEY_ID + ": " + getId() + ", " +
|
||||
KEY_ACCOUNT_ID + ": " + getAccountId() + ", " +
|
||||
KEY_JID + ": " + getJid().toString() +
|
||||
"]";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.mercury_im.messenger.persistence.room.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Index;
|
||||
|
@ -45,4 +46,13 @@ public class RoomRosterInformationModel implements RosterInformationModel {
|
|||
public void setRosterVersion(String rosterVersion) {
|
||||
this.rosterVersion = rosterVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String toString() {
|
||||
return "RosterInformationModel[" +
|
||||
KEY_ID + ": " + getAccountId() + ", " +
|
||||
KEY_ROSTER_VERSION + ": " + getRosterVersion() +
|
||||
"]";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,5 +6,5 @@ public class Chat {
|
|||
public long chatId;
|
||||
public long entityId;
|
||||
public EntityBareJid jid;
|
||||
|
||||
public boolean active;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package org.mercury_im.messenger.persistence.room.repository;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.mercury_im.messenger.persistence.repository.AccountRepository;
|
||||
import org.mercury_im.messenger.persistence.room.dao.AccountDao;
|
||||
import org.mercury_im.messenger.persistence.room.model.RoomAccountModel;
|
||||
|
@ -13,6 +17,8 @@ import io.reactivex.Maybe;
|
|||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
|
||||
import static org.mercury_im.messenger.persistence.room.AppDatabase.TAG;
|
||||
|
||||
public class IAccountRepository implements AccountRepository<RoomAccountModel> {
|
||||
|
||||
private final AccountDao accountDao;
|
||||
|
@ -28,10 +34,15 @@ public class IAccountRepository implements AccountRepository<RoomAccountModel> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Maybe<RoomAccountModel> getAccount(long accountId) {
|
||||
public Observable<RoomAccountModel> getAccount(long accountId) {
|
||||
return accountDao.getAccountById(accountId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Maybe<RoomAccountModel> maybeGetAccount(long accountId) {
|
||||
return accountDao.maybeGetAccountById(accountId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<List<RoomAccountModel>> getAllAccounts() {
|
||||
return accountDao.getAllAccounts();
|
||||
|
@ -39,13 +50,16 @@ public class IAccountRepository implements AccountRepository<RoomAccountModel> {
|
|||
|
||||
|
||||
@Override
|
||||
public Single<Long> insertAccount(RoomAccountModel accountModel) {
|
||||
return accountDao.insert(accountModel);
|
||||
public Single<Long> insertAccount(@NonNull RoomAccountModel accountModel) {
|
||||
return accountDao.insert(accountModel)
|
||||
.map(accountId -> accountModel.setId(accountId).getId())
|
||||
.doOnSubscribe(ignore -> Log.v(TAG, "Insert " + accountModel))
|
||||
.doAfterSuccess(accountId -> Log.v(TAG, "AccountModel has new ID " + accountId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Completable updateState(long accountId, String state) {
|
||||
return accountDao.updateConnectionState(accountId, state);
|
||||
public Completable updateAccount(RoomAccountModel accountModel) {
|
||||
return accountDao.update(accountModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
package org.mercury_im.messenger.persistence.room.repository;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.mercury_im.messenger.persistence.model.AccountModel;
|
||||
import org.mercury_im.messenger.persistence.model.ChatModel;
|
||||
import org.mercury_im.messenger.persistence.model.ContactModel;
|
||||
import org.mercury_im.messenger.persistence.model.EntityModel;
|
||||
import org.mercury_im.messenger.persistence.repository.ChatRepository;
|
||||
import org.mercury_im.messenger.persistence.repository.RosterRepository;
|
||||
import org.mercury_im.messenger.persistence.room.dao.ChatDao;
|
||||
import org.mercury_im.messenger.persistence.room.dao.EntityDao;
|
||||
import org.mercury_im.messenger.persistence.room.model.RoomChatModel;
|
||||
import org.mercury_im.messenger.persistence.room.model.RoomEntityModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -18,21 +16,19 @@ import javax.inject.Inject;
|
|||
import io.reactivex.Completable;
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Scheduler;
|
||||
import io.reactivex.SingleTransformer;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.functions.Consumer;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import io.reactivex.Single;
|
||||
|
||||
import static org.mercury_im.messenger.persistence.room.AppDatabase.TAG;
|
||||
|
||||
public class IChatRepository implements ChatRepository<RoomChatModel> {
|
||||
|
||||
private final ChatDao chatDao;
|
||||
private final EntityDao entityDao;
|
||||
private final RosterRepository rosterRepository;
|
||||
|
||||
@Inject
|
||||
public IChatRepository(ChatDao chatDao, EntityDao entityDao) {
|
||||
public IChatRepository(ChatDao chatDao, RosterRepository rosterRepository) {
|
||||
this.chatDao = chatDao;
|
||||
this.entityDao = entityDao;
|
||||
this.rosterRepository = rosterRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -42,7 +38,36 @@ public class IChatRepository implements ChatRepository<RoomChatModel> {
|
|||
|
||||
@Override
|
||||
public Observable<RoomChatModel> getOrCreateChatWith(long accountId, EntityBareJid jid) {
|
||||
return Observable.fromCallable(() -> {
|
||||
EntityModel entity = (EntityModel) rosterRepository.getOrCreateEntityForAccountAndJid(accountId, jid).blockingGet();
|
||||
RoomChatModel chat = maybeGetChatWithEntity(entity).blockingGet();
|
||||
if (chat == null) {
|
||||
chat = newChatModel();
|
||||
chat.setPeerEntityId(entity.getId());
|
||||
chat.setId(insertChat(chat).blockingGet());
|
||||
}
|
||||
return chat;
|
||||
}).concatWith(getChatWith(accountId, jid).skip(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<RoomChatModel> getChatWith(long accountId, EntityBareJid jid) {
|
||||
return chatDao.getChatWithJid(accountId, jid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<RoomChatModel> getChatWithEntity(long entityId) {
|
||||
return chatDao.getChatWithEntity(entityId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Completable updateChat(RoomChatModel chat) {
|
||||
return chatDao.update(chat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<RoomChatModel> getChatWithContact(long contactId) {
|
||||
return chatDao.getChatWithContact(contactId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -51,27 +76,39 @@ public class IChatRepository implements ChatRepository<RoomChatModel> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Observable<List<RoomChatModel>> getAllChatsOf(AccountModel accountModel) {
|
||||
return chatDao.getAllChatsOf(accountModel.getId());
|
||||
public Observable<List<RoomChatModel>> getAllChatsOfAccount(long accountId) {
|
||||
return chatDao.getAllChatsOfAccount(accountId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Maybe<RoomChatModel> getChatWith(EntityModel identity) {
|
||||
return chatDao.getChatWithIdentity(identity.getId());
|
||||
public Maybe<RoomChatModel> maybeGetChatWith(long accountId, EntityBareJid jid) {
|
||||
return chatDao.maybeGetChatWithJid(accountId, jid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Maybe<RoomChatModel> getChatWith(AccountModel account, EntityBareJid jid) {
|
||||
return chatDao.getChatWithJid(account.getId(), jid);
|
||||
public Maybe<RoomChatModel> maybeGetChatWithEntity(long entityId) {
|
||||
return chatDao.maybeGetChatWithEntity(entityId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Maybe<RoomChatModel> getChatWith(ContactModel contact) {
|
||||
return chatDao.getChatWithContact(contact.getId());
|
||||
public Maybe<RoomChatModel> maybeGetChatWithContact(long contactId) {
|
||||
return chatDao.maybeGetChatWithContact(contactId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Single<Long> insertChat(RoomChatModel chat) {
|
||||
return chatDao.insert(chat)
|
||||
.map(chatId -> {
|
||||
chat.setId(chatId);
|
||||
return chatId;
|
||||
})
|
||||
.doOnSubscribe(ignore -> Log.v(TAG, "Insert " + chat))
|
||||
.doAfterSuccess(chatId -> Log.v(TAG, "Assign ID " + chatId + " to " + chat));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Completable closeChat(RoomChatModel chat) {
|
||||
return chatDao.delete(chat);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.util.List;
|
|||
import javax.inject.Inject;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
|
||||
|
||||
|
@ -29,7 +30,7 @@ public class IEntityCapsRepository implements EntityCapsRepository<RoomEntityCap
|
|||
}
|
||||
|
||||
@Override
|
||||
public Single<List<RoomEntityCapsModel>> getAllEntityCaps() {
|
||||
public Observable<List<RoomEntityCapsModel>> getAllEntityCaps() {
|
||||
return dao.getAllEntityCaps();
|
||||
}
|
||||
|
||||
|
@ -39,12 +40,12 @@ public class IEntityCapsRepository implements EntityCapsRepository<RoomEntityCap
|
|||
}
|
||||
|
||||
@Override
|
||||
public Completable insertOrReplaceEntityCaps(List<RoomEntityCapsModel> entityCaps) {
|
||||
public Single<List<Long>> insertOrReplaceEntityCaps(List<RoomEntityCapsModel> entityCaps) {
|
||||
return dao.insert(entityCaps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Completable insertOrReplaceEntityCaps(RoomEntityCapsModel entityCaps) {
|
||||
public Single<Long> insertOrReplaceEntityCaps(RoomEntityCapsModel entityCaps) {
|
||||
return dao.insert(entityCaps);
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,11 @@ public class IMessageRepository implements MessageRepository<RoomMessageModel> {
|
|||
return messageDao.insert(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Single<List<Long>> insertMessages(List<RoomMessageModel> messages) {
|
||||
return messageDao.insert(messages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<RoomMessageModel> getLastMessageFrom(long accountId, EntityBareJid peer) {
|
||||
return messageDao.getLastMessageFrom(accountId, peer);
|
||||
|
|
|
@ -12,18 +12,16 @@ import org.mercury_im.messenger.persistence.room.model.RoomEntityModel;
|
|||
import org.mercury_im.messenger.persistence.room.model.RoomRosterInformationModel;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.CompletableSource;
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.SingleObserver;
|
||||
import io.reactivex.SingleSource;
|
||||
import io.reactivex.functions.Consumer;
|
||||
import io.reactivex.functions.Function;
|
||||
|
||||
import static org.mercury_im.messenger.persistence.room.AppDatabase.TAG;
|
||||
|
||||
public class IRosterRepository extends RosterRepository<RoomEntityModel, RoomContactModel, RoomRosterInformationModel> {
|
||||
|
||||
|
@ -72,21 +70,43 @@ public class IRosterRepository extends RosterRepository<RoomEntityModel, RoomCon
|
|||
}
|
||||
|
||||
@Override
|
||||
public Single<Long> updateOrInsertContact(RoomContactModel contact) {
|
||||
//noinspection unchecked
|
||||
return entityDao.getEntityFor(contact.getAccountId(), contact.getEntity().getJid())
|
||||
.switchIfEmpty(insertOrReplaceEntity((RoomEntityModel) contact.getEntity())
|
||||
.flatMap(entityId -> {
|
||||
Log.d("Mercury", "Insert entity since maybe did not emit: " + entityId);
|
||||
contact.getEntity().setId(entityId);
|
||||
return (SingleSource<RoomEntityModel>) observer ->
|
||||
observer.onSuccess((RoomEntityModel) contact.getEntity());
|
||||
}))
|
||||
.flatMap(entityModel -> {
|
||||
contact.setEntityId(entityModel.getId());
|
||||
contact.setEntity(entityModel);
|
||||
return contactDao.insert(contact);
|
||||
});
|
||||
public Single<Long> upsertContact(RoomContactModel contact) {
|
||||
|
||||
return Single.fromCallable(() -> {
|
||||
RoomEntityModel existingEntityModel = entityDao
|
||||
.getEntityFor(contact.getEntity().getAccountId(), contact.getEntity().getJid())
|
||||
.blockingGet();
|
||||
if (existingEntityModel == null) {
|
||||
// Insert missing entity
|
||||
existingEntityModel = (RoomEntityModel) contact.getEntity();
|
||||
long entityId = insertEntity(existingEntityModel).blockingGet();
|
||||
existingEntityModel.setId(entityId);
|
||||
contact.setEntity(existingEntityModel);
|
||||
}
|
||||
|
||||
RoomContactModel existingContactModel = contactDao.getContactForEntityId(existingEntityModel.getId()).blockingGet();
|
||||
if (existingContactModel == null) {
|
||||
// Insert missing contact
|
||||
existingContactModel = contact;
|
||||
return insertContact(existingContactModel).blockingGet();
|
||||
} else {
|
||||
contactDao.update(contact);
|
||||
}
|
||||
|
||||
return existingContactModel.getId();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Single<Long> insertContact(RoomContactModel contact) {
|
||||
return contactDao.insert(contact)
|
||||
.map(contactId -> {
|
||||
contact.setId(contactId);
|
||||
return contactId;
|
||||
})
|
||||
.doOnSubscribe(ignore -> Log.v(TAG, "Insert " + contact))
|
||||
.doAfterSuccess(cid -> Log.v(TAG, "Assigned ID " + cid + " to " + contact));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -107,6 +127,20 @@ public class IRosterRepository extends RosterRepository<RoomEntityModel, RoomCon
|
|||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Single<RoomEntityModel> getOrCreateEntityForAccountAndJid(long accountId, EntityBareJid jid) {
|
||||
return Single.fromCallable(() -> {
|
||||
RoomEntityModel existing = getEntityForAccountAndJid(accountId, jid).blockingGet();
|
||||
if (existing == null) {
|
||||
existing = newEntityModel();
|
||||
existing.setAccountId(accountId);
|
||||
existing.setJid(jid);
|
||||
existing.setId(insertEntity(existing).blockingGet());
|
||||
}
|
||||
return existing;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Maybe<RoomContactModel> getContactForEntity(long entityId) {
|
||||
return contactDao.getContactForEntityId(entityId)
|
||||
|
@ -133,13 +167,8 @@ public class IRosterRepository extends RosterRepository<RoomEntityModel, RoomCon
|
|||
public Completable deleteContact(long accountId, EntityBareJid jid) {
|
||||
// Since Room does not support "DELETE x FROM X x INNER JOIN Y...", we have to get the
|
||||
// entity for the jid first and then delete by using its entityId
|
||||
final Maybe<RoomEntityModel> entity = getEntity(accountId, jid.asEntityBareJidOrThrow());
|
||||
return entity.flatMapCompletable(new Function<RoomEntityModel, CompletableSource>() {
|
||||
@Override
|
||||
public CompletableSource apply(RoomEntityModel entityModel) {
|
||||
return contactDao.deleteContactForEntity(entityModel.getId());
|
||||
}
|
||||
});
|
||||
final Maybe<RoomEntityModel> entity = getEntityForAccountAndJid(accountId, jid.asEntityBareJidOrThrow());
|
||||
return entity.flatMapCompletable(entityModel -> contactDao.deleteContactForEntity(entityModel.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -173,7 +202,8 @@ public class IRosterRepository extends RosterRepository<RoomEntityModel, RoomCon
|
|||
|
||||
@Override
|
||||
public Single<Long> updateRosterInformation(RoomRosterInformationModel rosterInformation) {
|
||||
return rosterInformationDao.insertRosterInformation(rosterInformation);
|
||||
return rosterInformationDao.insertRosterInformation(rosterInformation)
|
||||
.doOnSubscribe(ignore -> Log.v(TAG, "Insert " + rosterInformation));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -191,13 +221,18 @@ public class IRosterRepository extends RosterRepository<RoomEntityModel, RoomCon
|
|||
}
|
||||
|
||||
@Override
|
||||
public Maybe<RoomEntityModel> getEntity(long accountId, EntityBareJid jid) {
|
||||
public Maybe<RoomEntityModel> getEntityForAccountAndJid(long accountId, EntityBareJid jid) {
|
||||
return entityDao.getEntityFor(accountId, jid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Single<Long> insertOrReplaceEntity(RoomEntityModel entity) {
|
||||
return entityDao.insert(entity);
|
||||
public Single<Long> insertEntity(RoomEntityModel entityModel) {
|
||||
return entityDao.insert(entityModel)
|
||||
.map(entityId -> {
|
||||
entityModel.setId(entityId);
|
||||
return entityId;
|
||||
})
|
||||
.doOnSubscribe(ignore -> Log.v(TAG, "Insert " + entityModel))
|
||||
.doAfterSuccess(entityId -> Log.v(TAG, "Assign ID " + entityId + " to " + entityModel));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ public interface AccountModel {
|
|||
*
|
||||
* @param id account id
|
||||
*/
|
||||
void setId(long id);
|
||||
AccountModel setId(long id);
|
||||
|
||||
/**
|
||||
* Return the password of the XMPP account.
|
||||
|
|
|
@ -13,4 +13,16 @@ public interface ChatModel {
|
|||
boolean isActive();
|
||||
|
||||
void setActive(boolean opened);
|
||||
|
||||
long getLastReadMessageId();
|
||||
|
||||
void setLastReadMessageId(long messageId);
|
||||
|
||||
String getMostRecentMamMessageId();
|
||||
|
||||
void setMostRecentMamMessageId(String uid);
|
||||
|
||||
String getEarliestMamMessageId();
|
||||
|
||||
void setEarliestMamMessageId(String uid);
|
||||
}
|
||||
|
|
|
@ -6,10 +6,6 @@ public interface ContactModel<E extends EntityModel> {
|
|||
|
||||
void setId(long id);
|
||||
|
||||
long getAccountId();
|
||||
|
||||
void setAccountId(long id);
|
||||
|
||||
long getEntityId();
|
||||
|
||||
void setEntityId(long id);
|
||||
|
|
|
@ -13,13 +13,15 @@ public interface AccountRepository<E extends AccountModel> {
|
|||
|
||||
E newAccountModel();
|
||||
|
||||
Maybe<E> getAccount(long accountId);
|
||||
Observable<E> getAccount(long accountId);
|
||||
|
||||
Maybe<E> maybeGetAccount(long accountId);
|
||||
|
||||
Observable<List<E>> getAllAccounts();
|
||||
|
||||
Single<Long> insertAccount(E accountModel);
|
||||
|
||||
Completable updateState(long accountId, String state);
|
||||
Completable updateAccount(E accountModel);
|
||||
|
||||
Completable deleteAccount(E item);
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import java.util.List;
|
|||
import io.reactivex.Completable;
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
|
||||
public interface ChatRepository<E extends ChatModel> {
|
||||
|
||||
|
@ -18,15 +19,60 @@ public interface ChatRepository<E extends ChatModel> {
|
|||
|
||||
Observable<List<E>> getAllChats();
|
||||
|
||||
Observable<List<E>> getAllChatsOf(AccountModel account);
|
||||
Observable<List<E>> getAllChatsOfAccount(long accountId);
|
||||
|
||||
default Observable<List<E>> getAllChatsOfAccount(AccountModel account) {
|
||||
return getAllChatsOfAccount(account.getId());
|
||||
}
|
||||
|
||||
Observable<E> getOrCreateChatWith(long accountId, EntityBareJid jid);
|
||||
|
||||
Maybe<E> getChatWith(AccountModel account, EntityBareJid jid);
|
||||
Observable<E> getChatWith(long accountId, EntityBareJid jid);
|
||||
|
||||
Maybe<E> getChatWith(EntityModel identity);
|
||||
default Observable<E> getChatWith(AccountModel accountModel, EntityBareJid jid) {
|
||||
return getChatWith(accountModel.getId(), jid);
|
||||
}
|
||||
|
||||
Maybe<E> getChatWith(ContactModel contact);
|
||||
Observable<E> getChatWithEntity(long entityId);
|
||||
|
||||
default Observable<E> getChatWithEntity(EntityModel entity) {
|
||||
return getChatWithEntity(entity.getId());
|
||||
}
|
||||
|
||||
Observable<E> getChatWithContact(long contactId);
|
||||
|
||||
default Observable<E> getChatWithContact(ContactModel contact) {
|
||||
return getChatWithContact(contact.getId());
|
||||
}
|
||||
|
||||
Maybe<E> maybeGetChatWith(long accountId, EntityBareJid jid);
|
||||
|
||||
default Maybe<E> maybeGetChatWith(AccountModel account, EntityBareJid jid) {
|
||||
return maybeGetChatWith(account.getId(), jid);
|
||||
}
|
||||
|
||||
Maybe<E> maybeGetChatWithContact(long contactId);
|
||||
|
||||
default Maybe<E> maybeGetChatWithContact(ContactModel contact) {
|
||||
return maybeGetChatWithContact(contact.getId());
|
||||
}
|
||||
|
||||
Maybe<E> maybeGetChatWithEntity(long entityId);
|
||||
|
||||
default Maybe<E> maybeGetChatWithEntity(EntityModel entity) {
|
||||
return maybeGetChatWithEntity(entity.getId());
|
||||
}
|
||||
|
||||
Completable updateChat(E chat);
|
||||
|
||||
Single<Long> insertChat(E chat);
|
||||
|
||||
Completable closeChat(E chat);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -5,19 +5,20 @@ import org.mercury_im.messenger.persistence.model.EntityCapsModel;
|
|||
import java.util.List;
|
||||
|
||||
import io.reactivex.Completable;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
|
||||
public interface EntityCapsRepository<E extends EntityCapsModel> {
|
||||
|
||||
E newEntityCapsModel(String nodeVer);
|
||||
|
||||
Single<List<E>> getAllEntityCaps();
|
||||
Observable<List<E>> getAllEntityCaps();
|
||||
|
||||
Single<E> getEntityCapsForNodeVer(String nodeVer);
|
||||
|
||||
Completable insertOrReplaceEntityCaps(List<E> entityCaps);
|
||||
Single<List<Long>> insertOrReplaceEntityCaps(List<E> entityCaps);
|
||||
|
||||
Completable insertOrReplaceEntityCaps(E entityCaps);
|
||||
Single<Long> insertOrReplaceEntityCaps(E entityCaps);
|
||||
|
||||
Completable deleteOrReplaceEntityCaps(List<E> entityCaps);
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ public interface MessageRepository<E extends MessageModel> {
|
|||
|
||||
Single<Long> insertMessage(E message);
|
||||
|
||||
Single<List<Long>> insertMessages(List<E> messages);
|
||||
|
||||
Observable<List<E>> getAllMessages();
|
||||
|
||||
Observable<List<E>> getAllMessagesOf(long accountId);
|
||||
|
|
|
@ -12,7 +12,6 @@ import io.reactivex.Completable;
|
|||
import io.reactivex.Maybe;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.functions.Consumer;
|
||||
|
||||
public abstract class RosterRepository<E extends EntityModel, C extends ContactModel, V extends RosterInformationModel> {
|
||||
|
||||
|
@ -64,7 +63,15 @@ public abstract class RosterRepository<E extends EntityModel, C extends ContactM
|
|||
* @param contact contact
|
||||
* @return single wrapping the contacts ID
|
||||
*/
|
||||
public abstract Single<Long> updateOrInsertContact(C contact);
|
||||
public abstract Single<Long> upsertContact(C contact);
|
||||
|
||||
/**
|
||||
* Insert a contact.
|
||||
*
|
||||
* @param contact contact
|
||||
* @return single wrapping the contacts ID
|
||||
*/
|
||||
public abstract Single<Long> insertContact(C contact);
|
||||
|
||||
/**
|
||||
* Return the {@link ContactModel} corresponding to the given ID wrapped in a {@link Maybe}.
|
||||
|
@ -75,7 +82,7 @@ public abstract class RosterRepository<E extends EntityModel, C extends ContactM
|
|||
public abstract Maybe<C> getContact(long id);
|
||||
|
||||
public Maybe<C> getContact(long accountId, EntityBareJid jid) {
|
||||
return getEntity(accountId, jid).flatMap(this::getContactForEntity);
|
||||
return getEntityForAccountAndJid(accountId, jid).flatMap(this::getContactForEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -223,7 +230,7 @@ public abstract class RosterRepository<E extends EntityModel, C extends ContactM
|
|||
* @param jid {@link EntityBareJid} of the {@link EntityModel}
|
||||
* @return
|
||||
*/
|
||||
public abstract Maybe<E> getEntity(long accountId, EntityBareJid jid);
|
||||
public abstract Maybe<E> getEntityForAccountAndJid(long accountId, EntityBareJid jid);
|
||||
|
||||
/**
|
||||
* Return the {@link EntityModel} which has foreign keys pointing to the {@link AccountModel}
|
||||
|
@ -233,22 +240,21 @@ public abstract class RosterRepository<E extends EntityModel, C extends ContactM
|
|||
* @param jid {@link EntityBareJid}
|
||||
* @return
|
||||
*/
|
||||
public Maybe<E> getEntity(AccountModel account, EntityBareJid jid) {
|
||||
return getEntity(account.getId(), jid);
|
||||
public Maybe<E> getEntityForAccountAndJid(AccountModel account, EntityBareJid jid) {
|
||||
return getEntityForAccountAndJid(account.getId(), jid);
|
||||
}
|
||||
|
||||
public abstract Single<E> getEntityForContact(long contactId);
|
||||
|
||||
public Single<E> getOrCreateEntityForAccountAndJid(AccountModel accountModel, EntityBareJid jid) {
|
||||
return getOrCreateEntityForAccountAndJid(accountModel.getId(), jid);
|
||||
}
|
||||
|
||||
public abstract Single<E> getOrCreateEntityForAccountAndJid(long accountId, EntityBareJid jid);
|
||||
|
||||
public Single<E> getEntityForContact(C contact) {
|
||||
return getEntityForContact(contact.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert or replace a {@link EntityModel} in the database.
|
||||
*
|
||||
* @param entity {@link EntityModel} which we want to insert or replace in the database
|
||||
* @return {@link Single} which wraps the ID assigned to the entity
|
||||
*/
|
||||
public abstract Single<Long> insertOrReplaceEntity(E entity);
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue