Delete old core-old module
This commit is contained in:
parent
fc754aa076
commit
5e47b39c5e
|
@ -1 +0,0 @@
|
||||||
/build
|
|
|
@ -1,44 +0,0 @@
|
||||||
apply plugin: 'java-library'
|
|
||||||
|
|
||||||
// Add the generated folder to the source directories so that we can work with generated classes
|
|
||||||
// This is apparently necessary for use with requery.
|
|
||||||
sourceSets {
|
|
||||||
main.java.srcDirs += "${buildDir}/generated/sources/annotationProcessor/java/main/"
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
|
|
||||||
api project(':entity')
|
|
||||||
|
|
||||||
// Smack
|
|
||||||
// Not all of those are needed, but it may be a good idea to define those versions explicitly
|
|
||||||
api "org.igniterealtime.smack:smack-core:$smackCoreVersion"
|
|
||||||
api "org.igniterealtime.smack:smack-experimental:$smackExperimentalVersion"
|
|
||||||
api "org.igniterealtime.smack:smack-extensions:$smackExtensionsVersion"
|
|
||||||
api "org.igniterealtime.smack:smack-im:$smackImVersion"
|
|
||||||
api "org.igniterealtime.smack:smack-tcp:$smackTcpVersion"
|
|
||||||
|
|
||||||
// api "org.igniterealtime.smack:smack-omemo:$smackOmemoVersion"
|
|
||||||
// api "org.igniterealtime.smack:smack-omemo-signal:$smackOmemoSignalVersion"
|
|
||||||
// api "org.igniterealtime.smack:smack-openpgp:$smackOpenpgpVersion"
|
|
||||||
// api "org.igniterealtime.smack:smack-resolver-minidns:$smackResolverMiniDnsVersion"
|
|
||||||
|
|
||||||
|
|
||||||
// RxJava2
|
|
||||||
api "io.reactivex.rxjava2:rxjava:$rxJava2Version"
|
|
||||||
|
|
||||||
// Dagger 2 for dependency injection
|
|
||||||
implementation "com.google.dagger:dagger:$daggerVersion"
|
|
||||||
annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
|
|
||||||
|
|
||||||
// Requery ORM
|
|
||||||
api "io.requery:requery:$requeryVersion"
|
|
||||||
annotationProcessor "io.requery:requery-processor:$requeryVersion"
|
|
||||||
|
|
||||||
// JUnit for testing
|
|
||||||
testImplementation "junit:junit:$junitVersion"
|
|
||||||
compile project(path: ':data')
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceCompatibility = "8"
|
|
||||||
targetCompatibility = "8"
|
|
|
@ -1,18 +0,0 @@
|
||||||
package org.mercury_im.domain.data.util;
|
|
||||||
|
|
||||||
import org.mercury_im.messenger.entity.contact.Peer;
|
|
||||||
|
|
||||||
public class ContactNameUtil {
|
|
||||||
|
|
||||||
public static String displayableNameFrom(Peer contact) {
|
|
||||||
if (contact == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contact.getName() != null) {
|
|
||||||
return contact.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
return contact.getAddress();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package org.mercury_im.messenger.core;
|
|
||||||
|
|
||||||
|
|
||||||
import org.mercury_im.messenger.xmpp.util.ChatAndPossiblyContact;
|
|
||||||
|
|
||||||
public interface NotificationManager {
|
|
||||||
|
|
||||||
int chatMessageReceived(ChatAndPossiblyContact chatAndPossiblyContact, String body);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,245 +0,0 @@
|
||||||
package org.mercury_im.messenger.core.centers;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.AbstractXMPPConnection;
|
|
||||||
import org.jivesoftware.smack.SmackException;
|
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
|
||||||
import org.jivesoftware.smack.XMPPException;
|
|
||||||
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.mercury_im.messenger.core.connection.MercuryConfiguration;
|
|
||||||
import org.mercury_im.messenger.core.connection.MercuryConnection;
|
|
||||||
import org.mercury_im.messenger.xmpp.model.AccountModel;
|
|
||||||
import org.mercury_im.messenger.xmpp.model.ChatModel;
|
|
||||||
import org.mercury_im.messenger.xmpp.repository.RequeryAccountRepository;
|
|
||||||
import org.mercury_im.messenger.xmpp.repository.RosterRepository;
|
|
||||||
import org.mercury_im.messenger.core.stores.EntityCapsStore;
|
|
||||||
import org.mercury_im.messenger.core.stores.PlainMessageStore;
|
|
||||||
import org.mercury_im.messenger.core.stores.RosterStore;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
|
||||||
import io.reactivex.disposables.Disposable;
|
|
||||||
import io.reactivex.schedulers.Schedulers;
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
public class ConnectionCenter {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(ConnectionCenter.class.getName());
|
|
||||||
|
|
||||||
// Injected
|
|
||||||
private final RequeryAccountRepository accountRepository;
|
|
||||||
private final RosterRepository rosterRepository;
|
|
||||||
private final PlainMessageStore messageStore;
|
|
||||||
private final EntityCapsStore entityCapsStore;
|
|
||||||
|
|
||||||
// Connections
|
|
||||||
private final Map<Long, MercuryConnection> connectionMap =
|
|
||||||
Collections.synchronizedMap(new HashMap<>());
|
|
||||||
|
|
||||||
// Disposable for rx
|
|
||||||
private final CompositeDisposable disposable = new CompositeDisposable();
|
|
||||||
|
|
||||||
private final AtomicBoolean isConnectionCenterStarted = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public ConnectionCenter(EntityCapsStore capsStore,
|
|
||||||
PlainMessageStore messageStore,
|
|
||||||
RequeryAccountRepository accountRepository,
|
|
||||||
RosterRepository rosterRepository) {
|
|
||||||
LOGGER.log(Level.INFO, "ConnectionCenter initialized");
|
|
||||||
this.entityCapsStore = capsStore;
|
|
||||||
this.messageStore = messageStore;
|
|
||||||
this.accountRepository = accountRepository;
|
|
||||||
this.rosterRepository = rosterRepository;
|
|
||||||
|
|
||||||
EntityCapsManager.setPersistentCache(capsStore);
|
|
||||||
startUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start up the center by subscribing to changes of the {@link AccountModel accounts} in the
|
|
||||||
* database. For each new {@link AccountModel} it creates a {@link MercuryConnection} and
|
|
||||||
* stores it in the {@link #connectionMap}.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public synchronized void startUp() {
|
|
||||||
|
|
||||||
if (isConnectionCenterStarted.getAndSet(true)) {
|
|
||||||
// already started.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// otherwise subscribe to accounts and create connections.
|
|
||||||
Disposable allAccounts = accountRepository.getAll()
|
|
||||||
.observeOn(Schedulers.newThread())
|
|
||||||
.subscribe(accounts -> {
|
|
||||||
LOGGER.log(Level.INFO, "Accounts changed.");
|
|
||||||
Set<Long> accountIds = new HashSet<>();
|
|
||||||
|
|
||||||
// Add missing connections to the map
|
|
||||||
for (AccountModel account : accounts.toList()) {
|
|
||||||
accountIds.add(account.getId());
|
|
||||||
if (connectionMap.get(account.getId()) != null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOGGER.log(Level.INFO, "Add new connection " + account.getJid().toString() + " to ConnectionCenter list.");
|
|
||||||
MercuryConnection connection = createConnection(account);
|
|
||||||
connectionMap.put(account.getId(), connection);
|
|
||||||
|
|
||||||
// initialize new connection
|
|
||||||
initializeConnection(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove unwanted connections from the map
|
|
||||||
for (long connectionId : connectionMap.keySet()) {
|
|
||||||
if (!accountIds.contains(connectionId)) {
|
|
||||||
LOGGER.log(Level.INFO, "Connection " + connectionId + " was deleted.");
|
|
||||||
AbstractXMPPConnection con =
|
|
||||||
(AbstractXMPPConnection) connectionMap.get(connectionId).getConnection();
|
|
||||||
con.disconnect();
|
|
||||||
connectionMap.remove(connectionId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (AccountModel account : accounts) {
|
|
||||||
MercuryConnection connection = connectionMap.get(account.getId());
|
|
||||||
if (account.isEnabled()) {
|
|
||||||
if (connection.getConnection().isConnected()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
LOGGER.log(Level.INFO, "Connecting connection " + account.getId() + " (" + account.getJid().toString() + ")");
|
|
||||||
connection.connect();
|
|
||||||
LOGGER.log(Level.INFO, "Connected!");
|
|
||||||
} else {
|
|
||||||
if (!connection.getConnection().isConnected()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
LOGGER.log(Level.INFO, "Account " + account.getJid() + " (" + account.getJid().toString() + ") not enabled. Disable.");
|
|
||||||
connection.disconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
disposable.add(allAccounts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MercuryConnection getConnection(AccountModel account) {
|
|
||||||
return getConnection(account.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
public MercuryConnection getConnection(long accountId) {
|
|
||||||
return connectionMap.get(accountId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void putConnection(MercuryConnection connection) {
|
|
||||||
connectionMap.put(connection.getAccountId(), connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new {@link MercuryConnection} with an underlying {@link XMPPTCPConnection}
|
|
||||||
* from the credentials of an {@link AccountModel}.
|
|
||||||
* The new connection will not be connected.
|
|
||||||
*
|
|
||||||
* @param accountModel accountModel
|
|
||||||
*
|
|
||||||
* @return disconnected mercury connection
|
|
||||||
*/
|
|
||||||
public MercuryConnection createConnection(AccountModel accountModel) {
|
|
||||||
LOGGER.log(Level.INFO, "Create Connection for " + accountModel.getJid().toString());
|
|
||||||
XMPPTCPConnectionConfiguration configuration = XMPPTCPConnectionConfiguration.builder()
|
|
||||||
.setHost(accountModel.getJid().getDomain().toString())
|
|
||||||
.setXmppAddressAndPassword(accountModel.getJid(), accountModel.getPassword())
|
|
||||||
.setConnectTimeout(2 * 60 * 1000)
|
|
||||||
.setEnabledSSLCiphers(MercuryConfiguration.enabledCiphers)
|
|
||||||
.setEnabledSSLProtocols(MercuryConfiguration.enabledProtocols)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
AbstractXMPPConnection tcpConnection = new XMPPTCPConnection(configuration);
|
|
||||||
return new MercuryConnection(tcpConnection, accountModel.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initializeConnection(MercuryConnection connection) {
|
|
||||||
// Register roster store
|
|
||||||
RosterStore rosterStore = new RosterStore(rosterRepository, accountRepository);
|
|
||||||
rosterStore.setAccountId(connection.getAccountId());
|
|
||||||
rosterStore.subscribe();
|
|
||||||
connection.getRoster().setRosterStore(rosterStore);
|
|
||||||
|
|
||||||
// Register message store
|
|
||||||
messageStore.registerForMercuryConnection(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set Client State Indication status to active.
|
|
||||||
*
|
|
||||||
* @see <a href="https://xmpp.org/extensions/xep-0352.html">XEP-0352: Client State Indication</a>
|
|
||||||
*/
|
|
||||||
public void clientStateActive() {
|
|
||||||
LOGGER.log(Level.INFO, "CSI: App is going to foreground -> active");
|
|
||||||
for (MercuryConnection mercuryConnection : connectionMap.values()) {
|
|
||||||
XMPPConnection connection = mercuryConnection.getConnection();
|
|
||||||
if (connection.isConnected() && ClientStateIndicationManager.isSupported(connection)) {
|
|
||||||
try {
|
|
||||||
ClientStateIndicationManager.active(mercuryConnection.getConnection());
|
|
||||||
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set Client State Indication status to inactive.
|
|
||||||
*
|
|
||||||
* @see <a href="https://xmpp.org/extensions/xep-0352.html">XEP-0352: Client State Indication</a>
|
|
||||||
*/
|
|
||||||
public void clientStateInactive() {
|
|
||||||
LOGGER.log(Level.INFO, "CSI: App is going to background -> inactive");
|
|
||||||
for (MercuryConnection mercuryConnection : connectionMap.values()) {
|
|
||||||
XMPPConnection connection = mercuryConnection.getConnection();
|
|
||||||
if (connection.isConnected() && ClientStateIndicationManager.isSupported(connection)) {
|
|
||||||
try {
|
|
||||||
ClientStateIndicationManager.inactive(connection);
|
|
||||||
} catch (SmackException.NotConnectedException | InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void requestMamMessagesFor(ChatModel chat) {
|
|
||||||
|
|
||||||
MercuryConnection connection = connectionMap.get(chat.getPeer().getAccount().getId());
|
|
||||||
if (connection == null) return;
|
|
||||||
MamManager mamManager = MamManager.getInstanceFor(connection.getConnection());
|
|
||||||
MamManager.MamQuery query;
|
|
||||||
//if (chat.getEarliestMamMessageId() == null) {
|
|
||||||
try {
|
|
||||||
query = mamManager.queryMostRecentPage(chat.getPeer().getJid(), 100);
|
|
||||||
messageStore.handleMamResult(chat.getPeer().getAccount().getId(), chat.getPeer().getJid(), query);
|
|
||||||
} catch (SmackException.NoResponseException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NotLoggedInException | InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
//} else {
|
|
||||||
//MamManager.MamQueryArgs queryArgs = MamManager.MamQueryArgs.builder()
|
|
||||||
// .beforeUid()
|
|
||||||
// .build();
|
|
||||||
//query = mamManager.queryArchive()
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
package org.mercury_im.messenger.core.centers;
|
|
||||||
|
|
||||||
public class ContactCenter {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
package org.mercury_im.messenger.core.centers;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class MessageCenter {
|
|
||||||
|
|
||||||
private final ConnectionCenter connectionCenter;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public MessageCenter(ConnectionCenter connectionCenter) {
|
|
||||||
this.connectionCenter = connectionCenter;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
package org.mercury_im.messenger.core.connection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@link MercuryConnection} modeled as a finite state machine.
|
|
||||||
* Below enums represent the states of the machine.
|
|
||||||
*/
|
|
||||||
public enum ConnectionState {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connection is disconnected.
|
|
||||||
* This is the initial state of the machine.
|
|
||||||
*/
|
|
||||||
DISCONNECTED,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The connection is in the process of connecting to the server.
|
|
||||||
* This state can be reached by issuing a connect() call from within the {@link #DISCONNECTED}
|
|
||||||
* state.
|
|
||||||
*/
|
|
||||||
CONNECTING,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The connection is successfully connected to the server and the stream has been initiated.
|
|
||||||
*/
|
|
||||||
CONNECTED,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* THe connection is authenticated.
|
|
||||||
* In this state the connection is ready to send and receive stanzas.
|
|
||||||
*/
|
|
||||||
AUTHENTICATED,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The connection is in the process of shutting down.
|
|
||||||
*/
|
|
||||||
DISCONNECTING,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The device doesn't have usable network connectivity.
|
|
||||||
*/
|
|
||||||
WAITING_FOR_NETWORK,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The connection already (unsuccessfully) tried to connect, but failed due to lack of network
|
|
||||||
* connectivity and is now waiting to retry connecting.
|
|
||||||
*/
|
|
||||||
WAITING_FOR_RETRY
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
package org.mercury_im.messenger.core.connection;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.SmackConfiguration;
|
|
||||||
|
|
||||||
public class MercuryConfiguration {
|
|
||||||
|
|
||||||
static {
|
|
||||||
SmackConfiguration.DEBUG = true;
|
|
||||||
// Make sure Smack is initialized.
|
|
||||||
SmackConfiguration.getVersion();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final String[] enabledCiphers = {
|
|
||||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
|
||||||
"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
|
|
||||||
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
|
|
||||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
|
|
||||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
|
||||||
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
|
|
||||||
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
|
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
|
||||||
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
|
|
||||||
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA"
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final String[] enabledProtocols = {
|
|
||||||
"TLSv1.2",
|
|
||||||
"TLSv1.3"
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,139 +0,0 @@
|
||||||
package org.mercury_im.messenger.core.connection;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.AbstractXMPPConnection;
|
|
||||||
import org.jivesoftware.smack.ConnectionListener;
|
|
||||||
import org.jivesoftware.smack.ReconnectionManager;
|
|
||||||
import org.jivesoftware.smack.SmackException;
|
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
|
||||||
import org.jivesoftware.smack.XMPPException;
|
|
||||||
import org.jivesoftware.smack.chat2.ChatManager;
|
|
||||||
import org.jivesoftware.smack.packet.Presence;
|
|
||||||
import org.jivesoftware.smack.roster.Roster;
|
|
||||||
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;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import io.reactivex.subjects.BehaviorSubject;
|
|
||||||
|
|
||||||
public class MercuryConnection {
|
|
||||||
|
|
||||||
public static final String TAG = "Mercury";
|
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(MercuryConnection.class.getName());
|
|
||||||
|
|
||||||
private final AbstractXMPPConnection mConnection;
|
|
||||||
private final long mAccountId;
|
|
||||||
|
|
||||||
// Managers
|
|
||||||
private final ReconnectionManager mReconnectionManager;
|
|
||||||
private final Roster mRoster;
|
|
||||||
private final ChatManager mChatManager;
|
|
||||||
private final CarbonManager mCarbonManager;
|
|
||||||
private final StableUniqueStanzaIdManager mStanzaIdManager;
|
|
||||||
private final ServiceDiscoveryManager mServiceDiscoveryManager;
|
|
||||||
private final MamManager mMamManager;
|
|
||||||
private final VersionManager mVersionManager;
|
|
||||||
|
|
||||||
private BehaviorSubject<ConnectionState> mState = BehaviorSubject.createDefault(ConnectionState.DISCONNECTED);
|
|
||||||
|
|
||||||
|
|
||||||
public MercuryConnection(AbstractXMPPConnection connection, long accountId) {
|
|
||||||
mConnection = connection;
|
|
||||||
mConnection.addConnectionListener(mConnectionListener);
|
|
||||||
mAccountId = accountId;
|
|
||||||
|
|
||||||
mReconnectionManager = ReconnectionManager.getInstanceFor(connection);
|
|
||||||
mReconnectionManager.enableAutomaticReconnection();
|
|
||||||
mReconnectionManager.abortPossiblyRunningReconnection();
|
|
||||||
|
|
||||||
mRoster = Roster.getInstanceFor(connection);
|
|
||||||
mRoster.setRosterLoadedAtLogin(true);
|
|
||||||
|
|
||||||
mChatManager = ChatManager.getInstanceFor(connection);
|
|
||||||
mCarbonManager = CarbonManager.getInstanceFor(connection);
|
|
||||||
|
|
||||||
mStanzaIdManager = StableUniqueStanzaIdManager.getInstanceFor(connection);
|
|
||||||
mStanzaIdManager.enable();
|
|
||||||
|
|
||||||
mServiceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
|
|
||||||
mServiceDiscoveryManager.setIdentity(new DiscoverInfo.Identity("client", "Mercury", "phone"));
|
|
||||||
|
|
||||||
mVersionManager = VersionManager.getInstanceFor(connection);
|
|
||||||
mVersionManager.setVersion("Mercury", "0.0.1-stealth", "Android");
|
|
||||||
VersionManager.setAutoAppendSmackVersion(false);
|
|
||||||
|
|
||||||
mMamManager = MamManager.getInstanceFor(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void connect() throws InterruptedException, XMPPException, SmackException, IOException {
|
|
||||||
LOGGER.log(Level.INFO, "Connecting...");
|
|
||||||
mState.onNext(ConnectionState.CONNECTING);
|
|
||||||
AbstractXMPPConnection con = (AbstractXMPPConnection) getConnection();
|
|
||||||
|
|
||||||
con.connect().login();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void disconnect() throws SmackException.NotConnectedException {
|
|
||||||
AbstractXMPPConnection con = (AbstractXMPPConnection) getConnection();
|
|
||||||
mState.onNext(ConnectionState.DISCONNECTING);
|
|
||||||
|
|
||||||
con.disconnect(new Presence(Presence.Type.unavailable));
|
|
||||||
|
|
||||||
mState.onNext(ConnectionState.DISCONNECTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
public XMPPConnection getConnection() {
|
|
||||||
return mConnection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getAccountId() {
|
|
||||||
return mAccountId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Roster getRoster() {
|
|
||||||
return mRoster;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private final ConnectionListener mConnectionListener = new ConnectionListener() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void connected(XMPPConnection connection) {
|
|
||||||
mState.onNext(ConnectionState.CONNECTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void authenticated(XMPPConnection connection, boolean resumed) {
|
|
||||||
mState.onNext(ConnectionState.AUTHENTICATED);
|
|
||||||
LOGGER.info("Connection " + getAccountId() + " authenticated (" + (resumed ? "resumed" : "initially") + ")");
|
|
||||||
if (!resumed) {
|
|
||||||
LOGGER.info("Enabling carbons!");
|
|
||||||
mCarbonManager.enableCarbonsAsync(exception -> {
|
|
||||||
LOGGER.severe("Could not enable carbons for connection " + mAccountId + ".");
|
|
||||||
exception.printStackTrace();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void connectionClosed() {
|
|
||||||
mState.onNext(ConnectionState.DISCONNECTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void connectionClosedOnError(Exception e) {
|
|
||||||
mState.onNext(ConnectionState.DISCONNECTED);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public BehaviorSubject<ConnectionState> getState() {
|
|
||||||
return mState;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
package org.mercury_im.messenger.core.di;
|
|
||||||
|
|
||||||
import org.mercury_im.messenger.core.NotificationManager;
|
|
||||||
import org.mercury_im.messenger.core.centers.ConnectionCenter;
|
|
||||||
import org.mercury_im.messenger.core.stores.EntityCapsStore;
|
|
||||||
import org.mercury_im.messenger.core.stores.PlainMessageStore;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
@Module
|
|
||||||
public class CenterModule {
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
static ConnectionCenter provideConnectionCenter(EntityCapsStore capsStore,
|
|
||||||
PlainMessageStore messageStore,
|
|
||||||
AccountRepository accountRepository,
|
|
||||||
RosterRepository rosterRepository) {
|
|
||||||
return new ConnectionCenter(capsStore, messageStore, accountRepository, rosterRepository);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
static EntityCapsStore providerEntityCapsStore(EntityCapsRepository entityCapsRepository) {
|
|
||||||
return new EntityCapsStore(entityCapsRepository);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Provides
|
|
||||||
static PlainMessageStore provideMessageStore(RosterRepository rosterRepository,
|
|
||||||
ChatRepository chatRepository,
|
|
||||||
MessageRepository messageRepository,
|
|
||||||
NotificationManager notificationManager) {
|
|
||||||
return new PlainMessageStore(rosterRepository, chatRepository, messageRepository, notificationManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
package org.mercury_im.messenger.core.di;
|
|
||||||
|
|
||||||
import org.mercury_im.messenger.xmpp.di.RequeryModule;
|
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.Component;
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
@Component(modules = {
|
|
||||||
CenterModule.class,
|
|
||||||
RequeryModule.class
|
|
||||||
})
|
|
||||||
public interface XmppComponent {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
package org.mercury_im.messenger.core.stores;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.PacketParserUtils;
|
|
||||||
import org.jivesoftware.smack.xml.XmlPullParser;
|
|
||||||
import org.jivesoftware.smackx.caps.cache.EntityCapsPersistentCache;
|
|
||||||
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
|
||||||
import org.mercury_im.messenger.data.model.EntityCapsModel;
|
|
||||||
import org.mercury_im.messenger.data.repository.XmppEntityCapsRepository;
|
|
||||||
|
|
||||||
|
|
||||||
import java.io.StringReader;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
|
||||||
|
|
||||||
public class EntityCapsStore implements EntityCapsPersistentCache {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(EntityCapsStore.class.getName());
|
|
||||||
|
|
||||||
private final XmppEntityCapsRepository entityCapsRepository;
|
|
||||||
private final Map<String, DiscoverInfo> discoverInfoMap = new HashMap<>();
|
|
||||||
|
|
||||||
private final CompositeDisposable disposable = new CompositeDisposable();
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public EntityCapsStore(XmppEntityCapsRepository entityCapsRepository) {
|
|
||||||
this.entityCapsRepository = entityCapsRepository;
|
|
||||||
populateFromDatabase();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Since nodeVers are - if ever - only deleted all at once but added one by one and never
|
|
||||||
* modified, we can simply determine the set of newly added nodeVers, process those and add
|
|
||||||
* them to the database.
|
|
||||||
*/
|
|
||||||
private void populateFromDatabase() {
|
|
||||||
disposable.add(entityCapsRepository.getAll()
|
|
||||||
.subscribe(
|
|
||||||
entityCapsModels -> {
|
|
||||||
Map<String, EntityCapsModel> nextEntityCaps = entityCapsModels.toMap(EntityCapsModel.NODE_VER);
|
|
||||||
|
|
||||||
// New set of nodeVers
|
|
||||||
Set<String> nextKeys = nextEntityCaps.keySet();
|
|
||||||
// Old set of nodeVers
|
|
||||||
Set<String> previousKeys = discoverInfoMap.keySet();
|
|
||||||
|
|
||||||
// Added nodeVers
|
|
||||||
nextKeys.removeAll(previousKeys);
|
|
||||||
|
|
||||||
for (String key : nextKeys) {
|
|
||||||
// Only add new items. Items itself cannot change, so we don't have to deal
|
|
||||||
// with changed items.
|
|
||||||
EntityCapsModel addedModel = nextEntityCaps.get(key);
|
|
||||||
DiscoverInfo info;
|
|
||||||
try {
|
|
||||||
XmlPullParser parser = PacketParserUtils.getParserFor(new StringReader(addedModel.getXml()));
|
|
||||||
info = (DiscoverInfo) PacketParserUtils.parseIQ(parser);
|
|
||||||
discoverInfoMap.put(addedModel.getNodeVer(), info);
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOGGER.log(Level.SEVERE, "Error parsing EntityCaps: ", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error -> LOGGER.log(Level.WARNING, "An error occurred while updating the EntityCaps cache.", error)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addDiscoverInfoByNodePersistent(String nodeVer, DiscoverInfo info) {
|
|
||||||
EntityCapsModel model = new EntityCapsModel();
|
|
||||||
model.setNodeVer(nodeVer);
|
|
||||||
CharSequence xml = info.toXML();
|
|
||||||
String string = xml.toString();
|
|
||||||
model.setXml(string);
|
|
||||||
disposable.add(entityCapsRepository.upsert(model).subscribe(
|
|
||||||
success -> LOGGER.log(Level.FINE, "Upserted EntityCaps model " + success),
|
|
||||||
error -> LOGGER.log(Level.WARNING, "An error occurred upserting EntityCaps model", error)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DiscoverInfo lookup(String nodeVer) {
|
|
||||||
LOGGER.log(Level.FINE, "Looking up caps for " + nodeVer + " in cache...");
|
|
||||||
DiscoverInfo info = discoverInfoMap.get(nodeVer);
|
|
||||||
LOGGER.log(Level.FINE, "Entry found: " + (info != null ? info.toXML().toString() : "null"));
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void emptyCache() {
|
|
||||||
disposable.add(entityCapsRepository.deleteAll().subscribe(
|
|
||||||
success -> LOGGER.log(Level.FINE, "EntityCaps table cleared successfully."),
|
|
||||||
error -> LOGGER.log(Level.WARNING, "An error occurred while clearing EntityCaps table.", error)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,202 +0,0 @@
|
||||||
package org.mercury_im.messenger.core.stores;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.chat2.Chat;
|
|
||||||
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.DelayInformationManager;
|
|
||||||
import org.jivesoftware.smackx.delay.packet.DelayInformation;
|
|
||||||
import org.jivesoftware.smackx.mam.MamManager;
|
|
||||||
import org.jivesoftware.smackx.sid.element.OriginIdElement;
|
|
||||||
import org.jivesoftware.smackx.sid.element.StanzaIdElement;
|
|
||||||
import org.jxmpp.jid.EntityBareJid;
|
|
||||||
import org.jxmpp.jid.impl.JidCreate;
|
|
||||||
import org.mercury_im.messenger.core.NotificationManager;
|
|
||||||
import org.mercury_im.messenger.core.connection.MercuryConnection;
|
|
||||||
import org.mercury_im.messenger.xmpp.model.ChatModel;
|
|
||||||
import org.mercury_im.messenger.xmpp.model.ContactModel;
|
|
||||||
import org.mercury_im.messenger.xmpp.model.EntityModel;
|
|
||||||
import org.mercury_im.messenger.xmpp.model.LastChatMessageRelation;
|
|
||||||
import org.mercury_im.messenger.xmpp.model.MessageModel;
|
|
||||||
import org.mercury_im.messenger.xmpp.repository.ChatRepository;
|
|
||||||
import org.mercury_im.messenger.xmpp.repository.MessageRepository;
|
|
||||||
import org.mercury_im.messenger.xmpp.repository.RosterRepository;
|
|
||||||
import org.mercury_im.messenger.xmpp.util.ChatAndPossiblyContact;
|
|
||||||
|
|
||||||
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.Completable;
|
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
|
||||||
import io.reactivex.schedulers.Schedulers;
|
|
||||||
|
|
||||||
public class PlainMessageStore {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(PlainMessageStore.class.getName());
|
|
||||||
private static final CompositeDisposable disposable = new CompositeDisposable();
|
|
||||||
|
|
||||||
private final RosterRepository rosterRepository;
|
|
||||||
private final ChatRepository chatRepository;
|
|
||||||
private final MessageRepository messageRepository;
|
|
||||||
|
|
||||||
private final NotificationManager notificationManager;
|
|
||||||
|
|
||||||
public PlainMessageStore(RosterRepository rosterRepository, ChatRepository chatRepository, MessageRepository messageRepository, NotificationManager notificationManager) {
|
|
||||||
this.rosterRepository = rosterRepository;
|
|
||||||
this.chatRepository = chatRepository;
|
|
||||||
this.messageRepository = messageRepository;
|
|
||||||
this.notificationManager = notificationManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handleIncomingMessage(long accountId, EntityBareJid from, Message message, Chat chat) {
|
|
||||||
if (message.getBody() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Completable.fromAction(() -> {
|
|
||||||
EntityModel entityModel = rosterRepository.getOrCreateEntity(accountId, from)
|
|
||||||
.blockingGet();
|
|
||||||
ContactModel contactModel = rosterRepository.getContact(accountId, entityModel.getJid()).blockingFirst().firstOrNull();
|
|
||||||
ChatModel chatModel = chatRepository.getChatWith(entityModel).blockingFirst().firstOr(() -> {
|
|
||||||
ChatModel freshChatModel = new ChatModel();
|
|
||||||
freshChatModel.setPeer(entityModel);
|
|
||||||
freshChatModel.setDisplayed(true);
|
|
||||||
return freshChatModel;
|
|
||||||
});
|
|
||||||
|
|
||||||
chatModel = chatRepository.upsert(chatModel).blockingGet();
|
|
||||||
|
|
||||||
MessageModel messageModel = setCommonMessageAttributes(message, chatModel);
|
|
||||||
messageModel.setSender(from);
|
|
||||||
messageModel.setIncoming(true);
|
|
||||||
|
|
||||||
final ChatModel fChatModel = chatModel;
|
|
||||||
disposable.add(messageRepository.insert(messageModel)
|
|
||||||
.subscribe(insertedMessageModel -> {
|
|
||||||
if (message.getBody() != null) {
|
|
||||||
notificationManager.chatMessageReceived(new ChatAndPossiblyContact(fChatModel, contactModel), message.getBody());
|
|
||||||
}
|
|
||||||
|
|
||||||
LastChatMessageRelation lastMessage = new LastChatMessageRelation();
|
|
||||||
lastMessage.setChat(fChatModel);
|
|
||||||
lastMessage.setMessage(insertedMessageModel);
|
|
||||||
}));
|
|
||||||
}).subscribeOn(Schedulers.io())
|
|
||||||
.subscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handleOutgoingMessage(long accountId, EntityBareJid to, Message message, Chat chat) {
|
|
||||||
MessageModel model = setCommonMessageAttributes(message, null);
|
|
||||||
EntityModel entityModel = rosterRepository.getOrCreateEntity(accountId, to).blockingGet();
|
|
||||||
|
|
||||||
model.setIncoming(false);
|
|
||||||
model.setTimestamp(new Date());
|
|
||||||
model.setSender(entityModel.getAccount().getJid());
|
|
||||||
model.setRecipient(to);
|
|
||||||
|
|
||||||
ChatModel chatModel = chatRepository.getChatWith(entityModel).blockingFirst().firstOr(() -> {
|
|
||||||
ChatModel freshChatModel = new ChatModel();
|
|
||||||
freshChatModel.setPeer(entityModel);
|
|
||||||
freshChatModel.setDisplayed(true);
|
|
||||||
return freshChatModel;
|
|
||||||
});
|
|
||||||
|
|
||||||
model.setChat(chatModel);
|
|
||||||
|
|
||||||
disposable.add(messageRepository.upsert(model)
|
|
||||||
.subscribe(messageId ->
|
|
||||||
LOGGER.log(Level.INFO, "Inserted outgoing message " + messageId)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handleCarbonCopy(long accountId, CarbonExtension.Direction direction, Message carbonCopy, Message wrappingMessage) {
|
|
||||||
if (carbonCopy.getBody() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
MessageModel messageModel = new MessageModel();
|
|
||||||
messageModel.setSender(carbonCopy.getFrom() != null ? carbonCopy.getFrom().asEntityBareJidIfPossible() : null);
|
|
||||||
messageModel.setRecipient(carbonCopy.getTo() != null ? carbonCopy.getTo().asEntityBareJidIfPossible() : null);
|
|
||||||
|
|
||||||
messageModel.setIncoming(direction == CarbonExtension.Direction.received);
|
|
||||||
|
|
||||||
messageModel.setBody(carbonCopy.getBody());
|
|
||||||
messageModel.setTimestamp(new Date());
|
|
||||||
|
|
||||||
disposable.add(messageRepository.upsert(messageModel)
|
|
||||||
.subscribe(messageId ->
|
|
||||||
LOGGER.log(Level.INFO, "Inserted carbon message " + messageId)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void registerForMercuryConnection(MercuryConnection connection) {
|
|
||||||
ChatManager chatManager = ChatManager.getInstanceFor(connection.getConnection());
|
|
||||||
CarbonManager carbonManager = CarbonManager.getInstanceFor(connection.getConnection());
|
|
||||||
|
|
||||||
// Add account ID to
|
|
||||||
chatManager.addIncomingListener((from, message, chat) ->
|
|
||||||
PlainMessageStore.this.handleIncomingMessage(
|
|
||||||
connection.getAccountId(), from, message, chat));
|
|
||||||
|
|
||||||
chatManager.addOutgoingListener((to, message, chat) ->
|
|
||||||
PlainMessageStore.this.handleOutgoingMessage(
|
|
||||||
connection.getAccountId(), to, message, chat));
|
|
||||||
|
|
||||||
carbonManager.addCarbonCopyReceivedListener((direction, carbonCopy, wrappingMessage) ->
|
|
||||||
PlainMessageStore.this.handleCarbonCopy(
|
|
||||||
connection.getAccountId(), direction, carbonCopy, wrappingMessage));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dispose() {
|
|
||||||
disposable.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handleMamResult(long accountId, EntityBareJid peerJid, MamManager.MamQuery query) {
|
|
||||||
List<MessageModel> messageModels = new ArrayList<>();
|
|
||||||
for (Message message : query.getMessages()) {
|
|
||||||
Date date = new Date();
|
|
||||||
DelayInformation delay = DelayInformation.from(message);
|
|
||||||
if (delay != null) {
|
|
||||||
date = delay.getStamp();
|
|
||||||
}
|
|
||||||
|
|
||||||
MessageModel messageModel = new MessageModel();
|
|
||||||
messageModel.setBody(message.getBody());
|
|
||||||
messageModel.setSender(message.getFrom().asEntityBareJidOrThrow());
|
|
||||||
messageModel.setRecipient(message.getTo().asEntityBareJidOrThrow());
|
|
||||||
messageModel.setIncoming(peerJid.equals(message.getFrom().asEntityBareJidOrThrow()));
|
|
||||||
messageModel.setTimestamp(date);
|
|
||||||
messageModels.add(messageModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
disposable.add(messageRepository.upsert(messageModels).subscribe());
|
|
||||||
}
|
|
||||||
|
|
||||||
private MessageModel incomingMessageToModel(Message message, ChatModel chat) {
|
|
||||||
MessageModel model = setCommonMessageAttributes(message, chat);
|
|
||||||
model.setIncoming(true);
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MessageModel setCommonMessageAttributes(Message message, ChatModel chat) {
|
|
||||||
MessageModel model = new MessageModel();
|
|
||||||
|
|
||||||
model.setBody(message.getBody());
|
|
||||||
Date timestamp = DelayInformationManager.getDelayTimestamp(message);
|
|
||||||
model.setTimestamp(timestamp == null ? new Date() : timestamp);
|
|
||||||
model.setThread(message.getThread());
|
|
||||||
model.setLegacyId(message.getStanzaId());
|
|
||||||
model.setChat(chat);
|
|
||||||
model.setRecipient(message.getTo().asEntityBareJidOrThrow());
|
|
||||||
model.setSender(message.getFrom() != null ? message.getFrom().asEntityBareJidIfPossible() : null);
|
|
||||||
OriginIdElement originId = OriginIdElement.getOriginId(message);
|
|
||||||
model.setOriginId(originId != null ? originId.getId() : null);
|
|
||||||
StanzaIdElement stanzaId = StanzaIdElement.getStanzaId(message);
|
|
||||||
model.setStanzaId(stanzaId != null ? stanzaId.getId() : null);
|
|
||||||
model.setStanzaIdBy(stanzaId != null ? JidCreate.entityBareFromOrThrowUnchecked(stanzaId.getBy()) : null);
|
|
||||||
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,217 +0,0 @@
|
||||||
package org.mercury_im.messenger.core.stores;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.roster.packet.RosterPacket;
|
|
||||||
import org.jxmpp.jid.Jid;
|
|
||||||
import org.mercury_im.messenger.xmpp.model.AccountModel;
|
|
||||||
import org.mercury_im.messenger.xmpp.model.ContactModel;
|
|
||||||
import org.mercury_im.messenger.xmpp.model.EntityModel;
|
|
||||||
import org.mercury_im.messenger.xmpp.repository.RequeryAccountRepository;
|
|
||||||
import org.mercury_im.messenger.xmpp.repository.RosterRepository;
|
|
||||||
import org.mercury_im.messenger.xmpp.enums.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 javax.inject.Inject;
|
|
||||||
|
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
|
||||||
import io.reactivex.schedulers.Schedulers;
|
|
||||||
|
|
||||||
public class RosterStore implements org.jivesoftware.smack.roster.rosterstore.RosterStore {
|
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(RosterStore.class.getName());
|
|
||||||
|
|
||||||
private final RosterRepository rosterRepository;
|
|
||||||
private final RequeryAccountRepository accountRepository;
|
|
||||||
private AccountModel account;
|
|
||||||
private CompositeDisposable disposable = null;
|
|
||||||
|
|
||||||
private final Map<Jid, RosterPacket.Item> itemMap = new HashMap<>();
|
|
||||||
private String rosterVersion;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public RosterStore(RosterRepository rosterRepository, RequeryAccountRepository accountRepository) {
|
|
||||||
this.rosterRepository = rosterRepository;
|
|
||||||
this.accountRepository = accountRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void subscribe() {
|
|
||||||
LOGGER.log(Level.INFO, "Subscribing...");
|
|
||||||
if (disposable != null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
disposable = new CompositeDisposable();
|
|
||||||
|
|
||||||
disposable.add(rosterRepository.getAllContactsOfAccount(account)
|
|
||||||
.observeOn(Schedulers.computation())
|
|
||||||
.subscribe(contactsList -> {
|
|
||||||
itemMap.clear();
|
|
||||||
for (ContactModel contactModel : contactsList) {
|
|
||||||
itemMap.put(contactModel.getEntity().getJid(), fromModel(contactModel));
|
|
||||||
LOGGER.log(Level.INFO, "Populate itemMap with " + contactsList.toList().size() + " items");
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error -> LOGGER.log(Level.WARNING, "An error occurred while updating roster cache", error)));
|
|
||||||
|
|
||||||
disposable.add(rosterRepository.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() {
|
|
||||||
if (disposable == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
disposable.dispose();
|
|
||||||
disposable = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAccountId(long accountId) {
|
|
||||||
this.account = accountRepository.getAccount(accountId)
|
|
||||||
.doOnSubscribe(subscribe -> LOGGER.log(Level.FINE, "Fetching account " + accountId))
|
|
||||||
.blockingFirst().first();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setRosterVersion(String rosterVersion) {
|
|
||||||
this.rosterVersion = rosterVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<RosterPacket.Item> 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
|
|
||||||
ContactModel contact = toModel(item);
|
|
||||||
disposable.add(rosterRepository.upsertContact(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(rosterRepository.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<RosterPacket.Item> items, String version) {
|
|
||||||
LOGGER.log(Level.INFO, "Reset Entries: " + Arrays.toString(items.toArray()));
|
|
||||||
// Update database
|
|
||||||
// TODO: Delete other contacts
|
|
||||||
for (RosterPacket.Item item : items) {
|
|
||||||
ContactModel model = toModel(item);
|
|
||||||
disposable.add(rosterRepository.upsertContact(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(rosterRepository.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(rosterRepository.deleteContact(account.getId(), bareJid.asEntityBareJidOrThrow())
|
|
||||||
.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(rosterRepository.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(rosterRepository.deleteAllContactsOfAccount(account)
|
|
||||||
.subscribe(
|
|
||||||
success -> LOGGER.log(Level.FINE, "Successfully reset store."),
|
|
||||||
error -> LOGGER.log(Level.WARNING, "An error occurred resetting store", error)
|
|
||||||
));
|
|
||||||
disposable.add(rosterRepository.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 fromModel(ContactModel contactModel) {
|
|
||||||
RosterPacket.Item item = new RosterPacket.Item(
|
|
||||||
contactModel.getEntity().getJid(),
|
|
||||||
contactModel.getRostername());
|
|
||||||
if (contactModel.getSub_direction() != null) {
|
|
||||||
item.setItemType(convert(contactModel.getSub_direction()));
|
|
||||||
}
|
|
||||||
item.setApproved(contactModel.isSub_approved());
|
|
||||||
item.setSubscriptionPending(contactModel.isSub_pending());
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContactModel toModel(RosterPacket.Item item) {
|
|
||||||
ContactModel contact = new ContactModel();
|
|
||||||
|
|
||||||
contact.setRostername(item.getName());
|
|
||||||
if (item.getItemType() != null) {
|
|
||||||
contact.setSub_direction(convert(item.getItemType()));
|
|
||||||
}
|
|
||||||
contact.setSub_approved(item.isApproved());
|
|
||||||
contact.setSub_pending(item.isSubscriptionPending());
|
|
||||||
|
|
||||||
EntityModel entity = new EntityModel();
|
|
||||||
entity.setAccount(account);
|
|
||||||
entity.setJid(item.getJid().asEntityBareJidOrThrow());
|
|
||||||
|
|
||||||
contact.setEntity(entity);
|
|
||||||
|
|
||||||
return contact;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SubscriptionDirection convert(RosterPacket.ItemType type) {
|
|
||||||
return SubscriptionDirection.valueOf(type.toString());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public RosterPacket.ItemType convert(SubscriptionDirection direction) {
|
|
||||||
return RosterPacket.ItemType.fromString(direction.toString());
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue