diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/viewmodel/QuasselViewModel.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/viewmodel/QuasselViewModel.kt index d9b90d73b843d64ab41a9da69fb23b73ffcf18ab..60c584f2cbffde72d87aa242289b61482b7623b9 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/ui/viewmodel/QuasselViewModel.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/ui/viewmodel/QuasselViewModel.kt @@ -25,7 +25,6 @@ import de.kuschku.quasseldroid_ng.util.helper.switchMap import de.kuschku.quasseldroid_ng.util.helper.switchMapRx import de.kuschku.quasseldroid_ng.util.helper.zip import io.reactivex.Observable -import io.reactivex.functions.BiFunction import io.reactivex.functions.Function import java.util.concurrent.TimeUnit @@ -102,7 +101,7 @@ class QuasselViewModel : ViewModel() { when (info.type.toInt()) { BufferInfo.Type.QueryBuffer.toInt() -> { network.liveIrcUser(info.bufferName).switchMap { user -> - user.live_realName.map { realName -> + user.liveRealName().map { realName -> ToolbarFragment.BufferData( info = info, network = network.networkInfo(), @@ -115,7 +114,7 @@ class QuasselViewModel : ViewModel() { network.liveIrcChannel( info.bufferName ).switchMap { channel -> - channel.live_topic.map { topic -> + channel.liveTopic().map { topic -> ToolbarFragment.BufferData( info = info, network = network.networkInfo(), @@ -145,39 +144,46 @@ class QuasselViewModel : ViewModel() { } } - val nickData = session.zip(buffer).switchMapRx { (session, buffer) -> + val nickData: LiveData<List<NickListAdapter.IrcUserItem>?> = session.zip( + buffer + ).switchMapRx { (session, buffer) -> val bufferSyncer = session?.bufferSyncer val bufferInfo = bufferSyncer?.bufferInfo(buffer) if (bufferInfo?.type?.hasFlag(Buffer_Type.ChannelBuffer) == true) { val network = session.networks[bufferInfo.networkId] val ircChannel = network?.ircChannel(bufferInfo.bufferName) if (ircChannel != null) { - Observable.combineLatest( - ircChannel.ircUsers().map { user -> - Observable.zip( - user.live_realName, user.live_away, - BiFunction<String, Boolean, Pair<String, Boolean>> { a, b -> Pair(a, b) } - ).map { (realName, away) -> - val userModes = ircChannel.userModes(user) - val prefixModes = network.prefixModes() + ircChannel.liveIrcUsers().switchMap { users -> + Observable.combineLatest( + users.map<IrcUser, Observable<NickListAdapter.IrcUserItem>?> { user -> + user.liveNick().switchMap { nick -> + user.liveRealName().switchMap { realName -> + user.liveIsAway().map { away -> + val userModes = ircChannel.userModes(user) + val prefixModes = network.prefixModes() - val lowestMode = userModes.mapNotNull { - prefixModes.indexOf(it) - }.min() ?: prefixModes.size + val lowestMode = userModes.mapNotNull { + prefixModes.indexOf(it) + }.min() ?: prefixModes.size - NickListAdapter.IrcUserItem( - user.nick(), - network.modesToPrefixes(userModes), - lowestMode, - realName, - away, - network.support("CASEMAPPING") - ) + NickListAdapter.IrcUserItem( + nick, + network.modesToPrefixes(userModes), + lowestMode, + realName, + away, + network.support("CASEMAPPING") + ) + } + } + } + }, + object : Function<Array<Any>, List<NickListAdapter.IrcUserItem>> { + override fun apply(array: Array<Any>) = + array.toList() as List<NickListAdapter.IrcUserItem> } - }, { array: Array<Any> -> - array.toList() as List<NickListAdapter.IrcUserItem> - } - ) + ) + } } else { Observable.just(emptyList()) } @@ -287,8 +293,8 @@ class QuasselViewModel : ViewModel() { when (info.type.toInt()) { BufferInfo.Type.QueryBuffer.toInt() -> { network.liveIrcUser(info.bufferName).switchMap { user -> - user.live_away.switchMap { away -> - user.live_realName.map { realName -> + user.liveIsAway().switchMap { away -> + user.liveRealName().map { realName -> BufferListAdapter.BufferProps( info = info, network = network.networkInfo(), @@ -310,7 +316,7 @@ class QuasselViewModel : ViewModel() { network.liveIrcChannel( info.bufferName ).switchMap { channel -> - channel.live_topic.map { topic -> + channel.liveTopic().map { topic -> BufferListAdapter.BufferProps( info = info, network = network.networkInfo(), diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/util/compatibility/AndroidLoggingHandler.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/util/compatibility/AndroidLoggingHandler.kt index 97b627e791ffc3e933e37af7d78fc8e236311e76..6453d805d878689536c559f678e6043ce048efa7 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/util/compatibility/AndroidLoggingHandler.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/util/compatibility/AndroidLoggingHandler.kt @@ -2,10 +2,11 @@ package de.kuschku.quasseldroid_ng.util.compatibility import android.util.Log import de.kuschku.libquassel.util.compatibility.LoggingHandler +import de.kuschku.quasseldroid_ng.BuildConfig object AndroidLoggingHandler : LoggingHandler() { override fun isLoggable(logLevel: LogLevel, tag: String): Boolean { - return Log.isLoggable(tag, priority(logLevel)) + return BuildConfig.DEBUG || Log.isLoggable(tag, priority(logLevel)) } override fun log(logLevel: LogLevel, tag: String, message: String?, throwable: Throwable?) { diff --git a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/StringSerializer.kt b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/StringSerializer.kt index 300f25a8743c7adedc6fe6f2e31d9665fdc9bf00..dfecdbe2cb826bf273ea9ed7bcd949ef2936b349 100644 --- a/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/StringSerializer.kt +++ b/lib/src/main/java/de/kuschku/libquassel/protocol/primitive/serializer/StringSerializer.kt @@ -87,12 +87,13 @@ abstract class StringSerializer( } else { val limit = buffer.limit() buffer.limit(buffer.position() + len - trailingNullBytes) - val charBuffer = charBuffer(len) - decoder.reset() - decoder.decode(buffer, charBuffer, true) + //val charBuffer = charBuffer(len) + //decoder.reset() + val charBuffer = decoder.charset().decode(buffer) + //decoder.decode(buffer, charBuffer, true) buffer.limit(limit) buffer.position(buffer.position() + trailingNullBytes) - charBuffer.flip() + //charBuffer.flip() charBuffer.toString() } } catch (e: Throwable) { @@ -109,12 +110,12 @@ abstract class StringSerializer( } else { val limit = buffer.limit() buffer.limit(buffer.position() + Math.max(0, len - trailingNullBytes)) - val charBuffer = charBuffer(len) - decoder.reset() - decoder.decode(buffer, charBuffer, true) + //val charBuffer = charBuffer(len) + val charBuffer = decoder.charset().decode(buffer) + //decoder.decode(buffer, charBuffer, true) buffer.limit(limit) buffer.position(buffer.position() + trailingNullBytes) - charBuffer.flip() + //charBuffer.flip() charBuffer.toString() } } catch (e: Throwable) { diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcChannel.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcChannel.kt index 34acb8ce5423527c150a6c6e23e1467f6af720b5..32daf7db49b996409e187e8eaa05c6dbc94ac492 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcChannel.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcChannel.kt @@ -6,6 +6,7 @@ import de.kuschku.libquassel.quassel.syncables.interfaces.IIrcChannel import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork import de.kuschku.libquassel.session.SignalProxy import de.kuschku.libquassel.util.helpers.getOr +import io.reactivex.Observable import io.reactivex.subjects.BehaviorSubject import java.nio.charset.Charset @@ -15,6 +16,9 @@ class IrcChannel( proxy: SignalProxy ) : SyncableObject(proxy, "IrcChannel"), IIrcChannel { override fun init() { + if (name().isEmpty()) { + println("Error: channelName is empty") + } renameObject("${network().networkId()}/${name()}") } @@ -79,6 +83,7 @@ class IrcChannel( network().newIrcUser(key) to value.value("") }.toMap() ) + live_userModes.onNext(_userModes) } override fun initSetProperties(properties: QVariantMap) { @@ -96,13 +101,30 @@ class IrcChannel( } fun name() = _name + fun liveName(): Observable<String> = live_name + fun topic() = _topic + fun liveTopic(): Observable<String> = live_topic + fun password() = _password + fun livePassword(): Observable<String> = live_password + fun encrypted() = _encrypted + fun liveEncrypted(): Observable<Boolean> = live_encrypted + fun network() = _network + fun ircUsers() = _userModes.keys + fun liveIrcUsers(): Observable<MutableSet<IrcUser>> = + live_userModes.map(MutableMap<IrcUser, String>::keys) + fun userModes(ircUser: IrcUser) = _userModes.getOr(ircUser, "") + fun liveUserModes(ircUser: IrcUser) = live_userModes.map { + _userModes.getOr(ircUser, "") + } fun userModes(nick: String) = network().ircUser(nick)?.let { userModes(it) } ?: "" + fun liveUserModes(nick: String) = network().ircUser(nick)?.let { userModes(it) } ?: "" + fun hasMode(mode: Char) = when (network().channelModeType(mode)) { INetwork.ChannelModeType.A_CHANMODE -> _A_channelModes.contains(mode) @@ -166,7 +188,6 @@ class IrcChannel( if (_topic == topic) return _topic = topic - live_topic.onNext(topic) super.setTopic(topic) } @@ -205,6 +226,7 @@ class IrcChannel( _userModes[user] = modes user.joinChannel(this, true) } + live_userModes.onNext(_userModes) if (newNicks.isNotEmpty()) super.joinIrcUsers( newNicks.map(Pair<IrcUser, String>::first).map(IrcUser::nick), @@ -222,12 +244,14 @@ class IrcChannel( if (!isKnownUser(ircuser)) return _userModes.remove(ircuser) + live_userModes.onNext(_userModes) ircuser.partChannel(this) if (network().isMe(ircuser) || _userModes.isEmpty()) { for (user in _userModes.keys) { user.partChannel(this) } _userModes.clear() + live_userModes.onNext(_userModes) network().removeIrcChannel(this) proxy.stopSynchronize(this) } @@ -242,6 +266,7 @@ class IrcChannel( if (ircuser == null || !isKnownUser(ircuser)) return _userModes[ircuser] = modes + live_userModes.onNext(_userModes) super.setUserModes(ircuser.nick(), modes) } @@ -259,6 +284,7 @@ class IrcChannel( if (_userModes.getOr(ircuser, "").contains(mode, ignoreCase = true)) return _userModes[ircuser] = _userModes.getOr(ircuser, "") + mode + live_userModes.onNext(_userModes) super.addUserMode(ircuser.nick(), mode) } @@ -273,6 +299,7 @@ class IrcChannel( return _userModes[ircuser] = _userModes.getOr(ircuser, "") .replace(mode, "", ignoreCase = true) + live_userModes.onNext(_userModes) super.addUserMode(ircuser.nick(), mode) } @@ -312,15 +339,36 @@ class IrcChannel( super.removeChannelMode(mode, value) } - private var _name: String = name - private var _topic: String = "" - val live_topic = BehaviorSubject.createDefault("") - private var _password: String = "" - private var _encrypted: Boolean = false - private var _userModes: MutableMap<IrcUser, String> = mutableMapOf() + private val live_name = BehaviorSubject.createDefault(name) + private var _name: String + get() = live_name.value + set(value) = live_name.onNext(value) + + private val live_topic = BehaviorSubject.createDefault("") + private var _topic: String + get() = live_topic.value + set(value) = live_topic.onNext(value) + + private val live_password = BehaviorSubject.createDefault("") + private var _password: String + get() = live_password.value + set(value) = live_password.onNext(value) + + private val live_encrypted = BehaviorSubject.createDefault(false) + private var _encrypted: Boolean + get() = live_encrypted.value + set(value) = live_encrypted.onNext(value) + + private val live_userModes = BehaviorSubject.createDefault(mutableMapOf<IrcUser, String>()) + private var _userModes: MutableMap<IrcUser, String> + get() = live_userModes.value + set(value) = live_userModes.onNext(value) + private var _network: Network = network + private var _codecForEncoding: Charset? = null private var _codecForDecoding: Charset? = null + private var _A_channelModes: MutableMap<Char, MutableSet<String>> = mutableMapOf() private var _B_channelModes: MutableMap<Char, String> = mutableMapOf() private var _C_channelModes: MutableMap<Char, String> = mutableMapOf() diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcUser.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcUser.kt index c50dd64002c6fa9a86f913f5c518d81ad073e4a8..06d7c06bab8fc1a8c0e6e85197ac9207db25c0c2 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcUser.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/IrcUser.kt @@ -7,6 +7,7 @@ import de.kuschku.libquassel.protocol.valueOr import de.kuschku.libquassel.quassel.syncables.interfaces.IIrcUser import de.kuschku.libquassel.session.SignalProxy import de.kuschku.libquassel.util.irc.HostmaskHelper +import io.reactivex.Observable import io.reactivex.subjects.BehaviorSubject import org.threeten.bp.Instant import java.nio.charset.Charset @@ -67,29 +68,70 @@ class IrcUser( setUserModes(properties["userModes"].valueOr(this::userModes)) } + fun nick() = _nick + fun liveNick(): Observable<String> = live_nick + fun user() = _user + fun liveUser(): Observable<String> = live_user + fun host() = _host - fun nick() = _nick + fun liveHost(): Observable<String> = live_host + fun realName() = _realName + fun liveRealName(): Observable<String> = live_realName + fun account() = _account + fun liveAccount(): Observable<String> = live_account + fun hostMask() = "${nick()}!${user()}@${host()}" + fun liveHostMask() = liveNick().switchMap { nick -> + liveUser().switchMap { user -> + liveHost().map { host -> + "$nick!$user@$host" + } + } + } + fun isAway() = _away + fun liveIsAway(): Observable<Boolean> = live_away + fun awayMessage() = _awayMessage + fun liveAwayMessage(): Observable<String> = live_awayMessage + + fun server() = _server + fun liveServer(): Observable<String> = live_server + fun idleTime(): Instant { if (Instant.now().epochSecond - _idleTimeSet.epochSecond > 1200) _idleTime = Instant.EPOCH return _idleTime } + fun liveIdleTime(): Observable<Instant> = live_idleTime + fun loginTime() = _loginTime - fun server() = _server + fun liveLoginTime(): Observable<Instant> = live_loginTime + fun ircOperator() = _ircOperator + fun liveIrcOperator(): Observable<String> = live_ircOperator + fun lastAwayMessage() = _lastAwayMessage + fun liveLastAwayMessage(): Observable<Int> = live_lastAwayMessage + fun whoisServiceReply() = _whoisServiceReply + fun liveWhoisServiceReply(): Observable<String> = live_whoisServiceReply + fun suserHost() = _suserHost + fun liveSuserHost(): Observable<String> = live_suserHost + fun encrypted() = _encrypted + fun liveEncrypted(): Observable<Boolean> = live_encrypted + fun network() = _network + fun userModes() = _userModes + fun liveUserModes(): Observable<String> = live_userModes + fun channels() = _channels.map(IrcChannel::name) fun codecForEncoding() = _codecForEncoding fun codecForDecoding() = _codecForDecoding @@ -119,6 +161,7 @@ class IrcUser( override fun setNick(nick: String) { if (nick.isNotEmpty() && _nick != nick) { + network().ircUserNickChanged(_nick, nick) _nick = nick updateObjectName() super.setNick(nick) @@ -128,7 +171,6 @@ class IrcUser( override fun setRealName(realName: String) { if (_realName != realName) { _realName = realName - live_realName.onNext(realName) super.setRealName(realName) } } @@ -143,7 +185,6 @@ class IrcUser( override fun setAway(away: Boolean) { if (_away != away) { _away = away - live_away.onNext(away) super.setAway(away) } } @@ -268,29 +309,97 @@ class IrcUser( } fun updateObjectName() { - renameObject("${network().networkId()}/$_nick") - } - - private var _nick: String = HostmaskHelper.nick(hostmask) - private var _user: String = HostmaskHelper.user(hostmask) - private var _host: String = HostmaskHelper.host(hostmask) - private var _realName: String = "" - val live_realName = BehaviorSubject.createDefault("") - private var _account: String = "" - private var _awayMessage: String = "" - private var _away: Boolean = false - val live_away = BehaviorSubject.createDefault(false) - private var _server: String = "" - private var _idleTime: Instant = Instant.EPOCH - private var _idleTimeSet: Instant = Instant.EPOCH - private var _loginTime: Instant = Instant.EPOCH - private var _ircOperator: String = "" - private var _lastAwayMessage: Int = 0 - private var _whoisServiceReply: String = "" - private var _suserHost: String = "" - private var _encrypted: Boolean = false + val identifier = "${network().networkId()}/${nick()}" + renameObject(identifier) + } + + private val live_nick = BehaviorSubject.createDefault(HostmaskHelper.nick(hostmask)) + private var _nick: String + get() = live_nick.value + set(value) = live_nick.onNext(value) + + private val live_user = BehaviorSubject.createDefault(HostmaskHelper.user(hostmask)) + private var _user: String + get() = live_user.value + set(value) = live_user.onNext(value) + + private val live_host = BehaviorSubject.createDefault(HostmaskHelper.host(hostmask)) + private var _host: String + get() = live_host.value + set(value) = live_host.onNext(value) + + private val live_realName = BehaviorSubject.createDefault("") + private var _realName: String + get() = live_realName.value + set(value) = live_realName.onNext(value) + + private val live_account = BehaviorSubject.createDefault("") + private var _account: String + get() = live_account.value + set(value) = live_account.onNext(value) + + private val live_awayMessage = BehaviorSubject.createDefault("") + private var _awayMessage: String + get() = live_awayMessage.value + set(value) = live_awayMessage.onNext(value) + + private val live_away = BehaviorSubject.createDefault(false) + private var _away: Boolean + get() = live_away.value + set(value) = live_away.onNext(value) + + private val live_server = BehaviorSubject.createDefault("") + private var _server: String + get() = live_server.value + set(value) = live_server.onNext(value) + + private val live_idleTime = BehaviorSubject.createDefault(Instant.EPOCH) + private var _idleTime: Instant + get() = live_idleTime.value + set(value) = live_idleTime.onNext(value) + + private val live_idleTimeSet = BehaviorSubject.createDefault(Instant.EPOCH) + private var _idleTimeSet: Instant + get() = live_idleTimeSet.value + set(value) = live_idleTimeSet.onNext(value) + + private val live_loginTime = BehaviorSubject.createDefault(Instant.EPOCH) + private var _loginTime: Instant + get() = live_loginTime.value + set(value) = live_loginTime.onNext(value) + + private val live_ircOperator = BehaviorSubject.createDefault("") + private var _ircOperator: String + get() = live_ircOperator.value + set(value) = live_ircOperator.onNext(value) + + private val live_lastAwayMessage = BehaviorSubject.createDefault(0) + private var _lastAwayMessage: Int + get() = live_lastAwayMessage.value + set(value) = live_lastAwayMessage.onNext(value) + + private val live_whoisServiceReply = BehaviorSubject.createDefault("") + private var _whoisServiceReply: String + get() = live_whoisServiceReply.value + set(value) = live_whoisServiceReply.onNext(value) + + private val live_suserHost = BehaviorSubject.createDefault("") + private var _suserHost: String + get() = live_suserHost.value + set(value) = live_suserHost.onNext(value) + + private val live_encrypted = BehaviorSubject.createDefault(false) + private var _encrypted: Boolean + get() = live_encrypted.value + set(value) = live_encrypted.onNext(value) + private var _channels: MutableSet<IrcChannel> = mutableSetOf() - private var _userModes: String = "" + + private val live_userModes = BehaviorSubject.createDefault("") + private var _userModes: String + get() = live_userModes.value + set(value) = live_userModes.onNext(value) + private var _network: Network = network private var _codecForEncoding: Charset? = null private var _codecForDecoding: Charset? = null diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt index 246741dd42659ebab82c3500008bb09877219b5b..e0be80108be439ec05e5a3857ac6a091226277ab 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/Network.kt @@ -262,7 +262,7 @@ class Network constructor( } } - fun supports(param: String) = _supports.contains(param.toUpperCase(Locale.ENGLISH)) + fun supports(param: String) = _supports.contains(param.toUpperCase(Locale.US)) fun support(param: String) = _supports.getOr(param, "") /** * Checks if a given capability is advertised by the server. @@ -274,8 +274,7 @@ class Network constructor( * @param capability Name of capability * @return True if connected and advertised by the server, otherwise false */ - fun capAvailable(capability: String) - = _caps.contains(capability.toLowerCase(Locale.ENGLISH)) + fun capAvailable(capability: String) = _caps.contains(capability.toLowerCase(Locale.US)) /** * Checks if a given capability is acknowledged and active. @@ -283,8 +282,7 @@ class Network constructor( * @param capability Name of capability * @return True if acknowledged (active), otherwise false */ - fun capEnabled(capability: String) - = _capsEnabled.contains(capability.toLowerCase(Locale.ENGLISH)) + fun capEnabled(capability: String) = _capsEnabled.contains(capability.toLowerCase(Locale.US)) /** * Gets the value of an available capability, e.g. for SASL, "EXTERNAL,PLAIN". @@ -292,8 +290,7 @@ class Network constructor( * @param capability Name of capability * @return Value of capability if one was specified, otherwise isEmpty string */ - fun capValue(capability: String) - = _caps.getOr(capability.toLowerCase(Locale.ENGLISH), "") + fun capValue(capability: String) = _caps.getOr(capability.toLowerCase(Locale.US), "") /** * Check if the given authentication mechanism is likely to be supported. @@ -323,7 +320,7 @@ class Network constructor( } fun newIrcUser(hostMask: String, initData: QVariantMap = emptyMap()): IrcUser { - val nick = HostmaskHelper.nick(hostMask).toLowerCase(Locale.ENGLISH) + val nick = HostmaskHelper.nick(hostMask).toLowerCase(Locale.US) val user = ircUser(nick) return if (user == null) { val ircUser = IrcUser(hostMask, this, proxy) @@ -343,7 +340,7 @@ class Network constructor( } } - fun ircUser(nickName: String?) = _ircUsers[nickName?.toLowerCase(Locale.ENGLISH)] + fun ircUser(nickName: String?) = _ircUsers[nickName?.toLowerCase(Locale.US)] fun liveIrcUser(nickName: String?) = live_ircUsers.map { ircUser( nickName @@ -362,7 +359,7 @@ class Network constructor( ircChannel.initialized = true } proxy.synchronize(ircChannel) - _ircChannels[channelName.toLowerCase(Locale.ENGLISH)] = ircChannel + _ircChannels[channelName.toLowerCase(Locale.US)] = ircChannel live_ircChannels.onNext(_ircChannels) super.addIrcChannel(channelName) return ircChannel @@ -371,7 +368,7 @@ class Network constructor( } } - fun ircChannel(channelName: String?) = _ircChannels[channelName?.toLowerCase(Locale.ENGLISH)] + fun ircChannel(channelName: String?) = _ircChannels[channelName?.toLowerCase(Locale.US)] fun liveIrcChannel(channelName: String?) = live_ircChannels.map { ircChannel( channelName @@ -644,12 +641,12 @@ class Network constructor( } override fun addCap(capability: String, value: String) { - _caps[capability.toLowerCase(Locale.ENGLISH)] = value + _caps[capability.toLowerCase(Locale.US)] = value super.addCap(capability, value) } override fun acknowledgeCap(capability: String) { - val lowerCase = capability.toLowerCase(Locale.ENGLISH) + val lowerCase = capability.toLowerCase(Locale.US) if (!_capsEnabled.contains(lowerCase)) return _capsEnabled.add(lowerCase) @@ -657,7 +654,7 @@ class Network constructor( } override fun removeCap(capability: String) { - val lowerCase = capability.toLowerCase(Locale.ENGLISH) + val lowerCase = capability.toLowerCase(Locale.US) if (!_caps.contains(lowerCase)) return _caps.remove(lowerCase) @@ -842,7 +839,7 @@ class Network constructor( } fun updateNickFromMask(mask: String): IrcUser { - val nick = HostmaskHelper.nick(mask).toLowerCase(Locale.ENGLISH) + val nick = HostmaskHelper.nick(mask).toLowerCase(Locale.US) val user = _ircUsers[nick] return if (user != null) { user.updateHostmask(mask) @@ -852,8 +849,11 @@ class Network constructor( } } - override fun ircUserNickChanged(newnick: String) { - throw RuntimeException("Look at this: $newnick") + override fun ircUserNickChanged(old: String, new: String) { + val value = _ircUsers.remove(old.toLowerCase(Locale.US)) + if (value != null) { + _ircUsers[new.toLowerCase(Locale.US)] = value + } } override fun emitConnectionError(error: String) { diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/SyncableObject.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/SyncableObject.kt index 91fa2511423ca450d3c04e8e9fcd2fa9cb24b4be..893561e95d77f134ab44c0d383117c310638eb53 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/SyncableObject.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/SyncableObject.kt @@ -9,20 +9,20 @@ abstract class SyncableObject( ) : ISyncableObject { final override var objectName: String = "" private set - override var identifier: String = "$className:" + override var identifier = Pair(className, objectName) override var initialized: Boolean = false protected fun renameObject(newName: String) { val oldName = objectName if (!initialized) { objectName = newName - identifier = "$className:$objectName" + identifier = Pair(className, objectName) } else if (oldName != newName) { objectName = newName - identifier = "$className:$objectName" + identifier = Pair(className, objectName) proxy.renameObject(this, newName, oldName) } } - override fun toString() = identifier + override fun toString() = "${identifier.first}:${identifier.second}" } diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetwork.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetwork.kt index 02c7d6de8902423d4cadf28a40a516b78b4a1863..9dc0caa42dfc65843dd737dee883a3556120911c 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetwork.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/INetwork.kt @@ -62,7 +62,7 @@ interface INetwork : ISyncableObject { } @Slot - fun ircUserNickChanged(newnick: String) + fun ircUserNickChanged(old: String, new: String) @Slot fun removeCap(capability: String) { diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ISyncableObject.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ISyncableObject.kt index fa2d663addeea8ae46d435ed89f1460ac12a88d9..3c557684324a3d5a21bff236b323700cdbc65645 100644 --- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ISyncableObject.kt +++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/interfaces/ISyncableObject.kt @@ -8,7 +8,7 @@ import de.kuschku.libquassel.session.SignalProxy interface ISyncableObject { val objectName: String - var identifier: String + var identifier: Pair<String, String> val className: String var initialized: Boolean val proxy: SignalProxy diff --git a/lib/src/main/java/de/kuschku/libquassel/session/ObjectStorage.kt b/lib/src/main/java/de/kuschku/libquassel/session/ObjectStorage.kt index c1ad35b6827792e23063cb0d41107e5e4c3dd469..5bb8962dfbd1dd0e047eeedf115d68b9607a2777 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/ObjectStorage.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/ObjectStorage.kt @@ -8,32 +8,55 @@ import de.kuschku.libquassel.quassel.exceptions.ObjectNotFoundException import de.kuschku.libquassel.quassel.syncables.interfaces.ISyncableObject class ObjectStorage(private val proxy: SignalProxy) { - private val objectTree: MutableMap<String, ISyncableObject> = HashMap() + private val objectTree: MutableMap<Pair<String, String>, ISyncableObject> = HashMap() - fun add(obj: ISyncableObject) = objectTree.put(obj.identifier, obj) + fun add(obj: ISyncableObject) { + objectTree[obj.identifier] = obj + if (get(obj.className, obj.objectName) != obj) { + throw IllegalStateException("Object should be existing") + } + } - fun remove(obj: ISyncableObject) = objectTree.remove(obj.identifier) + fun remove(obj: ISyncableObject) { + objectTree.remove(obj.identifier) + if (get(obj.className, obj.objectName) == obj) { + throw IllegalStateException("Object should not be existing") + } + } fun rename(className: String, new: String, old: String) { - val obj = get(className, old) ?: throw ObjectNotFoundException(className, old) - rename(obj, new, old) + val obj = get(className, old) + if (obj != null) { + rename(obj, new, old) + } else { + throw ObjectNotFoundException(className, old) + } } fun rename(obj: ISyncableObject, new: String, old: String) { - objectTree.put("${obj.className}:$new", obj) - objectTree.remove("${obj.className}:$old") - proxy.dispatch( - SignalProxyMessage.RpcCall( - "__objectRenamed__", listOf( - QVariant_(obj.className, Type.QString), QVariant_(new, Type.QString), - QVariant_(old, Type.QString) - ) + objectTree[Pair(obj.className, new)] = obj + objectTree.remove(Pair(obj.className, old), obj) + if (get(obj.className, new) != obj) { + throw IllegalStateException("Object should be existing") + } + if (get(obj.className, old) == obj) { + throw IllegalStateException("Object should not be referenced by the old name") + } + if (proxy.shouldRpc("__objectRenamed__")) { + proxy.dispatch( + SignalProxyMessage.RpcCall( + "__objectRenamed__", + listOf( + QVariant_(obj.className, Type.QString), QVariant_(new, Type.QString), + QVariant_(old, Type.QString) + ) + ) ) - ) + } } fun get(className: QType, objectName: String) = get(className.typeName, objectName) - fun get(className: String, objectName: String) = objectTree["$className:$objectName"] + fun get(className: String, objectName: String) = objectTree[Pair(className, objectName)] fun clear() = objectTree.clear() } diff --git a/lib/src/main/java/de/kuschku/libquassel/session/ProtocolHandler.kt b/lib/src/main/java/de/kuschku/libquassel/session/ProtocolHandler.kt index bf67186a9242d5b5ee6f7931125ab61e6e194a7d..8675961766be81e7957fd9d775153ce4e27ec585 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/ProtocolHandler.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/ProtocolHandler.kt @@ -161,7 +161,7 @@ abstract class ProtocolHandler : SignalProxy, AuthHandler, Closeable { if (!syncableObject.initialized) { if (baseInit) { - toInit.put(syncableObject, mutableListOf()) + toInit[syncableObject] = mutableListOf() totalInitCount++ } dispatch(SignalProxyMessage.InitRequest(syncableObject.className, syncableObject.objectName))