From 687288e6970dd6d365d90eb25762258ff09d3dd3 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sat, 21 Dec 2019 05:34:19 +0100 Subject: [PATCH] Work on contact list - wip! --- .../messenger/ui/chat/ChatViewModel.java | 23 +- .../ui/chat/MessagesRecyclerViewAdapter.java | 44 ++-- .../ui/chatlist/ChatListFragment.java | 6 +- .../ui/chatlist/ChatListViewModel.java | 3 +- .../roster/contacts/ContactListViewModel.java | 5 +- .../converter/MessageDirectionConverter.java | 44 ++++ .../SubscriptionDirectionConverter.java | 2 +- .../data/mapping/MessageMapping.java | 2 + .../messenger/data/mapping/PeerMapping.java | 78 +------ .../data/model/AbstractMessageModel.java | 8 + .../data/model/AbstractPeerModel.java | 2 +- .../repository/XmppMessageRepository.java | 2 +- .../data/repository/XmppPeerRepository.java | 27 +++ .../data/mapping/PeerMappingTest.java | 3 +- .../repository/AccountRepositoryTest.java | 6 +- .../org/mercury_im/messenger/Messenger.java | 9 + .../data/repository/PeerRepository.java | 8 + .../messenger/store/MercuryRosterStore.java | 207 ++++++++++++++++++ .../messenger/entity/contact/IPeer.java | 36 ++- .../messenger/entity/contact/Peer.java | 12 +- .../contact}/SubscriptionDirection.java | 2 +- .../entity/contact/SubscriptionMode.java | 55 ----- .../messenger/entity/message/IMessage.java | 12 + .../messenger/entity/message/Message.java | 8 + .../entity/message/MessageDirection.java | 6 + 25 files changed, 420 insertions(+), 190 deletions(-) create mode 100644 data/src/main/java/org/mercury_im/messenger/data/converter/MessageDirectionConverter.java create mode 100644 domain/src/main/java/org/mercury_im/messenger/store/MercuryRosterStore.java rename {data/src/main/java/org/mercury_im/messenger/data/enums => entity/src/main/java/org/mercury_im/messenger/entity/contact}/SubscriptionDirection.java (64%) delete mode 100644 entity/src/main/java/org/mercury_im/messenger/entity/contact/SubscriptionMode.java create mode 100644 entity/src/main/java/org/mercury_im/messenger/entity/message/MessageDirection.java 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 521d429..159335a 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 @@ -18,8 +18,11 @@ import java.util.List; import javax.inject.Inject; import io.reactivex.Completable; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.functions.Consumer; +import io.reactivex.schedulers.Schedulers; public class ChatViewModel extends ViewModel { @@ -85,22 +88,12 @@ public class ChatViewModel extends ViewModel { } public void queryTextChanged(String query) { - /* - if (query.isEmpty()) { - disposable.add(messageRepository.getAllMessagesOfChat(accountId, jid) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe((Consumer>) - messages -> ChatViewModel.this.messages.setValue(messages))); - } - disposable.add(messageRepository.findMessageByQuery(accountId, jid, query) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe((Consumer>) o -> { - messages.setValue(o); - })); + Observable> observable = query.isEmpty() ? + messageRepository.observeMessages(chat.getValue()) : + messageRepository.findMessagesWithBody(chat.getValue(), query); - */ + disposable.add(observable.subscribe(messages -> + ChatViewModel.this.messages.setValue(messages))); } public Completable requestMamMessages() { diff --git a/app/src/main/java/org/mercury_im/messenger/ui/chat/MessagesRecyclerViewAdapter.java b/app/src/main/java/org/mercury_im/messenger/ui/chat/MessagesRecyclerViewAdapter.java index c0306c0..09da304 100644 --- a/app/src/main/java/org/mercury_im/messenger/ui/chat/MessagesRecyclerViewAdapter.java +++ b/app/src/main/java/org/mercury_im/messenger/ui/chat/MessagesRecyclerViewAdapter.java @@ -1,6 +1,6 @@ package org.mercury_im.messenger.ui.chat; -import android.util.SparseArray; +import android.util.SparseBooleanArray; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -20,7 +20,7 @@ import java.util.List; public class MessagesRecyclerViewAdapter extends RecyclerView.Adapter { private List messages = new ArrayList<>(); - private SparseArray checkedItems = new SparseArray<>(); + private SparseBooleanArray checkedItems = new SparseBooleanArray(); public MessagesRecyclerViewAdapter() { @@ -50,22 +50,22 @@ public class MessagesRecyclerViewAdapter extends RecyclerView.Adapter { - if (chatModels == null) { - Log.d(Messenger.TAG, "Displaying null chats"); - return; - } Log.d(Messenger.TAG, "Displaying " + chatModels.size() + " chats"); recyclerViewAdapter.setModels(chatModels); }); diff --git a/app/src/main/java/org/mercury_im/messenger/ui/chatlist/ChatListViewModel.java b/app/src/main/java/org/mercury_im/messenger/ui/chatlist/ChatListViewModel.java index ce13c2e..cd7be0b 100644 --- a/app/src/main/java/org/mercury_im/messenger/ui/chatlist/ChatListViewModel.java +++ b/app/src/main/java/org/mercury_im/messenger/ui/chatlist/ChatListViewModel.java @@ -8,6 +8,7 @@ import org.mercury_im.messenger.MercuryImApplication; import org.mercury_im.messenger.data.repository.DirectChatRepository; import org.mercury_im.messenger.entity.chat.DirectChat; +import java.util.ArrayList; import java.util.List; import javax.inject.Inject; @@ -21,7 +22,7 @@ public class ChatListViewModel extends ViewModel { private CompositeDisposable disposable = new CompositeDisposable(); - private final MutableLiveData> chats = new MutableLiveData<>(); + private final MutableLiveData> chats = new MutableLiveData<>(new ArrayList<>()); public ChatListViewModel() { MercuryImApplication.getApplication().getAppComponent().inject(this); diff --git a/app/src/main/java/org/mercury_im/messenger/ui/roster/contacts/ContactListViewModel.java b/app/src/main/java/org/mercury_im/messenger/ui/roster/contacts/ContactListViewModel.java index 5f4904d..86c87fc 100644 --- a/app/src/main/java/org/mercury_im/messenger/ui/roster/contacts/ContactListViewModel.java +++ b/app/src/main/java/org/mercury_im/messenger/ui/roster/contacts/ContactListViewModel.java @@ -31,10 +31,7 @@ public class ContactListViewModel extends ViewModel { Log.d("ContactListViewModel", "Start observing database"); // Subscribe to changes to the contacts table and update the LiveData object for the UI. compositeDisposable.add(xmppContactRepository.observeAllPeers() - .subscribe(list -> { - Log.d("ContactListViewModel", "Room changed contacts: " + list.size()); - rosterEntryList.setValue(list); - })); + .subscribe(rosterEntryList::setValue)); } @Override diff --git a/data/src/main/java/org/mercury_im/messenger/data/converter/MessageDirectionConverter.java b/data/src/main/java/org/mercury_im/messenger/data/converter/MessageDirectionConverter.java new file mode 100644 index 0000000..412b063 --- /dev/null +++ b/data/src/main/java/org/mercury_im/messenger/data/converter/MessageDirectionConverter.java @@ -0,0 +1,44 @@ +package org.mercury_im.messenger.data.converter; + +import org.mercury_im.messenger.entity.message.MessageDirection; + +import io.requery.Converter; + +public class MessageDirectionConverter implements Converter { + @Override + public Class getMappedType() { + return MessageDirection.class; + } + + @Override + public Class getPersistedType() { + return Integer.class; + } + + @Override + public Integer getPersistedSize() { + return null; + } + + @Override + public Integer convertToPersisted(MessageDirection value) { + switch (value) { + case outgoing: + return 0; + case incoming: + return 1; + default: return 1; + } + } + + @Override + public MessageDirection convertToMapped(Class type, Integer value) { + switch (value) { + case 0: + return MessageDirection.outgoing; + case 1: + return MessageDirection.incoming; + default: return MessageDirection.incoming; + } + } +} diff --git a/data/src/main/java/org/mercury_im/messenger/data/converter/SubscriptionDirectionConverter.java b/data/src/main/java/org/mercury_im/messenger/data/converter/SubscriptionDirectionConverter.java index af84446..b8ecf7f 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/converter/SubscriptionDirectionConverter.java +++ b/data/src/main/java/org/mercury_im/messenger/data/converter/SubscriptionDirectionConverter.java @@ -1,6 +1,6 @@ package org.mercury_im.messenger.data.converter; -import org.mercury_im.messenger.data.enums.SubscriptionDirection; +import org.mercury_im.messenger.entity.contact.SubscriptionDirection; import io.requery.Converter; diff --git a/data/src/main/java/org/mercury_im/messenger/data/mapping/MessageMapping.java b/data/src/main/java/org/mercury_im/messenger/data/mapping/MessageMapping.java index 34e4c71..6b12c60 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/mapping/MessageMapping.java +++ b/data/src/main/java/org/mercury_im/messenger/data/mapping/MessageMapping.java @@ -35,6 +35,7 @@ public class MessageMapping extends AbstractMapping { model.setSender(entity.getSender()); model.setRecipient(entity.getRecipient()); model.setTimestamp(entity.getTimestamp()); + model.setDirection(entity.getDirection()); model.getPayloads().clear(); for (PayloadContainer payload : entity.getMessagePayloads()) { @@ -52,6 +53,7 @@ public class MessageMapping extends AbstractMapping { entity.setSender(model.getSender()); entity.setRecipient(model.getRecipient()); entity.setTimestamp(model.getTimestamp()); + entity.setDirection(model.getDirection()); List payloadContainers = new ArrayList<>(entity.getMessagePayloads().size()); for (MessagePayloadContainerModel containerModel : model.getPayloads()) { diff --git a/data/src/main/java/org/mercury_im/messenger/data/mapping/PeerMapping.java b/data/src/main/java/org/mercury_im/messenger/data/mapping/PeerMapping.java index 91b6141..f4d95f6 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/mapping/PeerMapping.java +++ b/data/src/main/java/org/mercury_im/messenger/data/mapping/PeerMapping.java @@ -1,6 +1,5 @@ package org.mercury_im.messenger.data.mapping; -import org.mercury_im.messenger.data.enums.SubscriptionDirection; import org.mercury_im.messenger.data.model.PeerModel; import org.mercury_im.messenger.entity.contact.IPeer; import org.mercury_im.messenger.entity.contact.Peer; @@ -32,7 +31,9 @@ public class PeerMapping extends AbstractMapping { model.setAddress(entity.getAddress()); model.setName(entity.getName()); - // TODO: sub direction + model.setSubscriptionDirection(entity.getSubscriptionDirection()); + model.setSubscriptionPending(entity.isSubscriptionPending()); + model.setSubscriptionPreApproved(entity.isSubscriptionApproved()); return model; } @@ -43,75 +44,10 @@ public class PeerMapping extends AbstractMapping { entity.setAddress(model.getAddress()); entity.setId(model.getId()); - // TODO: Sub direction + entity.setSubscriptionDirection(model.getSubscriptionDirection()); + entity.setSubscriptionPending(model.isSubscriptionPending()); + entity.setSubscriptionApproved(model.isSubscriptionPreApproved()); + return entity; } - - private void setSubscriptionDirectionInModel(PeerModel model, Peer entity) { - switch (entity.getSubscriptionMode()) { - case NONE: - model.setSubscriptionDirection(SubscriptionDirection.none); - model.setSubscriptionPreApproved(false); - model.setSubscriptionPending(false); - break; - - case NONE_PREAPPROVED: - model.setSubscriptionDirection(SubscriptionDirection.none); - model.setSubscriptionPreApproved(true); - model.setSubscriptionPending(false); - break; - - case TO_THEM_PENDING: - model.setSubscriptionDirection(SubscriptionDirection.to); - model.setSubscriptionPending(true); - model.setSubscriptionPreApproved(false); - break; - case TO_THEM_PENDING_PREAPPROVED: - model.setSubscriptionDirection(SubscriptionDirection.to); - model.setSubscriptionPending(true); - model.setSubscriptionPreApproved(true); - break; - case TO_THEM_ACCEPTED: - model.setSubscriptionDirection(SubscriptionDirection.to); - model.setSubscriptionPending(false); - model.setSubscriptionPreApproved(false); - break; - case TO_THEM_ACCEPTED_PREAPPROVED: - model.setSubscriptionDirection(SubscriptionDirection.to); - model.setSubscriptionPending(false); - model.setSubscriptionPreApproved(true); - break; - case FROM_THEM_PENDING: - model.setSubscriptionDirection(SubscriptionDirection.from); - model.setSubscriptionPending(true); - model.setSubscriptionPreApproved(false); - break; - case FROM_THEM_ACCEPTED: - model.setSubscriptionDirection(SubscriptionDirection.from); - model.setSubscriptionPending(false); - model.setSubscriptionPreApproved(false); - break; - case BIDIRECTIONAL: - model.setSubscriptionDirection(SubscriptionDirection.both); - model.setSubscriptionPending(false); - model.setSubscriptionPreApproved(false); - break; - } - } - - private void setSubscriptionDirectionInEntity(Peer entity, PeerModel model) { - - switch (model.getSubscriptionDirection()) { - case none: - break; - case to: - break; - case from: - break; - case both: - break; - case remove: - break; - } - } } diff --git a/data/src/main/java/org/mercury_im/messenger/data/model/AbstractMessageModel.java b/data/src/main/java/org/mercury_im/messenger/data/model/AbstractMessageModel.java index a773d83..81233ee 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/model/AbstractMessageModel.java +++ b/data/src/main/java/org/mercury_im/messenger/data/model/AbstractMessageModel.java @@ -1,9 +1,13 @@ package org.mercury_im.messenger.data.model; +import org.mercury_im.messenger.data.converter.MessageDirectionConverter; +import org.mercury_im.messenger.entity.message.MessageDirection; + import java.util.Date; import java.util.Set; import io.requery.Column; +import io.requery.Convert; import io.requery.Entity; import io.requery.Generated; import io.requery.Key; @@ -27,6 +31,10 @@ public abstract class AbstractMessageModel implements Persistable { @Column(name = "\"timestamp\"", nullable = false) Date timestamp; + @Column(nullable = false) + @Convert(MessageDirectionConverter.class) + MessageDirection direction; + @OneToMany Set payloads; diff --git a/data/src/main/java/org/mercury_im/messenger/data/model/AbstractPeerModel.java b/data/src/main/java/org/mercury_im/messenger/data/model/AbstractPeerModel.java index 12b82fb..a4a3df5 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/model/AbstractPeerModel.java +++ b/data/src/main/java/org/mercury_im/messenger/data/model/AbstractPeerModel.java @@ -1,7 +1,7 @@ package org.mercury_im.messenger.data.model; import org.mercury_im.messenger.data.converter.SubscriptionDirectionConverter; -import org.mercury_im.messenger.data.enums.SubscriptionDirection; +import org.mercury_im.messenger.entity.contact.SubscriptionDirection; import io.requery.Column; import io.requery.Convert; diff --git a/data/src/main/java/org/mercury_im/messenger/data/repository/XmppMessageRepository.java b/data/src/main/java/org/mercury_im/messenger/data/repository/XmppMessageRepository.java index 9a5f8b7..9ef7259 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/repository/XmppMessageRepository.java +++ b/data/src/main/java/org/mercury_im/messenger/data/repository/XmppMessageRepository.java @@ -180,7 +180,7 @@ public class XmppMessageRepository .and(DirectMessagesRelation.MESSAGE_ID.eq(message.getId())) .get().maybe() .switchIfEmpty(Single.just(message) - .map(messageMapping::toModel) + .map(messageMapping::toEntity) .flatMap(messageModel -> ) */ } diff --git a/data/src/main/java/org/mercury_im/messenger/data/repository/XmppPeerRepository.java b/data/src/main/java/org/mercury_im/messenger/data/repository/XmppPeerRepository.java index bb7d894..a3edc87 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/repository/XmppPeerRepository.java +++ b/data/src/main/java/org/mercury_im/messenger/data/repository/XmppPeerRepository.java @@ -2,6 +2,7 @@ package org.mercury_im.messenger.data.repository; import org.mercury_im.messenger.data.mapping.PeerMapping; import org.mercury_im.messenger.data.model.PeerModel; +import org.mercury_im.messenger.entity.contact.SubscriptionDirection; import org.mercury_im.messenger.util.Optional; import org.mercury_im.messenger.entity.Account; import org.mercury_im.messenger.entity.contact.IPeer; @@ -9,6 +10,8 @@ import org.mercury_im.messenger.entity.contact.Peer; import org.mercury_im.messenger.util.ThreadUtils; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import javax.inject.Inject; @@ -130,6 +133,25 @@ public class XmppPeerRepository .observeOn(observerScheduler()); } + @Override + public Observable> observeAllContactsOfAccount(long accountId) { + return data().select(PeerModel.class) + .where(PeerModel.ACCOUNT_ID.eq(accountId)) + .and(PeerModel.SUBSCRIPTION_DIRECTION.in( + Arrays.asList(SubscriptionDirection.both, SubscriptionDirection.to))) + .get().observableResult() + .map(ResultDelegate::toList) + .map(peerModels -> { + List peerEntities = new ArrayList<>(peerModels.size()); + for (PeerModel model : peerModels) { + peerEntities.add(peerMapping.toEntity(model)); + } + return peerEntities; + }) + .subscribeOn(subscriberScheduler()) + .observeOn(observerScheduler()); + } + @Override public Single updatePeer(Peer peer) { // In order to update, we fetch the model, update it and write it back. @@ -170,4 +192,9 @@ public class XmppPeerRepository .subscribeOn(subscriberScheduler()) .observeOn(observerScheduler()); } + + @Override + public Completable deletePeer(long accountId, String address) { + return null; + } } diff --git a/data/src/test/java/org/mercury_im/messenger/data/mapping/PeerMappingTest.java b/data/src/test/java/org/mercury_im/messenger/data/mapping/PeerMappingTest.java index c3d7c36..32a5dd7 100644 --- a/data/src/test/java/org/mercury_im/messenger/data/mapping/PeerMappingTest.java +++ b/data/src/test/java/org/mercury_im/messenger/data/mapping/PeerMappingTest.java @@ -6,7 +6,6 @@ import org.mercury_im.messenger.data.model.AccountModel; import org.mercury_im.messenger.data.model.PeerModel; import org.mercury_im.messenger.entity.contact.IPeer; import org.mercury_im.messenger.entity.contact.Peer; -import org.mercury_im.messenger.entity.contact.SubscriptionMode; import javax.inject.Inject; @@ -25,7 +24,7 @@ public class PeerMappingTest { PEER_GORDO.setId(1); PEER_GORDO.setAddress("gordo@big.joe"); PEER_GORDO.setName("Gordo"); - PEER_GORDO.setSubscriptionMode(SubscriptionMode.TO_THEM_ACCEPTED_PREAPPROVED); + PEER_GORDO.setSubscriptionDirection(SubscriptionMode.TO_THEM_ACCEPTED_PREAPPROVED); } public PeerMappingTest() { diff --git a/data/src/test/java/org/mercury_im/messenger/data/repository/AccountRepositoryTest.java b/data/src/test/java/org/mercury_im/messenger/data/repository/AccountRepositoryTest.java index 0348c96..dddc8f1 100644 --- a/data/src/test/java/org/mercury_im/messenger/data/repository/AccountRepositoryTest.java +++ b/data/src/test/java/org/mercury_im/messenger/data/repository/AccountRepositoryTest.java @@ -47,7 +47,7 @@ public class AccountRepositoryTest { Account a1 = new IAccount(); a1.setAddress("a1@example.com"); - a1.setAuthentication(new PasswordAuthentication("a1a1a1")); + a1.setPassword("a1a1a1"); a1.setEnabled(true); d.add(accountRepository.insertAccount(a1).subscribe()); @@ -57,7 +57,7 @@ public class AccountRepositoryTest { Account a2 = new IAccount(); a2.setAddress("a2@example.com"); - a2.setAuthentication(new PasswordAuthentication("a2a2a2")); + a2.setPassword("a2a2a2"); a2.setEnabled(false); d.add(accountRepository.insertAccount(a2).subscribe()); @@ -66,7 +66,7 @@ public class AccountRepositoryTest { Account a3 = new IAccount(); a3.setAddress("a3@example.com"); - a3.setAuthentication(new PasswordAuthentication("a3a3a3")); + a3.setPassword("a3a3a3"); a3.setEnabled(false); d.add(accountRepository.insertAccount(a3).subscribe()); diff --git a/domain/src/main/java/org/mercury_im/messenger/Messenger.java b/domain/src/main/java/org/mercury_im/messenger/Messenger.java index 4ea055e..2fbc6b6 100644 --- a/domain/src/main/java/org/mercury_im/messenger/Messenger.java +++ b/domain/src/main/java/org/mercury_im/messenger/Messenger.java @@ -2,9 +2,11 @@ package org.mercury_im.messenger; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.roster.Roster; import org.jivesoftware.smackx.csi.ClientStateIndicationManager; import org.mercury_im.messenger.data.repository.Repositories; import org.mercury_im.messenger.entity.Account; +import org.mercury_im.messenger.store.MercuryRosterStore; import org.mercury_im.messenger.usecase.AddAccount; import org.mercury_im.messenger.xmpp.MercuryConnection; @@ -30,6 +32,13 @@ public class Messenger implements ClientStateListener { public void addConnection(MercuryConnection connection) { connections.put(connection.getAccount().getId(), connection); + initRosterStore(connection); + } + + private void initRosterStore(MercuryConnection connection) { + Roster.getInstanceFor(connection.getConnection()) + .setRosterStore(new MercuryRosterStore(connection.getAccount(), + repositories.getPeerRepository(), repositories.getAccountRepository())); } public MercuryConnection getConnection(Account account) { diff --git a/domain/src/main/java/org/mercury_im/messenger/data/repository/PeerRepository.java b/domain/src/main/java/org/mercury_im/messenger/data/repository/PeerRepository.java index cb4a64d..ff161ba 100644 --- a/domain/src/main/java/org/mercury_im/messenger/data/repository/PeerRepository.java +++ b/domain/src/main/java/org/mercury_im/messenger/data/repository/PeerRepository.java @@ -41,9 +41,17 @@ public interface PeerRepository { Observable> observeAllPeers(); + default Observable> observeAllContactsOfAccount(Account account) { + return observeAllContactsOfAccount(account.getId()); + } + + Observable> observeAllContactsOfAccount(long accountId); + Single updatePeer(Peer Peer); Single upsertPeer(Peer Peer); Completable deletePeer(Peer Peer); + + Completable deletePeer(long accountId, String address); } diff --git a/domain/src/main/java/org/mercury_im/messenger/store/MercuryRosterStore.java b/domain/src/main/java/org/mercury_im/messenger/store/MercuryRosterStore.java new file mode 100644 index 0000000..e3acd82 --- /dev/null +++ b/domain/src/main/java/org/mercury_im/messenger/store/MercuryRosterStore.java @@ -0,0 +1,207 @@ +package org.mercury_im.messenger.store; + +import org.jivesoftware.smack.roster.packet.RosterPacket; +import org.jivesoftware.smack.roster.rosterstore.RosterStore; +import org.jxmpp.jid.Jid; +import org.jxmpp.jid.impl.JidCreate; +import org.mercury_im.messenger.data.repository.AccountRepository; +import org.mercury_im.messenger.data.repository.PeerRepository; +import org.mercury_im.messenger.entity.Account; +import org.mercury_im.messenger.entity.contact.IPeer; +import org.mercury_im.messenger.entity.contact.Peer; +import org.mercury_im.messenger.entity.contact.SubscriptionDirection; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; + +public class MercuryRosterStore implements RosterStore { + + private static final Logger LOGGER = Logger.getLogger(RosterStore.class.getName()); + + private final PeerRepository peerRepository; + private final AccountRepository accountRepository; + private Account account; + private final CompositeDisposable disposable = new CompositeDisposable(); + + private final Map itemMap = new HashMap<>(); + private String rosterVersion; + + public MercuryRosterStore(Account account, PeerRepository rosterRepository, AccountRepository accountRepository) { + this.account = account; + this.peerRepository = rosterRepository; + this.accountRepository = accountRepository; + } + + public void subscribe() { + disposable.add(peerRepository.observeAllContactsOfAccount(account) + .observeOn(Schedulers.computation()) + .subscribe(contactsList -> { + itemMap.clear(); + for (Peer contactModel : contactsList) { + itemMap.put(contactModel.getAddress(), fromEntity(contactModel)); + LOGGER.log(Level.INFO, "Populate itemMap with " + contactsList.size() + " items"); + + } + }, + error -> LOGGER.log(Level.WARNING, "An error occurred while updating roster cache", error))); + + /* + disposable.add(peerRepository.getRosterVersion(account) + .observeOn(Schedulers.computation()) + .subscribe( + result -> setRosterVersion(result), + error -> LOGGER.log(Level.WARNING, "An error occurred updating cached roster version", error))); + + */ + } + + public void unsubscribe() { + disposable.dispose(); + } + + private void setRosterVersion(String rosterVersion) { + this.rosterVersion = rosterVersion; + } + + @Override + public List getEntries() { + return new ArrayList<>(itemMap.values()); + } + + @Override + public RosterPacket.Item getEntry(Jid bareJid) { + return itemMap.get(bareJid); + } + + @Override + public String getRosterVersion() { + return rosterVersion != null ? rosterVersion : ""; + } + + @Override + public boolean addEntry(RosterPacket.Item item, String version) { + LOGGER.log(Level.INFO, "Add entry " + item.toXML().toString()); + // Update database + Peer contact = toEntity(item); + disposable.add(peerRepository.upsertPeer(contact) + .subscribe( + success -> LOGGER.log(Level.FINE, "Upserted contact model " + success + " successfully"), + error -> LOGGER.log(Level.WARNING, "An error occurred upserting contact " + contact, error) + )); + /* + disposable.add(peerRepository.updateRosterVersion(account, version) + .subscribe( + success -> LOGGER.log(Level.FINE, "Upserted roster version to " + rosterVersion + " successfully"), + error -> LOGGER.log(Level.WARNING, "An error occurred upserting roster version", error) + )); + */ + + return true; + } + + @Override + public boolean resetEntries(Collection items, String version) { + LOGGER.log(Level.INFO, "Reset Entries: " + Arrays.toString(items.toArray())); + // Update database + // TODO: Delete other contacts + for (RosterPacket.Item item : items) { + Peer model = toEntity(item); + disposable.add(peerRepository.upsertPeer(model) + .subscribe( + success -> LOGGER.log(Level.FINE, "Upserted contact model " + success + " successfully"), + error -> LOGGER.log(Level.WARNING, "An error occurred upserting contact " + model, error) + )); + } + + /* + disposable.add(peerRepository.updateRosterVersion(account, version) + .subscribe( + success -> LOGGER.log(Level.FINE, "Upserted roster version to " + rosterVersion + " successfully"), + error -> LOGGER.log(Level.WARNING, "An error occurred upserting roster version", error) + )); + */ + + return true; + } + + @Override + public boolean removeEntry(Jid bareJid, String version) { + LOGGER.log(Level.INFO, "Remove entry " + bareJid.toString()); + + disposable.add(peerRepository.deletePeer(account.getId(), bareJid.asEntityBareJidOrThrow().asEntityBareJidString()) + .subscribe( + () -> LOGGER.log(Level.FINE, "Deletion of contact " + bareJid.toString() + " successful"), + error -> LOGGER.log(Level.WARNING, "An error occurred deleting contact " + bareJid.toString(), error) + )); + /* + disposable.add(peerRepository.updateRosterVersion(account, version) + .subscribe( + success -> LOGGER.log(Level.FINE, "Upserted roster version to " + rosterVersion + " successfully"), + error -> LOGGER.log(Level.WARNING, "An error occurred upserting roster version", error) + )); + */ + return true; + } + + @Override + public void resetStore() { + LOGGER.log(Level.INFO, "Reset Store"); + + /* + disposable.add(peerRepository.deleteAllContactsOfAccount(account) + .subscribe( + success -> LOGGER.log(Level.FINE, "Successfully reset store."), + error -> LOGGER.log(Level.WARNING, "An error occurred resetting store", error) + )); + disposable.add(peerRepository.updateRosterVersion(account, "") + .subscribe( + success -> LOGGER.log(Level.FINE, "Successfully reset roster version"), + error -> LOGGER.log(Level.WARNING, "An error occurred resetting roster version", error) + )); + */ + } + + public RosterPacket.Item fromEntity(Peer contactModel) { + RosterPacket.Item item = new RosterPacket.Item( + JidCreate.entityBareFromOrThrowUnchecked(contactModel.getAddress()), + contactModel.getName()); + if (contactModel.getSubscriptionDirection() != null) { + item.setItemType(convert(contactModel.getSubscriptionDirection())); + } + item.setApproved(contactModel.isSubscriptionApproved()); + item.setSubscriptionPending(contactModel.isSubscriptionPending()); + return item; + } + + public Peer toEntity(RosterPacket.Item item) { + Peer peer = new IPeer(); + peer.setAccount(account); + peer.setAddress(item.getJid().asEntityBareJidOrThrow().asEntityBareJidString()); + peer.setName(item.getName()); + if (item.getItemType() != null) { + peer.setSubscriptionDirection(convert(item.getItemType())); + } + peer.setSubscriptionApproved(item.isApproved()); + peer.setSubscriptionPending(item.isSubscriptionPending()); + + return peer; + } + + public SubscriptionDirection convert(RosterPacket.ItemType type) { + return SubscriptionDirection.valueOf(type.toString()); + + } + + public RosterPacket.ItemType convert(SubscriptionDirection direction) { + return RosterPacket.ItemType.fromString(direction.toString()); + } +} diff --git a/entity/src/main/java/org/mercury_im/messenger/entity/contact/IPeer.java b/entity/src/main/java/org/mercury_im/messenger/entity/contact/IPeer.java index f461042..e8be5d1 100644 --- a/entity/src/main/java/org/mercury_im/messenger/entity/contact/IPeer.java +++ b/entity/src/main/java/org/mercury_im/messenger/entity/contact/IPeer.java @@ -8,7 +8,9 @@ public class IPeer implements Peer { protected Account account; protected String address; protected String name; - protected SubscriptionMode subscriptionMode; + protected SubscriptionDirection subscriptionDirection; + protected boolean pending; + protected boolean approved; @Override public long getId() { @@ -51,18 +53,38 @@ public class IPeer implements Peer { } @Override - public SubscriptionMode getSubscriptionMode() { - return subscriptionMode; + public SubscriptionDirection getSubscriptionDirection() { + return subscriptionDirection; } @Override - public void setSubscriptionMode(SubscriptionMode mode) { - this.subscriptionMode = mode; + public void setSubscriptionDirection(SubscriptionDirection mode) { + this.subscriptionDirection = mode; + } + + @Override + public boolean isSubscriptionPending() { + return pending; + } + + @Override + public void setSubscriptionPending(boolean pending) { + this.pending = pending; + } + + @Override + public boolean isSubscriptionApproved() { + return approved; + } + + @Override + public void setSubscriptionApproved(boolean approved) { + this.approved = approved; } @Override public boolean isContact() { - return subscriptionMode != SubscriptionMode.NONE - && subscriptionMode != SubscriptionMode.FROM_THEM_PENDING; + return subscriptionDirection != SubscriptionDirection.none + && subscriptionDirection != SubscriptionDirection.from; } } diff --git a/entity/src/main/java/org/mercury_im/messenger/entity/contact/Peer.java b/entity/src/main/java/org/mercury_im/messenger/entity/contact/Peer.java index d91a5e1..cd57826 100644 --- a/entity/src/main/java/org/mercury_im/messenger/entity/contact/Peer.java +++ b/entity/src/main/java/org/mercury_im/messenger/entity/contact/Peer.java @@ -26,9 +26,17 @@ public interface Peer { void setName(String name); - SubscriptionMode getSubscriptionMode(); + SubscriptionDirection getSubscriptionDirection(); - void setSubscriptionMode(SubscriptionMode mode); + void setSubscriptionDirection(SubscriptionDirection mode); + + boolean isSubscriptionPending(); + + void setSubscriptionPending(boolean pending); + + boolean isSubscriptionApproved(); + + void setSubscriptionApproved(boolean approved); boolean isContact(); } diff --git a/data/src/main/java/org/mercury_im/messenger/data/enums/SubscriptionDirection.java b/entity/src/main/java/org/mercury_im/messenger/entity/contact/SubscriptionDirection.java similarity index 64% rename from data/src/main/java/org/mercury_im/messenger/data/enums/SubscriptionDirection.java rename to entity/src/main/java/org/mercury_im/messenger/entity/contact/SubscriptionDirection.java index 38ea670..90a3671 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/enums/SubscriptionDirection.java +++ b/entity/src/main/java/org/mercury_im/messenger/entity/contact/SubscriptionDirection.java @@ -1,4 +1,4 @@ -package org.mercury_im.messenger.data.enums; +package org.mercury_im.messenger.entity.contact; public enum SubscriptionDirection { none, diff --git a/entity/src/main/java/org/mercury_im/messenger/entity/contact/SubscriptionMode.java b/entity/src/main/java/org/mercury_im/messenger/entity/contact/SubscriptionMode.java deleted file mode 100644 index aeb728f..0000000 --- a/entity/src/main/java/org/mercury_im/messenger/entity/contact/SubscriptionMode.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.mercury_im.messenger.entity.contact; - -/** - * Enum describing the relationship between the user and another entity. - */ -public enum SubscriptionMode { - /** - * No subscription between us an them. - */ - NONE, - - /** - * No subscription between us and them. - * However, we pre-approved their subscription, should they decide to subscribe to us. - */ - NONE_PREAPPROVED, - - /** - * We sent a subscription request, which is now pending to be accepted. - * We do not pre-approve their subscription to us. - */ - TO_THEM_PENDING, - - /** - * We sent a subscription request, which is now pending to be accepted. - * Also, we pre-approved their subscription to us. - */ - TO_THEM_PENDING_PREAPPROVED, - - /** - * We sent a subscription request and they approved it. - */ - TO_THEM_ACCEPTED, - - /** - * We sent a subscription request and they approved it. - * Also, we pre-approved their subscription, should they decide to subscribe back. - */ - TO_THEM_ACCEPTED_PREAPPROVED, - - /** - * They sent us a subscription request, but we did not yet accept it. - */ - FROM_THEM_PENDING, - - /** - * They sent us a subscription request and we accepted it. - */ - FROM_THEM_ACCEPTED, - - /** - * They accepted us and we accepted them. - */ - BIDIRECTIONAL -} diff --git a/entity/src/main/java/org/mercury_im/messenger/entity/message/IMessage.java b/entity/src/main/java/org/mercury_im/messenger/entity/message/IMessage.java index b052aca..c708497 100644 --- a/entity/src/main/java/org/mercury_im/messenger/entity/message/IMessage.java +++ b/entity/src/main/java/org/mercury_im/messenger/entity/message/IMessage.java @@ -12,6 +12,8 @@ public class IMessage implements Message { protected List payloads; protected MessageDeliveryState deliveryState; protected MessageMetadata metadata; + protected MessageDirection direction; + @Override public long getId() { @@ -53,6 +55,16 @@ public class IMessage implements Message { this.timestamp = timestamp; } + @Override + public MessageDirection getDirection() { + return direction; + } + + @Override + public void setDirection(MessageDirection direction) { + this.direction = direction; + } + @Override public List getMessagePayloads() { return payloads; diff --git a/entity/src/main/java/org/mercury_im/messenger/entity/message/Message.java b/entity/src/main/java/org/mercury_im/messenger/entity/message/Message.java index 81eab37..9253823 100644 --- a/entity/src/main/java/org/mercury_im/messenger/entity/message/Message.java +++ b/entity/src/main/java/org/mercury_im/messenger/entity/message/Message.java @@ -21,6 +21,14 @@ public interface Message { void setTimestamp(Date timestamp); + MessageDirection getDirection(); + + void setDirection(MessageDirection direction); + + default boolean isIncoming() { + return getDirection() == MessageDirection.incoming; + } + List getMessagePayloads(); void setMessagePayloads(List payloadContainers); diff --git a/entity/src/main/java/org/mercury_im/messenger/entity/message/MessageDirection.java b/entity/src/main/java/org/mercury_im/messenger/entity/message/MessageDirection.java new file mode 100644 index 0000000..1315138 --- /dev/null +++ b/entity/src/main/java/org/mercury_im/messenger/entity/message/MessageDirection.java @@ -0,0 +1,6 @@ +package org.mercury_im.messenger.entity.message; + +public enum MessageDirection { + incoming, + outgoing +}