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 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 getState() { return mState; } }