Start work on new message sending logic

This commit is contained in:
Paul Schaub 2020-07-30 19:52:58 +02:00
parent 9ea08b11da
commit 0adbcc94b0
Signed by: vanitasvitae
GPG Key ID: 62BEE9264BF17311
35 changed files with 517 additions and 142 deletions

View File

@ -8,6 +8,7 @@ import org.mercury_im.messenger.android.ui.roster.contacts.AndroidContactListVie
import org.mercury_im.messenger.core.di.module.OpenPgpModule;
import org.mercury_im.messenger.core.di.module.RxMercuryMessageStoreFactoryModule;
import org.mercury_im.messenger.core.di.module.RxMercuryRosterStoreFactoryModule;
import org.mercury_im.messenger.core.di.module.StanzaIdSourceFactoryModule;
import org.mercury_im.messenger.core.di.module.XmppTcpConnectionFactoryModule;
import org.mercury_im.messenger.core.viewmodel.account.detail.AccountDetailsViewModel;
import org.mercury_im.messenger.core.viewmodel.account.list.AccountListViewModel;
@ -48,7 +49,8 @@ import dagger.Component;
XmppTcpConnectionFactoryModule.class,
RxMercuryMessageStoreFactoryModule.class,
OpenPgpModule.class,
RxMercuryRosterStoreFactoryModule.class
RxMercuryRosterStoreFactoryModule.class,
StanzaIdSourceFactoryModule.class
})
public interface AppComponent {

View File

@ -1,6 +1,5 @@
package org.mercury_im.messenger.data.converter;
import org.jxmpp.jid.EntityFullJid;
import org.jxmpp.jid.EntityJid;
import org.jxmpp.jid.impl.JidCreate;
@ -27,18 +26,15 @@ public class EntityJidConverter implements Converter<EntityJid, String> {
@Override
public String convertToPersisted(EntityJid value) {
if (value == null) return null;
return value.toString();
}
@Override
public EntityJid convertToMapped(Class<? extends EntityJid> type, @Nullable String value) {
switch (type.getName()) {
case "EntityFullJid":
return JidCreate.entityFullFromOrThrowUnchecked(value);
case "EntityBareJid":
return JidCreate.entityBareFromOrThrowUnchecked(value);
default:
throw new IllegalArgumentException("Unknown jid type encountered: " + type.getName());
if (value == null) {
return null;
}
return JidCreate.entityFromOrThrowUnchecked(value);
}
}

View File

@ -0,0 +1,37 @@
package org.mercury_im.messenger.data.mapping;
import org.jivesoftware.smackx.chat_markers.ChatMarkersState;
import org.mercury_im.messenger.entity.message.ChatMarkerState;
public class ChatMarkerMapping {
public static ChatMarkersState toSmackChatMarker(ChatMarkerState mercuryChatMarker) {
switch (mercuryChatMarker) {
case markable:
return ChatMarkersState.markable;
case received:
return ChatMarkersState.received;
case displayed:
return ChatMarkersState.displayed;
case acknowledged:
return ChatMarkersState.acknowledged;
default:
throw new IllegalArgumentException("Illegal ChatMarkersState: " + mercuryChatMarker);
}
}
public static ChatMarkerState toMercuryChatMarker(ChatMarkersState smackChatMarker) {
switch (smackChatMarker) {
case markable:
return ChatMarkerState.markable;
case received:
return ChatMarkerState.received;
case displayed:
return ChatMarkerState.displayed;
case acknowledged:
return ChatMarkerState.acknowledged;
default:
throw new IllegalArgumentException("Illegal ChatMarkersState: " + smackChatMarker);
}
}
}

View File

@ -26,6 +26,7 @@ public class MessageMapping extends AbstractMapping<Message, MessageModel> {
@Override
public MessageModel mapToModel(Message entity, MessageModel model) {
model.setId(entity.getId());
model.setChatId(entity.getChatId());
model.setSender(entity.getSender());
model.setRecipient(entity.getRecipient());
model.setTimestamp(entity.getTimestamp());
@ -42,6 +43,7 @@ public class MessageMapping extends AbstractMapping<Message, MessageModel> {
@Override
public Message mapToEntity(MessageModel model, Message entity) {
entity.setId(model.getId());
entity.setChatId(model.getChatId());
entity.setSender(model.getSender());
entity.setRecipient(model.getRecipient());
entity.setTimestamp(model.getTimestamp());

View File

@ -1,9 +1,11 @@
package org.mercury_im.messenger.data.model;
import org.jxmpp.jid.EntityFullJid;
import org.mercury_im.messenger.data.converter.EntityFullJidConverter;
import org.jxmpp.jid.EntityJid;
import org.mercury_im.messenger.data.converter.EntityJidConverter;
import org.mercury_im.messenger.data.converter.MessageDirectionConverter;
import org.mercury_im.messenger.entity.Encryption;
import org.mercury_im.messenger.entity.message.ChatMarkerState;
import org.mercury_im.messenger.entity.message.MessageDeliveryState;
import org.mercury_im.messenger.entity.message.MessageDirection;
import org.pgpainless.key.OpenPgpV4Fingerprint;
@ -27,15 +29,16 @@ public abstract class AbstractMessageModel implements Persistable {
UUID id;
@Column(nullable = false)
@Convert(UUIDConverter.class)
UUID chatId;
@Column(nullable = false)
@Convert(EntityFullJidConverter.class)
EntityFullJid sender;
@Convert(EntityJidConverter.class)
EntityJid sender;
@Column(nullable = false)
@Convert(EntityFullJidConverter.class)
EntityFullJid recipient;
@Convert(EntityJidConverter.class)
EntityJid recipient;
@Column(name = "\"timestamp\"", nullable = false)
Date timestamp;
@ -68,4 +71,10 @@ public abstract class AbstractMessageModel implements Persistable {
@Column
OpenPgpV4Fingerprint senderOXFingerprint;
@Column
ChatMarkerState chatMarkerState;
@Column
MessageDeliveryState deliveryState;
}

View File

@ -1,5 +1,6 @@
package org.mercury_im.messenger.data.repository;
import org.jxmpp.jid.EntityBareJid;
import org.mercury_im.messenger.core.data.repository.MessageRepository;
import org.mercury_im.messenger.data.mapping.DirectChatMapping;
import org.mercury_im.messenger.data.mapping.GroupChatMapping;
@ -10,7 +11,9 @@ import org.mercury_im.messenger.data.repository.dao.GroupChatDao;
import org.mercury_im.messenger.data.repository.dao.MessageDao;
import org.mercury_im.messenger.entity.chat.DirectChat;
import org.mercury_im.messenger.entity.chat.GroupChat;
import org.mercury_im.messenger.entity.message.ChatMarkerState;
import org.mercury_im.messenger.entity.message.Message;
import org.mercury_im.messenger.entity.message.MessageDeliveryState;
import java.util.ArrayList;
import java.util.List;
@ -105,7 +108,7 @@ public class RxMessageRepository
.from(MessageModel.class)
.where(MessageModel.BODY.like("%" + body + "%")
.and(MessageModel.CHAT_ID.eq(chat.getId())))
.and(MessageModel.CHAT_ID.eq(chat.getId())))
.get().observableResult()
.map(ResultDelegate::toList)
.map(this::messageModelsToEntities);
@ -117,7 +120,7 @@ public class RxMessageRepository
.from(MessageModel.class)
.where(MessageModel.BODY.like("%" + body + "%")
.and(MessageModel.CHAT_ID.eq(chat.getId())))
.and(MessageModel.CHAT_ID.eq(chat.getId())))
.get().observableResult()
.map(ResultDelegate::toList)
.map(this::messageModelsToEntities);
@ -146,6 +149,24 @@ public class RxMessageRepository
.get().single().ignoreElement();
}
@Override
public Completable markMessage(String stanzaId, EntityBareJid recipientJid, ChatMarkerState state) {
return data().update(MessageModel.class)
.set(MessageModel.CHAT_MARKER_STATE, state)
.where(MessageModel.LEGACY_ID.eq(stanzaId)
.and(MessageModel.RECIPIENT.eq(recipientJid)))
.get().single().ignoreElement();
}
@Override
public Completable updateDeliveryState(UUID messageId, MessageDeliveryState deliveryState) {
return data().update(MessageModel.class)
.set(MessageModel.DELIVERY_STATE, deliveryState)
.where(MessageModel.ID.eq(messageId))
.get().single()
.ignoreElement();
}
private List<Message> messageModelsToEntities(List<MessageModel> models) {
List<Message> entities = new ArrayList<>(models.size());
for (MessageModel model : models) {

View File

@ -1,5 +1,10 @@
apply plugin: 'java-library'
sourceSets {
//main.java.srcDirs += "${buildDir}/generated/sources/annotationProcessor/java/main/"
test.java.srcDirs += "${buildDir}/generated/sources/annotationProcessor/java/test/"
}
dependencies {
implementation project(':entity')
@ -20,7 +25,9 @@ dependencies {
// Dagger 2 for dependency injection
implementation "com.google.dagger:dagger:$daggerVersion"
testImplementation "com.google.dagger:dagger:$daggerVersion"
annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
testAnnotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
compileOnly "org.projectlombok:lombok:$lombokVersion"
annotationProcessor "org.projectlombok:lombok:$lombokVersion"

View File

@ -13,8 +13,13 @@ import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
import org.mercury_im.messenger.core.connection.MercuryConnection;
import org.mercury_im.messenger.core.connection.MercuryConnectionManager;
import org.mercury_im.messenger.core.connection.message.MessageConsignor;
import org.mercury_im.messenger.core.connection.message.OxMessageConsignor;
import org.mercury_im.messenger.core.connection.message.PlaintextMessageConsignor;
import org.mercury_im.messenger.core.data.repository.MessageRepository;
import org.mercury_im.messenger.core.exception.ConnectionNotFoundException;
import org.mercury_im.messenger.core.exception.ContactAlreadyAddedException;
import org.mercury_im.messenger.entity.chat.Chat;
import org.mercury_im.messenger.entity.contact.Peer;
import java.io.IOException;
@ -34,12 +39,15 @@ public class Messenger {
private static final Logger LOGGER = Logger.getLogger(Messenger.class.getName());
private final MercuryConnectionManager connectionManager;
private final MessageRepository messageRepository;
private final SchedulersFacade schedulers;
@Inject
public Messenger(MercuryConnectionManager connectionManager,
MessageRepository messageRepository,
SchedulersFacade schedulers) {
this.connectionManager = connectionManager;
this.messageRepository = messageRepository;
this.schedulers = schedulers;
}
@ -127,4 +135,16 @@ public class Messenger {
}
return connection;
}
public MessageConsignor getMessageConsignor(Chat chat) {
switch (chat.getChatPreferences().getEncryption()) {
case plain:
return new PlaintextMessageConsignor(connectionManager, messageRepository, chat);
case ox_sign:
case ox_crypt:
case ox_signcrypt:
return new OxMessageConsignor(connectionManager, messageRepository, chat);
}
throw new AssertionError("Illegal Encryption Type: " + chat.getChatPreferences().getEncryption());
}
}

View File

@ -159,7 +159,6 @@ public class MercuryConnectionManager {
MercuryMessageStore mercuryMessageStore = messageStoreFactory.createMessageStore(account);
ChatManager chatManager = ChatManager.getInstanceFor(connection.getConnection());
chatManager.addIncomingListener(mercuryMessageStore);
chatManager.addOutgoingListener(mercuryMessageStore);
}));
cryptoManager.initialize(connection);
}

View File

@ -1,42 +0,0 @@
package org.mercury_im.messenger.core.connection
import org.mercury_im.messenger.entity.message.Message as MercuryMessage;
import org.jivesoftware.smack.packet.Message as SmackMessage;
import java.util.UUID
import io.reactivex.Completable
import io.reactivex.Single
import org.mercury_im.messenger.core.util.AppendCompletableToSingle
import org.mercury_im.messenger.entity.chat.Chat
class MessageCenter(private val composer: Composer,
private val persister: Persister,
private val encryptor: Encryptor,
private val sender: Sender) {
fun sendSingleTextMessage(chat: Chat, body: String): Single<UUID> {
val messageEntity = composer.createChatMessage(chat, body)
val messageId = persister.persistPendingMessage(messageEntity)
val smackMessage = encryptor.encrypt(messageEntity)
val sendCompletable = sender.send(smackMessage);
return messageId.compose(AppendCompletableToSingle(sendCompletable));
}
interface Composer {
fun createChatMessage(chat: Chat, body: String): MercuryMessage
}
interface Persister {
fun persistPendingMessage(message: MercuryMessage): Single<UUID>
}
interface Encryptor {
fun encrypt(message: MercuryMessage): SmackMessage
}
interface Sender {
fun send(message: SmackMessage): Completable
}
}

View File

@ -1,22 +0,0 @@
package org.mercury_im.messenger.core.connection;
import org.mercury_im.messenger.core.data.repository.MessageRepository;
import org.mercury_im.messenger.entity.message.Message;
import java.util.UUID;
import io.reactivex.Single;
public class MessagePersister implements MessageCenter.Persister {
private final MessageRepository messageRepository;
public MessagePersister(MessageRepository messageRepository) {
this.messageRepository = messageRepository;
}
@Override
public Single<UUID> persistPendingMessage(Message message) {
return null;
}
}

View File

@ -1,6 +1,7 @@
package org.mercury_im.messenger.core.connection;
import org.jivesoftware.smack.ReconnectionManager;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smackx.carbons.CarbonManager;
import org.jivesoftware.smackx.iqversion.VersionManager;
@ -10,7 +11,7 @@ import org.jivesoftware.smackx.sid.StableUniqueStanzaIdManager;
public class SmackConfig {
static void staticConfiguration() {
//SmackConfiguration.DEBUG = true;
SmackConfiguration.DEBUG = true;
ReconnectionManager.setEnabledPerDefault(true);
ReconnectionManager.setDefaultReconnectionPolicy(ReconnectionManager.ReconnectionPolicy.RANDOM_INCREASING_DELAY);

View File

@ -1,19 +1,31 @@
package org.mercury_im.messenger.core.connection;
import org.jivesoftware.smack.AbstractXMPPConnection;
import org.jivesoftware.smack.packet.id.StanzaIdSource;
import org.jivesoftware.smack.packet.id.StanzaIdSourceFactory;
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jxmpp.stringprep.XmppStringprepException;
import org.mercury_im.messenger.entity.Account;
import javax.inject.Inject;
public class XmppTcpConnectionFactory implements XmppConnectionFactory {
private static final int CONNECTION_TIMEOUT = 30 * 1000;
private final StanzaIdSourceFactory stanzaIdSourceFactory;
@Inject
public XmppTcpConnectionFactory(StanzaIdSourceFactory stanzaIdSourceFactory) {
this.stanzaIdSourceFactory = stanzaIdSourceFactory;
}
public AbstractXMPPConnection createConnection(Account account) {
try {
XMPPTCPConnectionConfiguration.Builder configBuilder =
XMPPTCPConnectionConfiguration.builder()
.setStanzaIdSourceFactory(stanzaIdSourceFactory)
.setConnectTimeout(CONNECTION_TIMEOUT)
.setXmppAddressAndPassword(account.getAddress(), account.getPassword());
if (account.getHost() != null) {

View File

@ -0,0 +1,24 @@
package org.mercury_im.messenger.core.connection.message;
import org.mercury_im.messenger.core.connection.MercuryConnectionManager;
import org.mercury_im.messenger.core.data.repository.MessageRepository;
import org.mercury_im.messenger.entity.chat.Chat;
public abstract class AbstractMessageConsignor implements MessageConsignor {
protected final MessageComposer messageComposer;
protected final MercuryConnectionManager connectionManager;
protected final MessageRepository messageRepository;
protected final Chat chat;
public AbstractMessageConsignor(MessageComposer composer,
MercuryConnectionManager connectionManager,
MessageRepository messageRepository,
Chat chat) {
this.messageComposer = composer;
this.connectionManager = connectionManager;
this.messageRepository = messageRepository;
this.chat = chat;
}
}

View File

@ -1,27 +1,33 @@
package org.mercury_im.messenger.core.connection;
package org.mercury_im.messenger.core.connection.message;
import org.jivesoftware.smack.packet.id.StandardStanzaIdSource;
import org.mercury_im.messenger.entity.chat.Chat;
import org.mercury_im.messenger.entity.message.Message;
import org.mercury_im.messenger.entity.message.MessageDeliveryState;
import org.mercury_im.messenger.entity.message.MessageDirection;
import java.util.Date;
import java.util.UUID;
public class MercuryMessageComposer implements MessageCenter.Composer {
public class MessageComposer {
@Override
public Message createChatMessage(Chat chat, String body) {
UUID messageId = UUID.randomUUID();
Message message = new Message();
message.setId(messageId);
message.setChatId(chat.getId());
message.setLegacyStanzaId(new StandardStanzaIdSource().getNewStanzaId());
message.setOriginId(messageId.toString());
message.setBody(body);
message.setSender(chat.getAccount().getJid());
message.setRecipient(chat.getAddress());
message.setRecipient(chat.getJid());
message.setDeliveryState(MessageDeliveryState.pending_delivery);
message.setDirection(MessageDirection.outgoing);
message.setRead(false);
message.setTimestamp(new Date());
UUID messageId = UUID.randomUUID();
message.setId(messageId);
message.setOriginId(messageId.toString());
return message;
}
}

View File

@ -0,0 +1,4 @@
package org.mercury_im.messenger.core.connection.message;
public interface MessageConsignee {
}

View File

@ -0,0 +1,12 @@
package org.mercury_im.messenger.core.connection.message;
import org.mercury_im.messenger.entity.chat.Chat;
import java.util.UUID;
import io.reactivex.Single;
public interface MessageConsignor {
Single<UUID> sendTextMessage(Chat chat, String body);
}

View File

@ -0,0 +1,25 @@
package org.mercury_im.messenger.core.connection.message;
import org.mercury_im.messenger.core.connection.MercuryConnectionManager;
import org.mercury_im.messenger.core.data.repository.MessageRepository;
import org.mercury_im.messenger.entity.chat.Chat;
import java.util.UUID;
import io.reactivex.Single;
public class OxMessageConsignor extends AbstractMessageConsignor {
public OxMessageConsignor(MercuryConnectionManager connectionManager, MessageRepository messageRepository, Chat chat) {
this(new MessageComposer(), connectionManager, messageRepository, chat);
}
public OxMessageConsignor(MessageComposer composer, MercuryConnectionManager connectionManager, MessageRepository messageRepository, Chat chat) {
super(composer, connectionManager, messageRepository, chat);
}
@Override
public Single<UUID> sendTextMessage(Chat chat, String body) {
return null;
}
}

View File

@ -0,0 +1,91 @@
package org.mercury_im.messenger.core.connection.message;
import org.jivesoftware.smack.chat2.ChatManager;
import org.jivesoftware.smack.packet.MessageBuilder;
import org.jivesoftware.smackx.muc.MultiUserChat;
import org.jivesoftware.smackx.muc.MultiUserChatManager;
import org.jivesoftware.smackx.sid.element.OriginIdElement;
import org.jxmpp.jid.parts.Resourcepart;
import org.mercury_im.messenger.core.connection.MercuryConnection;
import org.mercury_im.messenger.core.connection.MercuryConnectionManager;
import org.mercury_im.messenger.core.data.repository.MessageRepository;
import org.mercury_im.messenger.core.util.AppendCompletableToSingle;
import org.mercury_im.messenger.entity.chat.Chat;
import org.mercury_im.messenger.entity.chat.DirectChat;
import org.mercury_im.messenger.entity.chat.GroupChat;
import org.mercury_im.messenger.entity.message.Message;
import org.mercury_im.messenger.entity.message.MessageDeliveryState;
import java.util.UUID;
import io.reactivex.Completable;
import io.reactivex.Single;
public class PlaintextMessageConsignor extends AbstractMessageConsignor {
public PlaintextMessageConsignor(MercuryConnectionManager connectionManager, MessageRepository messageRepository, Chat chat) {
this(new MessageComposer(), connectionManager, messageRepository, chat);
}
public PlaintextMessageConsignor(MessageComposer composer,
MercuryConnectionManager connectionManager,
MessageRepository messageRepository,
Chat chat) {
super(composer, connectionManager, messageRepository, chat);
}
@Override
public Single<UUID> sendTextMessage(Chat chat, String body) {
Message message = messageComposer.createChatMessage(chat, body);
MessageBuilder messageBuilder = commonMessageBuilder(message);
Completable deliverMessage;
if (chat instanceof DirectChat) {
deliverMessage = sendDirectChatMessage((DirectChat) chat, messageBuilder);
} else if (chat instanceof GroupChat) {
deliverMessage = sendGroupChatMessage((GroupChat) chat, messageBuilder);
} else {
throw new AssertionError("Unknown chat type.");
}
Single<UUID> deliverAndStore = messageRepository.insertMessage(message)
.map(Message::getId)
.compose(new AppendCompletableToSingle<>(deliverMessage))
.flatMap(messageId -> messageRepository.updateDeliveryState(messageId, MessageDeliveryState.delivered_to_server)
.toSingle(() -> messageId));
return deliverAndStore;
}
private MessageBuilder commonMessageBuilder(Message message) {
return MessageBuilder.buildMessage(message.getLegacyStanzaId())
.ofType(org.jivesoftware.smack.packet.Message.Type.chat)
.addExtension(new OriginIdElement(message.getOriginId()))
.to(chat.getJid())
.from(chat.getAccount().getJid())
.setBody(message.getBody());
}
private Completable sendDirectChatMessage(DirectChat chat, MessageBuilder messageBuilder) {
return Completable.fromAction(() -> {
MercuryConnection connection = connectionManager.getConnection(chat.getAccount());
ChatManager chatManager = ChatManager.getInstanceFor(connection.getConnection());
org.jivesoftware.smack.chat2.Chat smackChat = chatManager.chatWith(chat.getJid().asEntityBareJid());
smackChat.send(messageBuilder.build());
});
}
private Completable sendGroupChatMessage(GroupChat chat, MessageBuilder messageBuilder) {
return Completable.fromAction(() -> {
MercuryConnection connection = connectionManager.getConnection(chat.getAccount());
MultiUserChatManager multiUserChatManager = MultiUserChatManager.getInstanceFor(connection.getConnection());
MultiUserChat muc = multiUserChatManager.getMultiUserChat(chat.getJid().asEntityBareJid());
if (!muc.isJoined()) {
muc.join(Resourcepart.from(chat.getNickname()));
}
muc.sendMessage(messageBuilder);
});
}
}

View File

@ -1,10 +1,14 @@
package org.mercury_im.messenger.core.data.repository;
import org.jxmpp.jid.EntityBareJid;
import org.mercury_im.messenger.entity.chat.DirectChat;
import org.mercury_im.messenger.entity.chat.GroupChat;
import org.mercury_im.messenger.entity.message.ChatMarkerState;
import org.mercury_im.messenger.entity.message.Message;
import org.mercury_im.messenger.entity.message.MessageDeliveryState;
import java.util.List;
import java.util.UUID;
import io.reactivex.Completable;
import io.reactivex.Observable;
@ -29,4 +33,8 @@ public interface MessageRepository {
Single<Message> updateMessage(Message message);
Completable deleteMessage(Message message);
Completable markMessage(String stanzaId, EntityBareJid xmppAddressOfChatPartner, ChatMarkerState received);
Completable updateDeliveryState(UUID messageId, MessageDeliveryState deliveryState);
}

View File

@ -0,0 +1,19 @@
package org.mercury_im.messenger.core.di.module;
import org.jivesoftware.smack.packet.id.StandardStanzaIdSource;
import org.jivesoftware.smack.packet.id.StanzaIdSourceFactory;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class StanzaIdSourceFactoryModule {
@Provides
@Singleton
static StanzaIdSourceFactory provideStanzaIdSourceFactory() {
return new StandardStanzaIdSource.Factory();
}
}

View File

@ -1,5 +1,6 @@
package org.mercury_im.messenger.core.di.module;
import org.jivesoftware.smack.packet.id.StanzaIdSourceFactory;
import org.mercury_im.messenger.core.connection.XmppConnectionFactory;
import org.mercury_im.messenger.core.connection.XmppTcpConnectionFactory;
@ -13,7 +14,7 @@ public class XmppTcpConnectionFactoryModule {
@Provides
@Singleton
static XmppConnectionFactory provideConnectionFactory() {
return new XmppTcpConnectionFactory();
static XmppConnectionFactory provideConnectionFactory(StanzaIdSourceFactory stanzaIdSourceFactory) {
return new XmppTcpConnectionFactory(stanzaIdSourceFactory);
}
}

View File

@ -29,7 +29,7 @@ import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
@Singleton
public class MercuryMessageStore implements IncomingChatMessageListener, OutgoingChatMessageListener, OxMessageListener {
public class MercuryMessageStore implements IncomingChatMessageListener, OxMessageListener {
private static final Logger LOGGER = Logger.getLogger(MercuryMessageStore.class.getName());
@ -66,8 +66,8 @@ public class MercuryMessageStore implements IncomingChatMessageListener, Outgoin
message.setDirection(MessageDirection.incoming);
DelayInformation delayInformation = DelayInformation.from(smackMessage);
message.setTimestamp(delayInformation != null ? delayInformation.getStamp() : new Date());
message.setSender(from.asEntityBareJidString());
message.setRecipient(smackMessage.getTo().asBareJid().toString());
message.setSender(from);
message.setRecipient(smackMessage.getTo().asEntityJidOrThrow());
message.setXml(smackMessage.toXML().toString());
if (smackMessage.getBody() != null) {
message.setBody(smackMessage.getBody());
@ -75,25 +75,6 @@ public class MercuryMessageStore implements IncomingChatMessageListener, Outgoin
disposable.add(writeMessageToStore(from, message));
}
@Override
public void newOutgoingMessage(EntityBareJid to,
MessageBuilder smackMessage,
org.jivesoftware.smack.chat2.Chat smackChat) {
if (smackMessage.hasExtension(ExplicitMessageEncryptionElement.QNAME)) {
return;
}
Message message = new Message();
message.setDirection(MessageDirection.outgoing);
message.setTimestamp(new Date());
message.setSender(account.getAddress());
message.setRecipient(to.asBareJid().toString());
message.setXml(smackMessage.build().toXML().toString());
if (smackMessage.getBody() != null) {
message.setBody(smackMessage.getBody());
}
disposable.add(writeMessageToStore(to, message));
}
private Disposable writeMessageToStore(EntityBareJid peer, Message message) {
return peerRepository.getOrCreatePeer(account, peer)
.flatMap(directChatRepository::getOrCreateChatWithPeer)
@ -115,8 +96,8 @@ public class MercuryMessageStore implements IncomingChatMessageListener, Outgoin
message.setDirection(MessageDirection.incoming);
DelayInformation delayInformation = DelayInformation.from(smackMessage);
message.setTimestamp(delayInformation != null ? delayInformation.getStamp() : new Date());
message.setSender(contact.getJid().toString());
message.setRecipient(smackMessage.getTo().asBareJid().toString());
message.setSender(contact.getJid().asEntityJidOrThrow());
message.setRecipient(smackMessage.getTo().asEntityJidOrThrow());
message.setXml(smackMessage.toXML().toString());
org.jivesoftware.smack.packet.Message.Body body = decryptedPayload.getExtension(org.jivesoftware.smack.packet.Message.Body.ELEMENT,
org.jivesoftware.smack.packet.Message.Body.NAMESPACE);

View File

@ -117,11 +117,11 @@ public class LoginViewModel implements MercuryViewModel {
Account account = createAccountEntity();
MercuryConnection connection = connectionManager.createConnection(account);
//MercuryConnection connection = connectionManager.createConnection(account);
addDisposable(accountRepository.upsertAccount(account).ignoreElement()
.andThen(connection.connect())
.andThen(connection.login())
.andThen(connectionManager.registerConnection(connection))
//.andThen(connection.connect())
//.andThen(connection.login())
//.andThen(connectionManager.registerConnection(connection))
.subscribeOn(schedulers.getNewThread())
.observeOn(schedulers.getUiScheduler())
.subscribe(

View File

@ -19,6 +19,7 @@ import java.util.logging.Logger;
import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.functions.Function;
import io.reactivex.subjects.BehaviorSubject;
import lombok.Getter;
@ -35,8 +36,6 @@ public class ChatViewModel implements MercuryViewModel {
@Getter
private BehaviorSubject<Optional<Peer>> peer = BehaviorSubject.create();
private Single<DirectChat> directChat;
@Getter
private BehaviorSubject<DirectChat> chat = BehaviorSubject.create();
@ -62,13 +61,20 @@ public class ChatViewModel implements MercuryViewModel {
}
public void init(UUID accountId, EntityBareJid contactJid) {
Single<Peer> peerSingle = contactRepository.getOrCreatePeer(accountId, contactJid);
peerSingle.flatMapObservable(contactRepository::observePeer).subscribe(peer);
directChat = peerSingle.flatMap(directChatRepository::getOrCreateChatWithPeer);
contactRepository.getOrCreatePeer(accountId, contactJid)
.flatMapObservable(contactRepository::observePeer)
.subscribe(peer);
directChat.toObservable().compose(schedulers.executeUiSafeObservable()).subscribe(chat);
peer.compose(schedulers.executeUiSafeObservable())
.subscribe();
Observable<List<Message>> allMessagesObservable = directChat.flatMapObservable(messageRepository::observeMessages);
peer.filter(Optional::isPresent).map(Optional::getItem)
.flatMap(p -> directChatRepository.getOrCreateChatWithPeer(p).toObservable())
.subscribe(chat);
chat.compose(schedulers.executeUiSafeObservable()).subscribe();
Observable<List<Message>> allMessagesObservable = chat.flatMap(messageRepository::observeMessages);
messageQueryObservable.onNext(allMessagesObservable);
messages = Observable.switchOnNext(messageQueryObservable);
contactDisplayName = peer.filter(Optional::isPresent).map(Optional::getItem)
@ -77,9 +83,9 @@ public class ChatViewModel implements MercuryViewModel {
public void onQueryTextChanged(String query) {
if (query.trim().isEmpty()) {
messageQueryObservable.onNext(directChat.flatMapObservable(messageRepository::observeMessages));
messageQueryObservable.onNext(chat.flatMap(messageRepository::observeMessages));
} else {
messageQueryObservable.onNext(directChat.flatMapObservable(c -> messageRepository.findMessagesWithBody(c, query)));
messageQueryObservable.onNext(chat.flatMap(c -> messageRepository.findMessagesWithBody(c, query)));
}
}
@ -95,9 +101,10 @@ public class ChatViewModel implements MercuryViewModel {
}
public void sendMessage(String body) {
addDisposable(messenger.sendEncryptedMessage(peer.getValue().getItem(), body)
.compose(schedulers.executeUiSafeCompletable())
.subscribe(() -> LOGGER.log(Level.INFO, "Successfully sent encrypted message."),
e -> LOGGER.log(Level.SEVERE, "Error sending encrypted message.", e)));
addDisposable(messenger.getMessageConsignor(chat.getValue())
.sendTextMessage(chat.getValue(), body)
.compose(schedulers.executeUiSafeSingle())
.subscribe(messageId -> LOGGER.log(Level.INFO, "Successfully sent message."),
e -> LOGGER.log(Level.WARNING, "Error sending message.", e)));
}
}

View File

@ -0,0 +1,111 @@
package org.mercury_im.messenger.learning_tests.dagger;
import org.junit.Test;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import static junit.framework.TestCase.assertEquals;
public class DaggerTest {
@dagger.Component(modules = {SingletonModule.class})
@Singleton
public interface Component {
void inject(Consumer consumer);
}
@Module
public class NewInstanceModule {
private int DEPENDENCY_INDEX = 0;
private final String MODULE_NAME;
public NewInstanceModule(String moduleName) {
this.MODULE_NAME = moduleName;
}
@Provides
Dependency provideDependency() {
return new Dependency(DEPENDENCY_INDEX++, MODULE_NAME);
}
}
@Module
public class SingletonModule {
private int DEPENDENCY_INDEX = 0;
private final String MODULE_NAME;
public SingletonModule(String moduleName) {
this.MODULE_NAME = moduleName;
}
@Provides
@Singleton
Dependency provideDependency() {
return new Dependency(DEPENDENCY_INDEX++, MODULE_NAME);
}
}
public static class Dependency {
final int index;
final String moduleName;
@Inject
public Dependency(int index, String moduleName) {
this.index = index;
this.moduleName = moduleName;
}
public int getIndex() {
return index;
}
public String getModuleName() {
return moduleName;
}
}
public static class Consumer {
@Inject
Dependency dependency;
public Consumer() {
}
public Dependency getDependency() {
return dependency;
}
}
@Test
public void test() {
Component component0 = DaggerDaggerTest_Component.builder().singletonModule(new SingletonModule("First")).build();
Consumer consumer0 = new Consumer();
Consumer consumer1 = new Consumer();
component0.inject(consumer0);
component0.inject(consumer1);
Component component1 = DaggerDaggerTest_Component.builder().singletonModule(new SingletonModule("Second")).build();
Consumer consumer2 = new Consumer();
Consumer consumer3 = new Consumer();
component1.inject(consumer2);
component1.inject(consumer3);
assertEquals(0, consumer0.getDependency().getIndex());
assertEquals(1, consumer1.getDependency().getIndex());
assertEquals(0, consumer2.getDependency().getIndex());
assertEquals(1, consumer3.getDependency().getIndex());
}
}

View File

@ -0,0 +1,26 @@
package org.mercury_im.messenger.learning_tests.rx;
import org.junit.Test;
import io.reactivex.Observable;
import io.reactivex.subjects.BehaviorSubject;
import io.reactivex.subjects.Subject;
import static junit.framework.TestCase.assertNotNull;
public class BehaviourSubjectSubscriptionTest {
@Test
public void test() throws InterruptedException {
Observable<String> observable = Observable.just("One", "Two", "Three");
BehaviorSubject<String> behaviorSubject = BehaviorSubject.create();
observable.subscribe(behaviorSubject);
behaviorSubject.subscribe(System.out::println);
Thread.sleep(100);
String s = behaviorSubject.getValue();
assertNotNull(s);
}
}

View File

@ -1,5 +1,7 @@
package org.mercury_im.messenger.entity.chat;
import org.jxmpp.jid.EntityJid;
import org.jxmpp.jid.impl.JidCreate;
import org.mercury_im.messenger.entity.Account;
import java.util.UUID;
@ -15,11 +17,15 @@ import lombok.Data;
public abstract class Chat {
UUID id;
Account account;
ChatPreferences chatPreferences;
ChatPreferences chatPreferences = new ChatPreferences();
public Chat() {
this.id = UUID.randomUUID();
}
public abstract String getAddress();
public EntityJid getJid() {
return JidCreate.entityFromOrThrowUnchecked(getAddress());
}
}

View File

@ -1,5 +1,7 @@
package org.mercury_im.messenger.entity.chat;
import org.mercury_im.messenger.entity.Encryption;
import lombok.Data;
/**
@ -12,6 +14,7 @@ public class ChatPreferences {
boolean sendTypingNotificationsEnabled;
boolean readNotificationsSupported;
boolean sendReadNotificationsEnabled;
Encryption encryption = Encryption.plain; // TODO: Fix
@Data
public static class NotificationPreferences {

View File

@ -16,6 +16,7 @@ public class GroupChat extends Chat {
Set<Peer> participants;
String roomAddress;
String roomName;
String nickname = "NICK"; // TODO: Fix
@Override
public String getAddress() {

View File

@ -0,0 +1,8 @@
package org.mercury_im.messenger.entity.message;
public enum ChatMarkerState {
markable,
received,
displayed,
acknowledged
}

View File

@ -1,10 +1,9 @@
package org.mercury_im.messenger.entity.message;
import org.jxmpp.jid.EntityFullJid;
import org.jxmpp.jid.EntityJid;
import org.mercury_im.messenger.entity.Encryption;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import lombok.Data;
@ -13,8 +12,8 @@ import lombok.Data;
public class Message {
UUID id;
UUID chatId;
EntityFullJid sender;
EntityFullJid recipient;
EntityJid sender;
EntityJid recipient;
String body;
@ -34,6 +33,7 @@ public class Message {
Encryption encryption;
boolean received;
boolean read;
boolean pending;
public boolean isIncoming() {
return getDirection() == MessageDirection.incoming;

View File

@ -1,6 +1,6 @@
#Sun Sep 01 01:05:38 CEST 2019
#Mon Jul 27 15:17:47 CEST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip

@ -1 +1 @@
Subproject commit 1267729430f2c53f966b98febf16ef7f262b94d7
Subproject commit 30030941307262173ac7347e11235a8cadf68787

View File

@ -86,7 +86,7 @@ ext {
rxAndroidVersion = "2.1.1"
// Dagger 2
daggerVersion = '2.25.4'
daggerVersion = '2.28.3'
// Lombok
lombokVersion = '1.18.12'