From c786a25273bcb0356d26aefc68e12ac3fb80e82d Mon Sep 17 00:00:00 2001
From: Janne Koschinski <janne@kuschku.de>
Date: Fri, 17 May 2019 00:22:50 +0200
Subject: [PATCH] Fixes crash cluster a62ffe4d

---
 .../chat/add/create/ChannelCreateFragment.kt  |  5 +-
 .../ui/chat/add/join/ChannelJoinFragment.kt   |  3 +-
 .../ui/chat/add/query/QueryCreateFragment.kt  |  5 +-
 .../chat/buffers/BufferViewConfigFragment.kt  |  2 +-
 .../ui/chat/input/AutoCompleteHelper.kt       |  3 +-
 .../ui/chat/messages/MessageListFragment.kt   |  2 +-
 .../ui/coresettings/CoreSettingsFragment.kt   |  7 +--
 .../chatlist/ChatListBaseFragment.kt          |  3 +-
 .../network/NetworkBaseFragment.kt            |  5 +-
 .../ui/info/channel/ChannelInfoFragment.kt    |  3 +-
 .../ui/info/user/UserInfoFragment.kt          | 19 +++----
 .../ui/setup/ServiceBoundSetupActivity.kt     |  2 +-
 .../quasseldroid/ui/setup/SetupActivity.kt    |  2 +-
 .../setup/network/NetworkSetupNetworkSlide.kt |  5 +-
 invokergenerator/build.gradle.kts             |  2 +
 .../libquassel/session/SessionManager.kt      |  7 +--
 .../libquassel/session/SessionStateHandler.kt |  7 +--
 .../util/helper/ObservableHelper.kt           |  4 ++
 .../util/helper/LiveDataHelper.kt             |  2 +-
 .../viewmodel/helper/ChatViewModelHelper.kt   | 54 +++++++++----------
 .../viewmodel/helper/EditorViewModelHelper.kt | 15 +++---
 .../helper/QuasselViewModelHelper.kt          | 12 ++---
 22 files changed, 92 insertions(+), 77 deletions(-)

diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/create/ChannelCreateFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/create/ChannelCreateFragment.kt
index ff3d3b6c6..06f8557de 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/create/ChannelCreateFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/create/ChannelCreateFragment.kt
@@ -37,6 +37,7 @@ import de.kuschku.libquassel.quassel.syncables.IrcChannel
 import de.kuschku.libquassel.quassel.syncables.Network
 import de.kuschku.libquassel.util.helper.combineLatest
 import de.kuschku.libquassel.util.helper.nullIf
+import de.kuschku.libquassel.util.helper.safeSwitchMap
 import de.kuschku.libquassel.util.helper.value
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.ui.chat.ChatActivity
@@ -107,7 +108,7 @@ class ChannelCreateFragment : ServiceBoundSettingsFragment() {
     }
 
     var hasSetNetwork = false
-    modelHelper.networks.switchMap {
+    modelHelper.networks.safeSwitchMap {
       combineLatest(it.values.map(Network::liveNetworkInfo)).map {
         it.map {
           NetworkItem(it.networkId, it.networkName)
@@ -177,7 +178,7 @@ class ChannelCreateFragment : ServiceBoundSettingsFragment() {
           )?.let { statusBuffer ->
             modelHelper.connectedSession.value?.orNull()?.rpcHandler?.apply {
               sendInput(statusBuffer, "/join $channelName")
-              modelHelper.networks.switchMap {
+              modelHelper.networks.safeSwitchMap {
                 it[selectedNetworkId]?.liveIrcChannel(channelName)
                 ?: Observable.empty()
               }.subscribe {
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/join/ChannelJoinFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/join/ChannelJoinFragment.kt
index 8e0a5715a..187d0e4f6 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/join/ChannelJoinFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/join/ChannelJoinFragment.kt
@@ -33,6 +33,7 @@ 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.libquassel.util.helper.safeSwitchMap
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.ui.chat.ChatActivity
 import de.kuschku.quasseldroid.ui.chat.add.NetworkAdapter
@@ -85,7 +86,7 @@ class ChannelJoinFragment : ServiceBoundFragment() {
     }
 
     var hasSetNetwork = false
-    modelHelper.networks.switchMap {
+    modelHelper.networks.safeSwitchMap {
       combineLatest(it.values.map(Network::liveNetworkInfo)).map {
         it.map {
           NetworkItem(it.networkId, it.networkName)
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/query/QueryCreateFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/query/QueryCreateFragment.kt
index 669871c74..fc9f7d400 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/query/QueryCreateFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/add/query/QueryCreateFragment.kt
@@ -50,6 +50,7 @@ import de.kuschku.libquassel.util.Optional
 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.helper.safeSwitchMap
 import de.kuschku.libquassel.util.irc.IrcCaseMappers
 import de.kuschku.libquassel.util.irc.SenderColorUtil
 import de.kuschku.quasseldroid.GlideApp
@@ -129,7 +130,7 @@ class QueryCreateFragment : ServiceBoundFragment() {
     }
 
     var hasSetNetwork = false
-    modelHelper.networks.switchMap {
+    modelHelper.networks.safeSwitchMap {
       combineLatest(it.values.map(Network::liveNetworkInfo)).map {
         it.map {
           NetworkItem(it.networkId, it.networkName)
@@ -206,7 +207,7 @@ class QueryCreateFragment : ServiceBoundFragment() {
         Optional.ofNullable(networks[networkId])
       }.mapSwitchMap {
         it.liveIrcUsers()
-      }.mapOrElse(emptyList()).switchMap {
+      }.mapOrElse(emptyList()).safeSwitchMap {
         combineLatest<IrcUserItem>(
           it.map<IrcUser, Observable<IrcUserItem>?> {
             it.updates().map { user ->
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt
index 65b9ac2ab..d316c46e5 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/buffers/BufferViewConfigFragment.kt
@@ -263,7 +263,7 @@ class BufferViewConfigFragment : ServiceBoundFragment() {
     ButterKnife.bind(this, view)
 
     val adapter = BufferViewConfigAdapter()
-    modelHelper.bufferViewConfigs.switchMap {
+    modelHelper.bufferViewConfigs.safeSwitchMap {
       combineLatest(it.map(BufferViewConfig::liveUpdates))
     }.toLiveData().observe(this, Observer {
       if (it != null) {
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt
index 5782de5e7..0993a610a 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt
@@ -34,6 +34,7 @@ import de.kuschku.libquassel.session.ISession
 import de.kuschku.libquassel.util.Optional
 import de.kuschku.libquassel.util.flag.hasFlag
 import de.kuschku.libquassel.util.helper.nullIf
+import de.kuschku.libquassel.util.helper.safeSwitchMap
 import de.kuschku.libquassel.util.helper.value
 import de.kuschku.libquassel.util.irc.SenderColorUtil
 import de.kuschku.quasseldroid.R
@@ -260,7 +261,7 @@ class AutoCompleteHelper(
   }
 
   fun autoComplete(reverse: Boolean = false) {
-    helper.editor.lastWord.switchMap { it }.value?.let { originalWord ->
+    helper.editor.lastWord.safeSwitchMap { it }.value?.let { originalWord ->
       val previous = autoCompletionState
       if (!originalWord.second.isEmpty()) {
         val autoCompletedWords = helper.rawAutoCompleteData.value?.let { (sessionOptional, id, lastWord) ->
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt
index 6c78f4bca..699792185 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/messages/MessageListFragment.kt
@@ -372,7 +372,7 @@ class MessageListFragment : ServiceBoundFragment() {
                              modelHelper.chat.expandedMessages,
                              modelHelper.markerLine)
       .toLiveData().switchMapNotNull { (buffer, selected, expanded, markerLine) ->
-        accountDatabase.accounts().listen(accountId).switchMap {
+        accountDatabase.accounts().listen(accountId).safeSwitchMap {
           database.filtered().listen(accountId,
                                      buffer,
                                      it.defaultFiltered).switchMapNotNull { filtered ->
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragment.kt
index d26783664..7b934204f 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/CoreSettingsFragment.kt
@@ -37,6 +37,7 @@ import de.kuschku.libquassel.quassel.syncables.BufferViewConfig
 import de.kuschku.libquassel.quassel.syncables.Identity
 import de.kuschku.libquassel.quassel.syncables.Network
 import de.kuschku.libquassel.util.helper.combineLatest
+import de.kuschku.libquassel.util.helper.safeSwitchMap
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.ui.coresettings.aliaslist.AliasListActivity
 import de.kuschku.quasseldroid.ui.coresettings.chatlist.ChatlistCreateActivity
@@ -136,7 +137,7 @@ class CoreSettingsFragment : ServiceBoundFragment() {
     networks.addItemDecoration(itemDecoration)
     ViewCompat.setNestedScrollingEnabled(networks, false)
 
-    modelHelper.networks.switchMap {
+    modelHelper.networks.safeSwitchMap {
       if (it.isEmpty()) {
         Observable.just(emptyList())
       } else {
@@ -155,7 +156,7 @@ class CoreSettingsFragment : ServiceBoundFragment() {
     identities.addItemDecoration(itemDecoration)
     ViewCompat.setNestedScrollingEnabled(identities, false)
 
-    modelHelper.identities.switchMap {
+    modelHelper.identities.safeSwitchMap {
       if (it.isEmpty()) {
         Observable.just(emptyList())
       } else {
@@ -174,7 +175,7 @@ class CoreSettingsFragment : ServiceBoundFragment() {
     chatlists.addItemDecoration(itemDecoration)
     ViewCompat.setNestedScrollingEnabled(chatlists, false)
 
-    modelHelper.bufferViewConfigMap.switchMap {
+    modelHelper.bufferViewConfigMap.safeSwitchMap {
       combineLatest(it.values.map(BufferViewConfig::liveUpdates)).map {
         it.map {
           SettingsItem(it.bufferViewId(), it.bufferViewName())
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListBaseFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListBaseFragment.kt
index 085771d32..1e1c1b334 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListBaseFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/chatlist/ChatListBaseFragment.kt
@@ -42,6 +42,7 @@ import de.kuschku.libquassel.util.flag.hasFlag
 import de.kuschku.libquassel.util.flag.minus
 import de.kuschku.libquassel.util.flag.plus
 import de.kuschku.libquassel.util.helper.combineLatest
+import de.kuschku.libquassel.util.helper.safeSwitchMap
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.defaults.Defaults
 import de.kuschku.quasseldroid.util.helper.toLiveData
@@ -121,7 +122,7 @@ abstract class ChatListBaseFragment(private val initDefault: Boolean) :
     val networkAdapter = NetworkAdapter(R.string.settings_chatlist_network_all)
     networkId.adapter = networkAdapter
 
-    modelHelper.networks.switchMap {
+    modelHelper.networks.safeSwitchMap {
       combineLatest(it.values.map(Network::liveNetworkInfo)).map {
         it.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, INetwork.NetworkInfo::networkName))
       }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkBaseFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkBaseFragment.kt
index 29e9f28eb..6d4bfe039 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkBaseFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/coresettings/network/NetworkBaseFragment.kt
@@ -45,6 +45,7 @@ import de.kuschku.libquassel.session.ISession
 import de.kuschku.libquassel.util.Optional
 import de.kuschku.libquassel.util.helper.combineLatest
 import de.kuschku.libquassel.util.helper.nullIf
+import de.kuschku.libquassel.util.helper.safeSwitchMap
 import de.kuschku.libquassel.util.helper.value
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.defaults.Defaults
@@ -171,7 +172,7 @@ abstract class NetworkBaseFragment(private val initDefault: Boolean) :
     val identityAdapter = IdentityAdapter()
     identity.adapter = identityAdapter
 
-    modelHelper.identities.switchMap {
+    modelHelper.identities.safeSwitchMap {
       combineLatest(it.values.map(Identity::liveUpdates)).map {
         it.sortedBy(Identity::identityName)
       }
@@ -211,7 +212,7 @@ abstract class NetworkBaseFragment(private val initDefault: Boolean) :
       modelHelper.networks.map { Optional.ofNullable(it[networkId]) }
         .filter(Optional<Network>::isPresent)
         .map(Optional<Network>::get)
-        .switchMap(Network::liveCaps)
+        .safeSwitchMap(Network::liveCaps)
         .toLiveData()
         .observe(this, Observer {
           autoidentifyWarning.visibleIf(it.contains("sasl"))
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoFragment.kt
index eadfc1dbe..6d856bbca 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/channel/ChannelInfoFragment.kt
@@ -35,6 +35,7 @@ import de.kuschku.libquassel.protocol.NetworkId
 import de.kuschku.libquassel.quassel.BufferInfo
 import de.kuschku.libquassel.quassel.syncables.IrcChannel
 import de.kuschku.libquassel.util.helper.combineLatest
+import de.kuschku.libquassel.util.helper.safeSwitchMap
 import de.kuschku.libquassel.util.helper.value
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.settings.MessageSettings
@@ -109,7 +110,7 @@ class ChannelInfoFragment : ServiceBoundFragment() {
       } ?: Pair(null, IrcChannel.NULL)
     }.filter {
       it.second != IrcChannel.NULL
-    }.switchMap { (info, channel) ->
+    }.safeSwitchMap { (info, channel) ->
       channel.updates().map {
         Pair(info, it)
       }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoFragment.kt
index 902642367..ae98b7a3d 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/info/user/UserInfoFragment.kt
@@ -47,10 +47,7 @@ import de.kuschku.libquassel.quassel.syncables.IgnoreListManager
 import de.kuschku.libquassel.quassel.syncables.IrcChannel
 import de.kuschku.libquassel.quassel.syncables.IrcUser
 import de.kuschku.libquassel.util.Optional
-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.libquassel.util.helper.*
 import de.kuschku.libquassel.util.irc.HostmaskHelper
 import de.kuschku.libquassel.util.irc.IrcCaseMappers
 import de.kuschku.quasseldroid.R
@@ -182,7 +179,7 @@ class UserInfoFragment : ServiceBoundFragment() {
     }
 
     combineLatest(modelHelper.connectedSession,
-                  modelHelper.networks).switchMap { (sessionOptional, networks) ->
+                  modelHelper.networks).safeSwitchMap { (sessionOptional, networks) ->
       fun processUser(user: IrcUser, bufferSyncer: BufferSyncer? = null, info: BufferInfo? = null,
                       ignoreItems: List<IgnoreListManager.IgnoreListItem>? = null): Observable<Optional<IrcUserInfo>> {
         actionShortcut.post(::updateShortcutVisibility)
@@ -223,7 +220,7 @@ class UserInfoFragment : ServiceBoundFragment() {
               combineLatest(user.channels().map { channelName ->
                 user.network().liveIrcChannel(
                   channelName
-                ).switchMap { channel ->
+                ).safeSwitchMap { channel ->
                   channel.updates().map {
                     Optional.ofNullable(
                       bufferSyncer?.find(
@@ -267,7 +264,7 @@ class UserInfoFragment : ServiceBoundFragment() {
         val bufferSyncer = session?.bufferSyncer
         val bufferInfo = bufferSyncer?.bufferInfo(bufferId)
         bufferInfo?.let {
-          networks[it.networkId]?.liveIrcUser(it.bufferName)?.switchMap(IrcUser::updates)?.switchMap {
+          networks[it.networkId]?.liveIrcUser(it.bufferName)?.safeSwitchMap(IrcUser::updates)?.safeSwitchMap {
             processUser(it, bufferSyncer, bufferInfo)
           }
         }
@@ -276,17 +273,17 @@ class UserInfoFragment : ServiceBoundFragment() {
 
         networks[networkId]
           ?.liveIrcUser(nickName)
-          ?.switchMap(IrcUser::updates)
-          ?.switchMap { user ->
+          ?.safeSwitchMap(IrcUser::updates)
+          ?.safeSwitchMap { user ->
             ignoreListManager?.liveMatchingRules(user.hostMask())?.map {
               Pair(user, it)
             } ?: Observable.just(Pair(user, emptyList()))
-          }?.switchMap { (user, ignoreItems) ->
+          }?.safeSwitchMap { (user, ignoreItems) ->
             processUser(user,
                         sessionOptional?.orNull()?.bufferSyncer,
                         ignoreItems = ignoreItems)
           }
-      } ?: Observable.just(IrcUser.NULL).switchMap { user -> processUser(user, null, null) }
+      } ?: Observable.just(IrcUser.NULL).safeSwitchMap { user -> processUser(user, null, null) }
     }.toLiveData().observe(this, Observer {
       val user = it.orNull()
       if (user != null) {
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSetupActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSetupActivity.kt
index 4e70ff472..0a1c9f54b 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSetupActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/ServiceBoundSetupActivity.kt
@@ -68,7 +68,7 @@ abstract class ServiceBoundSetupActivity :
   protected abstract val fragments: List<SlideFragment>
 
   private val currentPage = MutableLiveData<SlideFragment?>()
-  private val isValid = currentPage.switchMap(SlideFragment::valid).or(false)
+  private val isValid = currentPage.safeSwitchMap(SlideFragment::valid).or(false)
 
   @DrawableRes
   protected val icon: Int = R.mipmap.ic_launcher_recents
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/SetupActivity.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/SetupActivity.kt
index e8305e491..f11fda315 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/SetupActivity.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/SetupActivity.kt
@@ -57,7 +57,7 @@ abstract class SetupActivity : DaggerAppCompatActivity() {
   protected abstract val fragments: List<SlideFragment>
 
   private val currentPage = MutableLiveData<SlideFragment?>()
-  private val isValid = currentPage.switchMap(SlideFragment::valid).or(false)
+  private val isValid = currentPage.safeSwitchMap(SlideFragment::valid).or(false)
 
   @DrawableRes
   protected val icon: Int = R.mipmap.ic_launcher_recents
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupNetworkSlide.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupNetworkSlide.kt
index 822c21649..836b7ee6a 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupNetworkSlide.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/setup/network/NetworkSetupNetworkSlide.kt
@@ -40,6 +40,7 @@ import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork
 import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork.PortDefaults.PORT_PLAINTEXT
 import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork.PortDefaults.PORT_SSL
 import de.kuschku.libquassel.util.helper.combineLatest
+import de.kuschku.libquassel.util.helper.safeSwitchMap
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.defaults.DefaultNetworkServer
 import de.kuschku.quasseldroid.ui.coresettings.chatlist.NetworkAdapter
@@ -187,7 +188,7 @@ class NetworkSetupNetworkSlide : ServiceBoundSlideFragment() {
 
     identity.adapter = identityAdapter
 
-    modelHelper.identities.switchMap {
+    modelHelper.identities.safeSwitchMap {
       combineLatest(it.values.map(Identity::liveUpdates)).map {
         it.sortedBy(Identity::identityName)
       }
@@ -197,7 +198,7 @@ class NetworkSetupNetworkSlide : ServiceBoundSlideFragment() {
       }
     })
 
-    modelHelper.networks.switchMap {
+    modelHelper.networks.safeSwitchMap {
       combineLatest(it.values.map(Network::liveNetworkInfo)).map {
         it.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER, INetwork.NetworkInfo::networkName))
       }
diff --git a/invokergenerator/build.gradle.kts b/invokergenerator/build.gradle.kts
index b8f008f9c..6af7e0c76 100644
--- a/invokergenerator/build.gradle.kts
+++ b/invokergenerator/build.gradle.kts
@@ -1,3 +1,5 @@
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
 /*
  * Quasseldroid - Quassel client for Android
  *
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 69fac5d68..f7b676a28 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt
@@ -29,6 +29,7 @@ import de.kuschku.libquassel.util.compatibility.LoggingHandler.Companion.log
 import de.kuschku.libquassel.util.compatibility.LoggingHandler.LogLevel.*
 import de.kuschku.libquassel.util.helper.combineLatest
 import de.kuschku.libquassel.util.helper.or
+import de.kuschku.libquassel.util.helper.safeSwitchMap
 import de.kuschku.libquassel.util.helper.value
 import io.reactivex.Observable
 import io.reactivex.disposables.Disposable
@@ -48,7 +49,7 @@ class SessionManager(
   private val disposables = mutableListOf<Disposable>()
 
   // Helping Rx Mappers
-  val connectionProgress: Observable<Triple<ConnectionState, Int, Int>> = progressData.switchMap {
+  val connectionProgress: Observable<Triple<ConnectionState, Int, Int>> = progressData.safeSwitchMap {
     combineLatest(it.state, it.progress).map { (state, progress) ->
       Triple(state, progress.first, progress.second)
     }
@@ -69,8 +70,8 @@ class SessionManager(
   init {
     log(INFO, "Session", "Session created")
 
-    disposables.add(state.distinctUntilChanged().subscribe {
-      if (it == ConnectionState.CONNECTED) {
+    disposables.add(state.distinctUntilChanged().subscribe { state: ConnectionState ->
+      if (state == ConnectionState.CONNECTED) {
         updateStateConnected()
       }
     })
diff --git a/lib/src/main/java/de/kuschku/libquassel/session/SessionStateHandler.kt b/lib/src/main/java/de/kuschku/libquassel/session/SessionStateHandler.kt
index 7c2bdef05..36a463737 100644
--- a/lib/src/main/java/de/kuschku/libquassel/session/SessionStateHandler.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/session/SessionStateHandler.kt
@@ -20,6 +20,7 @@
 package de.kuschku.libquassel.session
 
 import de.kuschku.libquassel.session.manager.SessionState
+import de.kuschku.libquassel.util.helper.safeSwitchMap
 import io.reactivex.subjects.BehaviorSubject
 
 open class SessionStateHandler constructor(
@@ -42,15 +43,15 @@ open class SessionStateHandler constructor(
     it.progress
   }
 
-  val errors = progressData.switchMap {
+  val errors = progressData.safeSwitchMap {
     it.error
   }
 
-  val progress = progressData.switchMap {
+  val progress = progressData.safeSwitchMap {
     it.progress
   }
 
-  val state = progressData.switchMap {
+  val state = progressData.safeSwitchMap {
     it.state
   }
 
diff --git a/lib/src/main/java/de/kuschku/libquassel/util/helper/ObservableHelper.kt b/lib/src/main/java/de/kuschku/libquassel/util/helper/ObservableHelper.kt
index c3dd50921..682618b75 100644
--- a/lib/src/main/java/de/kuschku/libquassel/util/helper/ObservableHelper.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/util/helper/ObservableHelper.kt
@@ -43,6 +43,10 @@ fun <T : Any, U : Any> Observable<Optional<T>>.mapMapNullable(
   }
 }
 
+inline fun <T : Any, R : Any> Observable<T>.safeSwitchMap(
+  noinline mapper: (T) -> ObservableSource<R>): Observable<R> =
+  switchMap(mapper)
+
 fun <T : Any, U : Any> Observable<T>.mapNullable(
   nullableValue: T,
   mapper: (T?) -> U): Observable<U> = map {
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/util/helper/LiveDataHelper.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/util/helper/LiveDataHelper.kt
index b6744b99b..03b8b5687 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/util/helper/LiveDataHelper.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/util/helper/LiveDataHelper.kt
@@ -25,7 +25,7 @@ import androidx.lifecycle.*
 import io.reactivex.Observable
 
 @MainThread
-inline fun <X, Y> LiveData<X?>.switchMap(
+inline fun <X, Y> LiveData<X?>.safeSwitchMap(
   crossinline func: (X) -> LiveData<Y>?
 ): LiveData<Y> {
   val result = MediatorLiveData<Y>()
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/ChatViewModelHelper.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/ChatViewModelHelper.kt
index 896280377..68b177a1e 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/ChatViewModelHelper.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/ChatViewModelHelper.kt
@@ -60,19 +60,19 @@ open class ChatViewModelHelper @Inject constructor(
    * An observable of the changes of the markerline, as pairs of `(old, new)`
    */
   val markerLine = connectedSession.mapSwitchMap { currentSession ->
-    chat.bufferId.switchMap { currentBuffer ->
+    chat.bufferId.safeSwitchMap { currentBuffer ->
       // Get a stream of the latest marker line
       currentSession.bufferSyncer.liveMarkerLine(currentBuffer)
     }
   }
 
   val bufferData = combineLatest(connectedSession, chat.bufferId)
-    .switchMap { (sessionOptional, id) ->
+    .safeSwitchMap { (sessionOptional, id) ->
       val session = sessionOptional.orNull()
       val bufferSyncer = session?.bufferSyncer
       if (bufferSyncer != null) {
-        session.liveNetworks().switchMap { networks ->
-          bufferSyncer.liveBufferInfos().switchMap {
+        session.liveNetworks().safeSwitchMap { networks ->
+          bufferSyncer.liveBufferInfos().safeSwitchMap {
             val info = bufferSyncer.bufferInfo(id)
             val network = networks[info?.networkId]
             if (info == null || network == null) {
@@ -80,7 +80,7 @@ open class ChatViewModelHelper @Inject constructor(
             } else {
               when (info.type.toInt()) {
                 BufferInfo.Type.QueryBuffer.toInt()   -> {
-                  network.liveIrcUser(info.bufferName).switchMap {
+                  network.liveIrcUser(info.bufferName).safeSwitchMap {
                     it.updates().map { user ->
                       BufferData(
                         info = info,
@@ -96,7 +96,7 @@ open class ChatViewModelHelper @Inject constructor(
                 BufferInfo.Type.ChannelBuffer.toInt() -> {
                   network.liveIrcChannel(
                     info.bufferName
-                  ).switchMap { channel ->
+                  ).safeSwitchMap { channel ->
                     channel.updates().map {
                       BufferData(
                         info = info,
@@ -132,15 +132,15 @@ open class ChatViewModelHelper @Inject constructor(
     bufferData.distinctUntilChanged().throttleLast(100, TimeUnit.MILLISECONDS)
 
   val nickData: Observable<List<IrcUserItem>> = combineLatest(connectedSession, chat.bufferId)
-    .switchMap { (sessionOptional, buffer) ->
+    .safeSwitchMap { (sessionOptional, buffer) ->
       val session = sessionOptional.orNull()
       val bufferSyncer = session?.bufferSyncer
       val bufferInfo = bufferSyncer?.bufferInfo(buffer)
       if (bufferInfo?.type?.hasFlag(Buffer_Type.ChannelBuffer) == true) {
-        session.liveNetworks().switchMap { networks ->
+        session.liveNetworks().safeSwitchMap { networks ->
           val network = networks[bufferInfo.networkId]
           network?.liveIrcChannel(bufferInfo.bufferName)?.switchMapNullable(IrcChannel.NULL) { ircChannel ->
-            ircChannel?.liveIrcUsers()?.switchMap { users ->
+            ircChannel?.liveIrcUsers()?.safeSwitchMap { users ->
               combineLatest<IrcUserItem>(
                 users.map<IrcUser, Observable<IrcUserItem>?> {
                   it.updates().map { user ->
@@ -166,7 +166,7 @@ open class ChatViewModelHelper @Inject constructor(
                 }
               )
             } ?: Observable.just(emptyList())
-          }
+          } ?: Observable.just(emptyList())
         }
       } else {
         Observable.just(emptyList())
@@ -176,12 +176,12 @@ open class ChatViewModelHelper @Inject constructor(
     nickData.distinctUntilChanged().throttleLast(100, TimeUnit.MILLISECONDS)
 
   val selectedBuffer = combineLatest(connectedSession, chat.selectedBufferId, bufferViewConfig)
-    .switchMap { (sessionOptional, buffer, bufferViewConfigOptional) ->
+    .safeSwitchMap { (sessionOptional, buffer, bufferViewConfigOptional) ->
       val session = sessionOptional.orNull()
       val bufferSyncer = session?.bufferSyncer
       val bufferViewConfig = bufferViewConfigOptional.orNull()
       if (bufferSyncer != null && bufferViewConfig != null) {
-        session.liveNetworks().switchMap { networks ->
+        session.liveNetworks().safeSwitchMap { networks ->
           val hiddenState = when {
             bufferViewConfig.removedBuffers().contains(buffer)            ->
               BufferHiddenState.HIDDEN_PERMANENT
@@ -235,22 +235,22 @@ open class ChatViewModelHelper @Inject constructor(
 
   val bufferList: Observable<Pair<BufferViewConfig?, List<BufferProps>>> =
     combineLatest(connectedSession, bufferViewConfig, chat.showHidden, chat.bufferSearch)
-      .switchMap { (sessionOptional, configOptional, showHiddenRaw, bufferSearch) ->
+      .safeSwitchMap { (sessionOptional, configOptional, showHiddenRaw, bufferSearch) ->
         val session = sessionOptional.orNull()
         val bufferSyncer = session?.bufferSyncer
         val showHidden = showHiddenRaw ?: false
         val config = configOptional.orNull()
         if (bufferSyncer != null && config != null) {
-          session.liveNetworks().switchMap { networks ->
+          session.liveNetworks().safeSwitchMap { networks ->
             config.liveUpdates()
-              .switchMap { currentConfig ->
+              .safeSwitchMap { currentConfig ->
                 combineLatest<Collection<BufferId>>(
                   listOf(
                     config.liveBuffers(),
                     config.liveTemporarilyRemovedBuffers(),
                     config.liveRemovedBuffers()
                   )
-                ).switchMap { (ids, temp, perm) ->
+                ).safeSwitchMap { (ids, temp, perm) ->
                   fun transformIds(ids: Collection<BufferId>, state: BufferHiddenState) =
                     ids.asSequence().mapNotNull { id ->
                       bufferSyncer.bufferInfo(id)
@@ -271,11 +271,11 @@ open class ChatViewModelHelper @Inject constructor(
                         it to network
                       }
                     }.map<Pair<BufferInfo, Network>, Observable<BufferProps>?> { (info, network) ->
-                      bufferSyncer.liveActivity(info.bufferId).switchMap { activity ->
+                      bufferSyncer.liveActivity(info.bufferId).safeSwitchMap { activity ->
                         bufferSyncer.liveHighlightCount(info.bufferId).map { highlights ->
                           activity to highlights
                         }
-                      }.switchMap { (activity, highlights) ->
+                      }.safeSwitchMap { (activity, highlights) ->
                         val name = info.bufferName?.trim() ?: ""
                         val search = bufferSearch.trim()
                         val matchMode = when {
@@ -285,9 +285,9 @@ open class ChatViewModelHelper @Inject constructor(
                         }
                         when (info.type.toInt()) {
                           BufferInfo.Type.QueryBuffer.toInt()   -> {
-                            network.liveNetworkInfo().switchMap { networkInfo ->
-                              network.liveConnectionState().switchMap { connectionState ->
-                                network.liveIrcUser(info.bufferName).switchMap {
+                            network.liveNetworkInfo().safeSwitchMap { networkInfo ->
+                              network.liveConnectionState().safeSwitchMap { connectionState ->
+                                network.liveIrcUser(info.bufferName).safeSwitchMap {
                                   it.updates().mapNullable(IrcUser.NULL) { user ->
                                     BufferProps(
                                       info = info,
@@ -311,9 +311,9 @@ open class ChatViewModelHelper @Inject constructor(
                             }
                           }
                           BufferInfo.Type.ChannelBuffer.toInt() -> {
-                            network.liveNetworkInfo().switchMap { networkInfo ->
-                              network.liveConnectionState().switchMap { connectionState ->
-                                network.liveIrcChannel(info.bufferName).switchMap { channel ->
+                            network.liveNetworkInfo().safeSwitchMap { networkInfo ->
+                              network.liveConnectionState().safeSwitchMap { connectionState ->
+                                network.liveIrcChannel(info.bufferName).safeSwitchMap { channel ->
                                   channel.updates().mapNullable(IrcChannel.NULL) {
                                     BufferProps(
                                       info = info,
@@ -335,7 +335,7 @@ open class ChatViewModelHelper @Inject constructor(
                             }
                           }
                           BufferInfo.Type.StatusBuffer.toInt()  -> {
-                            network.liveNetworkInfo().switchMap { networkInfo ->
+                            network.liveNetworkInfo().safeSwitchMap { networkInfo ->
                               network.liveConnectionState().map { connectionState ->
                                 BufferProps(
                                   info = info,
@@ -381,7 +381,7 @@ open class ChatViewModelHelper @Inject constructor(
                     }.filter {
                       !config.hideInactiveNetworks() || it.isConnected()
                     }.map<Network, Observable<BufferProps>?> { network ->
-                      network.liveNetworkInfo().switchMap { networkInfo ->
+                      network.liveNetworkInfo().safeSwitchMap { networkInfo ->
                         network.liveConnectionState().map { connectionState ->
                           BufferProps(
                             info = BufferInfo(
@@ -404,7 +404,7 @@ open class ChatViewModelHelper @Inject constructor(
                     }
                   }
 
-                  bufferSyncer.liveBufferInfos().switchMap {
+                  bufferSyncer.liveBufferInfos().safeSwitchMap {
                     val buffers = if (showHidden || bufferSearch.isNotBlank()) {
                       transformIds(ids, BufferHiddenState.VISIBLE) +
                       transformIds(temp - ids, BufferHiddenState.HIDDEN_TEMPORARY) +
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/EditorViewModelHelper.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/EditorViewModelHelper.kt
index 773e6b4ae..cb09100f6 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/EditorViewModelHelper.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/EditorViewModelHelper.kt
@@ -31,6 +31,7 @@ import de.kuschku.libquassel.util.flag.hasFlag
 import de.kuschku.libquassel.util.helper.combineLatest
 import de.kuschku.libquassel.util.helper.mapNullable
 import de.kuschku.libquassel.util.helper.nullIf
+import de.kuschku.libquassel.util.helper.safeSwitchMap
 import de.kuschku.quasseldroid.viewmodel.ChatViewModel
 import de.kuschku.quasseldroid.viewmodel.EditorViewModel
 import de.kuschku.quasseldroid.viewmodel.QuasselViewModel
@@ -47,7 +48,7 @@ open class EditorViewModelHelper @Inject constructor(
 ) : ChatViewModelHelper(chat, quassel) {
   val rawAutoCompleteData: Observable<Triple<Optional<ISession>, BufferId, Pair<String, IntRange>>> =
     combineLatest(connectedSession, chat.bufferId, editor.lastWord)
-      .switchMap { (sessionOptional, id, lastWordWrapper) ->
+      .safeSwitchMap { (sessionOptional, id, lastWordWrapper) ->
         lastWordWrapper
           .distinctUntilChanged()
           .map { lastWord ->
@@ -58,19 +59,19 @@ open class EditorViewModelHelper @Inject constructor(
   val autoCompleteData: Observable<Pair<String, List<AutoCompleteItem>>> = rawAutoCompleteData
     .distinctUntilChanged()
     .debounce(300, TimeUnit.MILLISECONDS)
-    .switchMap { (sessionOptional, id, lastWord) ->
+    .safeSwitchMap { (sessionOptional, id, lastWord) ->
       val session = sessionOptional.orNull()
       val bufferSyncer = session?.bufferSyncer
       val bufferInfo = bufferSyncer?.bufferInfo(id)
       if (bufferSyncer != null) {
-        session.liveNetworks().switchMap { networks ->
-          bufferSyncer.liveBufferInfos().switchMap { infos ->
-            session.aliasManager.updates().map(AliasManager::aliasList).switchMap { aliases ->
+        session.liveNetworks().safeSwitchMap { networks ->
+          bufferSyncer.liveBufferInfos().safeSwitchMap { infos ->
+            session.aliasManager.updates().map(AliasManager::aliasList).safeSwitchMap { aliases ->
               val network = networks[bufferInfo?.networkId] ?: Network.NULL
               val ircChannel = if (bufferInfo?.type?.hasFlag(Buffer_Type.ChannelBuffer) == true) {
                 network.ircChannel(bufferInfo.bufferName) ?: IrcChannel.NULL
               } else IrcChannel.NULL
-              ircChannel.liveIrcUsers().switchMap { users ->
+              ircChannel.liveIrcUsers().safeSwitchMap { users ->
                 fun processResults(results: List<Observable<out AutoCompleteItem>>) =
                   combineLatest<AutoCompleteItem>(results)
                     .map { list ->
@@ -102,7 +103,7 @@ open class EditorViewModelHelper @Inject constructor(
                   }.map { (info, network) ->
                     network.liveIrcChannel(
                       info.bufferName
-                    ).switchMap { channel ->
+                    ).safeSwitchMap { channel ->
                       channel.updates().mapNullable(IrcChannel.NULL) {
                         AutoCompleteItem.ChannelItem(
                           info = info,
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/QuasselViewModelHelper.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/QuasselViewModelHelper.kt
index 793c298a8..005e006e7 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/QuasselViewModelHelper.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/helper/QuasselViewModelHelper.kt
@@ -39,13 +39,13 @@ import javax.net.ssl.SSLSession
 open class QuasselViewModelHelper @Inject constructor(
   val quassel: QuasselViewModel
 ) {
-  val backend = quassel.backendWrapper.switchMap { it }
+  val backend = quassel.backendWrapper.safeSwitchMap { it }
   val sessionManager = backend.mapMapNullable(Backend::sessionManager)
   val connectedSession = sessionManager.mapSwitchMap(SessionManager::connectedSession)
   val rpcHandler = connectedSession.mapMap(ISession::rpcHandler)
   val ircListHelper = connectedSession.mapMap(ISession::ircListHelper)
   val features = sessionManager.mapSwitchMap { manager ->
-    manager.state.switchMap { state ->
+    manager.state.safeSwitchMap { state ->
       if (state != ConnectionState.CONNECTED) {
         Observable.just(Pair(false, Features.empty()))
       } else {
@@ -70,7 +70,7 @@ open class QuasselViewModelHelper @Inject constructor(
 
   val bufferViewManager = connectedSession.mapMap(ISession::bufferViewManager)
 
-  val errors = sessionManager.switchMap {
+  val errors = sessionManager.safeSwitchMap {
     it.orNull()?.errors ?: Observable.empty()
   }
 
@@ -92,11 +92,11 @@ open class QuasselViewModelHelper @Inject constructor(
 
   val aliasManager = connectedSession.mapMap(ISession::aliasManager)
 
-  val networks = connectedSession.switchMap {
+  val networks = connectedSession.safeSwitchMap {
     it.map(ISession::liveNetworks).orElse(Observable.just(emptyMap()))
   }
 
-  val identities = connectedSession.switchMap {
+  val identities = connectedSession.safeSwitchMap {
     it.map(ISession::liveIdentities).orElse(Observable.just(emptyMap()))
   }
 
@@ -115,7 +115,7 @@ open class QuasselViewModelHelper @Inject constructor(
     }
   }.mapOrElse(emptyList())
 
-  val bufferViewConfigMap = bufferViewManager.switchMap {
+  val bufferViewConfigMap = bufferViewManager.safeSwitchMap {
     it.map { manager ->
       manager.liveBufferViewConfigs().map {
         it.mapNotNull(manager::bufferViewConfig).associateBy(BufferViewConfig::bufferViewId)
-- 
GitLab