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.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()); protected final AbstractXMPPConnection connection; protected final long accountId; protected final ReconnectionManager reconnectionManager; protected final Roster roster; protected final ChatManager chatManager; protected final CarbonManager carbonManager; protected final StableUniqueStanzaIdManager stanzaIdManager; protected final ServiceDiscoveryManager serviceDiscoveryManager; BehaviorSubject state = BehaviorSubject.createDefault(ConnectionState.DISCONNECTED); public MercuryConnection(AbstractXMPPConnection connection, long accountId) { this.connection = connection; connection.addConnectionListener(connectionListener); this.accountId = accountId; reconnectionManager = ReconnectionManager.getInstanceFor(connection); reconnectionManager.enableAutomaticReconnection(); this.roster = Roster.getInstanceFor(connection); roster.setRosterLoadedAtLogin(true); this.chatManager = ChatManager.getInstanceFor(connection); this.carbonManager = CarbonManager.getInstanceFor(connection); this.stanzaIdManager = StableUniqueStanzaIdManager.getInstanceFor(connection); stanzaIdManager.enable(); this.serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection); VersionManager.setAutoAppendSmackVersion(false); VersionManager.getInstanceFor(connection).setVersion("Mercury", "0.0.1-stealth", "Android"); serviceDiscoveryManager.setIdentity(new DiscoverInfo.Identity("client", "Mercury", "phone")); } public void connect() { LOGGER.log(Level.INFO, "Connecting..."); state.onNext(ConnectionState.CONNECTING); AbstractXMPPConnection con = (AbstractXMPPConnection) getConnection(); try { con.connect().login(); } catch (InterruptedException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (SmackException e) { e.printStackTrace(); } catch (XMPPException e) { e.printStackTrace(); } } public void disconnect() { AbstractXMPPConnection con = (AbstractXMPPConnection) getConnection(); state.onNext(ConnectionState.DISCONNECTING); try { con.disconnect(new Presence(Presence.Type.unavailable)); } catch (SmackException.NotConnectedException e) { e.printStackTrace(); } state.onNext(ConnectionState.DISCONNECTED); } public XMPPConnection getConnection() { return connection; } public long getAccountId() { return accountId; } public Roster getRoster() { return roster; } private final ConnectionListener connectionListener = new ConnectionListener() { @Override public void connected(XMPPConnection connection) { state.onNext(ConnectionState.CONNECTED); } @Override public void authenticated(XMPPConnection connection, boolean resumed) { state.onNext(ConnectionState.AUTHENTICATED); LOGGER.info("Connection " + getAccountId() + " authenticated (" + (resumed ? "resumed" : "initially") + ")"); if (!resumed) { LOGGER.info("Enabling carbons!"); carbonManager.enableCarbonsAsync(exception -> { LOGGER.severe("Could not enable carbons for connection " + accountId + ": " + exception.getMessage()); exception.printStackTrace(); }); } } @Override public void connectionClosed() { state.onNext(ConnectionState.DISCONNECTED); } @Override public void connectionClosedOnError(Exception e) { state.onNext(ConnectionState.DISCONNECTED); } }; }