diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/AndroidHeartBeatRunner.kt b/app/src/main/java/de/kuschku/quasseldroid/service/AndroidHeartBeatRunner.kt index bb44fb01c39d2191ef19153b7830bda34fbed1a2..65ec1ea482f8275f48db9fe52e7bd6e9cbfddd93 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/service/AndroidHeartBeatRunner.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/service/AndroidHeartBeatRunner.kt @@ -3,31 +3,39 @@ package de.kuschku.quasseldroid.service import android.os.Handler import de.kuschku.libquassel.protocol.message.SignalProxyMessage import de.kuschku.libquassel.session.HeartBeatRunner -import de.kuschku.libquassel.session.Session import de.kuschku.libquassel.util.compatibility.LoggingHandler.Companion.log import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.INFO import org.threeten.bp.Duration import org.threeten.bp.Instant -class AndroidHeartBeatRunner( - private val session: Session, - private val handler: Handler -) : HeartBeatRunner { +class AndroidHeartBeatRunner : HeartBeatRunner { + private val handler = Handler() private var running = true private var lastHeartBeatReply: Instant = Instant.now() private var lastHeartBeatSend: Instant = Instant.now() + private var closeCallback: (() -> Unit)? = null + private var heartbeatDispatchCallback: ((SignalProxyMessage.HeartBeat) -> Unit)? = null + + override fun setCloseCallback(callback: (() -> Unit)?) { + this.closeCallback = callback + } + + override fun setHeartbeatDispatchCallback(callback: ((SignalProxyMessage.HeartBeat) -> Unit)?) { + this.heartbeatDispatchCallback = callback + } + override fun start() { if (running) { val duration = Duration.between(lastHeartBeatReply, lastHeartBeatSend).toMillis() if (duration > TIMEOUT) { log(INFO, "Heartbeat", "Ping Timeout: Last Response ${duration}ms ago") - session.close() + closeCallback?.invoke() } else { log(INFO, "Heartbeat", "Sending Heartbeat") val now = Instant.now() lastHeartBeatSend = now - session.dispatch(SignalProxyMessage.HeartBeat(now)) + heartbeatDispatchCallback?.invoke(SignalProxyMessage.HeartBeat(now)) } handler.postDelayed(::start, DELAY) } @@ -35,6 +43,8 @@ class AndroidHeartBeatRunner( override fun end() { running = false + setCloseCallback(null) + setHeartbeatDispatchCallback(null) } override fun setLastHeartBeatReply(time: Instant) { diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/AsyncBackend.kt b/app/src/main/java/de/kuschku/quasseldroid/service/AsyncBackend.kt index 29791d8e70ecb82db032e3d46421eee3988d8c56..cc2ef12ed43492e9aff998a48fac5beb67657386 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/service/AsyncBackend.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/service/AsyncBackend.kt @@ -25,9 +25,14 @@ import de.kuschku.libquassel.util.compatibility.HandlerService class AsyncBackend( private val handler: HandlerService, - private val backend: Backend, - private val disconnectCallback: () -> Unit + private val backend: Backend ) : Backend { + private var disconnectCallback: (() -> Unit)? = null + + fun setDisconnectCallback(callback: (() -> Unit)?) { + this.disconnectCallback = callback + } + override fun updateUserDataAndLogin(user: String, pass: String) { handler.backend { backend.updateUserDataAndLogin(user, pass) diff --git a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt index 428c988944b835809b43f8af62af8a233f66b245..210607ba0ad3e35b98f186b206de115a78f2e06c 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/service/QuasselService.kt @@ -21,7 +21,6 @@ package de.kuschku.quasseldroid.service import android.content.* import android.net.ConnectivityManager -import android.os.Handler import android.text.SpannableString import androidx.core.app.RemoteInput import androidx.lifecycle.Observer @@ -259,56 +258,72 @@ class QuasselService : DaggerLifecycleService(), private lateinit var certificateManager: QuasselCertificateManager - private val backendImplementation = object : Backend { + class BackendImplementation : Backend { + var service: QuasselService? = null + override fun updateUserDataAndLogin(user: String, pass: String) { - accountDatabase.accounts().findById(accountId)?.let { old -> - accountDatabase.accounts().save(old.copy(user = user, pass = pass)) - sessionManager.login(user, pass) + service?.apply { + accountDatabase.accounts().findById(accountId)?.let { old -> + accountDatabase.accounts().save(old.copy(user = user, pass = pass)) + sessionManager.login(user, pass) + } } } - override fun sessionManager() = sessionManager + override fun sessionManager() = service!!.sessionManager override fun connectUnlessConnected(address: SocketAddress, user: String, pass: String, reconnect: Boolean) { - sessionManager.ifDisconnected { - this.connect(address, user, pass, reconnect) + service?.apply { + sessionManager.ifDisconnected { + connect(address, user, pass, reconnect) + } } } override fun connect(address: SocketAddress, user: String, pass: String, reconnect: Boolean) { - disconnect() - sessionManager.connect( - clientData, trustManager, hostnameVerifier, address, user to pass, reconnect - ) + service?.apply { + disconnect() + sessionManager.connect( + clientData, trustManager, hostnameVerifier, address, user to pass, reconnect + ) + } } override fun reconnect() { - sessionManager.reconnect() + service?.apply { + sessionManager.reconnect() + } } override fun disconnect(forever: Boolean) { - sessionManager.disconnect(forever) + service?.apply { + sessionManager.disconnect(forever) + } } override fun requestConnectNewNetwork() { - sessionManager.session.flatMap(ISession::liveNetworkAdded).firstElement().flatMap { id -> - sessionManager.session.flatMap(ISession::liveNetworks) - .map { it[id] } - .flatMap { network -> - network.liveInitialized - .filter { it } - .map { network } - }.firstElement() - }.toLiveData().observe(this@QuasselService, Observer { - it?.requestConnect() - }) + service?.apply { + sessionManager.session.flatMap(ISession::liveNetworkAdded).firstElement().flatMap { id -> + sessionManager.session.flatMap(ISession::liveNetworks) + .map { it[id] } + .flatMap { network -> + network.liveInitialized + .filter { it } + .map { network } + }.firstElement() + }.toLiveData().observe(this, Observer { + it?.requestConnect() + }) + } } } + private val backendImplementation = BackendImplementation() + private val handlerService = AndroidHandlerService() - private val asyncBackend = AsyncBackend(handlerService, backendImplementation, ::stopSelf) + private val asyncBackend = AsyncBackend(handlerService, backendImplementation) @Inject lateinit var database: QuasselDatabase @@ -316,14 +331,17 @@ class QuasselService : DaggerLifecycleService(), @Inject lateinit var accountDatabase: AccountDatabase - private val connectivityReceiver = object : BroadcastReceiver() { + class CustomConnectivityReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { if (context != null && intent != null) { connectivity.onNext(Unit) } } + + val connectivity = PublishSubject.create<Unit>() } - private val connectivity = PublishSubject.create<Unit>() + + private val connectivityReceiver = CustomConnectivityReceiver() private fun disconnectFromCore() { getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE).editCommit { @@ -334,6 +352,9 @@ class QuasselService : DaggerLifecycleService(), override fun onCreate() { super.onCreate() + backendImplementation.service = this + asyncBackend.setDisconnectCallback(::stopSelf) + translatedLocale = LocaleHelper.setLocale(this) certificateManager = QuasselCertificateManager(database.validityWhitelist()) @@ -345,12 +366,13 @@ class QuasselService : DaggerLifecycleService(), QuasselBacklogStorage(database), notificationBackend, handlerService, - { session: Session -> AndroidHeartBeatRunner(session, Handler()) }, - ::disconnectFromCore, - ::initCallback, + ::AndroidHeartBeatRunner, CrashHandler::handle ) + sessionManager.setDisconnectFromCore(::disconnectFromCore) + sessionManager.setInitCallback(::initCallback) + clientData = ClientData( identifier = "${resources.getString(R.string.app_name)} ${BuildConfig.FANCY_VERSION_NAME}", buildDate = Instant.ofEpochSecond(BuildConfig.GIT_COMMIT_DATE), @@ -377,7 +399,7 @@ class QuasselService : DaggerLifecycleService(), } }) - connectivity + connectivityReceiver.connectivity .delay(200, TimeUnit.MILLISECONDS) .throttleFirst(1, TimeUnit.SECONDS) .toLiveData() @@ -446,6 +468,10 @@ class QuasselService : DaggerLifecycleService(), unregisterReceiver(connectivityReceiver) + sessionManager.dispose() + asyncBackend.setDisconnectCallback(null) + backendImplementation.service = null + notificationHandle?.let { notificationManager.remove(it) } super.onDestroy() } diff --git a/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundActivity.kt index 7444675033efef794b85b757e2fc0c9c46af9944..fe5176f84a899f0d91973068acaf61dfec2bd99e 100644 --- a/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundActivity.kt +++ b/app/src/main/java/de/kuschku/quasseldroid/util/service/ServiceBoundActivity.kt @@ -133,6 +133,7 @@ abstract class ServiceBoundActivity : override fun onDestroy() { lifecycle.removeObserver(connection) + connection.context = null super.onDestroy() } diff --git a/lib/src/main/java/de/kuschku/libquassel/connection/CoreConnection.kt b/lib/src/main/java/de/kuschku/libquassel/connection/CoreConnection.kt index 8d42465c81b8a637dfd78017a0e4111ca729292d..bd2e3832d18c487fe1479f3b062be0ef3f5ba23a 100644 --- a/lib/src/main/java/de/kuschku/libquassel/connection/CoreConnection.kt +++ b/lib/src/main/java/de/kuschku/libquassel/connection/CoreConnection.kt @@ -49,20 +49,29 @@ import javax.net.ssl.SSLSession import javax.net.ssl.X509TrustManager class CoreConnection( - private val handler: ProtocolHandler, private val clientData: ClientData, private val features: Features, private val trustManager: X509TrustManager, private val hostnameVerifier: HostnameVerifier, private val address: SocketAddress, - private val handlerService: HandlerService, - private val securityExceptionCallback: (QuasselSecurityException) -> Unit, - private val exceptionCallback: (Throwable) -> Unit + private val handlerService: HandlerService ) : Thread(), Closeable { companion object { private const val TAG = "CoreConnection" } + private var handler: ProtocolHandler? = null + private var securityExceptionCallback: ((QuasselSecurityException) -> Unit)? = null + private var exceptionCallback: ((Throwable) -> Unit)? = null + + fun setHandlers(handler: ProtocolHandler?, + securityExceptionCallback: ((QuasselSecurityException) -> Unit)?, + exceptionCallback: ((Throwable) -> Unit)?) { + this.handler = handler + this.securityExceptionCallback = securityExceptionCallback + this.exceptionCallback = exceptionCallback + } + private val exceptionHandler = UncaughtExceptionHandler { thread, throwable -> log(WARN, TAG, thread.name, throwable) } @@ -157,6 +166,7 @@ class CoreConnection( setState(ConnectionState.CLOSED) channel?.flush() channel?.close() + setHandlers(null, null, null) interrupt() } catch (e: Throwable) { log(WARN, TAG, "Error encountered while closing connection: $e") @@ -239,12 +249,12 @@ class CoreConnection( } while (cause != null && exception == null) if (exception != null) { close() - securityExceptionCallback(exception) + securityExceptionCallback?.invoke(exception) } else { if (!closed) { log(WARN, TAG, "Error encountered in connection", e) log(WARN, TAG, "Last sent message: ${MessageRunnable.lastSent.get()}") - exceptionCallback(e) + exceptionCallback?.invoke(e) } close() } @@ -258,7 +268,7 @@ class CoreConnection( ) handlerService.backend { try { - handler.handle(msg) + handler?.handle(msg) } catch (e: Throwable) { log(WARN, TAG, "Error encountered while handling sigproxy message", e) log(WARN, TAG, msg.toString()) @@ -277,7 +287,7 @@ class CoreConnection( HandshakeVariantMapSerializer.deserialize(dataBuffer, features.negotiated) ) try { - handler.handle(msg) + handler?.handle(msg) } catch (e: Throwable) { log(WARN, TAG, "Error encountered while handling handshake message", e) diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BacklogManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BacklogManager.kt index de63bc96eaaab952989c1f8eddba30644edfaa20..d111394cce385e3cbdef5dbf9297b016afbc2669 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BacklogManager.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BacklogManager.kt @@ -22,17 +22,22 @@ package de.kuschku.libquassel.quassel.syncables import de.kuschku.libquassel.protocol.* import de.kuschku.libquassel.quassel.syncables.interfaces.IBacklogManager import de.kuschku.libquassel.session.BacklogStorage -import de.kuschku.libquassel.session.Session +import de.kuschku.libquassel.session.ISession import de.kuschku.libquassel.util.compatibility.LoggingHandler.Companion.log import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.DEBUG class BacklogManager( - private val session: Session, + var session: ISession, private val backlogStorage: BacklogStorage -) : SyncableObject(session, "BacklogManager"), IBacklogManager { +) : SyncableObject(session.proxy, "BacklogManager"), IBacklogManager { private val loading = mutableMapOf<BufferId, (List<Message>) -> Boolean>() private val loadingFiltered = mutableMapOf<BufferId, (List<Message>) -> Boolean>() + override fun deinit() { + super.deinit() + session = ISession.NULL + } + init { initialized = true } diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferSyncer.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferSyncer.kt index 62695bcecb345e3b22531d3ac015fb878f77a00f..235aa911c0395e1e8a7ee63bc7d886671634074b 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferSyncer.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferSyncer.kt @@ -31,9 +31,14 @@ import io.reactivex.Observable import io.reactivex.subjects.BehaviorSubject class BufferSyncer constructor( - private val session: ISession, + var session: ISession, private val notificationManager: NotificationManager? ) : SyncableObject(session.proxy, "BufferSyncer"), IBufferSyncer { + override fun deinit() { + super.deinit() + session = ISession.NULL + } + fun lastSeenMsg(buffer: BufferId): MsgId = _lastSeenMsg[buffer] ?: 0 fun liveLastSeenMsg(buffer: BufferId): Observable<MsgId> = live_lastSeenMsg.map { markerLine(buffer) diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewManager.kt index 12e475f26a08e44972f0fc9a215d974ec552fe41..83979dfbdb5daf4bf65ac48fbccd91bd01d6feec 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewManager.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/BufferViewManager.kt @@ -87,4 +87,8 @@ class BufferViewManager constructor( bufferViewConfig.handleBuffer(info, bufferSyncer, unhide) } } + + override fun deinit() { + _bufferViewConfigs.values.map(BufferViewConfig::deinit) + } } diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IgnoreListManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IgnoreListManager.kt index c411d25ab5d94e7b669d5dd2aac83797b94a7cc0..1c1dcb05b55bb8043329c27d12efb1d9f6a150a6 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IgnoreListManager.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IgnoreListManager.kt @@ -30,11 +30,16 @@ import io.reactivex.subjects.BehaviorSubject import java.io.Serializable class IgnoreListManager constructor( - private val session: ISession, + var session: ISession, proxy: SignalProxy ) : SyncableObject(proxy, "IgnoreListManager"), IIgnoreListManager { constructor(session: Session) : this(session, session) + override fun deinit() { + super.deinit() + session = ISession.NULL + } + override fun toVariantMap(): QVariantMap = mapOf( "IgnoreList" to QVariant.of(initIgnoreList(), Type.QVariantMap) ) diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt index fb1dbf08117c23fa5c03eeb68a97f0b4bc9353d9..de985864fb7e26b975cdd8b1e64164fba7e31863 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/RpcHandler.kt @@ -51,7 +51,9 @@ class RpcHandler( override fun passwordChanged(ignored: Long, success: Boolean) { } - override fun disconnectFromCore() = session.disconnectFromCore() + override fun disconnectFromCore() { + session.disconnectFromCore?.invoke() + } override fun objectRenamed(classname: ByteBuffer, newname: String, oldname: String) { session.renameObject(classname.deserializeString(StringSerializer.UTF8) ?: "", newname, oldname) diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/SyncableObject.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/SyncableObject.kt index a86753f4313fd43a12f2b2a99a2ce90d82c30668..d9a4147a16c31633197471c4bdeaf86c9e25b7f6 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/SyncableObject.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/SyncableObject.kt @@ -25,7 +25,7 @@ import io.reactivex.Observable import io.reactivex.subjects.BehaviorSubject abstract class SyncableObject( - override val proxy: SignalProxy, + override var proxy: SignalProxy, final override val className: String ) : ISyncableObject { final override var objectName: String = "" @@ -53,5 +53,9 @@ abstract class SyncableObject( } } + override fun deinit() { + this.proxy = SignalProxy.NULL + } + override fun toString() = "${identifier.first}:${identifier.second}" } diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ISyncableObject.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ISyncableObject.kt index 16308a69012aecd679585f500d70a3c3c97a259e..5f75128bf49ee944306e712ab5a7548eea1d315e 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ISyncableObject.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ISyncableObject.kt @@ -43,6 +43,7 @@ interface ISyncableObject { SYNC("update", ARG(properties, Type.QVariantMap)) } + fun deinit() fun init() {} fun fromVariantMap(properties: QVariantMap) = Unit diff --git a/lib/src/main/java/de/kuschku/libquassel/session/BacklogStorage.kt b/lib/src/main/java/de/kuschku/libquassel/session/BacklogStorage.kt index 275c24db7b43dd8495c3b8fb838e77482952700f..e122de5c0c471d59189cdb94087f48e56fb2143d 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/BacklogStorage.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/BacklogStorage.kt @@ -23,10 +23,10 @@ import de.kuschku.libquassel.protocol.BufferId import de.kuschku.libquassel.protocol.Message interface BacklogStorage { - fun updateIgnoreRules(session: Session) + fun updateIgnoreRules(session: ISession) - fun storeMessages(session: Session, vararg messages: Message) - fun storeMessages(session: Session, messages: Iterable<Message>) + fun storeMessages(session: ISession, vararg messages: Message) + fun storeMessages(session: ISession, messages: Iterable<Message>) fun clearMessages(bufferId: BufferId, idRange: IntRange) diff --git a/lib/src/main/java/de/kuschku/libquassel/session/HeartBeatRunner.kt b/lib/src/main/java/de/kuschku/libquassel/session/HeartBeatRunner.kt index d59696be0b6fb9bc06efb7df8b74d067729d288b..966f666e0e9dac606f428d8cf74fac3d19d60712 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/HeartBeatRunner.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/HeartBeatRunner.kt @@ -1,8 +1,12 @@ package de.kuschku.libquassel.session +import de.kuschku.libquassel.protocol.message.SignalProxyMessage import org.threeten.bp.Instant interface HeartBeatRunner { + fun setCloseCallback(callback: (() -> Unit)?) + fun setHeartbeatDispatchCallback(callback: ((SignalProxyMessage.HeartBeat) -> Unit)?) + fun start() fun end() diff --git a/lib/src/main/java/de/kuschku/libquassel/session/JavaHeartBeatRunner.kt b/lib/src/main/java/de/kuschku/libquassel/session/JavaHeartBeatRunner.kt index b5ae349a6e2fe2a3ce4ee9e83ff1953d5937d49f..76f32a5b9d8e9467cdb13478cf09f42922fc1b61 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/JavaHeartBeatRunner.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/JavaHeartBeatRunner.kt @@ -6,22 +6,31 @@ import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.INFO import org.threeten.bp.Duration import org.threeten.bp.Instant -class JavaHeartBeatRunner( - private val session: Session -) : Thread(), HeartBeatRunner { +class JavaHeartBeatRunner : Thread(), HeartBeatRunner { private var running = true private var lastHeartBeatReply: Instant = Instant.now() + private var closeCallback: (() -> Unit)? = null + private var heartbeatDispatchCallback: ((SignalProxyMessage.HeartBeat) -> Unit)? = null + + override fun setCloseCallback(callback: (() -> Unit)?) { + this.closeCallback = callback + } + + override fun setHeartbeatDispatchCallback(callback: ((SignalProxyMessage.HeartBeat) -> Unit)?) { + this.heartbeatDispatchCallback = callback + } + override fun start() { while (running) { val now = Instant.now() val duration = Duration.between(lastHeartBeatReply, now).toMillis() if (duration > TIMEOUT) { log(INFO, "Heartbeat", "Ping Timeout: Last Response ${duration}ms ago") - session.close() + closeCallback?.invoke() } else { log(INFO, "Heartbeat", "Sending Heartbeat") - session.dispatch(SignalProxyMessage.HeartBeat(now)) + heartbeatDispatchCallback?.invoke(SignalProxyMessage.HeartBeat(now)) } Thread.sleep(DELAY) } diff --git a/lib/src/main/java/de/kuschku/libquassel/session/ObjectStorage.kt b/lib/src/main/java/de/kuschku/libquassel/session/ObjectStorage.kt index b4bb5cdd01bd925d1b5e3e2d88d6e1940906ea06..a1a753c03462f9a9326aea3e3c878993a232711e 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/ObjectStorage.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/ObjectStorage.kt @@ -27,7 +27,13 @@ import de.kuschku.libquassel.quassel.exceptions.ObjectNotFoundException import de.kuschku.libquassel.quassel.syncables.interfaces.ISyncableObject import de.kuschku.libquassel.util.helpers.removeIfEqual -class ObjectStorage(private val proxy: SignalProxy) { +class ObjectStorage(private var proxy: SignalProxy) { + fun deinit() { + proxy = SignalProxy.NULL + objectTree.values.forEach(ISyncableObject::deinit) + objectTree.clear() + } + private val objectTree: MutableMap<Pair<String, String>, ISyncableObject> = HashMap() fun add(obj: ISyncableObject) { diff --git a/lib/src/main/java/de/kuschku/libquassel/session/ProtocolHandler.kt b/lib/src/main/java/de/kuschku/libquassel/session/ProtocolHandler.kt index 7255cf2fb810be9f5241c2410599627e734b4f7d..8775eb8f1d50e19de8e5a8d3d5bd71d10a0baf47 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/ProtocolHandler.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/ProtocolHandler.kt @@ -193,6 +193,7 @@ abstract class ProtocolHandler( if (syncableObject == null) return + syncableObject.deinit() objectStorage.remove(syncableObject) toInit.remove(syncableObject) } diff --git a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt index 6e44916642fd56341ef969d51a097e9226338cd6..9f9867bda743b0976bc61c916cef965e922ebe36 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/Session.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/Session.kt @@ -45,9 +45,9 @@ class Session( backlogStorage: BacklogStorage, private val notificationManager: NotificationManager?, private var userData: Pair<String, String>, - heartBeatFactory: (Session) -> HeartBeatRunner, - val disconnectFromCore: () -> Unit, - private val initCallback: (Session) -> Unit, + heartBeatFactory: () -> HeartBeatRunner, + val disconnectFromCore: (() -> Unit)?, + private val initCallback: ((Session) -> Unit)?, exceptionHandler: (Throwable) -> Unit ) : ProtocolHandler(exceptionHandler), ISession { override val objectStorage: ObjectStorage = ObjectStorage(this) @@ -58,15 +58,12 @@ class Session( get() = coreConnection.sslSession private val coreConnection = CoreConnection( - this, clientData, features, trustManager, hostnameVerifier, address, - handlerService, - ::handle, - ::handleConnectionError + handlerService ) override val state = coreConnection.state @@ -107,9 +104,12 @@ class Session( override val lag = BehaviorSubject.createDefault(0L) - private val heartBeatThread = heartBeatFactory(this) + private val heartBeatThread = heartBeatFactory() init { + heartBeatThread.setCloseCallback(::close) + heartBeatThread.setHeartbeatDispatchCallback(::dispatch) + coreConnection.setHandlers(this, ::handle, ::handleConnectionError) coreConnection.start() } @@ -247,7 +247,7 @@ class Session( } override fun onInitDone() { - initCallback(this) + initCallback?.invoke(this) for (config in bufferViewManager.bufferViewConfigs()) { for (info in bufferSyncer.bufferInfos()) { config.handleBuffer(info, bufferSyncer) @@ -283,12 +283,28 @@ class Session( super.close() heartBeatThread.end() - coreConnection.close() + objectStorage.deinit() + + aliasManager.deinit() + bufferSyncer.deinit() + bufferViewManager.deinit() + coreInfo.deinit() + dccConfig.deinit() + ignoreListManager.deinit() + highlightRuleManager.deinit() + ircListHelper.deinit() + networkConfig.deinit() + backlogManager.deinit() + + rpcHandler = null + certManagers.clear() identities.clear() + live_identities.onNext(Unit) networks.clear() + live_networks.onNext(Unit) } fun join() { diff --git a/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt b/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt index 9fd414ee340e1b9270e843902eb76e6f978e25cd..4c7d4448b24aa2ede4c7520c5a50128c2efa609c 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt @@ -32,6 +32,7 @@ import de.kuschku.libquassel.util.helpers.or import io.reactivex.BackpressureStrategy import io.reactivex.Flowable import io.reactivex.Observable +import io.reactivex.disposables.Disposable import io.reactivex.functions.BiFunction import io.reactivex.subjects.BehaviorSubject import javax.net.ssl.X509TrustManager @@ -41,11 +42,20 @@ class SessionManager( private val backlogStorage: BacklogStorage, private val notificationManager: NotificationManager?, val handlerService: HandlerService, - private val heartBeatFactory: (Session) -> HeartBeatRunner, - private val disconnectFromCore: () -> Unit, - private val initCallback: (Session) -> Unit, + private val heartBeatFactory: () -> HeartBeatRunner, private val exceptionHandler: (Throwable) -> Unit ) { + private var disconnectFromCore: (() -> Unit)? = null + private var initCallback: ((Session) -> Unit)? = null + + fun setDisconnectFromCore(callback: (() -> Unit)?) { + this.disconnectFromCore = callback + } + + fun setInitCallback(callback: ((Session) -> Unit)?) { + this.initCallback = callback + } + fun close() = session.or(lastSession).close() private var lastClientData: ClientData? = null @@ -85,29 +95,27 @@ class SessionManager( Triple(t1, t2.first, t2.second) }) + val disposables = mutableListOf<Disposable>() + init { log(INFO, "Session", "Session created") - state.subscribe { + disposables.add(state.subscribe { if (it == ConnectionState.CONNECTED) { lastSession.close() } - } + }) - error.subscribe { + disposables.add(error.subscribe { hasErrored = true - } + }) // This should preload them Invokers } - fun ifDisconnected(closure: (ISession) -> Unit) { - state.or(ConnectionState.DISCONNECTED).let { - if (it == ConnectionState.CLOSED || it == ConnectionState.DISCONNECTED) { - closure(inProgressSession.value) - } - } + fun login(user: String, pass: String) { + inProgressSession.value.login(user, pass) } fun connect( @@ -189,7 +197,20 @@ class SessionManager( inProgressSession.onNext(ISession.NULL) } - fun login(user: String, pass: String) { - inProgressSession.value.login(user, pass) + fun ifDisconnected(closure: (ISession) -> Unit) { + state.or(ConnectionState.DISCONNECTED).let { + if (it == ConnectionState.CLOSED || it == ConnectionState.DISCONNECTED) { + closure(inProgressSession.value) + } + } + } + + fun dispose() { + setDisconnectFromCore(null) + setInitCallback(null) + for (disposable in disposables) { + if (!disposable.isDisposed) + disposable.dispose() + } } } diff --git a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselBacklogStorage.kt b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselBacklogStorage.kt index c429c43767245dd387af037810c21203bad03371..241d12bb3c085718447bfd01fcde09ccb2bb9695 100644 --- a/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselBacklogStorage.kt +++ b/persistence/src/main/java/de/kuschku/quasseldroid/persistence/QuasselBacklogStorage.kt @@ -24,10 +24,9 @@ import de.kuschku.libquassel.protocol.Message import de.kuschku.libquassel.quassel.syncables.IgnoreListManager import de.kuschku.libquassel.session.BacklogStorage import de.kuschku.libquassel.session.ISession -import de.kuschku.libquassel.session.Session class QuasselBacklogStorage(private val db: QuasselDatabase) : BacklogStorage { - override fun updateIgnoreRules(session: Session) { + override fun updateIgnoreRules(session: ISession) { db.message().save( *db.message().all().map { it.copy(ignored = isIgnored(session, it)) @@ -35,10 +34,10 @@ class QuasselBacklogStorage(private val db: QuasselDatabase) : BacklogStorage { ) } - override fun storeMessages(session: Session, vararg messages: Message) = + override fun storeMessages(session: ISession, vararg messages: Message) = storeMessages(session, messages.asIterable()) - override fun storeMessages(session: Session, messages: Iterable<Message>) { + override fun storeMessages(session: ISession, messages: Iterable<Message>) { db.message().save(*messages.map { QuasselDatabase.MessageData( messageId = it.messageId,