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 ff3d3b6c601d6a4d4191277a3a0013ce58155d40..06f8557dea64fdb4cdcb5ef3d8a50ba433c089c9 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 8e0a5715a74c5f6fbd036557b2567850e9516118..187d0e4f62f45a14d051578443362e4cf325b640 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 669871c742bbefc3eb2f777af46a6ee38377ba11..fc9f7d4006032d510a848d3405e0ebeac67dce2d 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 65b9ac2aba43c88b2d62b5b080e2a0b87c670ded..d316c46e5c961f051b293ee4f07f92f596475cd4 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 5782de5e71de1d947094697f47ff53d5f88c8396..0993a610a08be43e024361ea9e7e53abb907363a 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 6c78f4bca13ebf591b4913f27c0865cc52817b60..69979218595b957f5f01c28a25e1729bb1c7f57c 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 d26783664235c8edc040992249797a315b706cc9..7b934204ff39c3c5b68dddd4b336b7d6a4191ff3 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 085771d322fff8a99c979b3d3625ccd95339f437..1e1c1b3344b87f9b9a0d36653f1b85b5bb4b58a1 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 29e9f28ebccf483159128bf9d931e95c62486086..6d4bfe039fd86ef4b8e253c30f1cb915a46a0aaf 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 eadfc1dbeb2cac3ee9720ad70a27d93147f8157a..6d856bbca52e4039826a617ed29e850cf2775c40 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 902642367443ee3e62c1da32cadd24f319ff055f..ae98b7a3db75149af609276a28629550489c655e 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 4e70ff4722980f0132333e83e2d14b656f4fe28f..0a1c9f54b9dd6d76f0f48c9a7a6a65603efed10d 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 e8305e4918791f799b2eacfc7b9cdf1e748fdc6c..f11fda3157d603605083b700f6f75f24eaffa4c4 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 822c2164949e68a5488f4c0766db9730ed783386..836b7ee6a7508c16f9b1dacd5ad364369f233b5a 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 b8f008f9c5e755ee8b1abd8319ec24f3c22bb21e..6af7e0c76df20c763ccf5bad551cf2e18bfc6764 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 69fac5d68154396395bc4899ab11bfff5dbb2156..f7b676a2885e1227f3a6e60ff0c758b784d56300 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 7c2bdef0557b9427c6998876a3dbb3e4687e8955..36a463737b769bf7ce10a30593228d6c2631426b 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 c3dd50921332d2f09c8899b85f199050351fac9b..682618b75198f24db859cb2f933c3dc2828a40ef 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 b6744b99bdfd92e29ecabe7bec06cfa20905503b..03b8b5687400f86fa5cad9ae6d0e2a628ca57ea5 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 896280377575eb953f5cc8b289502477e4fa787d..68b177a1e2c83755ecb63ebdd6a1b24414dd4449 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 773e6b4ae66bba32e22b7a2807a84bf1929468ff..cb09100f690396f85858d5950e015e648ad7f41f 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 793c298a886bab17a6f0f424eaf4468e6203a50d..005e006e7891d49f0acd7b51f30231da5bde9a03 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)