Refactor Session Management

- Implements a cleaner state machine for session states
- Updates RxJava
- Improves auto reconnection handling
parent 817b6ac9
Pipeline #479 failed with stages
in 27 minutes and 17 seconds
......@@ -140,6 +140,7 @@ dependencies {
implementation("commons-codec", "commons-codec", "1.12")
implementation("com.squareup.retrofit2", "retrofit", "2.5.0")
implementation("com.squareup.retrofit2", "converter-gson", "2.5.0")
implementation("com.github.pwittchen", "reactivenetwork-rx2", "3.0.2")
withVersion("10.1.0") {
implementation("com.jakewharton", "butterknife", version)
kapt("com.jakewharton", "butterknife-compiler", version)
......
......@@ -19,9 +19,8 @@
package de.kuschku.quasseldroid.service
import de.kuschku.libquassel.connection.SocketAddress
import de.kuschku.libquassel.session.Backend
import de.kuschku.libquassel.util.compatibility.HandlerService
import de.kuschku.quasseldroid.Backend
class AsyncBackend(
private val handler: HandlerService,
......@@ -39,23 +38,19 @@ class AsyncBackend(
}
}
override fun connectUnlessConnected(address: SocketAddress, user: String, pass: String,
requireSsl: Boolean, reconnect: Boolean) {
override fun autoConnect(
ignoreConnectionState: Boolean,
ignoreSetting: Boolean,
ignoreErrors: Boolean,
connectionInfo: Backend.ConnectionInfo?
) {
handler.backend {
backend.connectUnlessConnected(address, user, pass, requireSsl, reconnect)
}
}
override fun connect(address: SocketAddress, user: String, pass: String, requireSsl: Boolean,
reconnect: Boolean) {
handler.backend {
backend.connect(address, user, pass, requireSsl, reconnect)
}
}
override fun reconnect() {
handler.backend {
backend.reconnect()
backend.autoConnect(
ignoreConnectionState,
ignoreSetting,
ignoreErrors,
connectionInfo
)
}
}
......
......@@ -26,7 +26,7 @@ import de.kuschku.libquassel.session.ISession
import de.kuschku.libquassel.util.Optional
import de.kuschku.libquassel.util.compatibility.LoggingHandler.Companion.log
import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.DEBUG
import de.kuschku.libquassel.util.helpers.value
import de.kuschku.libquassel.util.helper.value
import de.kuschku.quasseldroid.persistence.dao.findFirstByBufferId
import de.kuschku.quasseldroid.persistence.dao.get
import de.kuschku.quasseldroid.persistence.db.AccountDatabase
......
......@@ -20,6 +20,6 @@
package de.kuschku.quasseldroid.service
import android.os.Binder
import de.kuschku.libquassel.session.Backend
import de.kuschku.quasseldroid.Backend
class QuasselBinder(val backend: Backend) : Binder()
......@@ -31,7 +31,8 @@ import de.kuschku.libquassel.quassel.syncables.IgnoreListManager
import de.kuschku.libquassel.session.ISession
import de.kuschku.libquassel.session.NotificationManager
import de.kuschku.libquassel.util.flag.hasFlag
import de.kuschku.libquassel.util.helpers.clampOf
import de.kuschku.libquassel.util.helper.clampOf
import de.kuschku.libquassel.util.helper.or
import de.kuschku.libquassel.util.irc.HostmaskHelper
import de.kuschku.libquassel.util.irc.SenderColorUtil
import de.kuschku.quasseldroid.GlideApp
......@@ -259,7 +260,7 @@ class QuasselNotificationBackend @Inject constructor(
this.showNotification(buffer)
}
},
clampOf(session.lag.value * 3 + 100, 16, 3_000),
clampOf(session.lag.or(0) * 3 + 100, 16, 3_000),
TimeUnit.MILLISECONDS
)
}
......
......@@ -19,11 +19,14 @@
package de.kuschku.quasseldroid.service
import android.content.*
import android.net.ConnectivityManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.text.SpannableString
import androidx.core.app.RemoteInput
import androidx.lifecycle.Observer
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import de.kuschku.libquassel.connection.ConnectionState
import de.kuschku.libquassel.connection.HostnameVerifier
import de.kuschku.libquassel.connection.SocketAddress
......@@ -31,16 +34,16 @@ import de.kuschku.libquassel.protocol.*
import de.kuschku.libquassel.quassel.BufferInfo
import de.kuschku.libquassel.quassel.QuasselFeatures
import de.kuschku.libquassel.quassel.syncables.interfaces.IAliasManager
import de.kuschku.libquassel.session.Backend
import de.kuschku.libquassel.session.ISession
import de.kuschku.libquassel.session.Session
import de.kuschku.libquassel.session.SessionManager
import de.kuschku.libquassel.util.compatibility.LoggingHandler
import de.kuschku.libquassel.session.manager.ConnectionInfo
import de.kuschku.libquassel.util.compatibility.LoggingHandler.Companion.log
import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.INFO
import de.kuschku.libquassel.util.helpers.clampOf
import de.kuschku.libquassel.util.helpers.value
import de.kuschku.libquassel.util.helper.clampOf
import de.kuschku.libquassel.util.helper.value
import de.kuschku.malheur.CrashHandler
import de.kuschku.quasseldroid.Backend
import de.kuschku.quasseldroid.BuildConfig
import de.kuschku.quasseldroid.Keys
import de.kuschku.quasseldroid.R
......@@ -64,9 +67,7 @@ import de.kuschku.quasseldroid.util.compatibility.AndroidHandlerService
import de.kuschku.quasseldroid.util.helper.*
import de.kuschku.quasseldroid.util.irc.format.IrcFormatSerializer
import de.kuschku.quasseldroid.util.ui.LocaleHelper
import io.reactivex.subjects.PublishSubject
import org.threeten.bp.Instant
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.net.ssl.X509TrustManager
......@@ -117,12 +118,14 @@ class QuasselService : DaggerLifecycleService(),
backendImplementation.disconnect(true)
stopSelf()
} else {
backendImplementation.connectUnlessConnected(
SocketAddress(account.host, account.port),
account.user,
account.pass,
account.requireSsl,
true
backendImplementation.autoConnect(
connectionInfo = Backend.ConnectionInfo(
SocketAddress(account.host, account.port),
account.user,
account.pass,
account.requireSsl,
true
)
)
}
}
......@@ -185,7 +188,7 @@ class QuasselService : DaggerLifecycleService(),
it.toString() to ircFormatSerializer.toEscapeCodes(SpannableString(it))
}
sessionManager.session.value?.let { session ->
sessionManager.connectedSession.value?.let { session ->
session.bufferSyncer.bufferInfo(bufferId)?.also { bufferInfo ->
val output = mutableListOf<IAliasManager.Command>()
for ((_, formatted) in lines) {
......@@ -203,15 +206,17 @@ class QuasselService : DaggerLifecycleService(),
} else {
val clearMessageId = MsgId(intent.getLongExtra("mark_read_message", -1))
if (bufferId.isValidId() && clearMessageId.isValidId()) {
sessionManager.session.value?.bufferSyncer?.requestSetLastSeenMsg(bufferId, clearMessageId)
sessionManager.session.value?.bufferSyncer?.requestMarkBufferAsRead(bufferId)
sessionManager.connectedSession.value?.bufferSyncer?.requestSetLastSeenMsg(bufferId,
clearMessageId)
sessionManager.connectedSession.value?.bufferSyncer?.requestMarkBufferAsRead(bufferId)
}
val hideMessageId = MsgId(intent.getLongExtra("hide_message", -1))
if (bufferId.isValidId() && hideMessageId.isValidId()) {
if (notificationSettings.markReadOnSwipe) {
sessionManager.session.value?.bufferSyncer?.requestSetLastSeenMsg(bufferId, hideMessageId)
sessionManager.session.value?.bufferSyncer?.requestMarkBufferAsRead(bufferId)
sessionManager.connectedSession.value?.bufferSyncer?.requestSetLastSeenMsg(bufferId,
hideMessageId)
sessionManager.connectedSession.value?.bufferSyncer?.requestMarkBufferAsRead(bufferId)
} else {
handlerService.backend {
database.notifications().markHidden(bufferId, hideMessageId)
......@@ -277,36 +282,32 @@ class QuasselService : DaggerLifecycleService(),
override fun sessionManager() = service?.sessionManager
override fun connectUnlessConnected(address: SocketAddress, user: String, pass: String,
requireSsl: Boolean, reconnect: Boolean) {
override fun autoConnect(
ignoreConnectionState: Boolean,
ignoreSetting: Boolean,
ignoreErrors: Boolean,
connectionInfo: Backend.ConnectionInfo?
) {
service?.apply {
sessionManager.ifDisconnected {
connect(address, user, pass, requireSsl, reconnect)
}
}
}
override fun connect(address: SocketAddress, user: String, pass: String, requireSsl: Boolean,
reconnect: Boolean) {
service?.apply {
disconnect()
sessionManager.connect(
SessionManager.ConnectionInfo(
clientData = clientData,
trustManager = trustManager,
hostnameVerifier = hostnameVerifier,
address = address,
userData = Pair(user, pass),
requireSsl = requireSsl,
shouldReconnect = reconnect
if (connectionInfo != null) {
sessionManager.autoConnect(
ignoreConnectionState,
ignoreSetting,
ignoreErrors,
ConnectionInfo(
clientData = clientData,
trustManager = trustManager,
hostnameVerifier = hostnameVerifier,
address = connectionInfo.address,
userData = Pair(connectionInfo.username,
connectionInfo.password),
requireSsl = connectionInfo.requireSsl,
shouldReconnect = connectionInfo.shouldReconnect
)
)
)
}
}
override fun reconnect() {
service?.apply {
sessionManager.reconnect()
} else {
sessionManager.autoConnect(ignoreConnectionState, ignoreSetting, ignoreErrors)
}
}
}
......@@ -318,8 +319,8 @@ class QuasselService : DaggerLifecycleService(),
override fun requestConnectNewNetwork() {
service?.apply {
sessionManager.session.flatMap(ISession::liveNetworkAdded).firstElement().flatMap { id ->
sessionManager.session.flatMap(ISession::liveNetworks)
sessionManager.connectedSession.flatMap(ISession::liveNetworkAdded).firstElement().flatMap { id ->
sessionManager.connectedSession.flatMap(ISession::liveNetworks)
.map { it[id] }
.flatMap { network ->
network.liveInitialized
......@@ -345,18 +346,6 @@ class QuasselService : DaggerLifecycleService(),
@Inject
lateinit var accountDatabase: AccountDatabase
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 connectivityReceiver = CustomConnectivityReceiver()
private fun disconnectFromCore() {
getSharedPreferences(Keys.Status.NAME, Context.MODE_PRIVATE).editCommit {
putBoolean(Keys.Status.reconnect, false)
......@@ -413,14 +402,17 @@ class QuasselService : DaggerLifecycleService(),
}
})
connectivityReceiver.connectivity
.delay(200, TimeUnit.MILLISECONDS)
.throttleFirst(1, TimeUnit.SECONDS)
ReactiveNetwork
.observeNetworkConnectivity(applicationContext)
.toLiveData()
.observe(this, Observer {
.observe(this, Observer { connectivity ->
log(INFO, "QuasselService", "Connectivity changed: $connectivity")
handlerService.backend {
LoggingHandler.log(INFO, "QuasselService", "Autoreconnect: Network changed")
sessionManager.autoReconnect(true)
log(INFO, "QuasselService", "Reconnect triggered: Network changed")
sessionManager.autoConnect(
ignoreConnectionState = true,
ignoreSetting = true
)
}
})
......@@ -448,8 +440,6 @@ class QuasselService : DaggerLifecycleService(),
registerOnSharedPreferenceChangeListener(this@QuasselService)
}
registerReceiver(connectivityReceiver, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))
notificationManager.init()
update()
......@@ -467,7 +457,7 @@ class QuasselService : DaggerLifecycleService(),
scheduled = false
backoff = clampOf(backoff * 2, BACKOFF_MIN, BACKOFF_MAX)
sessionManager.autoReconnect(true)
sessionManager.autoConnect(ignoreSetting = true)
}
}
}
......@@ -480,8 +470,6 @@ class QuasselService : DaggerLifecycleService(),
unregisterOnSharedPreferenceChangeListener(this@QuasselService)
}
unregisterReceiver(connectivityReceiver)
sessionManager.dispose()
asyncBackend.setDisconnectCallback(null)
backendImplementation.service = null
......
......@@ -56,12 +56,16 @@ import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork.PortDefaults.
import de.kuschku.libquassel.session.Error
import de.kuschku.libquassel.session.ISession
import de.kuschku.libquassel.util.Optional
import de.kuschku.libquassel.util.compatibility.LoggingHandler.Companion.log
import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.INFO
import de.kuschku.libquassel.util.flag.and
import de.kuschku.libquassel.util.flag.hasFlag
import de.kuschku.libquassel.util.flag.minus
import de.kuschku.libquassel.util.flag.or
import de.kuschku.libquassel.util.helpers.nullIf
import de.kuschku.libquassel.util.helpers.value
import de.kuschku.libquassel.util.helper.combineLatest
import de.kuschku.libquassel.util.helper.invoke
import de.kuschku.libquassel.util.helper.nullIf
import de.kuschku.libquassel.util.helper.value
import de.kuschku.quasseldroid.Keys
import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.defaults.DefaultNetworkServer
......@@ -206,7 +210,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
val forceJoin = intent.getBooleanExtra(KEY_FORCE_JOIN, false)
modelHelper.session.filter(Optional<ISession>::isPresent).firstElement().subscribe {
modelHelper.connectedSession.filter(Optional<ISession>::isPresent).firstElement().subscribe {
it.orNull()?.also { session ->
val info = session.bufferSyncer.find(
bufferName = channel,
......@@ -249,7 +253,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
val forceJoin = intent.getBooleanExtra(KEY_FORCE_JOIN, false)
modelHelper.session.filter(Optional<ISession>::isPresent).firstElement().subscribe {
modelHelper.connectedSession.filter(Optional<ISession>::isPresent).firstElement().subscribe {
it.orNull()?.also { session ->
val info = session.bufferSyncer.find(
bufferName = channel,
......@@ -465,7 +469,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
modelHelper.errors.toLiveData(BackpressureStrategy.BUFFER).observe(this, Observer { error ->
error?.let {
when (it) {
is Error.HandshakeError -> it.message.let {
is Error.HandshakeError -> it.message.let {
when (it) {
is HandshakeMessage.ClientInitAck ->
if (it.coreConfigured == false)
......@@ -530,7 +534,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
val user = userField.text.toString()
val pass = passField.text.toString()
backend.value.orNull()?.updateUserDataAndLogin(user, pass)
backend.value?.orNull()?.updateUserDataAndLogin(user, pass)
}
}
.titleColorAttr(R.attr.colorTextPrimary)
......@@ -555,7 +559,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
.show()
}
}
is Error.SslError -> {
is Error.SslError -> {
it.exception.let {
if (it == QuasselSecurityException.NoSsl) {
// Ssl is required but not available
......@@ -616,7 +620,8 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
)
runOnUiThread {
backend.value.orNull()?.reconnect()
log(INFO, "ChatActivity", "Reconnect triggered: User action")
backend.value?.orNull()?.autoConnect(ignoreErrors = true)
}
}
}
......@@ -661,7 +666,8 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
}
runOnUiThread {
backend.value.orNull()?.reconnect()
log(INFO, "ChatActivity", "Reconnect triggered: User action")
backend.value?.orNull()?.autoConnect(ignoreErrors = true)
}
}
}
......@@ -699,7 +705,8 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
)
runOnUiThread {
backend.value.orNull()?.reconnect()
log(INFO, "ChatActivity", "Reconnect triggered: User action")
backend.value?.orNull()?.autoConnect(ignoreErrors = true)
}
}
}
......@@ -714,51 +721,49 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
}
}
}
}
}
})
// Connection errors that should show up as toast
modelHelper.connectionErrors.toLiveData().observe(this, Observer { error ->
error?.let {
val cause = it.cause
when {
it is UnknownHostException -> {
val host = it.message?.replace("Host is unresolved: ", "")
Toast.makeText(this,
getString(R.string.label_error_unknown_host, host),
Toast.LENGTH_LONG).show()
}
it is ProtocolVersionException -> {
Toast.makeText(this,
getString(R.string.label_error_invalid_protocol_version,
it.protocol.version.toInt()),
Toast.LENGTH_LONG).show()
}
it is ConnectException &&
cause is libcore.io.ErrnoException -> {
val errorCode = OsConstants.errnoName(cause.errno)
val errorName = OsConstants.strerror(cause.errno)
Toast.makeText(this,
getString(R.string.label_error_connection, errorName, errorCode),
Toast.LENGTH_LONG).show()
}
it is ConnectException &&
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
cause is ErrnoException -> {
val errorCode = OsConstants.errnoName(cause.errno)
val errorName = OsConstants.strerror(cause.errno)
Toast.makeText(this,
getString(R.string.label_error_connection, errorName, errorCode),
Toast.LENGTH_LONG).show()
}
else -> {
Toast.makeText(this,
getString(R.string.label_error_connection_closed),
Toast.LENGTH_LONG).show()
is Error.ConnectionError -> {
it.throwable.let {
val cause = it.cause
when {
it is UnknownHostException -> {
val host = it.message?.replace("Host is unresolved: ", "")
Toast.makeText(this,
getString(R.string.label_error_unknown_host, host),
Toast.LENGTH_LONG).show()
}
it is ProtocolVersionException -> {
Toast.makeText(this,
getString(R.string.label_error_invalid_protocol_version,
it.protocol.version.toInt()),
Toast.LENGTH_LONG).show()
}
it is ConnectException &&
cause is libcore.io.ErrnoException -> {
val errorCode = OsConstants.errnoName(cause.errno)
val errorName = OsConstants.strerror(cause.errno)
Toast.makeText(this,
getString(R.string.label_error_connection, errorName, errorCode),
Toast.LENGTH_LONG).show()
}
it is ConnectException &&
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
cause is ErrnoException -> {
val errorCode = OsConstants.errnoName(cause.errno)
val errorName = OsConstants.strerror(cause.errno)
Toast.makeText(this,
getString(R.string.label_error_connection, errorName, errorCode),
Toast.LENGTH_LONG).show()
}
else -> {
Toast.makeText(this,
getString(R.string.label_error_connection_closed),
Toast.LENGTH_LONG).show()
}
}
}
}
}
}
......@@ -777,7 +782,7 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
drawerLayout.openDrawer(GravityCompat.START)
}
connectedAccount = accountId
modelHelper.session.value?.orNull()?.let { session ->
modelHelper.connectedSession.value?.orNull()?.let { session ->
if (session.identities.isEmpty()) {
UserSetupActivity.launch(this)
}
......@@ -808,9 +813,8 @@ class ChatActivity : ServiceBoundActivity(), SharedPreferences.OnSharedPreferenc
connectionStatusDisplay.setOnClickListener {
modelHelper.sessionManager.value?.orNull()?.apply {
ifDisconnected {
autoReconnect()
}
log(INFO, "ChatActivity", "Reconnect triggered: User action")
backend.value?.orNull()?.autoConnect(ignoreErrors = true, ignoreSetting = true)
}
}
......
......@@ -31,7 +31,8 @@ import butterknife.ButterKnife
import de.kuschku.libquassel.protocol.Buffer_Type
import de.kuschku.libquassel.quassel.BufferInfo
import de.kuschku.libquassel.util.flag.hasFlag
import de.kuschku.libquassel.util.helpers.value
import de.kuschku.libquassel.util.helper.combineLatest
import de.kuschku.libquassel.util.helper.value
import de.kuschku.quasseldroid.GlideApp
import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.settings.AppearanceSettings
......@@ -40,7 +41,10 @@ import de.kuschku.quasseldroid.ui.info.channel.ChannelInfoActivity
import de.kuschku.quasseldroid.ui.info.user.UserInfoActivity
import de.kuschku.quasseldroid.util.ColorContext
import de.kuschku.quasseldroid.util.avatars.AvatarHelper
import de.kuschku.quasseldroid.util.helper.*
import de.kuschku.quasseldroid.util.helper.loadAvatars
import de.kuschku.quasseldroid.util.helper.setTooltip
import de.kuschku.quasseldroid.util.helper.toLiveData
import de.kuschku.quasseldroid.util.helper.visibleIf
import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer
import de.kuschku.quasseldroid.util.service.ServiceBoundFragment
import de.kuschku.quasseldroid.util.ui.SpanFormatter
......
......@@ -35,13 +35,13 @@ import de.kuschku.libquassel.protocol.Buffer_Type
import de.kuschku.libquassel.protocol.NetworkId
import de.kuschku.libquassel.quassel.syncables.IrcChannel
import de.kuschku.libquassel.quassel.syncables.Network
import de.kuschku.libquassel.util.helpers.nullIf
import de.kuschku.libquassel.util.helpers.value
import de.kuschku.libquassel.util.helper.combineLatest
import de.kuschku.libquassel.util.helper.nullIf
import de.kuschku.libquassel.util.helper.value
import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.ui.chat.ChatActivity
import de.kuschku.quasseldroid.ui.chat.add.NetworkAdapter
import de.kuschku.quasseldroid.ui.chat.add.NetworkItem
import de.kuschku.quasseldroid.util.helper.combineLatest
import de.kuschku.quasseldroid.util.helper.setDependent
import de.kuschku.quasseldroid.util.helper.toLiveData
import de.kuschku.quasseldroid.util.ui.settings.fragment.ServiceBoundSettingsFragment
......@@ -158,7 +158,7 @@ class ChannelCreateFragment : ServiceBoundSettingsFragment() {
networkId = selectedNetworkId,
type = Buffer_Type.of(Buffer_Type.StatusBuffer)
)?.let { statusBuffer ->
modelHelper.session.value?.orNull()?.rpcHandler?.apply {
modelHelper.connectedSession.value?.orNull()?.rpcHandler?.apply {
sendInput(statusBuffer, "/join $channelName")
}
}
......@@ -175,7 +175,7 @@ class ChannelCreateFragment : ServiceBoundSettingsFragment() {
networkId = selectedNetworkId,
type = Buffer_Type.of(Buffer_Type.StatusBuffer)
)?.let { statusBuffer ->
modelHelper.session.value?.orNull()?.rpcHandler?.apply {
modelHelper.connectedSession.value?.orNull()?.rpcHandler?.apply {
sendInput(statusBuffer, "/join $channelName")
modelHelper.networks.switchMap {
it[selectedNetworkId]?.liveIrcChannel(channelName)
......
......@@ -32,11 +32,11 @@ import butterknife.BindView
import butterknife.ButterKnife
import de.kuschku.libquassel.protocol.NetworkId
import de.kuschku.libquassel.quassel.syncables.Network
import de.kuschku.libquassel.util.helper.combineLatest
import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.ui.chat.ChatActivity
import de.kuschku.quasseldroid.ui.chat.add.NetworkAdapter
import de.kuschku.quasseldroid.ui.chat.add.NetworkItem
import de.kuschku.quasseldroid.util.helper.combineLatest
import de.kuschku.quasseldroid.util.helper.toLiveData
import de.kuschku.quasseldroid.util.service.ServiceBoundFragment
import de.kuschku.quasseldroid.viewmodel.helper.QuasselViewModelHelper
......
......@@ -47,8 +47,9 @@ import de.kuschku.libquassel.protocol.NetworkId
import de.kuschku.libquassel.quassel.syncables.IrcUser
import de.kuschku.libquassel.quassel.syncables.Network
import de.kuschku.libquassel.util.Optional
import de.kuschku.libquassel.util.helpers.mapOrElse
import de.kuschku.libquassel.util.helpers.mapSwitchMap
import de.kuschku.libquassel.util.helper.combineLatest
import de.kuschku.libquassel.util.helper.mapOrElse
import de.kuschku.libquassel.util.helper.mapSwitchMap
import de.kuschku.libquassel.util.irc.IrcCaseMappers
import de.kuschku.libquassel.util.irc.SenderColorUtil
import de.kuschku.quasseldroid.GlideApp
......@@ -60,7 +61,6 @@ import de.kuschku.quasseldroid.ui.chat.add.NetworkItem
import de.kuschku.quasseldroid.ui.chat.nicks.NickListAdapter
import de.kuschku.quasseldroid.util.ColorContext
import de.kuschku.quasseldroid.util.avatars.AvatarHelper
import de.kuschku.quasseldroid.util.helper.combineLatest
import de.kuschku.quasseldroid.util.helper.loadWithFallbacks
import de.kuschku.quasseldroid.util.helper.styledAttributes
import de.kuschku.quasseldroid.util.helper.toLiveData
......
......@@ -49,10 +49,7 @@ import de.kuschku.libquassel.quassel.syncables.BufferViewConfig
import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork
import de.kuschku.libquassel.util.flag.hasFlag
import de.kuschku.libquassel.util.flag.minus
import de.kuschku.libquassel.util.helpers.mapMap
import de.kuschku.libquassel.util.helpers.mapOrElse
import de.kuschku.libquassel.util.helpers.nullIf
import de.kuschku.libquassel.util.helpers.value
import de.kuschku.libquassel.util.helper.*
import de.kuschku.quasseldroid.BuildConfig
import de.kuschku.quasseldroid.R
import de.kuschku.quasseldroid.persistence.db.AccountDatabase
......@@ -68,7 +65,10 @@ import de.kuschku.quasseldroid.ui.coresettings.network.NetworkEditActivity
import de.kuschku.quasseldroid.ui.info.channellist.ChannelListActivity
import de.kuschku.quasseldroid.util.ColorContext
import de.kuschku.quasseldroid.util.avatars.AvatarHelper
import de.kuschku.quasseldroid.util.helper.*