Select Git revision
SessionManager.kt
-
Janne Mareike Koschinski authoredJanne Mareike Koschinski authored
SessionManager.kt 6.75 KiB
/*
* Quasseldroid - Quassel client for Android
*
* Copyright (c) 2019 Janne Koschinski
* Copyright (c) 2019 The Quassel Project
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.kuschku.libquassel.session
import de.kuschku.libquassel.connection.ConnectionState
import de.kuschku.libquassel.connection.HostnameVerifier
import de.kuschku.libquassel.connection.SocketAddress
import de.kuschku.libquassel.protocol.ClientData
import de.kuschku.libquassel.protocol.message.HandshakeMessage
import de.kuschku.libquassel.quassel.syncables.interfaces.invokers.Invokers
import de.kuschku.libquassel.util.compatibility.HandlerService
import de.kuschku.libquassel.util.compatibility.LoggingHandler.Companion.log
import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.*
import de.kuschku.libquassel.util.helpers.or
import io.reactivex.Observable
import io.reactivex.disposables.Disposable
import io.reactivex.functions.BiFunction
import io.reactivex.subjects.BehaviorSubject
import javax.net.ssl.X509TrustManager
class SessionManager(
offlineSession: ISession,
private val backlogStorage: BacklogStorage,
private val notificationManager: NotificationManager?,
val handlerService: HandlerService,
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
private var lastTrustManager: X509TrustManager? = null
private var lastHostnameVerifier: HostnameVerifier? = null
private var lastAddress: SocketAddress? = null
private var lastUserData: Pair<String, String>? = null
private var lastShouldReconnect = false
private var inProgressSession = BehaviorSubject.createDefault(ISession.NULL)
private var lastSession: ISession = offlineSession
val state: Observable<ConnectionState> = inProgressSession.switchMap(ISession::state)
private val initStatus: Observable<Pair<Int, Int>> = inProgressSession.switchMap(ISession::initStatus)
val session: Observable<ISession> = state.map { connectionState ->
if (connectionState == ConnectionState.CONNECTED)
inProgressSession.value
else
lastSession
}
private var hasErrored: Boolean = false
val error = inProgressSession.switchMap(ISession::error)
val connectionError = inProgressSession.switchMap(ISession::connectionError)
val connectionProgress: Observable<Triple<ConnectionState, Int, Int>> = Observable.combineLatest(
state, initStatus,
BiFunction<ConnectionState, Pair<Int, Int>, Triple<ConnectionState, Int, Int>> { t1, t2 ->
Triple(t1, t2.first, t2.second)
})
val disposables = mutableListOf<Disposable>()
init {
log(INFO, "Session", "Session created")
disposables.add(state.subscribe {
if (it == ConnectionState.CONNECTED) {
lastSession.close()
}
})
// This should preload them
Invokers
}
fun login(user: String, pass: String) {
inProgressSession.value.login(user, pass)
}
fun setupCore(setupData: HandshakeMessage.CoreSetupData) {
inProgressSession.value.setupCore(setupData)
}
fun connect(
clientData: ClientData,
trustManager: X509TrustManager,
hostnameVerifier: HostnameVerifier,
address: SocketAddress,
userData: Pair<String, String>,
shouldReconnect: Boolean = false
) {
log(DEBUG, "SessionManager", "Connecting")
inProgressSession.value.close()
lastClientData = clientData
lastTrustManager = trustManager
lastHostnameVerifier = hostnameVerifier
lastAddress = address
lastUserData = userData
lastShouldReconnect = shouldReconnect
hasErrored = false
inProgressSession.onNext(
Session(
address,
userData,
trustManager,
hostnameVerifier,
clientData,
handlerService,
heartBeatFactory,
disconnectFromCore,
initCallback,
exceptionHandler,
::hasErroredCallback,
notificationManager,
backlogStorage
)
)
}
fun hasErroredCallback(error: Error) {
log(WARN, "SessionManager", "Callback Error occured: $error")
hasErrored = true
}
/**
* @return if an autoreconnect has been necessary
*/
fun autoReconnect(forceReconnect: Boolean = false) = if (!hasErrored) {
state.or(ConnectionState.DISCONNECTED).let {
if (it == ConnectionState.CLOSED) {
log(INFO, "SessionManager", "Autoreconnect triggered")
reconnect(forceReconnect)
true
} else {
log(INFO, "SessionManager", "Autoreconnect failed: state is $it")
false
}
}
} else {
log(INFO, "SessionManager", "Autoreconnect failed: hasErrored")
false
}
fun reconnect(forceReconnect: Boolean = false) {
if (lastShouldReconnect || forceReconnect) {
val clientData = lastClientData
val trustManager = lastTrustManager
val hostnameVerifier = lastHostnameVerifier
val address = lastAddress
val userData = lastUserData
if (clientData != null && trustManager != null && hostnameVerifier != null && address != null && userData != null) {
connect(clientData, trustManager, hostnameVerifier, address, userData, forceReconnect)
} else {
log(INFO, "SessionManager", "Reconnect failed: not enough data available")
}
} else {
log(INFO, "SessionManager", "Reconnect failed: reconnect not allowed")
}
}
fun disconnect(forever: Boolean) {
if (forever)
backlogStorage.clearMessages()
inProgressSession.value.close()
inProgressSession.onNext(ISession.NULL)
}
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()
}
}
}