diff --git a/app/src/main/java/org/mercury_im/messenger/ui/MainActivity.java b/app/src/main/java/org/mercury_im/messenger/ui/MainActivity.java index 89bbbe1..505700b 100644 --- a/app/src/main/java/org/mercury_im/messenger/ui/MainActivity.java +++ b/app/src/main/java/org/mercury_im/messenger/ui/MainActivity.java @@ -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; diff --git a/app/src/main/java/org/mercury_im/messenger/ui/chat/ChatActivity.java b/app/src/main/java/org/mercury_im/messenger/ui/chat/ChatActivity.java index b9e2e72..960de8e 100644 --- a/app/src/main/java/org/mercury_im/messenger/ui/chat/ChatActivity.java +++ b/app/src/main/java/org/mercury_im/messenger/ui/chat/ChatActivity.java @@ -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; diff --git a/app/src/main/java/org/mercury_im/messenger/ui/chat/ChatViewModel.java b/app/src/main/java/org/mercury_im/messenger/ui/chat/ChatViewModel.java index ac6a56f..c93a4f8 100644 --- a/app/src/main/java/org/mercury_im/messenger/ui/chat/ChatViewModel.java +++ b/app/src/main/java/org/mercury_im/messenger/ui/chat/ChatViewModel.java @@ -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 contact = new MutableLiveData<>(); private MutableLiveData> messages = new MutableLiveData<>(); private MutableLiveData contactDisplayName = new MutableLiveData<>(); + private MutableLiveData chat = new MutableLiveData<>(); public ChatViewModel() { super(); @@ -66,6 +77,12 @@ public class ChatViewModel extends ViewModel { .observeOn(AndroidSchedulers.mainThread()) .subscribe((Consumer>) messages -> ChatViewModel.this.messages.setValue(messages))); + + disposable.add(chatRepository.getOrCreateChatWith(accountId, jid) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe((Consumer) + 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); + } + }); + + } } diff --git a/app/src/test/java/org/mercury_im/messenger/ExampleUnitTest.java b/app/src/test/java/org/mercury_im/messenger/ExampleUnitTest.java index 629d7f8..3977c6f 100644 --- a/app/src/test/java/org/mercury_im/messenger/ExampleUnitTest.java +++ b/app/src/test/java/org/mercury_im/messenger/ExampleUnitTest.java @@ -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; diff --git a/core/src/main/java/org/mercury_im/messenger/core/centers/ConnectionCenter.java b/core/src/main/java/org/mercury_im/messenger/core/centers/ConnectionCenter.java index 57f9d92..3307cad 100644 --- a/core/src/main/java/org/mercury_im/messenger/core/centers/ConnectionCenter.java +++ b/core/src/main/java/org/mercury_im/messenger/core/centers/ConnectionCenter.java @@ -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) 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); + })); + } } diff --git a/core/src/main/java/org/mercury_im/messenger/core/centers/NotificationCenter.java b/core/src/main/java/org/mercury_im/messenger/core/centers/MessageCenter.java similarity index 66% rename from core/src/main/java/org/mercury_im/messenger/core/centers/NotificationCenter.java rename to core/src/main/java/org/mercury_im/messenger/core/centers/MessageCenter.java index 7cc290b..013ddf1 100644 --- a/core/src/main/java/org/mercury_im/messenger/core/centers/NotificationCenter.java +++ b/core/src/main/java/org/mercury_im/messenger/core/centers/MessageCenter.java @@ -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; } } diff --git a/core/src/main/java/org/mercury_im/messenger/core/connection/MercuryConnection.java b/core/src/main/java/org/mercury_im/messenger/core/connection/MercuryConnection.java index 498db49..ed253f7 100644 --- a/core/src/main/java/org/mercury_im/messenger/core/connection/MercuryConnection.java +++ b/core/src/main/java/org/mercury_im/messenger/core/connection/MercuryConnection.java @@ -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 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 diff --git a/core/src/main/java/org/mercury_im/messenger/core/stores/EntityCapsStore.java b/core/src/main/java/org/mercury_im/messenger/core/stores/EntityCapsStore.java index 316b0a7..52e6633 100644 --- a/core/src/main/java/org/mercury_im/messenger/core/stores/EntityCapsStore.java +++ b/core/src/main/java/org/mercury_im/messenger/core/stores/EntityCapsStore.java @@ -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 discoverInfoMap = new HashMap<>(); private final CompositeDisposable disposable = new CompositeDisposable(); - private Single> allEntityCaps; + private Observable> 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>() { - @Override - public void onSuccess(List 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 diff --git a/core/src/main/java/org/mercury_im/messenger/core/stores/PlainMessageStore.java b/core/src/main/java/org/mercury_im/messenger/core/stores/PlainMessageStore.java index 779943c..da5941b 100644 --- a/core/src/main/java/org/mercury_im/messenger/core/stores/PlainMessageStore.java +++ b/core/src/main/java/org/mercury_im/messenger/core/stores/PlainMessageStore.java @@ -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 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()); + } } diff --git a/core/src/main/java/org/mercury_im/messenger/core/stores/RosterStore.java b/core/src/main/java/org/mercury_im/messenger/core/stores/RosterStore.java index 8ead691..7d4fbf1 100644 --- a/core/src/main/java/org/mercury_im/messenger/core/stores/RosterStore.java +++ b/core/src/main/java/org/mercury_im/messenger/core/stores/RosterStore.java @@ -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) { diff --git a/persistence-room/build.gradle b/persistence-room/build.gradle index 2a45bd6..5d0d65e 100644 --- a/persistence-room/build.gradle +++ b/persistence-room/build.gradle @@ -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" } diff --git a/persistence-room/schemas/org.mercury_im.messenger.persistence.room.AppDatabase/1.json b/persistence-room/schemas/org.mercury_im.messenger.persistence.room.AppDatabase/1.json index dd651e8..0d76b14 100644 --- a/persistence-room/schemas/org.mercury_im.messenger.persistence.room.AppDatabase/1.json +++ b/persistence-room/schemas/org.mercury_im.messenger.persistence.room.AppDatabase/1.json @@ -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')" ] } } \ No newline at end of file diff --git a/persistence-room/src/androidTest/java/org/mercury_im/messenger/persistence/room/AbstractDatabaseTest.java b/persistence-room/src/androidTest/java/org/mercury_im/messenger/persistence/room/AbstractDatabaseTest.java new file mode 100644 index 0000000..c1ebd20 --- /dev/null +++ b/persistence-room/src/androidTest/java/org/mercury_im/messenger/persistence/room/AbstractDatabaseTest.java @@ -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(); + } +} diff --git a/persistence-room/src/androidTest/java/org/mercury_im/messenger/persistence/room/ExampleInstrumentedTest.java b/persistence-room/src/androidTest/java/org/mercury_im/messenger/persistence/room/ExampleInstrumentedTest.java index 92e759d..da95477 100644 --- a/persistence-room/src/androidTest/java/org/mercury_im/messenger/persistence/room/ExampleInstrumentedTest.java +++ b/persistence-room/src/androidTest/java/org/mercury_im/messenger/persistence/room/ExampleInstrumentedTest.java @@ -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 Testing documentation */ @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> 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>() { - @Override - public boolean test(List 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 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); } } diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/AppDatabase.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/AppDatabase.java index 9b5ba26..a27f18f 100644 --- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/AppDatabase.java +++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/AppDatabase.java @@ -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()); diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/RoomRepositoryModule.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/RoomRepositoryModule.java index b844f03..7cd6721 100644 --- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/RoomRepositoryModule.java +++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/RoomRepositoryModule.java @@ -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 diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/AccountDao.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/AccountDao.java index 9d36031..31c993a 100644 --- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/AccountDao.java +++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/AccountDao.java @@ -36,7 +36,10 @@ public interface AccountDao extends BaseDao { * @return account or null */ @Query("select * from accounts where pk_account_id = :id") - Maybe getAccountById(long id); + Maybe maybeGetAccountById(long id); + + @Query("select * from accounts where pk_account_id = :id") + Observable getAccountById(long id); @Query("select * from accounts where jid = :jid") Maybe getAccountByJid(EntityBareJid jid); diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/BaseDao.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/BaseDao.java index 02994ee..5e2e42d 100644 --- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/BaseDao.java +++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/BaseDao.java @@ -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 { - @Insert + @Insert(onConflict = OnConflictStrategy.IGNORE) Single insert(T entity); + @Insert(onConflict = OnConflictStrategy.IGNORE) + Single insert(T[] entities); + + @Insert(onConflict = OnConflictStrategy.IGNORE) + Single> insert(List entities); + @Update Completable update(T entity); @@ -23,4 +34,7 @@ public interface BaseDao { @Delete Completable delete(T[] entities); + + @Delete + Completable delete(List entities); } diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/ChatDao.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/ChatDao.java index 1c99699..e8bdbaf 100644 --- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/ChatDao.java +++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/ChatDao.java @@ -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 { Observable> getAllChats(); @Query("SELECT chats.* FROM chats JOIN entities WHERE fk_account_id = :accountId") - Observable> getAllChatsOf(long accountId); + Observable> getAllChatsOfAccount(long accountId); @Query("SELECT * FROM chats WHERE fk_entity_id = :entityId") - Maybe getChatWithIdentity(long entityId); + Maybe maybeGetChatWithEntity(long entityId); + + @Query("SELECT * FROM chats WHERE fk_entity_id = :entityId") + Observable getChatWithEntity(long entityId); @Query("SELECT chats.* FROM chats JOIN entities WHERE fk_account_id = :accountId AND jid = :jid") - Maybe getChatWithJid(long accountId, EntityBareJid jid); + Maybe maybeGetChatWithJid(long accountId, EntityBareJid jid); + + @Query("SELECT chats.* FROM chats JOIN entities WHERE fk_account_id = :accountId AND jid = :jid") + Observable getChatWithJid(long accountId, EntityBareJid jid); @Query("SELECT chats.* FROM chats JOIN contacts WHERE contacts.pk_contact_id = :contactId") - Maybe getChatWithContact(long contactId); - - @Query("SELECT chats.* FROM chats JOIN entities WHERE fk_account_id = :accountId AND jid = :jid") - Observable 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 getChat(); + Maybe maybeGetChatWithContact(long contactId); + @Query("SELECT chats.* FROM chats JOIN contacts WHERE contacts.pk_contact_id = :contactId") + Observable getChatWithContact(long contactId); } diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/ContactDao.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/ContactDao.java index 342a338..3d41709 100644 --- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/ContactDao.java +++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/ContactDao.java @@ -37,10 +37,11 @@ public interface ContactDao extends BaseDao { Observable> 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 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> getContactsForAccount(long accountId); @Query("DELETE FROM contacts WHERE pk_contact_id = :id") @@ -52,12 +53,15 @@ public interface ContactDao extends BaseDao { @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 getEntityForContactId(long contactId); } diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/EntityCapsDao.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/EntityCapsDao.java index ece93de..f7ed0ed 100644 --- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/EntityCapsDao.java +++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/EntityCapsDao.java @@ -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 { @Query("SELECT * FROM entity_caps WHERE pk_node_ver = :nodeVer") Single getEntityCapsForNodeVer(String nodeVer); @Query("SELECT * FROM entity_caps") - Single> getAllEntityCaps(); - - @Insert(onConflict = REPLACE) - Completable insert(List models); - - @Insert(onConflict = REPLACE) - Completable insert(RoomEntityCapsModel model); - - @Delete - Completable delete(List models); - - @Delete - Completable delete(RoomEntityCapsModel model); + Observable> getAllEntityCaps(); @Query("DELETE FROM entity_caps") Completable deleteAllEntityCaps(); diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/EntityDao.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/EntityDao.java index f0c99c4..fce5013 100644 --- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/EntityDao.java +++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/dao/EntityDao.java @@ -18,10 +18,6 @@ import static androidx.room.OnConflictStrategy.REPLACE; @TypeConverters(EntityBareJidConverter.class) public interface EntityDao extends BaseDao { - @Override - @Insert(onConflict = REPLACE) - Single insert(RoomEntityModel entity); - @Query("SELECT * FROM entities WHERE pk_entity_id = :id") Maybe getEntity(long id); diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomAccountModel.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomAccountModel.java index 84096c4..b47d72f 100644 --- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomAccountModel.java +++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomAccountModel.java @@ -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() + + "]"; + } } diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomChatModel.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomChatModel.java index dd89e2d..b9dd3b1 100644 --- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomChatModel.java +++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomChatModel.java @@ -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() + + "]"; + } } diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomContactModel.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomContactModel.java index 56bbfd2..8bab642 100644 --- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomContactModel.java +++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomContactModel.java @@ -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() + + "]"; } } diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomEntityModel.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomEntityModel.java index 8870dd4..9b321c8 100644 --- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomEntityModel.java +++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomEntityModel.java @@ -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() + + "]"; + } } diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomRosterInformationModel.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomRosterInformationModel.java index 2b7cfc5..6c0b234 100644 --- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomRosterInformationModel.java +++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/model/RoomRosterInformationModel.java @@ -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() + + "]"; + } } diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/pojo/Chat.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/pojo/Chat.java index 47a396a..a0f184b 100644 --- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/pojo/Chat.java +++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/pojo/Chat.java @@ -6,5 +6,5 @@ public class Chat { public long chatId; public long entityId; public EntityBareJid jid; - + public boolean active; } diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IAccountRepository.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IAccountRepository.java index 246cc1e..5f4f9c6 100644 --- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IAccountRepository.java +++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IAccountRepository.java @@ -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 { private final AccountDao accountDao; @@ -28,10 +34,15 @@ public class IAccountRepository implements AccountRepository { } @Override - public Maybe getAccount(long accountId) { + public Observable getAccount(long accountId) { return accountDao.getAccountById(accountId); } + @Override + public Maybe maybeGetAccount(long accountId) { + return accountDao.maybeGetAccountById(accountId); + } + @Override public Observable> getAllAccounts() { return accountDao.getAllAccounts(); @@ -39,13 +50,16 @@ public class IAccountRepository implements AccountRepository { @Override - public Single insertAccount(RoomAccountModel accountModel) { - return accountDao.insert(accountModel); + public Single 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 diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IChatRepository.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IChatRepository.java index 3c61e1b..1ff57c5 100644 --- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IChatRepository.java +++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IChatRepository.java @@ -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 { 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 { @Override public Observable 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 getChatWith(long accountId, EntityBareJid jid) { + return chatDao.getChatWithJid(accountId, jid); + } + + @Override + public Observable getChatWithEntity(long entityId) { + return chatDao.getChatWithEntity(entityId); + } + + @Override + public Completable updateChat(RoomChatModel chat) { + return chatDao.update(chat); + } + + @Override + public Observable getChatWithContact(long contactId) { + return chatDao.getChatWithContact(contactId); } @Override @@ -51,27 +76,39 @@ public class IChatRepository implements ChatRepository { } @Override - public Observable> getAllChatsOf(AccountModel accountModel) { - return chatDao.getAllChatsOf(accountModel.getId()); + public Observable> getAllChatsOfAccount(long accountId) { + return chatDao.getAllChatsOfAccount(accountId); } @Override - public Maybe getChatWith(EntityModel identity) { - return chatDao.getChatWithIdentity(identity.getId()); + public Maybe maybeGetChatWith(long accountId, EntityBareJid jid) { + return chatDao.maybeGetChatWithJid(accountId, jid); } @Override - public Maybe getChatWith(AccountModel account, EntityBareJid jid) { - return chatDao.getChatWithJid(account.getId(), jid); + public Maybe maybeGetChatWithEntity(long entityId) { + return chatDao.maybeGetChatWithEntity(entityId); } @Override - public Maybe getChatWith(ContactModel contact) { - return chatDao.getChatWithContact(contact.getId()); + public Maybe maybeGetChatWithContact(long contactId) { + return chatDao.maybeGetChatWithContact(contactId); + } + + @Override + public Single 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); } + } diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IEntityCapsRepository.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IEntityCapsRepository.java index 2e9f2e4..2893eeb 100644 --- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IEntityCapsRepository.java +++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IEntityCapsRepository.java @@ -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> getAllEntityCaps() { + public Observable> getAllEntityCaps() { return dao.getAllEntityCaps(); } @@ -39,12 +40,12 @@ public class IEntityCapsRepository implements EntityCapsRepository entityCaps) { + public Single> insertOrReplaceEntityCaps(List entityCaps) { return dao.insert(entityCaps); } @Override - public Completable insertOrReplaceEntityCaps(RoomEntityCapsModel entityCaps) { + public Single insertOrReplaceEntityCaps(RoomEntityCapsModel entityCaps) { return dao.insert(entityCaps); } diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IMessageRepository.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IMessageRepository.java index d96b9aa..51bcc94 100644 --- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IMessageRepository.java +++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IMessageRepository.java @@ -37,6 +37,11 @@ public class IMessageRepository implements MessageRepository { return messageDao.insert(message); } + @Override + public Single> insertMessages(List messages) { + return messageDao.insert(messages); + } + @Override public Observable getLastMessageFrom(long accountId, EntityBareJid peer) { return messageDao.getLastMessageFrom(accountId, peer); diff --git a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IRosterRepository.java b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IRosterRepository.java index 0867679..5674a7f 100644 --- a/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IRosterRepository.java +++ b/persistence-room/src/main/java/org/mercury_im/messenger/persistence/room/repository/IRosterRepository.java @@ -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 { @@ -72,21 +70,43 @@ public class IRosterRepository extends RosterRepository 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) observer -> - observer.onSuccess((RoomEntityModel) contact.getEntity()); - })) - .flatMap(entityModel -> { - contact.setEntityId(entityModel.getId()); - contact.setEntity(entityModel); - return contactDao.insert(contact); - }); + public Single 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 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 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 getContactForEntity(long entityId) { return contactDao.getContactForEntityId(entityId) @@ -133,13 +167,8 @@ public class IRosterRepository extends RosterRepository entity = getEntity(accountId, jid.asEntityBareJidOrThrow()); - return entity.flatMapCompletable(new Function() { - @Override - public CompletableSource apply(RoomEntityModel entityModel) { - return contactDao.deleteContactForEntity(entityModel.getId()); - } - }); + final Maybe entity = getEntityForAccountAndJid(accountId, jid.asEntityBareJidOrThrow()); + return entity.flatMapCompletable(entityModel -> contactDao.deleteContactForEntity(entityModel.getId())); } @Override @@ -173,7 +202,8 @@ public class IRosterRepository extends RosterRepository 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 getEntity(long accountId, EntityBareJid jid) { + public Maybe getEntityForAccountAndJid(long accountId, EntityBareJid jid) { return entityDao.getEntityFor(accountId, jid); } - @Override - public Single insertOrReplaceEntity(RoomEntityModel entity) { - return entityDao.insert(entity); + public Single 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)); } } diff --git a/persistence/src/main/java/org/mercury_im/messenger/persistence/model/AccountModel.java b/persistence/src/main/java/org/mercury_im/messenger/persistence/model/AccountModel.java index 3ffd255..deb1e65 100644 --- a/persistence/src/main/java/org/mercury_im/messenger/persistence/model/AccountModel.java +++ b/persistence/src/main/java/org/mercury_im/messenger/persistence/model/AccountModel.java @@ -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. diff --git a/persistence/src/main/java/org/mercury_im/messenger/persistence/model/ChatModel.java b/persistence/src/main/java/org/mercury_im/messenger/persistence/model/ChatModel.java index dd433b6..f7b62c1 100644 --- a/persistence/src/main/java/org/mercury_im/messenger/persistence/model/ChatModel.java +++ b/persistence/src/main/java/org/mercury_im/messenger/persistence/model/ChatModel.java @@ -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); } diff --git a/persistence/src/main/java/org/mercury_im/messenger/persistence/model/ContactModel.java b/persistence/src/main/java/org/mercury_im/messenger/persistence/model/ContactModel.java index a6957ab..5af790c 100644 --- a/persistence/src/main/java/org/mercury_im/messenger/persistence/model/ContactModel.java +++ b/persistence/src/main/java/org/mercury_im/messenger/persistence/model/ContactModel.java @@ -6,10 +6,6 @@ public interface ContactModel { void setId(long id); - long getAccountId(); - - void setAccountId(long id); - long getEntityId(); void setEntityId(long id); diff --git a/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/AccountRepository.java b/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/AccountRepository.java index 998e8e3..00f3fb3 100644 --- a/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/AccountRepository.java +++ b/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/AccountRepository.java @@ -13,13 +13,15 @@ public interface AccountRepository { E newAccountModel(); - Maybe getAccount(long accountId); + Observable getAccount(long accountId); + + Maybe maybeGetAccount(long accountId); Observable> getAllAccounts(); Single insertAccount(E accountModel); - Completable updateState(long accountId, String state); + Completable updateAccount(E accountModel); Completable deleteAccount(E item); diff --git a/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/ChatRepository.java b/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/ChatRepository.java index ada5217..4362a45 100644 --- a/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/ChatRepository.java +++ b/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/ChatRepository.java @@ -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 { @@ -18,15 +19,60 @@ public interface ChatRepository { Observable> getAllChats(); - Observable> getAllChatsOf(AccountModel account); + Observable> getAllChatsOfAccount(long accountId); + + default Observable> getAllChatsOfAccount(AccountModel account) { + return getAllChatsOfAccount(account.getId()); + } Observable getOrCreateChatWith(long accountId, EntityBareJid jid); - Maybe getChatWith(AccountModel account, EntityBareJid jid); + Observable getChatWith(long accountId, EntityBareJid jid); - Maybe getChatWith(EntityModel identity); + default Observable getChatWith(AccountModel accountModel, EntityBareJid jid) { + return getChatWith(accountModel.getId(), jid); + } - Maybe getChatWith(ContactModel contact); + Observable getChatWithEntity(long entityId); + + default Observable getChatWithEntity(EntityModel entity) { + return getChatWithEntity(entity.getId()); + } + + Observable getChatWithContact(long contactId); + + default Observable getChatWithContact(ContactModel contact) { + return getChatWithContact(contact.getId()); + } + + Maybe maybeGetChatWith(long accountId, EntityBareJid jid); + + default Maybe maybeGetChatWith(AccountModel account, EntityBareJid jid) { + return maybeGetChatWith(account.getId(), jid); + } + + Maybe maybeGetChatWithContact(long contactId); + + default Maybe maybeGetChatWithContact(ContactModel contact) { + return maybeGetChatWithContact(contact.getId()); + } + + Maybe maybeGetChatWithEntity(long entityId); + + default Maybe maybeGetChatWithEntity(EntityModel entity) { + return maybeGetChatWithEntity(entity.getId()); + } + + Completable updateChat(E chat); + + Single insertChat(E chat); Completable closeChat(E chat); + + + + + + + } diff --git a/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/EntityCapsRepository.java b/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/EntityCapsRepository.java index 9cfdc86..ab49926 100644 --- a/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/EntityCapsRepository.java +++ b/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/EntityCapsRepository.java @@ -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 newEntityCapsModel(String nodeVer); - Single> getAllEntityCaps(); + Observable> getAllEntityCaps(); Single getEntityCapsForNodeVer(String nodeVer); - Completable insertOrReplaceEntityCaps(List entityCaps); + Single> insertOrReplaceEntityCaps(List entityCaps); - Completable insertOrReplaceEntityCaps(E entityCaps); + Single insertOrReplaceEntityCaps(E entityCaps); Completable deleteOrReplaceEntityCaps(List entityCaps); diff --git a/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/MessageRepository.java b/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/MessageRepository.java index 0e037e8..1c5592f 100644 --- a/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/MessageRepository.java +++ b/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/MessageRepository.java @@ -17,6 +17,8 @@ public interface MessageRepository { Single insertMessage(E message); + Single> insertMessages(List messages); + Observable> getAllMessages(); Observable> getAllMessagesOf(long accountId); diff --git a/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/RosterRepository.java b/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/RosterRepository.java index c629864..1009582 100644 --- a/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/RosterRepository.java +++ b/persistence/src/main/java/org/mercury_im/messenger/persistence/repository/RosterRepository.java @@ -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 { @@ -64,7 +63,15 @@ public abstract class RosterRepository updateOrInsertContact(C contact); + public abstract Single upsertContact(C contact); + + /** + * Insert a contact. + * + * @param contact contact + * @return single wrapping the contacts ID + */ + public abstract Single 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 getContact(long id); public Maybe 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 getEntity(long accountId, EntityBareJid jid); + public abstract Maybe 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 getEntity(AccountModel account, EntityBareJid jid) { - return getEntity(account.getId(), jid); + public Maybe getEntityForAccountAndJid(AccountModel account, EntityBareJid jid) { + return getEntityForAccountAndJid(account.getId(), jid); } public abstract Single getEntityForContact(long contactId); + public Single getOrCreateEntityForAccountAndJid(AccountModel accountModel, EntityBareJid jid) { + return getOrCreateEntityForAccountAndJid(accountModel.getId(), jid); + } + + public abstract Single getOrCreateEntityForAccountAndJid(long accountId, EntityBareJid jid); + public Single 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 insertOrReplaceEntity(E entity); }